The Events Calendar - Version 5.4.0.3

Version Description

= [5.0] =

Please see the changelog for the complete list of changes in this release. Previous versions of The Events Calendar are not cross-compatible with 5.X add-ons. Remember to always make a backup of your database and files before updating!

Download this release

Release Info

Developer bordoni
Plugin Icon The Events Calendar
Version 5.4.0.3
Comparing to
See all releases

Code changes from version 5.3.2.2 to 5.4.0.3

Files changed (62) hide show
  1. common/lang/tribe-common.pot +8 -10
  2. common/{vendor/faction23/a11y-dialog → node_modules/mt-a11y-dialog}/a11y-dialog.min.js +0 -0
  3. common/src/Tribe/Admin/Activation_Page.php +1 -1
  4. common/src/Tribe/Admin/Notice/WP_Version.php +17 -4
  5. common/src/Tribe/Ajax/Dropdown.php +2 -2
  6. common/src/Tribe/Assets.php +52 -1
  7. common/src/Tribe/Autoloader.php +1 -1
  8. common/src/Tribe/Customizer.php +6 -6
  9. common/src/Tribe/Main.php +1 -1
  10. common/src/Tribe/PUE/Checker.php +1 -1
  11. common/src/Tribe/Service_Providers/Dialog.php +1 -1
  12. common/src/Tribe/Widget/Widget_Abstract.php +32 -8
  13. common/src/admin-views/tabbed-view/tabbed-view.php +13 -1
  14. common/src/admin-views/widgets/components/fields.php +43 -0
  15. {src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/checkbox.php +3 -4
  16. {src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/dropdown.php +3 -4
  17. {src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/fieldset.php +4 -10
  18. {src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/radio.php +3 -4
  19. {src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/section.php +5 -20
  20. {src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/text.php +3 -4
  21. common/src/admin-views/widgets/components/form.php +31 -0
  22. common/src/resources/css/common-full.min.css +1 -1
  23. common/src/resources/css/datatables.min.css +1 -1
  24. common/src/resources/images/mascot.png +0 -0
  25. common/src/resources/js/admin-date-preview.js +2 -2
  26. common/src/resources/js/admin-date-preview.min.js +1 -1
  27. common/src/resources/js/admin/help-page.js +2 -2
  28. common/src/resources/js/admin/url-fragment-scroll.js +1 -1
  29. common/src/resources/js/app-shop.js +1 -1
  30. common/src/resources/js/app-shop.min.js +1 -1
  31. common/src/resources/js/bumpdown.js +32 -29
  32. common/src/resources/js/bumpdown.min.js +1 -1
  33. common/src/resources/js/buttonset.js +1 -1
  34. common/src/resources/js/buttonset.min.js +1 -1
  35. common/src/resources/js/dependency.js +15 -5
  36. common/src/resources/js/dependency.min.js +1 -1
  37. common/src/resources/js/dialog.js +1 -1
  38. common/src/resources/js/dialog.min.js +1 -1
  39. common/src/resources/js/dropdowns.js +7 -3
  40. common/src/resources/js/dropdowns.min.js +1 -1
  41. common/src/resources/js/notice-dismiss.js +15 -14
  42. common/src/resources/js/notice-dismiss.min.js +1 -1
  43. common/src/resources/js/timepicker.js +3 -3
  44. common/src/resources/js/timepicker.min.js +1 -1
  45. common/src/resources/js/tooltip.js +1 -1
  46. common/src/resources/js/tooltip.min.js +1 -1
  47. common/src/resources/js/validation.js +61 -55
  48. common/src/resources/js/validation.min.js +1 -1
  49. common/src/views/dialog/button.php +3 -2
  50. common/vendor/autoload.php +1 -1
  51. common/vendor/autoload_52.php +1 -1
  52. common/vendor/composer/autoload_real.php +4 -4
  53. common/vendor/composer/autoload_real_52.php +3 -3
  54. common/vendor/composer/autoload_static.php +5 -5
  55. common/vendor/composer/installed.json +7 -19
  56. common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_asc.png +0 -0
  57. common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_asc_disabled.png +0 -0
  58. common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_both.png +0 -0
  59. common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_desc.png +0 -0
  60. common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_desc_disabled.png +0 -0
  61. common/vendor/datatables/datatables.css +1279 -0
  62. common/vendor/datatables/datatables.js +27756 -0
common/lang/tribe-common.pot CHANGED
@@ -2,13 +2,13 @@
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.12.16\n"
6
  "Report-Msgid-Bugs-To: http://m.tri.be/191x\n"
7
- "POT-Creation-Date: 2021-01-27 06:50:27+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: 2021-01-27 06:50\n"
12
  "Last-Translator: \n"
13
  "Language-Team: \n"
14
 
@@ -40,6 +40,8 @@ msgstr ""
40
  msgid "Press \"Cmd + C\" to copy"
41
  msgstr ""
42
 
 
 
43
  #: src/Tribe/Admin/Help_Page.php:79 src/Tribe/Customizer.php:625
44
  #: src/Tribe/Plugins_API.php:25
45
  msgid "The Events Calendar"
@@ -219,21 +221,21 @@ msgid ""
219
  "compatible version of The Events Calendar. %2$s"
220
  msgstr ""
221
 
222
- #: src/Tribe/Admin/Notice/WP_Version.php:66
223
  msgid ""
224
  "You are using WordPress 5.7 which included a major jQuery update that may "
225
  "cause compatibility issues with past versions of The Events Calendar, Event "
226
  "Tickets and other plugins."
227
  msgstr ""
228
 
229
- #: src/Tribe/Admin/Notice/WP_Version.php:68
230
  msgid ""
231
  "WordPress 5.7 includes a major jQuery update that may cause compatibility "
232
  "issues with past versions of The Events Calendar, Event Tickets and other "
233
  "plugins."
234
  msgstr ""
235
 
236
- #: src/Tribe/Admin/Notice/WP_Version.php:70
237
  msgid "Read more."
238
  msgstr ""
239
 
@@ -3076,10 +3078,6 @@ msgstr ""
3076
  msgid "An event settings framework for managing shared options"
3077
  msgstr ""
3078
 
3079
- #. Author of the plugin/theme
3080
- msgid "Modern Tribe, Inc."
3081
- msgstr ""
3082
-
3083
  #. Author URI of the plugin/theme
3084
  msgid "http://evnt.is/1x"
3085
  msgstr ""
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.12.18\n"
6
  "Report-Msgid-Bugs-To: http://m.tri.be/191x\n"
7
+ "POT-Creation-Date: 2021-02-24 19:02:51+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: 2021-02-24 19:02\n"
12
  "Last-Translator: \n"
13
  "Language-Team: \n"
14
 
40
  msgid "Press \"Cmd + C\" to copy"
41
  msgstr ""
42
 
43
+ #. #-#-#-#-# tribe-common.pot (Tribe Common 4.12.18) #-#-#-#-#
44
+ #. Author of the plugin/theme
45
  #: src/Tribe/Admin/Help_Page.php:79 src/Tribe/Customizer.php:625
46
  #: src/Tribe/Plugins_API.php:25
47
  msgid "The Events Calendar"
221
  "compatible version of The Events Calendar. %2$s"
222
  msgstr ""
223
 
224
+ #: src/Tribe/Admin/Notice/WP_Version.php:79
225
  msgid ""
226
  "You are using WordPress 5.7 which included a major jQuery update that may "
227
  "cause compatibility issues with past versions of The Events Calendar, Event "
228
  "Tickets and other plugins."
229
  msgstr ""
230
 
231
+ #: src/Tribe/Admin/Notice/WP_Version.php:81
232
  msgid ""
233
  "WordPress 5.7 includes a major jQuery update that may cause compatibility "
234
  "issues with past versions of The Events Calendar, Event Tickets and other "
235
  "plugins."
236
  msgstr ""
237
 
238
+ #: src/Tribe/Admin/Notice/WP_Version.php:83
239
  msgid "Read more."
240
  msgstr ""
241
 
3078
  msgid "An event settings framework for managing shared options"
3079
  msgstr ""
3080
 
 
 
 
 
3081
  #. Author URI of the plugin/theme
3082
  msgid "http://evnt.is/1x"
3083
  msgstr ""
common/{vendor/faction23/a11y-dialog → node_modules/mt-a11y-dialog}/a11y-dialog.min.js RENAMED
File without changes
common/src/Tribe/Admin/Activation_Page.php CHANGED
@@ -221,7 +221,7 @@ class Tribe__Admin__Activation_Page {
221
  * Disused since TEC PR 88 (targeting Tribe__Events__Activation_Page,
222
  * which this class was derived from).
223
  *
224
- * @see https://github.com/moderntribe/the-events-calendar/pull/88
225
  *
226
  * @todo decide whether to reinstate or remove
227
  */
221
  * Disused since TEC PR 88 (targeting Tribe__Events__Activation_Page,
222
  * which this class was derived from).
223
  *
224
+ * @see https://github.com/the-events-calendar/the-events-calendar/pull/88
225
  *
226
  * @todo decide whether to reinstate or remove
227
  */
common/src/Tribe/Admin/Notice/WP_Version.php CHANGED
@@ -8,13 +8,13 @@ use Tribe__Main as Common;
8
  /**
9
  * Various WordPress version notices.
10
  *
11
- * @since TBD
12
  */
13
  class WP_Version {
14
  /**
15
  * Register the various WordPress version notices.
16
  *
17
- * @since TBD
18
  */
19
  public function hook() {
20
  tribe_notice(
@@ -33,13 +33,26 @@ class WP_Version {
33
  /**
34
  * Whether the WordPress 5.7 notice should display.
35
  *
36
- * @since TBD
37
  *
38
  * @return boolean
39
  */
40
  public function wp_version_57_should_display() {
41
  global $wp_version;
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  $wp_version_min_version_required = '5.8';
44
  $common_version_required = '4.12.18-dev';
45
 
@@ -51,7 +64,7 @@ class WP_Version {
51
  /**
52
  * HTML for the WordPress 5.7 notice.
53
  *
54
- * @since TBD
55
  *
56
  * @see https://evnt.is/wp5-7
57
  *
8
  /**
9
  * Various WordPress version notices.
10
  *
11
+ * @since 4.12.17
12
  */
13
  class WP_Version {
14
  /**
15
  * Register the various WordPress version notices.
16
  *
17
+ * @since 4.12.17
18
  */
19
  public function hook() {
20
  tribe_notice(
33
  /**
34
  * Whether the WordPress 5.7 notice should display.
35
  *
36
+ * @since 4.12.17
37
  *
38
  * @return boolean
39
  */
40
  public function wp_version_57_should_display() {
41
  global $wp_version;
42
 
43
+ $screens = [
44
+ 'tribe_events_page_tribe-app-shop', // App shop.
45
+ 'events_page_tribe-app-shop', // App shop.
46
+ 'tribe_events_page_tribe-common', // Settings & Welcome.
47
+ 'events_page_tribe-common', // Settings & Welcome.
48
+ 'toplevel_page_tribe-common', // Settings & Welcome.
49
+ ];
50
+
51
+ // If not a valid screen, don't display.
52
+ if ( empty( $current_screen->id ) || ! in_array( $current_screen->id, $screens, true ) ) {
53
+ return false;
54
+ }
55
+
56
  $wp_version_min_version_required = '5.8';
57
  $common_version_required = '4.12.18-dev';
58
 
64
  /**
65
  * HTML for the WordPress 5.7 notice.
66
  *
67
+ * @since 4.12.17
68
  *
69
  * @see https://evnt.is/wp5-7
70
  *
common/src/Tribe/Ajax/Dropdown.php CHANGED
@@ -97,7 +97,7 @@ class Tribe__Ajax__Dropdown {
97
  /**
98
  * Search for Posts using Select2
99
  *
100
- * @since TBD
101
  *
102
  * @param string|array<string,mixed> $search Search string from Select2.
103
  * @param int $page Page we want when we're dealing with pagination.
@@ -130,7 +130,7 @@ class Tribe__Ajax__Dropdown {
130
  /**
131
  * Formats a given array of posts to be displayed into the Dropdown.js module with SelectWoo.
132
  *
133
- * @since TBD
134
  *
135
  * @param array<WP_Post> $posts
136
  * @param null|int $selected
97
  /**
98
  * Search for Posts using Select2
99
  *
100
+ * @since 4.12.17
101
  *
102
  * @param string|array<string,mixed> $search Search string from Select2.
103
  * @param int $page Page we want when we're dealing with pagination.
130
  /**
131
  * Formats a given array of posts to be displayed into the Dropdown.js module with SelectWoo.
132
  *
133
+ * @since 4.12.17
134
  *
135
  * @param array<WP_Post> $posts
136
  * @param null|int $selected
common/src/Tribe/Assets.php CHANGED
@@ -38,6 +38,51 @@ class Tribe__Assets {
38
  public function __construct() {
39
  // Hook the actual registering of.
40
  add_action( 'init', [ $this, 'register_in_wp' ], 1, 0 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
  /**
@@ -439,8 +484,14 @@ class Tribe__Assets {
439
  'groups' => [],
440
  'version' => $version,
441
  'media' => 'all',
 
 
 
 
442
  'in_footer' => true,
443
  'is_registered' => false,
 
 
444
  'origin_path' => null,
445
  'origin_url' => null,
446
  'origin_name' => null,
@@ -514,7 +565,7 @@ class Tribe__Assets {
514
  $asset->priority = 1;
515
  }
516
 
517
- $is_vendor = strpos( $asset->file, 'vendor/' ) !== false ? true : false;
518
 
519
  // Setup the actual URL.
520
  if ( filter_var( $asset->file, FILTER_VALIDATE_URL ) ) {
38
  public function __construct() {
39
  // Hook the actual registering of.
40
  add_action( 'init', [ $this, 'register_in_wp' ], 1, 0 );
41
+ add_filter( 'script_loader_tag', [ $this, 'filter_tag_async_defer'], 50, 2 );
42
+ }
43
+
44
+ /**
45
+ * Filters the Script tags to attach Async and/or Defer based on the rules we set in our Asset class.
46
+ *
47
+ * @since TBD
48
+ *
49
+ * @param string $tag Tag we are filtering.
50
+ * @param string $handle Which is the ID/Handle of the tag we are about to print.
51
+ *
52
+ * @return string Script tag with the defer and/or async attached.
53
+ */
54
+ public function filter_tag_async_defer( $tag, $handle ) {
55
+ // Only filter for own own filters.
56
+ if ( ! $asset = $this->get( $handle ) ) {
57
+ return $tag;
58
+ }
59
+
60
+ // Bail when not dealing with JS assets.
61
+ if ( 'js' !== $asset->type ) {
62
+ return $tag;
63
+ }
64
+
65
+ // When async and defer are false we bail with the tag.
66
+ if ( ! $asset->defer && ! $asset->async ) {
67
+ return $tag;
68
+ }
69
+
70
+ $tag_has_async = false !== strpos( $tag, ' async ' );
71
+ $tag_has_defer = false !== strpos( $tag, ' defer ' );
72
+ $replacement = '<script ';
73
+
74
+ if ( $asset->async && ! $tag_has_async ) {
75
+ $replacement .= 'async ';
76
+ }
77
+
78
+ if ( $asset->defer && ! $tag_has_defer ) {
79
+ $replacement .= 'defer ';
80
+ }
81
+
82
+ $replacement_src = $replacement . 'src=';
83
+ $replacement_type = $replacement . 'type=';
84
+
85
+ return str_replace( [ '<script src=', '<script type=' ], [ $replacement_src, $replacement_type ], $tag );
86
  }
87
 
88
  /**
484
  'groups' => [],
485
  'version' => $version,
486
  'media' => 'all',
487
+
488
+ 'async' => false,
489
+ 'defer' => false,
490
+
491
  'in_footer' => true,
492
  'is_registered' => false,
493
+
494
+ // Origin related params
495
  'origin_path' => null,
496
  'origin_url' => null,
497
  'origin_name' => null,
565
  $asset->priority = 1;
566
  }
567
 
568
+ $is_vendor = strpos( $asset->file, 'vendor/' ) !== false || strpos( $asset->file, 'node_modules/' ) !== false ? true : false;
569
 
570
  // Setup the actual URL.
571
  if ( filter_var( $asset->file, FILTER_VALIDATE_URL ) ) {
common/src/Tribe/Autoloader.php CHANGED
@@ -53,7 +53,7 @@
53
  * An arrays of arrays each containing absolute paths.
54
  *
55
  * Paths are stored trimming any trailing `/`.
56
- * E.g. `/var/www/tribe-pro/wp-content/plugins/the-event-calendar/src/Tribe`
57
  *
58
  * @var string[][]
59
  */
53
  * An arrays of arrays each containing absolute paths.
54
  *
55
  * Paths are stored trimming any trailing `/`.
56
+ * E.g. `/var/www/tribe-pro/wp-content/plugins/the-events-calendar/src/Tribe`
57
  *
58
  * @var string[][]
59
  */
common/src/Tribe/Customizer.php CHANGED
@@ -116,8 +116,8 @@ final class Tribe__Customizer {
116
 
117
  // front end styles from customizer
118
  add_action( 'tribe_events_pro_widget_render', [ $this, 'inline_style' ], 101 );
119
- add_action( 'wp_print_footer_scripts', [ $this, 'shortcode_inline_style' ], 5 );
120
- add_action( 'wp_print_footer_scripts', [ $this, 'widget_inline_style' ], 5 );
121
 
122
  /**
123
  * Allows filtering the action that will be used to trigger the printing of inline scripts.
@@ -346,14 +346,14 @@ final class Tribe__Customizer {
346
  *
347
  * @param strings Using the following structure: self::has_option( 'section_name', 'setting_name' );
348
  *
349
- * @return boolean Wheter the option exists in the database
350
  */
351
  public function has_option() {
352
  $search = func_get_args();
353
  $option = self::get_option();
354
  $real_option = get_option( $this->ID, [] );
355
 
356
- // Get section and Settign based on keys
357
  $section = reset( $search );
358
  $setting = end( $search );
359
 
@@ -435,7 +435,7 @@ final class Tribe__Customizer {
435
  *
436
  * @since 4.12.15 Added the `$force` parameter to force the print of the style inline.
437
  *
438
- * @param bool $force Whether to ignore the context to try and printe the style inline, or not.
439
  */
440
  public function inline_style( $force = false ) {
441
  // Only load once on front-end.
@@ -567,7 +567,7 @@ final class Tribe__Customizer {
567
  $this->panel = apply_filters( 'tribe_customizer_panel', $this->register_panel(), $this );
568
 
569
  /**
570
- * Filter the Sections within our Panel before they are added to the Cutomize Manager
571
  *
572
  * @since 4.4
573
  *
116
 
117
  // front end styles from customizer
118
  add_action( 'tribe_events_pro_widget_render', [ $this, 'inline_style' ], 101 );
119
+ add_action( 'wp_print_footer_scripts', [ $this, 'shortcode_inline_style' ], 10 );
120
+ add_action( 'wp_print_footer_scripts', [ $this, 'widget_inline_style' ], 10 );
121
 
122
  /**
123
  * Allows filtering the action that will be used to trigger the printing of inline scripts.
346
  *
347
  * @param strings Using the following structure: self::has_option( 'section_name', 'setting_name' );
348
  *
349
+ * @return boolean Whether the option exists in the database
350
  */
351
  public function has_option() {
352
  $search = func_get_args();
353
  $option = self::get_option();
354
  $real_option = get_option( $this->ID, [] );
355
 
356
+ // Get section and Settings based on keys
357
  $section = reset( $search );
358
  $setting = end( $search );
359
 
435
  *
436
  * @since 4.12.15 Added the `$force` parameter to force the print of the style inline.
437
  *
438
+ * @param bool $force Whether to ignore the context to try and print the style inline, or not.
439
  */
440
  public function inline_style( $force = false ) {
441
  // Only load once on front-end.
567
  $this->panel = apply_filters( 'tribe_customizer_panel', $this->register_panel(), $this );
568
 
569
  /**
570
+ * Filter the Sections within our Panel before they are added to the Customize Manager
571
  *
572
  * @since 4.4
573
  *
common/src/Tribe/Main.php CHANGED
@@ -19,7 +19,7 @@ class Tribe__Main {
19
  const OPTIONNAME = 'tribe_events_calendar_options';
20
  const OPTIONNAMENETWORK = 'tribe_events_calendar_network_options';
21
 
22
- const VERSION = '4.12.16';
23
 
24
  const FEED_URL = 'https://theeventscalendar.com/feed/';
25
 
19
  const OPTIONNAME = 'tribe_events_calendar_options';
20
  const OPTIONNAMENETWORK = 'tribe_events_calendar_network_options';
21
 
22
+ const VERSION = '4.12.18';
23
 
24
  const FEED_URL = 'https://theeventscalendar.com/feed/';
25
 
common/src/Tribe/PUE/Checker.php CHANGED
@@ -587,7 +587,7 @@ if ( ! class_exists( 'Tribe__PUE__Checker' ) ) {
587
  var $el = $( this );
588
  var $field = $el.find( 'input' );
589
 
590
- if ( '' === $.trim( $field.val() ) ) {
591
  $el.find( '.license-test-results' ).hide();
592
  }
593
  } );
587
  var $el = $( this );
588
  var $field = $el.find( 'input' );
589
 
590
+ if ( '' === $field.val().trim() ) {
591
  $el.find( '.license-test-results' ).hide();
592
  }
593
  } );
common/src/Tribe/Service_Providers/Dialog.php CHANGED
@@ -83,7 +83,7 @@ class Dialog extends \tad_DI52_ServiceProvider {
83
  tribe_asset(
84
  $main,
85
  'mt-a11y-dialog',
86
- 'vendor/faction23/a11y-dialog/a11y-dialog.js',
87
  [ 'underscore', 'tribe-common' ],
88
  [],
89
  [ 'groups' => 'tribe-dialog' ]
83
  tribe_asset(
84
  $main,
85
  'mt-a11y-dialog',
86
+ 'node_modules/mt-a11y-dialog/a11y-dialog.js',
87
  [ 'underscore', 'tribe-common' ],
88
  [],
89
  [ 'groups' => 'tribe-dialog' ]
common/src/Tribe/Widget/Widget_Abstract.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace Tribe\Widget;
4
 
5
  use Tribe__Utils__Array as Arr;
 
6
 
7
  /**
8
  * The abstract base without Views that all widgets should implement.
@@ -27,7 +28,7 @@ abstract class Widget_Abstract extends \WP_Widget implements Widget_Interface {
27
  *
28
  * @since 4.12.14
29
  *
30
- * @var \Tribe__Template
31
  */
32
  protected $admin_template;
33
 
@@ -49,6 +50,15 @@ abstract class Widget_Abstract extends \WP_Widget implements Widget_Interface {
49
  */
50
  protected $default_arguments = [];
51
 
 
 
 
 
 
 
 
 
 
52
  /**
53
  * Array map allowing aliased widget arguments.
54
  *
@@ -112,7 +122,7 @@ abstract class Widget_Abstract extends \WP_Widget implements Widget_Interface {
112
  *
113
  * @return mixed
114
  */
115
- public abstract function setup();
116
 
117
  /**
118
  * Setup the widget.
@@ -121,7 +131,7 @@ abstract class Widget_Abstract extends \WP_Widget implements Widget_Interface {
121
  *
122
  * @param array<string,mixed> $arguments The widget arguments, as set by the user in the widget string.
123
  */
124
- public abstract function setup_view( $arguments );
125
 
126
  /**
127
  * {@inheritDoc}
@@ -129,7 +139,7 @@ abstract class Widget_Abstract extends \WP_Widget implements Widget_Interface {
129
  public function form( $instance ) {
130
  $arguments = $this->setup_arguments( $instance );
131
 
132
- $this->get_admin_html( $arguments );
133
  }
134
 
135
  /**
@@ -157,7 +167,7 @@ abstract class Widget_Abstract extends \WP_Widget implements Widget_Interface {
157
  *
158
  * @return string
159
  */
160
- public abstract function get_html();
161
 
162
  /**
163
  * {@inheritDoc}
@@ -237,7 +247,7 @@ abstract class Widget_Abstract extends \WP_Widget implements Widget_Interface {
237
  *
238
  * @return array<string,mixed> The array of widget admin fields.
239
  */
240
- protected abstract function setup_admin_fields();
241
 
242
  /**
243
  * {@inheritDoc}
@@ -367,7 +377,6 @@ abstract class Widget_Abstract extends \WP_Widget implements Widget_Interface {
367
  return $arguments;
368
  }
369
 
370
-
371
  /**
372
  * {@inheritDoc}
373
  */
@@ -492,8 +501,23 @@ abstract class Widget_Abstract extends \WP_Widget implements Widget_Interface {
492
  * @since 4.12.14
493
  *
494
  * @param array<string,mixed> $arguments Current set of arguments.
 
 
495
  */
496
  public function get_admin_html( $arguments ) {
497
- $this->get_admin_template()->template( $this->view_admin_slug, $arguments );
 
 
 
 
 
 
 
 
 
 
 
 
 
498
  }
499
  }
3
  namespace Tribe\Widget;
4
 
5
  use Tribe__Utils__Array as Arr;
6
+ use Tribe__Template;
7
 
8
  /**
9
  * The abstract base without Views that all widgets should implement.
28
  *
29
  * @since 4.12.14
30
  *
31
+ * @var Tribe__Template
32
  */
33
  protected $admin_template;
34
 
50
  */
51
  protected $default_arguments = [];
52
 
53
+ /**
54
+ * Default Asset slug prefix for assets associated with this widget.
55
+ *
56
+ * @since 4.12.18
57
+ *
58
+ * @var string
59
+ */
60
+ protected static $asset_slug_prefix;
61
+
62
  /**
63
  * Array map allowing aliased widget arguments.
64
  *
122
  *
123
  * @return mixed
124
  */
125
+ abstract public function setup();
126
 
127
  /**
128
  * Setup the widget.
131
  *
132
  * @param array<string,mixed> $arguments The widget arguments, as set by the user in the widget string.
133
  */
134
+ abstract public function setup_view( $arguments );
135
 
136
  /**
137
  * {@inheritDoc}
139
  public function form( $instance ) {
140
  $arguments = $this->setup_arguments( $instance );
141
 
142
+ return $this->get_admin_html( $arguments );
143
  }
144
 
145
  /**
167
  *
168
  * @return string
169
  */
170
+ abstract public function get_html();
171
 
172
  /**
173
  * {@inheritDoc}
247
  *
248
  * @return array<string,mixed> The array of widget admin fields.
249
  */
250
+ abstract protected function setup_admin_fields();
251
 
252
  /**
253
  * {@inheritDoc}
377
  return $arguments;
378
  }
379
 
 
380
  /**
381
  * {@inheritDoc}
382
  */
501
  * @since 4.12.14
502
  *
503
  * @param array<string,mixed> $arguments Current set of arguments.
504
+ *
505
+ * @return string HTML for the admin fields.
506
  */
507
  public function get_admin_html( $arguments ) {
508
+ return $this->get_admin_template()->template( $this->view_admin_slug, $arguments );
509
+ }
510
+
511
+ /**
512
+ * Get the asset name for this widget, properly prefixing it.
513
+ *
514
+ * @since 4.12.18
515
+ *
516
+ * @param string $append Which should be appended to the prefix.
517
+ *
518
+ * @return string Slug for the asset after appending the prefix.
519
+ */
520
+ public static function get_asset_slug( $append = '' ) {
521
+ return static::$asset_slug_prefix . ( ! empty( $append ) ? '-' . $append : '' );
522
  }
523
  }
common/src/admin-views/tabbed-view/tabbed-view.php CHANGED
@@ -12,7 +12,19 @@ $tabs = $view->get_visibles();
12
  <?php if ( count( $tabs ) > 1 ) : ?>
13
  <div class="tabbed-view-wrap wrap">
14
  <?php if ( $view->get_label() ) : ?>
15
- <h1><?php echo esc_html( $view->get_label() ); ?></h1>
 
 
 
 
 
 
 
 
 
 
 
 
16
  <?php endif; ?>
17
 
18
  <h2 class="nav-tab-wrapper">
12
  <?php if ( count( $tabs ) > 1 ) : ?>
13
  <div class="tabbed-view-wrap wrap">
14
  <?php if ( $view->get_label() ) : ?>
15
+ <h1>
16
+ <?php echo esc_html( $view->get_label() ); ?>
17
+ <?php
18
+ /**
19
+ * Add an action to render content after text label.
20
+ *
21
+ * @since 4.12.17
22
+ *
23
+ * @param Tribe__Tabbed_View $view Tabbed View Object.
24
+ */
25
+ do_action( 'tribe_tabbed_view_heading_after_text_label', $view );
26
+ ?>
27
+ </h1>
28
  <?php endif; ?>
29
 
30
  <h2 class="nav-tab-wrapper">
common/src/admin-views/widgets/components/fields.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin View: Widget Component Fields Container
4
+ *
5
+ * Administration Views cannot be overwritten by default from your theme.
6
+ *
7
+ * See more documentation about our views templating system.
8
+ *
9
+ * @link http://evnt.is/1aiy
10
+ *
11
+ * @var \Tribe__Template $this Instance of the template including this file.
12
+ * @var array<mixed> $fields_container_classes (optional) HTML classes used for the form element
13
+ * @var Widget_Abstract $widget_obj An instance of the widget abstract.
14
+ * @var array<array,mixed> $fields An array of admin fields to display in the widget form.
15
+ *
16
+ * @version 4.12.18
17
+ */
18
+
19
+ use Tribe\Widget\Widget_Abstract;
20
+
21
+ $default_classes = [
22
+ 'tribe-widget-fields'
23
+ ];
24
+
25
+ $classes = array_merge( $default_classes, $this->get( 'fields_container_classes', [] ) );
26
+ ?>
27
+
28
+ <div
29
+ <?php tribe_classes( $classes ); ?>
30
+ >
31
+ <?php
32
+ foreach ( $fields as $field ) {
33
+ // Try to load the component for this field type.
34
+ $this->template( "widgets/components/fields/{$field['type']}", $field );
35
+
36
+ // Sets the current field for possible usage inside of the entry point.
37
+ $this->set_values( [ 'field' => $field ] );
38
+
39
+ // Allow fields that were not registered as components to have something loaded.
40
+ $this->do_entry_point( "field-{$field['type']}" );
41
+ }
42
+ ?>
43
+ </div>
{src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/checkbox.php RENAMED
@@ -1,15 +1,14 @@
1
  <?php
2
  /**
3
- * Admin View: Widget Checkbox Component
4
  *
5
- * Override this template in your own theme by creating a file at:
6
- * [your-theme]/tribe/admin-views/widgets/components/checkbox.php
7
  *
8
  * See more documentation about our views templating system.
9
  *
10
  * @link http://evnt.is/1aiy
11
  *
12
- * @version 5.3.0
13
  *
14
  * @var string $label Label for the checkbox.
15
  * @var string $value Value for the checkbox.
1
  <?php
2
  /**
3
+ * Admin View: Widget Component Checkbox field
4
  *
5
+ * Administration Views cannot be overwritten by default from your theme.
 
6
  *
7
  * See more documentation about our views templating system.
8
  *
9
  * @link http://evnt.is/1aiy
10
  *
11
+ * @version 4.12.18
12
  *
13
  * @var string $label Label for the checkbox.
14
  * @var string $value Value for the checkbox.
{src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/dropdown.php RENAMED
@@ -1,15 +1,14 @@
1
  <?php
2
  /**
3
- * Admin View: Widget Dropdown Component
4
  *
5
- * Override this template in your own theme by creating a file at:
6
- * [your-theme]/tribe/admin-views/widgets/components/dropdown.php
7
  *
8
  * See more documentation about our views templating system.
9
  *
10
  * @link http://evnt.is/1aiy
11
  *
12
- * @version 5.3.0
13
  *
14
  * @var string $label Label for the dropdown.
15
  * @var string $value Value for the dropdown.
1
  <?php
2
  /**
3
+ * Admin View: Widget Component Dropdown field.
4
  *
5
+ * Administration Views cannot be overwritten by default from your theme.
 
6
  *
7
  * See more documentation about our views templating system.
8
  *
9
  * @link http://evnt.is/1aiy
10
  *
11
+ * @version 4.12.18
12
  *
13
  * @var string $label Label for the dropdown.
14
  * @var string $value Value for the dropdown.
{src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/fieldset.php RENAMED
@@ -1,17 +1,16 @@
1
  <?php
2
  /**
3
- * Admin View: Widget Fieldset Component.
4
  *
5
  * This component is different in that it calls other components!
6
  *
7
- * Override this template in your own theme by creating a file at:
8
- * [your-theme]/tribe/admin-views/widgets/components/fieldset.php
9
  *
10
  * See more documentation about our views templating system.
11
  *
12
  * @link http://evnt.is/1aiy
13
  *
14
- * @version 5.3.0
15
  *
16
  * @var string $label Title for the fieldset.
17
  * @var string $description Description for the fieldset.
@@ -37,10 +36,5 @@ $fieldset_classes = array_merge( [ 'tribe-widget-form-control', 'tribe-widget-fo
37
  <legend class="tribe-widget-form-control__legend"><?php echo esc_html( $label ); ?></legend>
38
  <?php endif; ?>
39
 
40
- <?php
41
- foreach ( $children as $child ) {
42
- // The provided name/value are passed through for radios.
43
- $this->template( "widgets/components/{$child['type']}", $child );
44
- }
45
- ?>
46
  </fieldset>
1
  <?php
2
  /**
3
+ * Admin View: Widget Component Fieldset field.
4
  *
5
  * This component is different in that it calls other components!
6
  *
7
+ * Administration Views cannot be overwritten by default from your theme.
 
8
  *
9
  * See more documentation about our views templating system.
10
  *
11
  * @link http://evnt.is/1aiy
12
  *
13
+ * @version 4.12.18
14
  *
15
  * @var string $label Title for the fieldset.
16
  * @var string $description Description for the fieldset.
36
  <legend class="tribe-widget-form-control__legend"><?php echo esc_html( $label ); ?></legend>
37
  <?php endif; ?>
38
 
39
+ <?php $this->template( "widgets/components/fields", [ 'fields' => $children ] ); ?>
 
 
 
 
 
40
  </fieldset>
{src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/radio.php RENAMED
@@ -1,15 +1,14 @@
1
  <?php
2
  /**
3
- * Admin View: Widget Radio Component
4
  *
5
- * Override this template in your own theme by creating a file at:
6
- * [your-theme]/tribe/admin-views/widgets/components/radio.php
7
  *
8
  * See more documentation about our views templating system.
9
  *
10
  * @link http://evnt.is/1aiy
11
  *
12
- * @version 5.3.0
13
  *
14
  * @var string $label Label for the radio group.
15
  * @var string $value Value for the radio group.
1
  <?php
2
  /**
3
+ * Admin View: Widget Component Radio field
4
  *
5
+ * Administration Views cannot be overwritten by default from your theme.
 
6
  *
7
  * See more documentation about our views templating system.
8
  *
9
  * @link http://evnt.is/1aiy
10
  *
11
+ * @version 4.12.18
12
  *
13
  * @var string $label Label for the radio group.
14
  * @var string $value Value for the radio group.
{src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/section.php RENAMED
@@ -1,17 +1,16 @@
1
  <?php
2
  /**
3
- * Admin View: Widget Section Component.
4
  *
5
  * This component is different in that it calls other components!
6
  *
7
- * Override this template in your own theme by creating a file at:
8
- * [your-theme]/tribe/admin-views/widgets/components/section.php
9
  *
10
  * See more documentation about our views templating system.
11
  *
12
  * @link http://evnt.is/1aiy
13
  *
14
- * @version 5.3.0
15
  *
16
  * @var string $label Title for the section. (optional)
17
  * @var string $description Description for the section. (optional)
@@ -33,23 +32,9 @@ $section_classes = array_merge( [ 'tribe-widget-form-control', 'tribe-widget-for
33
  ?>
34
  >
35
  <?php if ( ! empty( $label ) ) : ?>
36
- <?php // Note: the actual widget title/handle is an <h3>. ?>
37
  <h4 class="tribe-widget-form-control__section-title"><?php echo esc_html( $label ); ?></h4>
38
  <?php endif; ?>
39
 
40
- <?php
41
- foreach ( $children as $child_id => $child ) {
42
- $this->template( "widgets/components/{$child['type']}", $child );
43
-
44
- /**
45
- * Allows other plugins to hook in as needed to inject things that aren't necessarily an input.
46
- *
47
- * @since 5.3.0
48
- *
49
- * @param array<array,mixed> $child The child "field" info.
50
- * @var Widget_Abstract $widget_obj An instance of the widget abstract.
51
- */
52
- do_action( "tribe_events_views_v2_widget_admin_form_{$child['type']}_input", $child, $widget_obj );
53
- }
54
- ?>
55
  </div>
1
  <?php
2
  /**
3
+ * Admin View: Widget Component Section field.
4
  *
5
  * This component is different in that it calls other components!
6
  *
7
+ * Administration Views cannot be overwritten by default from your theme.
 
8
  *
9
  * See more documentation about our views templating system.
10
  *
11
  * @link http://evnt.is/1aiy
12
  *
13
+ * @version 4.12.18
14
  *
15
  * @var string $label Title for the section. (optional)
16
  * @var string $description Description for the section. (optional)
32
  ?>
33
  >
34
  <?php if ( ! empty( $label ) ) : ?>
35
+ <?php // Note: the actual widget title/handle is an <h2>. ?>
36
  <h4 class="tribe-widget-form-control__section-title"><?php echo esc_html( $label ); ?></h4>
37
  <?php endif; ?>
38
 
39
+ <?php $this->template( "widgets/components/fields", [ 'fields' => $children ] ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  </div>
{src/admin-views/widgets/components → common/src/admin-views/widgets/components/fields}/text.php RENAMED
@@ -1,15 +1,14 @@
1
  <?php
2
  /**
3
- * Admin View: Widget Text Component
4
  *
5
- * Override this template in your own theme by creating a file at:
6
- * [your-theme]/tribe/admin-views/widgets/components/text.php
7
  *
8
  * See more documentation about our views templating system.
9
  *
10
  * @link http://evnt.is/1aiy
11
  *
12
- * @version 5.3.0
13
  *
14
  * @var string $label Label for the text input.
15
  * @var string $value Value for the text input.
1
  <?php
2
  /**
3
+ * Admin View: Widget Component Text field
4
  *
5
+ * Administration Views cannot be overwritten by default from your theme.
 
6
  *
7
  * See more documentation about our views templating system.
8
  *
9
  * @link http://evnt.is/1aiy
10
  *
11
+ * @version 4.12.18
12
  *
13
  * @var string $label Label for the text input.
14
  * @var string $value Value for the text input.
common/src/admin-views/widgets/components/form.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin View: Widget Component Form
4
+ *
5
+ * Override this template in your own theme by creating a file at:
6
+ * [your-theme]/tribe/admin-views/widgets/components/form.php
7
+ *
8
+ * See more documentation about our views templating system.
9
+ *
10
+ * @link http://evnt.is/1aiy
11
+ *
12
+ * @var \Tribe__Template $this Instance of the template including this file.
13
+ * @var array<mixed> $form_classes (optional) HTML classes used for the form element
14
+ * @var array<string,mixed> $admin_fields Fields to be rendered.
15
+ *
16
+ * @version 4.12.18
17
+ */
18
+
19
+ $default_classes = [
20
+ 'tribe-widget-form'
21
+ ];
22
+
23
+ $classes = array_merge( $default_classes, $this->get( 'form_classes', [] ) );
24
+ ?>
25
+
26
+ <div
27
+ <?php tribe_classes( $classes ); ?>
28
+ >
29
+ <?php $this->template( 'widgets/components/fields', [ 'fields' => $admin_fields ] ); ?>
30
+ </div>
31
+
common/src/resources/css/common-full.min.css CHANGED
@@ -1 +1 @@
1
- .tribe-common figure{line-height:0}.tribe-common figcaption{line-height:normal}.tribe-common a{background-color:transparent;-webkit-text-decoration-skip:objects}.tribe-common abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.tribe-common code,.tribe-common kbd,.tribe-common pre,.tribe-common samp{font-family:monospace;font-size:1em}.tribe-common b,.tribe-common strong{font-weight:inherit;font-weight:bolder}.tribe-common dfn{font-style:italic}.tribe-common mark{background-color:#ff0;color:#000}.tribe-common small{font-size:80%}.tribe-common sub,.tribe-common sup{font-size:75%;line-height:0}.tribe-common hr{border:0;height:0}.tribe-common button,.tribe-common input[type=button],.tribe-common input[type=email],.tribe-common input[type=password],.tribe-common input[type=reset],.tribe-common input[type=search],.tribe-common input[type=submit],.tribe-common input[type=text],.tribe-common input[type=url],.tribe-common textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none}.tribe-common button,.tribe-common input,.tribe-common optgroup,.tribe-common select,.tribe-common textarea{color:inherit;font:inherit;line-height:normal;-webkit-font-smoothing:antialiased}.tribe-common button,.tribe-common input,.tribe-common select,.tribe-common textarea{outline:0;border-radius:0}.tribe-common select:-moz-focusring{color:transparent;text-shadow:0 0 0 #000}.tribe-common optgroup{font-weight:700}.tribe-common h1,.tribe-common h2,.tribe-common h3,.tribe-common h4,.tribe-common h5,.tribe-common h6,.tribe-common p{font-weight:400;text-rendering:optimizeLegibility}#top .main_color .tribe-common button[disabled],#top.tribe-theme-enfold .tribe-common button[disabled]{opacity:1}.tribe-theme-twentynineteen .tribe-common h1:before,.tribe-theme-twentynineteen .tribe-common h2:before{content:none}.tribe-theme-twentynineteen .tribe-common button,.tribe-theme-twentynineteen .tribe-common input[type=button],.tribe-theme-twentynineteen .tribe-common input[type=reset],.tribe-theme-twentynineteen .tribe-common input[type=submit]{outline:none}.tribe-theme-twentynineteen .tribe-common td,.tribe-theme-twentynineteen .tribe-common th{word-break:normal}.tribe-theme-twentyseventeen .tribe-common h5{letter-spacing:normal;text-transform:none}.tribe-theme-twentyseventeen .tribe-common input[type=text]{border-radius:0}.tribe-theme-twentytwenty .tribe-common{background-color:#fff;letter-spacing:normal}.tribe-theme-twentytwenty .tribe-common input,.tribe-theme-twentytwenty .tribe-common textarea{letter-spacing:normal}.tribe-theme-twentytwenty .tribe-common *{word-break:normal}.tribe-common .tribe-common-form-control-checkbox,.tribe-common .tribe-common-form-control-radio{line-height:0}.tribe-common .tribe-common-form-control-checkbox__label,.tribe-common .tribe-common-form-control-radio__label{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;line-height:1.62;font-weight:400}.tribe-common .tribe-common-form-control-checkbox__label:hover,.tribe-common .tribe-common-form-control-radio__label:hover{opacity:.8}.tribe-common .tribe-common-form-control-checkbox__input,.tribe-common .tribe-common-form-control-radio__input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border:1px solid #141827;height:20px;position:relative;width:20px}.tribe-common .tribe-common-form-control-checkbox__input:active,.tribe-common .tribe-common-form-control-checkbox__input:focus,.tribe-common .tribe-common-form-control-checkbox__input:hover,.tribe-common .tribe-common-form-control-radio__input:active,.tribe-common .tribe-common-form-control-radio__input:focus,.tribe-common .tribe-common-form-control-radio__input:hover{border-color:#141827;opacity:.8}.tribe-common .tribe-common-form-control-checkbox__input:checked,.tribe-common .tribe-common-form-control-radio__input:checked{background-color:#141827}.tribe-common .tribe-common-form-control-checkbox__input{border-radius:4px}.tribe-common .tribe-common-form-control-checkbox__input:checked:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.6.1L3.9 6.8 1.4 4.3c-.1-.1-.3-.1-.4 0l-.8.8c-.1.1-.1.3 0 .4l3.4 3.4c.2.1.4.1.5 0l7.7-7.7c.1-.1.1-.3 0-.4L11 .1c-.1-.1-.3-.1-.4 0z' fill='%23fff'/%3E%3C/svg%3E");background-repeat:no-repeat;background-size:contain;content:"";display:block;height:9px;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:12px}.tribe-common .tribe-common-form-control-checkbox__input:focus+.tribe-common-form-control-checkbox__label,.tribe-common .tribe-common-form-control-checkbox__input:hover+.tribe-common-form-control-checkbox__label{opacity:.8}.tribe-common .tribe-common-form-control-radio__input{border-radius:50%}.tribe-common .tribe-common-form-control-radio__input:checked:before{background-color:#fff;border-radius:50%;content:"";display:block;height:8px;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:8px}.tribe-common .tribe-common-form-control-radio__input:focus+.tribe-common-form-control-radio__label,.tribe-common .tribe-common-form-control-radio__input:hover+.tribe-common-form-control-radio__label{opacity:.8}.tribe-theme-twentytwenty .tribe-common .tribe-common-form-control-checkbox__input:checked:before{margin:0}#top .main_color .tribe-common .tribe-common-form-control-checkbox__label,#top .main_color .tribe-common .tribe-common-form-control-radio__label,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-checkbox__label,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-radio__label{font-weight:400;font-size:14px}.tribe-common .tribe-common-form-control-slider{line-height:0}.tribe-common .tribe-common-form-control-slider__input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0}.tribe-common .tribe-common-form-control-slider__input::-webkit-slider-runnable-track{border:none;border-radius:5px;height:10px;margin:5px 0;padding:0;position:relative;transition:background-color .2s ease;background-color:#334aff}.tribe-common .tribe-common-form-control-slider__input::-moz-range-track{border:none;border-radius:5px;height:10px;margin:5px 0;padding:0;position:relative;transition:background-color .2s ease;background-color:#334aff}.tribe-common .tribe-common-form-control-slider__input::-ms-track{background-color:transparent;border-color:transparent;border-width:5px 0;color:transparent;height:10px}.tribe-common .tribe-common-form-control-slider__input::-ms-fill-lower,.tribe-common .tribe-common-form-control-slider__input::-ms-fill-upper{background-color:#334aff;border-radius:10px}.tribe-common .tribe-common-form-control-slider__input::-webkit-slider-thumb{background-color:#fff;border:1px solid #7d7d7d;border-radius:50%;box-shadow:0 2px 5px 0 rgba(0,0,0,.14);height:20px;width:20px;margin-top:-5px;-webkit-appearance:none;appearance:none}.tribe-common .tribe-common-form-control-slider__input::-moz-range-thumb{background-color:#fff;border:1px solid #7d7d7d;border-radius:50%;box-shadow:0 2px 5px 0 rgba(0,0,0,.14);height:20px;width:20px;margin-top:-5px}.tribe-common .tribe-common-form-control-slider__input::-ms-thumb{background-color:#fff;border:1px solid #7d7d7d;border-radius:50%;box-shadow:0 2px 5px 0 rgba(0,0,0,.14);height:20px;width:20px;margin-top:-5px;box-shadow:none;margin-top:-1px}.tribe-common .tribe-common-form-control-slider__label{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;line-height:1.38;font-weight:400;color:#5d5d5d}#top .main_color .tribe-common .tribe-common-form-control-slider__label,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-slider__label{font-weight:400;font-size:12px}.tribe-common .tribe-common-form-control-text__input{font-size:16px;border:0;border-bottom:1px solid #d5d5d5}.tribe-common--breakpoint-medium.tribe-common .tribe-common-form-control-text__input,.tribe-common .tribe-common-form-control-text__input{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;line-height:1.62;font-weight:400}.tribe-common--breakpoint-medium.tribe-common .tribe-common-form-control-text__input{font-size:14px;border:0}.tribe-common .tribe-common-form-control-text__input::-webkit-input-placeholder{color:#5d5d5d;font-style:normal;opacity:1}.tribe-common .tribe-common-form-control-text__input::-moz-placeholder{color:#5d5d5d;font-style:normal;opacity:1}.tribe-common .tribe-common-form-control-text__input:-ms-input-placeholder,.tribe-common .tribe-common-form-control-text__input::-ms-input-placeholder{color:#5d5d5d;font-style:normal;opacity:1}.tribe-common .tribe-common-form-control-text__input::placeholder{color:#5d5d5d;font-style:normal;opacity:1}.tribe-common .tribe-common-form-control-text__input:focus{border-bottom-color:#141827;outline:0}.tribe-theme-twentyseventeen .tribe-common .tribe-common-form-control-text__input{color:#141827}.tribe-theme-twentytwenty .tribe-common .tribe-common-form-control-text__input{line-height:inherit}#top .main_color .tribe-common .tribe-common-form-control-text__input,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-text__input{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:16px;line-height:1.62;font-weight:400;background:#fff;border:0;border-bottom:1px solid #d5d5d5}#top .main_color .tribe-common .tribe-common-form-control-text__input:focus,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-text__input:focus{border-bottom-color:#141827;box-shadow:none}#top .main_color .tribe-common.tribe-common--breakpoint-medium .tribe-common-form-control-text__input,#top.tribe-theme-enfold .tribe-common.tribe-common--breakpoint-medium .tribe-common-form-control-text__input{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;line-height:1.62;font-weight:400;border:0}.tribe-common .tribe-common-form-control-toggle{line-height:0;position:relative}.tribe-common .tribe-common-form-control-toggle__input{border:none;border-radius:5px;height:10px;margin:5px 0;padding:0;position:relative;transition:background-color .2s ease;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#7d7d7d;width:40px}.tribe-common .tribe-common-form-control-toggle__input::-ms-check{display:none}.tribe-common .tribe-common-form-control-toggle__input+label:before{background-color:#fff;border:1px solid #7d7d7d;border-radius:50%;box-shadow:0 2px 5px 0 rgba(0,0,0,.14);height:20px;width:20px;content:"";left:0;position:absolute;top:0;transition:transform .2s ease}.tribe-common .tribe-common-form-control-toggle__input:checked{background-color:#334aff}.tribe-common .tribe-common-form-control-toggle__input:checked+label:before{transform:translateX(20px)}.tribe-common .tribe-common-form-control-toggle__label{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;line-height:1.38;font-weight:400;color:#5d5d5d}#top .main_color .tribe-common .tribe-common-form-control-toggle__label,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-toggle__label{font-weight:400;font-size:12px}.tribe-theme-twentytwenty .tribe-common .tribe-common-form-control-toggle__input:checked:before{content:none}.tribe-common a,.tribe-common a:active,.tribe-common a:focus,.tribe-common a:hover,.tribe-common a:visited{color:#141827;outline:0;text-decoration:none}.site-footer .widget-area .tribe-common a,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common a,.tribe-theme-twentyseventeen .tribe-common a{box-shadow:none}.site-footer .widget-area .tribe-common a:focus,.site-footer .widget-area .tribe-common a:hover,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common a:focus,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common a:hover,.tribe-theme-twentyseventeen .tribe-common a:focus,.tribe-theme-twentyseventeen .tribe-common a:hover{box-shadow:none;color:#141827}.tribe-theme-twentynineteen .entry .tribe-common a,.tribe-theme-twentynineteen .tribe-common a{text-decoration:none}.main_color .sidebar .tribe-common a,.main_color .sidebar .tribe-common a:active,.main_color .sidebar .tribe-common a:focus,.main_color .sidebar .tribe-common a:hover,.main_color .sidebar .tribe-common a:visited,.tribe-theme-enfold .tribe-common a,.tribe-theme-enfold .tribe-common a:active,.tribe-theme-enfold .tribe-common a:focus,.tribe-theme-enfold .tribe-common a:hover,.tribe-theme-enfold .tribe-common a:visited{color:#141827}.tribe-common .tribe-common-anchor{border-bottom:2px solid transparent;transition:border-color .2s ease}.tribe-common .tribe-common-anchor:active,.tribe-common .tribe-common-anchor:focus,.tribe-common .tribe-common-anchor:hover{border-bottom:2px solid #141827}.tribe-common .tribe-common-anchor-alt{border-bottom:2px solid #334aff;color:#141827;transition:color .2s ease}.tribe-common .tribe-common-anchor-alt:active,.tribe-common .tribe-common-anchor-alt:focus,.tribe-common .tribe-common-anchor-alt:hover{border-bottom:2px solid #334aff;color:#334aff}.tribe-common .tribe-common-anchor-thin{border-bottom:1px solid transparent;transition:border-color .2s ease}.tribe-common .tribe-common-anchor-thin:active,.tribe-common .tribe-common-anchor-thin:focus,.tribe-common .tribe-common-anchor-thin:hover{border-bottom:1px solid #141827}.tribe-common .tribe-common-anchor-thin-alt{border-bottom:1px solid #334aff;color:#141827;transition:color .2s ease}.tribe-common .tribe-common-anchor-thin-alt:active,.tribe-common .tribe-common-anchor-thin-alt:focus,.tribe-common .tribe-common-anchor-thin-alt:hover{border-bottom:1px solid #334aff;color:#334aff}.tribe-theme-twentyseventeen .tribe-common .tribe-common-anchor-alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-anchor-alt:hover,.tribe-theme-twentyseventeen .tribe-common .tribe-common-anchor-thin-alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-anchor-thin-alt:hover{color:#334aff}.site-footer .widget-area .tribe-common .tribe-common-anchor,.site-footer .widget-area .tribe-common .tribe-common-anchor-thin,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common .tribe-common-anchor,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common .tribe-common-anchor-thin{transition:border-color .2s ease}.site-footer .widget-area .tribe-common .tribe-common-anchor-alt,.site-footer .widget-area .tribe-common .tribe-common-anchor-thin-alt,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common .tribe-common-anchor-alt,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common .tribe-common-anchor-thin-alt{transition:color .2s ease}.tribe-common .tribe-common-b1{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1.62}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b1{font-size:16px;line-height:1.62}.tribe-common .tribe-common-b1--bold{font-weight:700}.tribe-common .tribe-common-b2{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;font-weight:400;line-height:1.38}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b2{font-size:14px;line-height:1.62}.tribe-common .tribe-common-b2--bold{font-weight:700}.tribe-common .tribe-common-b3{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:11px;font-weight:400;line-height:1.5}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b3{font-size:12px;line-height:1.38}.tribe-common .tribe-common-b3--bold{font-weight:700}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b1--min-medium{font-size:16px;line-height:1.62}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b2--min-medium{font-size:14px;line-height:1.62}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b3--min-medium{font-size:12px;line-height:1.38}.tribe-common .tribe-common-cta{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;line-height:1.38;font-weight:400;font-weight:700;border-bottom:2px solid transparent;transition:border-color .2s ease}.tribe-common .tribe-common-cta:active,.tribe-common .tribe-common-cta:focus,.tribe-common .tribe-common-cta:hover{border-bottom:2px solid #141827}.tribe-common .tribe-common-cta--alt{border-bottom:2px solid #334aff;color:#141827;transition:color .2s ease}.tribe-common .tribe-common-cta--alt:active,.tribe-common .tribe-common-cta--alt:focus,.tribe-common .tribe-common-cta--alt:hover{border-bottom:2px solid #334aff;color:#334aff}.tribe-common .tribe-common-cta--thin{border-bottom:1px solid transparent;transition:border-color .2s ease}.tribe-common .tribe-common-cta--thin:active,.tribe-common .tribe-common-cta--thin:focus,.tribe-common .tribe-common-cta--thin:hover{border-bottom:1px solid #141827}.tribe-common .tribe-common-cta--thin-alt{border-bottom:1px solid #334aff;color:#141827;transition:color .2s ease}.tribe-common .tribe-common-cta--thin-alt:active,.tribe-common .tribe-common-cta--thin-alt:focus,.tribe-common .tribe-common-cta--thin-alt:hover{border-bottom:1px solid #334aff;color:#334aff}.tribe-theme-twentyseventeen .tribe-common .tribe-common-cta--alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-cta--alt:hover,.tribe-theme-twentyseventeen .tribe-common .tribe-common-cta--thin-alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-cta--thin-alt:hover{color:#334aff}.tribe-common .tribe-common-h1{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:28px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h1{font-size:42px;line-height:1.38}.tribe-common .tribe-common-h2{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:24px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h2{font-size:32px;line-height:1.38}.tribe-common .tribe-common-h3{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:22px;line-height:1.5}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h3{font-size:28px;line-height:1.42}.tribe-common .tribe-common-h4{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:20px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h4{font-size:24px;line-height:1.42}.tribe-common .tribe-common-h5{font-size:18px}.tribe-common .tribe-common-h5,.tribe-common .tribe-common-h6{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;line-height:1.5}.tribe-common .tribe-common-h6{font-size:16px}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h6{font-size:16px;line-height:1.62}.tribe-common .tribe-common-h7{font-size:14px;line-height:1.62}.tribe-common .tribe-common-h7,.tribe-common .tribe-common-h8{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700}.tribe-common .tribe-common-h8{font-size:12px;line-height:1.38}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h3--min-medium{font-size:28px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h4--min-medium{font-size:24px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h5--min-medium{font-size:18px;line-height:1.5}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h6--min-medium{font-size:16px;line-height:1.62}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h7--min-medium{font-size:14px;line-height:1.62}.tribe-common .tribe-common-h--alt{font-weight:400}.tribe-theme-avada #main .tribe-common .tribe-common-h1{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:28px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h1{font-size:42px;line-height:1.38}.tribe-theme-avada #main .tribe-common .tribe-common-h2{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:24px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h2{font-size:32px;line-height:1.38}.tribe-theme-avada #main .tribe-common .tribe-common-h3{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:22px;line-height:1.5}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h3{font-size:28px;line-height:1.42}.tribe-theme-avada #main .tribe-common .tribe-common-h4{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:20px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h4{font-size:24px;line-height:1.42}.tribe-theme-avada #main .tribe-common .tribe-common-h5{font-size:18px}.tribe-theme-avada #main .tribe-common .tribe-common-h5,.tribe-theme-avada #main .tribe-common .tribe-common-h6{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;line-height:1.5}.tribe-theme-avada #main .tribe-common .tribe-common-h6{font-size:16px}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h6{font-size:16px;line-height:1.62}.tribe-theme-avada #main .tribe-common .tribe-common-h7{font-size:14px;line-height:1.62}.tribe-theme-avada #main .tribe-common .tribe-common-h7,.tribe-theme-avada #main .tribe-common .tribe-common-h8{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700}.tribe-theme-avada #main .tribe-common .tribe-common-h8{font-size:12px;line-height:1.38}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h3--min-medium{font-size:28px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h4--min-medium{font-size:24px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h5--min-medium{font-size:18px;line-height:1.5}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h6--min-medium{font-size:16px;line-height:1.62}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h7--min-medium{font-size:14px;line-height:1.62}.tribe-theme-avada #main .tribe-common .tribe-common-h--alt{font-weight:400}.tribe-common button{border:none}.tribe-common button,.tribe-common button:focus,.tribe-common button:hover,.tribe-theme-twentyseventeen .tribe-common button:focus,.tribe-theme-twentyseventeen .tribe-common button:hover{background-color:transparent}.tribe-theme-twentytwenty .tribe-common button{background-color:transparent;text-transform:inherit}.tribe-theme-twentytwenty .tribe-common button:focus,.tribe-theme-twentytwenty .tribe-common button:hover{text-decoration:none}.tribe-theme-enfold .tribe-common th{letter-spacing:0;text-transform:none}.tribe-common .tribe-common-c-btn-border,.tribe-common a.tribe-common-c-btn-border{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;line-height:1.62;font-weight:400;font-weight:700;border:0;cursor:pointer;display:inline-block;height:auto;padding:0;text-decoration:none;width:auto;background-color:#fff;border:1px solid #334aff;border-radius:4px;text-align:center;transition:all .2s ease;color:#334aff;padding:11px 20px;width:100%}.tribe-common--breakpoint-medium.tribe-common .tribe-common-c-btn-border,.tribe-common--breakpoint-medium.tribe-common a.tribe-common-c-btn-border{width:auto}.tribe-common .tribe-common-c-btn-border:focus,.tribe-common .tribe-common-c-btn-border:hover,.tribe-common a.tribe-common-c-btn-border:focus,.tribe-common a.tribe-common-c-btn-border:hover{color:#fff;background-color:#334aff}.tribe-common .tribe-common-c-btn-border:active,.tribe-common a.tribe-common-c-btn-border:active{opacity:.9}.tribe-common .tribe-common-c-btn-border--secondary,.tribe-common a.tribe-common-c-btn-border--secondary{border-color:#141827;color:#141827}.tribe-common .tribe-common-c-btn-border--secondary:focus,.tribe-common .tribe-common-c-btn-border--secondary:hover,.tribe-common a.tribe-common-c-btn-border--secondary:focus,.tribe-common a.tribe-common-c-btn-border--secondary:hover{background-color:#141827}.tribe-common .tribe-common-c-btn-border--secondary:active,.tribe-common a.tribe-common-c-btn-border--secondary:active{opacity:.9}.tribe-common .tribe-common-c-btn-border--alt,.tribe-common a.tribe-common-c-btn-border--alt{border-color:#e4e4e4;color:#141827;font-weight:400}.tribe-common .tribe-common-c-btn-border--alt:focus,.tribe-common .tribe-common-c-btn-border--alt:hover,.tribe-common a.tribe-common-c-btn-border--alt:focus,.tribe-common a.tribe-common-c-btn-border--alt:hover{border-color:#141827;background-color:#fff;color:#141827}.tribe-common .tribe-common-c-btn-border--alt:active,.tribe-common a.tribe-common-c-btn-border--alt:active{opacity:.9}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border:hover{background-color:#334aff}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border--secondary:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border--secondary:hover{background-color:#141827}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border--alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border--alt:hover{background-color:#fff}.tribe-common .tribe-common-c-btn-border-small,.tribe-common a.tribe-common-c-btn-border-small{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;line-height:1.38;font-weight:400;border:0;cursor:pointer;display:inline-block;height:auto;padding:0;text-decoration:none;width:auto;background-color:#fff;border:1px solid #d5d5d5;border-radius:4px;text-align:center;transition:color .2s ease,border-color .2s ease}.tribe-common .tribe-common-c-btn-border-small:focus,.tribe-common .tribe-common-c-btn-border-small:hover,.tribe-common a.tribe-common-c-btn-border-small:focus,.tribe-common a.tribe-common-c-btn-border-small:hover{background-color:#fff}.tribe-common .tribe-common-c-btn-border-small:active,.tribe-common a.tribe-common-c-btn-border-small:active{border-color:#141827}.tribe-common .tribe-common-c-btn-border-small,.tribe-common a.tribe-common-c-btn-border-small{color:#5d5d5d;padding:14px 20px;width:100%}.tribe-common--breakpoint-medium.tribe-common .tribe-common-c-btn-border-small,.tribe-common--breakpoint-medium.tribe-common a.tribe-common-c-btn-border-small{padding:6px 15px;width:auto}.tribe-common .tribe-common-c-btn-border-small:active,.tribe-common .tribe-common-c-btn-border-small:focus,.tribe-common .tribe-common-c-btn-border-small:hover,.tribe-common a.tribe-common-c-btn-border-small:active,.tribe-common a.tribe-common-c-btn-border-small:focus,.tribe-common a.tribe-common-c-btn-border-small:hover{color:#141827}.tribe-common .tribe-common-c-btn-border-small:disabled,.tribe-common a.tribe-common-c-btn-border-small:disabled{color:#d5d5d5}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border-small:hover{background-color:#fff}.tribe-common .tribe-common-c-btn-icon{border:0;cursor:pointer;display:inline-block;height:auto;padding:0;text-decoration:none;width:auto}.tribe-common .tribe-common-c-btn-icon--caret-left:active .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-left:focus .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-left:hover .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right:active .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right:focus .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right:hover .tribe-common-c-btn-icon__icon-svg path{fill:#5d5d5d}.tribe-common .tribe-common-c-btn-icon--caret-left:disabled .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right:disabled .tribe-common-c-btn-icon__icon-svg path{fill:#d5d5d5}.tribe-common .tribe-common-c-btn-icon--caret-left .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right .tribe-common-c-btn-icon__icon-svg path{fill:#bababa}.tribe-common .tribe-common-c-btn-icon--border{align-items:center;background-color:#fff;border:1px solid #d5d5d5;display:inline-flex;height:56px;justify-content:center;transition:none;width:56px}.tribe-common .tribe-common-c-btn-icon--border:focus,.tribe-common .tribe-common-c-btn-icon--border:hover{background-color:#fff}.tribe-common .tribe-common-c-btn-icon--border:active{border-color:#141827}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-icon--border:hover{background-color:#fff}.tribe-common .tribe-common-c-btn,.tribe-common a.tribe-common-c-btn{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;line-height:1.62;font-weight:400;font-weight:700;border:0;cursor:pointer;display:inline-block;height:auto;padding:0;text-decoration:none;width:auto;border-radius:4px;color:#fff;text-align:center;transition:background-color .2s ease;background-color:#334aff;padding:11px 20px;width:100%}.tribe-common--breakpoint-medium.tribe-common .tribe-common-c-btn,.tribe-common--breakpoint-medium.tribe-common a.tribe-common-c-btn{width:auto}.tribe-common .tribe-common-c-btn:focus,.tribe-common .tribe-common-c-btn:hover,.tribe-common a.tribe-common-c-btn:focus,.tribe-common a.tribe-common-c-btn:hover{background-color:rgba(51,74,255,.8)}.tribe-common .tribe-common-c-btn:active,.tribe-common a.tribe-common-c-btn:active{background-color:rgba(51,74,255,.9)}.tribe-common .tribe-common-c-btn:disabled,.tribe-common a.tribe-common-c-btn:disabled{background-color:rgba(51,74,255,.07)}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn:hover{background-color:rgba(51,74,255,.8);color:#fff}.tribe-theme-twentytwenty .tribe-common .tribe-common-c-btn{background-color:#334aff}.tribe-theme-twentytwenty .tribe-common .tribe-common-c-btn:focus,.tribe-theme-twentytwenty .tribe-common .tribe-common-c-btn:hover{background-color:rgba(51,74,255,.8);color:#fff}.tribe-common .tribe-common-c-loader__dot circle{animation-name:a;animation-duration:2.24s;animation-iteration-count:infinite;animation-direction:normal;fill:currentColor;opacity:.07}.tribe-common .tribe-common-c-loader__dot--first circle{animation-delay:.45s}.tribe-common .tribe-common-c-loader__dot--second circle{animation-delay:1.05s}.tribe-common .tribe-common-c-loader__dot--third circle{animation-delay:1.35s}@keyframes a{50%{opacity:1}}.tribe-common .tribe-common-c-svgicon{color:#334aff}.tribe-common .tribe-common-c-svgicon--featured path{fill:currentColor}.tribe-common .tribe-common-c-svgicon--recurring path{fill:#141827;stroke:#141827}.tribe-common .tribe-common-c-svgicon--close-alt path,.tribe-common .tribe-common-c-svgicon--close path{stroke:#bababa}.tribe-common .tribe-common-c-svgicon--messages-not-found path{stroke:#141827}.tribe-common .tribe-common-c-svgicon--messages-not-found .tribe-common-c-svgicon__svg-stroke{stroke:currentColor}.tribe-common .tribe-common-c-svgicon__svg-fill{fill:#141827}.tribe-common .tribe-common-c-svgicon__svg-stroke{stroke:#141827}
1
+ .tribe-common figure{line-height:0}.tribe-common figcaption{line-height:normal}.tribe-common a{background-color:transparent;-webkit-text-decoration-skip:objects}.tribe-common abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.tribe-common code,.tribe-common kbd,.tribe-common pre,.tribe-common samp{font-family:monospace;font-size:1em}.tribe-common b,.tribe-common strong{font-weight:inherit;font-weight:bolder}.tribe-common dfn{font-style:italic}.tribe-common mark{background-color:#ff0;color:#000}.tribe-common small{font-size:80%}.tribe-common sub,.tribe-common sup{font-size:75%;line-height:0}.tribe-common hr{border:0;height:0}.tribe-common button,.tribe-common input[type=button],.tribe-common input[type=email],.tribe-common input[type=password],.tribe-common input[type=reset],.tribe-common input[type=search],.tribe-common input[type=submit],.tribe-common input[type=text],.tribe-common input[type=url],.tribe-common textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none}.tribe-common button,.tribe-common input,.tribe-common optgroup,.tribe-common select,.tribe-common textarea{color:inherit;font:inherit;line-height:normal;-webkit-font-smoothing:antialiased}.tribe-common button,.tribe-common input,.tribe-common select,.tribe-common textarea{outline:0;border-radius:0}.tribe-common select:-moz-focusring{color:transparent;text-shadow:0 0 0 #000}.tribe-common optgroup{font-weight:700}.tribe-common h1,.tribe-common h2,.tribe-common h3,.tribe-common h4,.tribe-common h5,.tribe-common h6,.tribe-common p{font-weight:400;text-rendering:optimizeLegibility}#top .main_color .tribe-common button[disabled],#top.tribe-theme-enfold .tribe-common button[disabled]{opacity:1}.tribe-theme-twentynineteen .tribe-common h1:before,.tribe-theme-twentynineteen .tribe-common h2:before{content:none}.tribe-theme-twentynineteen .tribe-common button,.tribe-theme-twentynineteen .tribe-common input[type=button],.tribe-theme-twentynineteen .tribe-common input[type=reset],.tribe-theme-twentynineteen .tribe-common input[type=submit]{outline:none}.tribe-theme-twentynineteen .tribe-common td,.tribe-theme-twentynineteen .tribe-common th{word-break:normal}.tribe-theme-twentyseventeen .tribe-common h5{letter-spacing:normal;text-transform:none}.tribe-theme-twentyseventeen .tribe-common input[type=text]{border-radius:0}.tribe-theme-twentytwenty .tribe-common{background-color:#fff;letter-spacing:normal}.tribe-theme-twentytwenty .tribe-common input,.tribe-theme-twentytwenty .tribe-common textarea{letter-spacing:normal}.tribe-theme-twentytwenty .tribe-common *{word-break:normal}.tribe-common .tribe-common-form-control-checkbox,.tribe-common .tribe-common-form-control-radio{line-height:0}.tribe-common .tribe-common-form-control-checkbox__label,.tribe-common .tribe-common-form-control-radio__label{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;line-height:1.62;font-weight:400}.tribe-common .tribe-common-form-control-checkbox__label:hover,.tribe-common .tribe-common-form-control-radio__label:hover{opacity:.8}.tribe-common .tribe-common-form-control-checkbox__input,.tribe-common .tribe-common-form-control-radio__input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border:1px solid #141827;height:20px;position:relative;width:20px}.tribe-common .tribe-common-form-control-checkbox__input:active,.tribe-common .tribe-common-form-control-checkbox__input:focus,.tribe-common .tribe-common-form-control-checkbox__input:hover,.tribe-common .tribe-common-form-control-radio__input:active,.tribe-common .tribe-common-form-control-radio__input:focus,.tribe-common .tribe-common-form-control-radio__input:hover{border-color:#141827;opacity:.8}.tribe-common .tribe-common-form-control-checkbox__input:checked,.tribe-common .tribe-common-form-control-radio__input:checked{background-color:#141827}.tribe-common .tribe-common-form-control-checkbox__input{border-radius:4px}.tribe-common .tribe-common-form-control-checkbox__input:checked:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M10.6.1L3.9 6.8 1.4 4.3c-.1-.1-.3-.1-.4 0l-.8.8c-.1.1-.1.3 0 .4l3.4 3.4c.2.1.4.1.5 0l7.7-7.7c.1-.1.1-.3 0-.4L11 .1c-.1-.1-.3-.1-.4 0z' fill='%23fff'/%3E%3C/svg%3E");background-repeat:no-repeat;background-size:contain;content:"";display:block;height:9px;left:50%;margin:0;position:absolute;top:50%;transform:translate(-50%,-50%);width:12px}.tribe-common .tribe-common-form-control-checkbox__input:focus+.tribe-common-form-control-checkbox__label,.tribe-common .tribe-common-form-control-checkbox__input:hover+.tribe-common-form-control-checkbox__label{opacity:.8}.tribe-common .tribe-common-form-control-radio__input{border-radius:50%}.tribe-common .tribe-common-form-control-radio__input:checked:before{background-color:#fff;border-radius:50%;content:"";display:block;height:8px;left:50%;margin:0;position:absolute;top:50%;transform:translate(-50%,-50%);width:8px}.tribe-common .tribe-common-form-control-radio__input:focus+.tribe-common-form-control-radio__label,.tribe-common .tribe-common-form-control-radio__input:hover+.tribe-common-form-control-radio__label{opacity:.8}.tribe-theme-twentytwenty .tribe-common .tribe-common-form-control-checkbox__input:checked:before{margin:0}#top .main_color .tribe-common .tribe-common-form-control-checkbox__label,#top .main_color .tribe-common .tribe-common-form-control-radio__label,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-checkbox__label,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-radio__label{font-weight:400;font-size:14px}.tribe-common .tribe-common-form-control-slider{line-height:0}.tribe-common .tribe-common-form-control-slider__input{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0}.tribe-common .tribe-common-form-control-slider__input::-webkit-slider-runnable-track{border:none;border-radius:5px;height:10px;margin:5px 0;padding:0;position:relative;transition:background-color .2s ease;background-color:#334aff}.tribe-common .tribe-common-form-control-slider__input::-moz-range-track{border:none;border-radius:5px;height:10px;margin:5px 0;padding:0;position:relative;transition:background-color .2s ease;background-color:#334aff}.tribe-common .tribe-common-form-control-slider__input::-ms-track{background-color:transparent;border-color:transparent;border-width:5px 0;color:transparent;height:10px}.tribe-common .tribe-common-form-control-slider__input::-ms-fill-lower,.tribe-common .tribe-common-form-control-slider__input::-ms-fill-upper{background-color:#334aff;border-radius:10px}.tribe-common .tribe-common-form-control-slider__input::-webkit-slider-thumb{background-color:#fff;border:1px solid #7d7d7d;border-radius:50%;box-shadow:0 2px 5px 0 rgba(0,0,0,.14);height:20px;width:20px;margin-top:-5px;-webkit-appearance:none;appearance:none}.tribe-common .tribe-common-form-control-slider__input::-moz-range-thumb{background-color:#fff;border:1px solid #7d7d7d;border-radius:50%;box-shadow:0 2px 5px 0 rgba(0,0,0,.14);height:20px;width:20px;margin-top:-5px}.tribe-common .tribe-common-form-control-slider__input::-ms-thumb{background-color:#fff;border:1px solid #7d7d7d;border-radius:50%;box-shadow:0 2px 5px 0 rgba(0,0,0,.14);height:20px;width:20px;margin-top:-5px;box-shadow:none;margin-top:-1px}.tribe-common .tribe-common-form-control-slider__label{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;line-height:1.38;font-weight:400;color:#5d5d5d}#top .main_color .tribe-common .tribe-common-form-control-slider__label,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-slider__label{font-weight:400;font-size:12px}.tribe-common .tribe-common-form-control-text__input{font-size:16px;border:0;border-bottom:1px solid #d5d5d5}.tribe-common--breakpoint-medium.tribe-common .tribe-common-form-control-text__input,.tribe-common .tribe-common-form-control-text__input{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;line-height:1.62;font-weight:400}.tribe-common--breakpoint-medium.tribe-common .tribe-common-form-control-text__input{font-size:14px;border:0}.tribe-common .tribe-common-form-control-text__input::-webkit-input-placeholder{color:#5d5d5d;font-style:normal;opacity:1}.tribe-common .tribe-common-form-control-text__input::-moz-placeholder{color:#5d5d5d;font-style:normal;opacity:1}.tribe-common .tribe-common-form-control-text__input:-ms-input-placeholder,.tribe-common .tribe-common-form-control-text__input::-ms-input-placeholder{color:#5d5d5d;font-style:normal;opacity:1}.tribe-common .tribe-common-form-control-text__input::placeholder{color:#5d5d5d;font-style:normal;opacity:1}.tribe-common .tribe-common-form-control-text__input:focus{border-bottom-color:#141827;outline:0}.tribe-theme-twentyseventeen .tribe-common .tribe-common-form-control-text__input{color:#141827}.tribe-theme-twentytwenty .tribe-common .tribe-common-form-control-text__input{line-height:inherit}#top .main_color .tribe-common .tribe-common-form-control-text__input,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-text__input{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:16px;line-height:1.62;font-weight:400;background:#fff;border:0;border-bottom:1px solid #d5d5d5}#top .main_color .tribe-common .tribe-common-form-control-text__input:focus,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-text__input:focus{border-bottom-color:#141827;box-shadow:none}#top .main_color .tribe-common.tribe-common--breakpoint-medium .tribe-common-form-control-text__input,#top.tribe-theme-enfold .tribe-common.tribe-common--breakpoint-medium .tribe-common-form-control-text__input{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;line-height:1.62;font-weight:400;border:0}.tribe-common .tribe-common-form-control-toggle{line-height:0;position:relative}.tribe-common .tribe-common-form-control-toggle__input{border:none;border-radius:5px;height:10px;margin:5px 0;padding:0;position:relative;transition:background-color .2s ease;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#7d7d7d;width:40px}.tribe-common .tribe-common-form-control-toggle__input::-ms-check{display:none}.tribe-common .tribe-common-form-control-toggle__input+label:before{background-color:#fff;border:1px solid #7d7d7d;border-radius:50%;box-shadow:0 2px 5px 0 rgba(0,0,0,.14);height:20px;width:20px;content:"";left:0;position:absolute;top:0;transition:transform .2s ease}.tribe-common .tribe-common-form-control-toggle__input:checked{background-color:#334aff}.tribe-common .tribe-common-form-control-toggle__input:checked+label:before{transform:translateX(20px)}.tribe-common .tribe-common-form-control-toggle__label{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;line-height:1.38;font-weight:400;color:#5d5d5d}#top .main_color .tribe-common .tribe-common-form-control-toggle__label,#top.tribe-theme-enfold .tribe-common .tribe-common-form-control-toggle__label{font-weight:400;font-size:12px}.tribe-theme-twentytwenty .tribe-common .tribe-common-form-control-toggle__input{top:0}.tribe-theme-twentytwenty .tribe-common .tribe-common-form-control-toggle__input:checked:before{content:none}.tribe-theme-twentytwentyone .tribe-common .tribe-common-form-control-toggle__input:after{display:none}.tribe-common a,.tribe-common a:active,.tribe-common a:focus,.tribe-common a:hover,.tribe-common a:visited{color:#141827;outline:0;text-decoration:none}.site-footer .widget-area .tribe-common a,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common a,.tribe-theme-twentyseventeen .tribe-common a{box-shadow:none}.site-footer .widget-area .tribe-common a:focus,.site-footer .widget-area .tribe-common a:hover,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common a:focus,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common a:hover,.tribe-theme-twentyseventeen .tribe-common a:focus,.tribe-theme-twentyseventeen .tribe-common a:hover{box-shadow:none;color:#141827}.tribe-theme-twentynineteen .entry .tribe-common a,.tribe-theme-twentynineteen .tribe-common a,.tribe-theme-twentytwentyone .entry .tribe-common a,.tribe-theme-twentytwentyone .tribe-common a{text-decoration:none}.main_color .sidebar .tribe-common a,.main_color .sidebar .tribe-common a:active,.main_color .sidebar .tribe-common a:focus,.main_color .sidebar .tribe-common a:hover,.main_color .sidebar .tribe-common a:visited,.tribe-theme-enfold .tribe-common a,.tribe-theme-enfold .tribe-common a:active,.tribe-theme-enfold .tribe-common a:focus,.tribe-theme-enfold .tribe-common a:hover,.tribe-theme-enfold .tribe-common a:visited{color:#141827}.tribe-common .tribe-common-anchor{border-bottom:2px solid transparent;transition:border-color .2s ease}.tribe-common .tribe-common-anchor:active,.tribe-common .tribe-common-anchor:focus,.tribe-common .tribe-common-anchor:hover{border-bottom:2px solid #141827}.tribe-common .tribe-common-anchor-alt{border-bottom:2px solid #334aff;color:#141827;transition:color .2s ease}.tribe-common .tribe-common-anchor-alt:active,.tribe-common .tribe-common-anchor-alt:focus,.tribe-common .tribe-common-anchor-alt:hover{border-bottom:2px solid #334aff;color:#334aff}.tribe-common .tribe-common-anchor-thin{border-bottom:1px solid transparent;transition:border-color .2s ease}.tribe-common .tribe-common-anchor-thin:active,.tribe-common .tribe-common-anchor-thin:focus,.tribe-common .tribe-common-anchor-thin:hover{border-bottom:1px solid #141827}.tribe-common .tribe-common-anchor-thin-alt{border-bottom:1px solid #334aff;color:#141827;transition:color .2s ease}.tribe-common .tribe-common-anchor-thin-alt:active,.tribe-common .tribe-common-anchor-thin-alt:focus,.tribe-common .tribe-common-anchor-thin-alt:hover{border-bottom:1px solid #334aff;color:#334aff}.tribe-theme-twentyseventeen .tribe-common .tribe-common-anchor-alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-anchor-alt:hover,.tribe-theme-twentyseventeen .tribe-common .tribe-common-anchor-thin-alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-anchor-thin-alt:hover{color:#334aff}.site-footer .widget-area .tribe-common .tribe-common-anchor,.site-footer .widget-area .tribe-common .tribe-common-anchor-thin,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common .tribe-common-anchor,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common .tribe-common-anchor-thin{transition:border-color .2s ease}.site-footer .widget-area .tribe-common .tribe-common-anchor-alt,.site-footer .widget-area .tribe-common .tribe-common-anchor-thin-alt,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common .tribe-common-anchor-alt,.tribe-theme-twentyseventeen .site-footer .widget-area .tribe-common .tribe-common-anchor-thin-alt{transition:color .2s ease}.tribe-common .tribe-common-b1{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1.62}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b1{font-size:16px;line-height:1.62}.tribe-common .tribe-common-b1--bold{font-weight:700}.tribe-common .tribe-common-b2{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;font-weight:400;line-height:1.38}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b2{font-size:14px;line-height:1.62}.tribe-common .tribe-common-b2--bold{font-weight:700}.tribe-common .tribe-common-b3{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:11px;font-weight:400;line-height:1.5}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b3{font-size:12px;line-height:1.38}.tribe-common .tribe-common-b3--bold{font-weight:700}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b1--min-medium{font-size:16px;line-height:1.62}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b2--min-medium{font-size:14px;line-height:1.62}.tribe-common--breakpoint-medium.tribe-common .tribe-common-b3--min-medium{font-size:12px;line-height:1.38}.tribe-common .tribe-common-cta{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;line-height:1.38;font-weight:400;font-weight:700;border-bottom:2px solid transparent;transition:border-color .2s ease}.tribe-common .tribe-common-cta:active,.tribe-common .tribe-common-cta:focus,.tribe-common .tribe-common-cta:hover{border-bottom:2px solid #141827}.tribe-common .tribe-common-cta--alt{border-bottom:2px solid #334aff;color:#141827;transition:color .2s ease}.tribe-common .tribe-common-cta--alt:active,.tribe-common .tribe-common-cta--alt:focus,.tribe-common .tribe-common-cta--alt:hover{border-bottom:2px solid #334aff;color:#334aff}.tribe-common .tribe-common-cta--thin{border-bottom:1px solid transparent;transition:border-color .2s ease}.tribe-common .tribe-common-cta--thin:active,.tribe-common .tribe-common-cta--thin:focus,.tribe-common .tribe-common-cta--thin:hover{border-bottom:1px solid #141827}.tribe-common .tribe-common-cta--thin-alt{border-bottom:1px solid #334aff;color:#141827;transition:color .2s ease}.tribe-common .tribe-common-cta--thin-alt:active,.tribe-common .tribe-common-cta--thin-alt:focus,.tribe-common .tribe-common-cta--thin-alt:hover{border-bottom:1px solid #334aff;color:#334aff}.tribe-theme-twentyseventeen .tribe-common .tribe-common-cta--alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-cta--alt:hover,.tribe-theme-twentyseventeen .tribe-common .tribe-common-cta--thin-alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-cta--thin-alt:hover{color:#334aff}.tribe-common .tribe-common-h1{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:28px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h1{font-size:42px;line-height:1.38}.tribe-common .tribe-common-h2{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:24px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h2{font-size:32px;line-height:1.38}.tribe-common .tribe-common-h3{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:22px;line-height:1.5}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h3{font-size:28px;line-height:1.42}.tribe-common .tribe-common-h4{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:20px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h4{font-size:24px;line-height:1.42}.tribe-common .tribe-common-h5{font-size:18px}.tribe-common .tribe-common-h5,.tribe-common .tribe-common-h6{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;line-height:1.5}.tribe-common .tribe-common-h6{font-size:16px}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h6{font-size:16px;line-height:1.62}.tribe-common .tribe-common-h7{font-size:14px;line-height:1.62}.tribe-common .tribe-common-h7,.tribe-common .tribe-common-h8{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700}.tribe-common .tribe-common-h8{font-size:12px;line-height:1.38}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h3--min-medium{font-size:28px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h4--min-medium{font-size:24px;line-height:1.42}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h5--min-medium{font-size:18px;line-height:1.5}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h6--min-medium{font-size:16px;line-height:1.62}.tribe-common--breakpoint-medium.tribe-common .tribe-common-h7--min-medium{font-size:14px;line-height:1.62}.tribe-common .tribe-common-h--alt{font-weight:400}.tribe-theme-avada #main .tribe-common .tribe-common-h1{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:28px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h1{font-size:42px;line-height:1.38}.tribe-theme-avada #main .tribe-common .tribe-common-h2{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:24px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h2{font-size:32px;line-height:1.38}.tribe-theme-avada #main .tribe-common .tribe-common-h3{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:22px;line-height:1.5}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h3{font-size:28px;line-height:1.42}.tribe-theme-avada #main .tribe-common .tribe-common-h4{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;font-size:20px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h4{font-size:24px;line-height:1.42}.tribe-theme-avada #main .tribe-common .tribe-common-h5{font-size:18px}.tribe-theme-avada #main .tribe-common .tribe-common-h5,.tribe-theme-avada #main .tribe-common .tribe-common-h6{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700;line-height:1.5}.tribe-theme-avada #main .tribe-common .tribe-common-h6{font-size:16px}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h6{font-size:16px;line-height:1.62}.tribe-theme-avada #main .tribe-common .tribe-common-h7{font-size:14px;line-height:1.62}.tribe-theme-avada #main .tribe-common .tribe-common-h7,.tribe-theme-avada #main .tribe-common .tribe-common-h8{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-weight:700}.tribe-theme-avada #main .tribe-common .tribe-common-h8{font-size:12px;line-height:1.38}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h3--min-medium{font-size:28px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h4--min-medium{font-size:24px;line-height:1.42}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h5--min-medium{font-size:18px;line-height:1.5}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h6--min-medium{font-size:16px;line-height:1.62}.tribe-theme-avada #main .tribe-common.tribe-common--breakpoint-medium .tribe-common-h7--min-medium{font-size:14px;line-height:1.62}.tribe-theme-avada #main .tribe-common .tribe-common-h--alt{font-weight:400}.tribe-common button{border:none}.tribe-common button,.tribe-common button:focus,.tribe-common button:hover,.tribe-theme-twentyseventeen .tribe-common button:focus,.tribe-theme-twentyseventeen .tribe-common button:hover{background-color:transparent}.tribe-theme-twentytwenty .tribe-common button{background-color:transparent;text-transform:inherit}.tribe-theme-twentytwenty .tribe-common button:focus,.tribe-theme-twentytwenty .tribe-common button:hover{text-decoration:none}.tribe-theme-enfold .tribe-common th{letter-spacing:0;text-transform:none}.tribe-common .tribe-common-c-btn-border,.tribe-common a.tribe-common-c-btn-border{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;line-height:1.62;font-weight:400;font-weight:700;border:0;cursor:pointer;display:inline-block;height:auto;padding:0;text-decoration:none;width:auto;background-color:#fff;border:1px solid #334aff;border-radius:4px;text-align:center;transition:all .2s ease;color:#334aff;padding:11px 20px;width:100%}.tribe-common--breakpoint-medium.tribe-common .tribe-common-c-btn-border,.tribe-common--breakpoint-medium.tribe-common a.tribe-common-c-btn-border{width:auto}.tribe-common .tribe-common-c-btn-border:focus,.tribe-common .tribe-common-c-btn-border:hover,.tribe-common a.tribe-common-c-btn-border:focus,.tribe-common a.tribe-common-c-btn-border:hover{color:#fff;background-color:#334aff}.tribe-common .tribe-common-c-btn-border:active,.tribe-common a.tribe-common-c-btn-border:active{opacity:.9}.tribe-common .tribe-common-c-btn-border--secondary,.tribe-common a.tribe-common-c-btn-border--secondary{border-color:#141827;color:#141827}.tribe-common .tribe-common-c-btn-border--secondary:focus,.tribe-common .tribe-common-c-btn-border--secondary:hover,.tribe-common a.tribe-common-c-btn-border--secondary:focus,.tribe-common a.tribe-common-c-btn-border--secondary:hover{background-color:#141827}.tribe-common .tribe-common-c-btn-border--secondary:active,.tribe-common a.tribe-common-c-btn-border--secondary:active{opacity:.9}.tribe-common .tribe-common-c-btn-border--alt,.tribe-common a.tribe-common-c-btn-border--alt{border-color:#e4e4e4;color:#141827;font-weight:400}.tribe-common .tribe-common-c-btn-border--alt:focus,.tribe-common .tribe-common-c-btn-border--alt:hover,.tribe-common a.tribe-common-c-btn-border--alt:focus,.tribe-common a.tribe-common-c-btn-border--alt:hover{border-color:#141827;background-color:#fff;color:#141827}.tribe-common .tribe-common-c-btn-border--alt:active,.tribe-common a.tribe-common-c-btn-border--alt:active{opacity:.9}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border:hover{background-color:#334aff}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border--secondary:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border--secondary:hover{background-color:#141827}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border--alt:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border--alt:hover{background-color:#fff}.tribe-common .tribe-common-c-btn-border-small,.tribe-common a.tribe-common-c-btn-border-small{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:12px;line-height:1.38;font-weight:400;border:0;cursor:pointer;display:inline-block;height:auto;padding:0;text-decoration:none;width:auto;background-color:#fff;border:1px solid #d5d5d5;border-radius:4px;text-align:center;transition:color .2s ease,border-color .2s ease}.tribe-common .tribe-common-c-btn-border-small:focus,.tribe-common .tribe-common-c-btn-border-small:hover,.tribe-common a.tribe-common-c-btn-border-small:focus,.tribe-common a.tribe-common-c-btn-border-small:hover{background-color:#fff}.tribe-common .tribe-common-c-btn-border-small:active,.tribe-common a.tribe-common-c-btn-border-small:active{border-color:#141827}.tribe-common .tribe-common-c-btn-border-small,.tribe-common a.tribe-common-c-btn-border-small{color:#5d5d5d;padding:14px 20px;width:100%}.tribe-common--breakpoint-medium.tribe-common .tribe-common-c-btn-border-small,.tribe-common--breakpoint-medium.tribe-common a.tribe-common-c-btn-border-small{padding:6px 15px;width:auto}.tribe-common .tribe-common-c-btn-border-small:active,.tribe-common .tribe-common-c-btn-border-small:focus,.tribe-common .tribe-common-c-btn-border-small:hover,.tribe-common a.tribe-common-c-btn-border-small:active,.tribe-common a.tribe-common-c-btn-border-small:focus,.tribe-common a.tribe-common-c-btn-border-small:hover{color:#141827}.tribe-common .tribe-common-c-btn-border-small:disabled,.tribe-common a.tribe-common-c-btn-border-small:disabled{color:#d5d5d5}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-border-small:hover{background-color:#fff}.tribe-common .tribe-common-c-btn-icon{border:0;cursor:pointer;display:inline-block;height:auto;padding:0;text-decoration:none;width:auto}.tribe-common .tribe-common-c-btn-icon--caret-left:active .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-left:focus .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-left:hover .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right:active .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right:focus .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right:hover .tribe-common-c-btn-icon__icon-svg path{fill:#5d5d5d}.tribe-common .tribe-common-c-btn-icon--caret-left:disabled .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right:disabled .tribe-common-c-btn-icon__icon-svg path{fill:#d5d5d5}.tribe-common .tribe-common-c-btn-icon--caret-left .tribe-common-c-btn-icon__icon-svg path,.tribe-common .tribe-common-c-btn-icon--caret-right .tribe-common-c-btn-icon__icon-svg path{fill:#bababa}.tribe-common .tribe-common-c-btn-icon--border{align-items:center;background-color:#fff;border:1px solid #d5d5d5;display:inline-flex;height:56px;justify-content:center;transition:none;width:56px}.tribe-common .tribe-common-c-btn-icon--border:focus,.tribe-common .tribe-common-c-btn-icon--border:hover{background-color:#fff}.tribe-common .tribe-common-c-btn-icon--border:active{border-color:#141827}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn-icon--border:hover{background-color:#fff}.tribe-common .tribe-common-c-btn,.tribe-common a.tribe-common-c-btn{color:#141827;font-family:Helvetica Neue,Helvetica,-apple-system,BlinkMacSystemFont,Roboto,Arial,sans-serif;font-size:14px;line-height:1.62;font-weight:400;font-weight:700;border:0;cursor:pointer;display:inline-block;height:auto;padding:0;text-decoration:none;width:auto;border-radius:4px;color:#fff;text-align:center;transition:background-color .2s ease;background-color:#334aff;padding:11px 20px;width:100%}.tribe-common--breakpoint-medium.tribe-common .tribe-common-c-btn,.tribe-common--breakpoint-medium.tribe-common a.tribe-common-c-btn{width:auto}.tribe-common .tribe-common-c-btn:focus,.tribe-common .tribe-common-c-btn:hover,.tribe-common a.tribe-common-c-btn:focus,.tribe-common a.tribe-common-c-btn:hover{background-color:rgba(51,74,255,.8)}.tribe-common .tribe-common-c-btn:active,.tribe-common a.tribe-common-c-btn:active{background-color:rgba(51,74,255,.9)}.tribe-common .tribe-common-c-btn:disabled,.tribe-common a.tribe-common-c-btn:disabled{background-color:rgba(51,74,255,.07)}.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn:focus,.tribe-theme-twentyseventeen .tribe-common .tribe-common-c-btn:hover{background-color:rgba(51,74,255,.8);color:#fff}.tribe-theme-twentytwenty .tribe-common .tribe-common-c-btn{background-color:#334aff}.tribe-theme-twentytwenty .tribe-common .tribe-common-c-btn:focus,.tribe-theme-twentytwenty .tribe-common .tribe-common-c-btn:hover{background-color:rgba(51,74,255,.8);color:#fff}.tribe-common .tribe-common-c-loader__dot circle{animation-name:a;animation-duration:2.24s;animation-iteration-count:infinite;animation-direction:normal;fill:currentColor;opacity:.07}.tribe-common .tribe-common-c-loader__dot--first circle{animation-delay:.45s}.tribe-common .tribe-common-c-loader__dot--second circle{animation-delay:1.05s}.tribe-common .tribe-common-c-loader__dot--third circle{animation-delay:1.35s}@keyframes a{50%{opacity:1}}.tribe-common .tribe-common-c-svgicon{color:#334aff}.tribe-common .tribe-common-c-svgicon--featured path{fill:currentColor}.tribe-common .tribe-common-c-svgicon--recurring path{fill:#141827;stroke:#141827}.tribe-common .tribe-common-c-svgicon--close-alt path,.tribe-common .tribe-common-c-svgicon--close path{stroke:#bababa}.tribe-common .tribe-common-c-svgicon--messages-not-found path{stroke:#141827}.tribe-common .tribe-common-c-svgicon--messages-not-found .tribe-common-c-svgicon__svg-stroke{stroke:currentColor}.tribe-common .tribe-common-c-svgicon__svg-fill{fill:#141827}.tribe-common .tribe-common-c-svgicon__svg-stroke{stroke:#141827}
common/src/resources/css/datatables.min.css CHANGED
@@ -1 +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/DataTables/images/sort_both.png)}table.dataTable thead .sorting_asc{background-image:url(../../../vendor/datatables/DataTables/images/sort_asc.png)}table.dataTable thead .sorting_desc{background-image:url(../../../vendor/datatables/DataTables/images/sort_desc.png)}table.dataTable thead .sorting_asc_disabled{background-image:url(../../../vendor/datatables/DataTables/images/sort_asc_disabled.png)}table.dataTable thead .sorting_desc_disabled{background-image:url(../../../vendor/datatables/DataTables/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;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: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}}
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/DataTables-1.10.23/images/sort_both.png)}table.dataTable thead .sorting_asc{background-image:url(../../../vendor/datatables/DataTables-1.10.23/images/sort_asc.png)}table.dataTable thead .sorting_desc{background-image:url(../../../vendor/datatables/DataTables-1.10.23/images/sort_desc.png)}table.dataTable thead .sorting_asc_disabled{background-image:url(../../../vendor/datatables/DataTables-1.10.23/images/sort_asc_disabled.png)}table.dataTable thead .sorting_desc_disabled{background-image:url(../../../vendor/datatables/DataTables-1.10.23/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;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: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/images/mascot.png CHANGED
Binary file
common/src/resources/js/admin-date-preview.js CHANGED
@@ -3,10 +3,10 @@
3
  * to (and using the same ajax mechanism as) the date format preview in WP's
4
  * general settings screen.
5
  */
6
- jQuery( document ).ready( function( $ ) {
7
  // Whenever the input field for a date format changes, update the matching
8
  // live preview area
9
- $( ".live-date-preview" ).siblings( "input" ).change( function() {
10
  var $format_field = $( this );
11
  var new_format = $format_field.val();
12
  var $preview_field = $format_field.siblings( ".live-date-preview" );
3
  * to (and using the same ajax mechanism as) the date format preview in WP's
4
  * general settings screen.
5
  */
6
+ jQuery( function( $ ) {
7
  // Whenever the input field for a date format changes, update the matching
8
  // live preview area
9
+ $( ".live-date-preview" ).siblings( "input" ).on( 'change', function() {
10
  var $format_field = $( this );
11
  var new_format = $format_field.val();
12
  var $preview_field = $format_field.siblings( ".live-date-preview" );
common/src/resources/js/admin-date-preview.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(i){i(".live-date-preview").siblings("input").change(function(){var e=i(this),n=e.val(),t=e.siblings(".live-date-preview");t.append("<span class='spinner'></span>"),t.find(".spinner").css("visibility","visible");var a={action:"date_format",date:n};i.post(ajaxurl,a,function(e){e=i("<div/>").html(e).text(),t.html(e)},"text")})});
1
+ jQuery(function(i){i(".live-date-preview").siblings("input").on("change",function(){var n=i(this),t=n.val(),e=n.siblings(".live-date-preview");e.append("<span class='spinner'></span>"),e.find(".spinner").css("visibility","visible");var a={action:"date_format",date:t};i.post(ajaxurl,a,function(n){n=i("<div/>").html(n).text(),e.html(n)},"text")})});
common/src/resources/js/admin/help-page.js CHANGED
@@ -62,7 +62,7 @@ tribe.helpPage = tribe.helpPage || {};
62
  this.$system_info_opt_in = $( obj.selectors.autoInfoOptIn );
63
  this.$system_info_opt_in_msg = $( obj.selectors.optInMsg );
64
 
65
- this.$system_info_opt_in.change( function () {
66
  if ( this.checked ) {
67
  obj.doAjaxRequest( 'generate' );
68
  } else {
@@ -95,6 +95,6 @@ tribe.helpPage = tribe.helpPage || {};
95
 
96
  };
97
 
98
- $( document ).ready( obj.setup )
99
 
100
  } )( jQuery, tribe.helpPage );
62
  this.$system_info_opt_in = $( obj.selectors.autoInfoOptIn );
63
  this.$system_info_opt_in_msg = $( obj.selectors.optInMsg );
64
 
65
+ this.$system_info_opt_in.on( 'change', function () {
66
  if ( this.checked ) {
67
  obj.doAjaxRequest( 'generate' );
68
  } else {
95
 
96
  };
97
 
98
+ $( obj.setup );
99
 
100
  } )( jQuery, tribe.helpPage );
common/src/resources/js/admin/url-fragment-scroll.js CHANGED
@@ -114,6 +114,6 @@ tribe.urlFragmentScroll = tribe.urlFragmentScroll || {};
114
  return fragment[1];
115
  };
116
 
117
- $document.ready( obj.setup );
118
 
119
  } )( jQuery, tribe.urlFragmentScroll );
114
  return fragment[1];
115
  };
116
 
117
+ $( obj.setup );
118
 
119
  } )( jQuery, tribe.urlFragmentScroll );
common/src/resources/js/app-shop.js CHANGED
@@ -1,4 +1,4 @@
1
- jQuery( document ).ready( function() {
2
 
3
  var maxHeight = 0;
4
  jQuery( "div.tribe-addon .caption" ).each( function() {
1
+ jQuery( function() {
2
 
3
  var maxHeight = 0;
4
  jQuery( "div.tribe-addon .caption" ).each( function() {
common/src/resources/js/app-shop.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(){var i=0;jQuery("div.tribe-addon .caption").each(function(){var t=jQuery(this).height();i=t>i?t:i}),jQuery("div.tribe-addon:not(.first) .caption").css("height",i)});
1
+ jQuery(function(){var i=0;jQuery("div.tribe-addon .caption").each(function(){var t=jQuery(this).height();i=t>i?t:i}),jQuery("div.tribe-addon:not(.first) .caption").css("height",i)});
common/src/resources/js/bumpdown.js CHANGED
@@ -1,7 +1,7 @@
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
 
@@ -19,7 +19,7 @@
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 ) {
@@ -102,23 +102,26 @@
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() {
@@ -189,19 +192,19 @@
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' );
1
  (function( $, _ ) {
2
  'use strict';
3
  // Configure on Document ready for the default trigger
4
+ $( function() {
5
  $( '.tribe-bumpdown-trigger' ).bumpdown();
6
  } );
7
 
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 ) {
102
  }
103
  } );
104
 
105
+ if ( 'function' === typeof $.fn.hoverIntent ) {
106
+ $document
107
+ // Use hoverIntent to make sure we are not opening Bumpdown on a fast hover
108
+ .hoverIntent( {
109
+ over: function() {
110
+ var data = $( this ).data( 'bumpdown' );
111
 
112
+ // Flags that it's open
113
+ data.$trigger.data( 'is_hoverintent_queued', false );
114
 
115
+ // Actually opens
116
+ data.$bumpdown.trigger( 'open.bumpdown' );
117
+ },
118
+ out: function() {}, // Prevents Notice on JS
119
+ selector: selectors.hover_trigger,
120
+ interval: 200,
121
+ } );
122
+ }
123
 
124
+ $document
125
  // Setup Events on Trigger
126
  .on( {
127
  mouseenter: function() {
192
  // Configure all the fields
193
  return this.each( function() {
194
  var data = {
195
+ // Store the jQuery Elements
196
+ $trigger: $( this ),
197
+ $parent: null,
198
+ $bumpdown: null,
199
+
200
+ // Store other Variables
201
+ ID: null,
202
+ html: null,
203
+ type: 'block',
204
+
205
+ // Flags
206
+ is_permanent: false,
207
+ };
208
 
209
  // We need a ID for this Bumpdown
210
  data.ID = data.$trigger.attr( 'id' );
common/src/resources/js/bumpdown.min.js CHANGED
@@ -1 +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=0;t(r.trigger).each(function(){var e=t(this);if(e.data("width-rule")){var n=e.position();n.left>d&&(d=n.left)}}),d&&(d=d>600?d:600,a.css("max-width",d+"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(),void 0!==n&&void 0!==n.$bumpdown&&n.$bumpdown.trigger("close.bumpdown")}},r.close).on("click",function(e){var n=t(e.target);n.is(r.bumpdown)||0!==n.parents(r.bumpdown).length||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.attr("data-bumpdown"),n.html='<div class="tribe-bumpdown-content">'+n.html+"</div>",n.class=n.$trigger.attr("data-bumpdown-class"),n.is_permanent=n.$trigger.is(r.permanent),n.$parent=n.$trigger.parents().filter(function(){return-1<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,window.underscore||window._);
1
+ !function(t,e){"use strict";t(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=0;t(r.trigger).each(function(){var e=t(this);if(e.data("width-rule")){var n=e.position();n.left>d&&(d=n.left)}}),d&&(d=d>600?d:600,a.css("max-width",d+"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))})}}),"function"==typeof t.fn.hoverIntent&&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}),n.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(),void 0!==n&&void 0!==n.$bumpdown&&n.$bumpdown.trigger("close.bumpdown")}},r.close).on("click",function(e){var n=t(e.target);n.is(r.bumpdown)||0!==n.parents(r.bumpdown).length||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.attr("data-bumpdown"),n.html='<div class="tribe-bumpdown-content">'+n.html+"</div>",n.class=n.$trigger.attr("data-bumpdown-class"),n.is_permanent=n.$trigger.is(r.permanent),n.$parent=n.$trigger.parents().filter(function(){return-1<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,window.underscore||window._);
common/src/resources/js/buttonset.js CHANGED
@@ -87,5 +87,5 @@ var tribe_buttonset = tribe_buttonset || {};
87
  return false;
88
  };
89
 
90
- $( document ).ready( obj.ready );
91
  } )( jQuery, tribe_buttonset, window.underscore || window._ );
87
  return false;
88
  };
89
 
90
+ $( obj.ready );
91
  } )( jQuery, tribe_buttonset, window.underscore || window._ );
common/src/resources/js/buttonset.min.js CHANGED
@@ -1 +1 @@
1
- var tribe_buttonset=tribe_buttonset||{};!function(t,e,a){"use strict";e.$body,e.selector={buttonset:".tribe-buttonset",button:".tribe-button-field",input:".tribe-button-input",active:".tribe-active"},e.ready=function(a){e.$body=t("body"),e.$body.on("click.tribe_buttonset",e.selector.button,e.click),e.$body.on("change.tribe_buttonset",e.selector.input,e.change).find(e.selector.input).trigger("change")},e.change=function(a){var i=t(this),n=i.val();i.parents(e.selector.buttonset).eq(0).find('[data-value="'+n+'"]').addClass(e.selector.active.replace(".",""))},e.click=function(a){var i,n,r=t(this),o=(i=r.is("[data-group]")?t(r.data("group")):r.parents(e.selector.buttonset)).length>0,c=i.data("input")?i.data("input"):e.selector.input,s=r.data("value"),l=i.is("[data-multiple]");return o&&!l&&i.find(e.selector.button).removeClass(e.selector.active.replace(".","")),l?r.toggleClass(e.selector.active.replace(".","")):r.addClass(e.selector.active.replace(".","")),r.is("[data-input]")&&(c=r.data("input")),n=r.find(c),o&&0===n.length&&(n=i.find(c)),0===n.length&&(n=t(c)),r.is("[data-value]")&&n.val(s),"checkbox"===n.attr("type")?n.prop("checked",r.is(e.selector.active)):n.prop("disabled",!r.is(e.selector.active)),n.trigger("change"),a.preventDefault(),!1},t(document).ready(e.ready)}(jQuery,tribe_buttonset,window.underscore||window._);
1
+ var tribe_buttonset=tribe_buttonset||{};!function(t,e,a){"use strict";e.$body,e.selector={buttonset:".tribe-buttonset",button:".tribe-button-field",input:".tribe-button-input",active:".tribe-active"},e.ready=function(a){e.$body=t("body"),e.$body.on("click.tribe_buttonset",e.selector.button,e.click),e.$body.on("change.tribe_buttonset",e.selector.input,e.change).find(e.selector.input).trigger("change")},e.change=function(a){var i=t(this),n=i.val();i.parents(e.selector.buttonset).eq(0).find('[data-value="'+n+'"]').addClass(e.selector.active.replace(".",""))},e.click=function(a){var i,n,r=t(this),o=(i=r.is("[data-group]")?t(r.data("group")):r.parents(e.selector.buttonset)).length>0,c=i.data("input")?i.data("input"):e.selector.input,s=r.data("value"),l=i.is("[data-multiple]");return o&&!l&&i.find(e.selector.button).removeClass(e.selector.active.replace(".","")),l?r.toggleClass(e.selector.active.replace(".","")):r.addClass(e.selector.active.replace(".","")),r.is("[data-input]")&&(c=r.data("input")),n=r.find(c),o&&0===n.length&&(n=i.find(c)),0===n.length&&(n=t(c)),r.is("[data-value]")&&n.val(s),"checkbox"===n.attr("type")?n.prop("checked",r.is(e.selector.active)):n.prop("disabled",!r.is(e.selector.active)),n.trigger("change"),a.preventDefault(),!1},t(e.ready)}(jQuery,tribe_buttonset,window.underscore||window._);
common/src/resources/js/dependency.js CHANGED
@@ -34,6 +34,17 @@
34
  linked: '.tribe-dependent-linked'
35
  };
36
 
 
 
 
 
 
 
 
 
 
 
 
37
  /**
38
  * Set up each constraint truth condition
39
  * Each function will be passed the value, the constraint and the dependent field
@@ -56,10 +67,10 @@
56
  return '' === val;
57
  },
58
  'is_numeric': function ( val ) {
59
- return $.isNumeric( val );
60
  },
61
  'is_not_numeric': function ( val ) {
62
- return ! $.isNumeric( val );
63
  },
64
  'is_checked': function ( _, __, $field ) {
65
  return ( $field.is( ':checkbox' ) || $field.is( ':radio' ) ) ? $field.is( ':checked' ) : false;
@@ -266,8 +277,8 @@
266
  * @type {Function}
267
  */
268
  $.fn.dependency = function () {
269
- return this.each( function(){
270
- var $el = $(this);
271
  var selector = $el.data( 'depends' );
272
  var $selector = $( selector );
273
 
@@ -283,7 +294,6 @@
283
  } );
284
  };
285
 
286
-
287
  /**
288
  * Listen on async recurrent elements.
289
  *
34
  linked: '.tribe-dependent-linked'
35
  };
36
 
37
+ /**
38
+ * Replacemente for jQuery $.isNumeric that was deprecated on version 5.7 of WP.
39
+ *
40
+ * @param {string|int} number
41
+ *
42
+ * @returns {boolean}
43
+ */
44
+ obj.isNumeric = function( number ) {
45
+ return ! isNaN( parseFloat( number ) ) && isFinite( number );
46
+ };
47
+
48
  /**
49
  * Set up each constraint truth condition
50
  * Each function will be passed the value, the constraint and the dependent field
67
  return '' === val;
68
  },
69
  'is_numeric': function ( val ) {
70
+ return obj.isNumeric( val );
71
  },
72
  'is_not_numeric': function ( val ) {
73
+ return ! obj.isNumeric( val );
74
  },
75
  'is_checked': function ( _, __, $field ) {
76
  return ( $field.is( ':checkbox' ) || $field.is( ':radio' ) ) ? $field.is( ':checked' ) : false;
277
  * @type {Function}
278
  */
279
  $.fn.dependency = function () {
280
+ return this.each( function() {
281
+ var $el = $( this );
282
  var selector = $el.data( 'depends' );
283
  var $selector = $( selector );
284
 
294
  } );
295
  };
296
 
 
297
  /**
298
  * Listen on async recurrent elements.
299
  *
common/src/resources/js/dependency.min.js CHANGED
@@ -1 +1 @@
1
- !function(e,n,d){"use strict";var t=e(document),i=e(window);d.selectors={dependent:".tribe-dependent",active:".tribe-active",dependency:".tribe-dependency",dependencyVerified:".tribe-dependency-verified",dependencyManualControl:"[data-dependency-manual-control]",fields:"input, select, textarea",advanced_fields:".select2-container",linked:".tribe-dependent-linked"},d.constraintConditions={condition:function(e,d){return n.isArray(d)?-1!==d.indexOf(e):e==d},not_condition:function(e,d){return n.isArray(d)?-1===d.indexOf(e):e!=d},is_not_empty:function(e){return""!=e},is_empty:function(e){return""===e},is_numeric:function(n){return e.isNumeric(n)},is_not_numeric:function(n){return!e.isNumeric(n)},is_checked:function(e,n,d){return!(!d.is(":checkbox")&&!d.is(":radio"))&&d.is(":checked")},is_not_checked:function(e,n,d){return!(!d.is(":checkbox")&&!d.is(":radio"))&&!d.is(":checked")}},d.verify=function(i){var o=e(this),c="#"+o.attr("id"),a=o.val();if(c){if(o.is(":radio")){var s=e("[name='"+o.attr("name")+"']");s.not(d.selectors.linked).on("change",function(){s.trigger("verify.dependency")}).addClass(d.selectors.linked.replace(".",""))}var r=t.find('[data-depends="'+c+'"]').not(".select2-container");0!==r.length&&(r.each(function(t,i){var c=e(i);if(c.is("[data-dependent-parent]")){var s=c.data("dependent-parent"),r=c.closest(s);if(0===r.length)return void console.warn("Dependency: `data-dependent-parent` has bad selector",c);c=r.find(i)}var l={condition:!!c.is("[data-condition]")&&c.data("condition"),not_condition:!!c.is("[data-condition-not]")&&c.data("conditionNot"),is_not_empty:c.data("conditionIsNotEmpty")||c.is("[data-condition-is-not-empty]")||c.data("conditionNotEmpty")||c.is("[data-condition-not-empty]"),is_empty:c.data("conditionIsEmpty")||c.is("[data-condition-is-empty]")||c.data("conditionEmpty")||c.is("[data-condition-empty]"),is_numeric:c.data("conditionIsNumeric")||c.is("[data-condition-is-numeric]")||c.data("conditionNumeric")||c.is("[data-condition-numeric]"),is_not_numeric:c.data("conditionIsNotNumeric")||c.is("[data-condition-is-not-numeric]"),is_checked:c.data("conditionIsChecked")||c.is("[data-condition-is-checked]")||c.data("conditionChecked")||c.is("[data-condition-checked]"),is_not_checked:c.data("conditionIsNotChecked")||c.is("[data-condition-is-not-checked]")||c.data("conditionNotChecked")||c.is("[data-condition-not-checked]")},p=d.selectors.active.replace(".",""),u=c.is("[data-dependency-check-disabled]"),f=c.is("[data-dependency-always-visible]"),y=!u&&o.is(":disabled"),h=c.data("condition-relation")||"or";l=n.pick(l,function(e){return!1!==e}),("or"===h?n.reduce(l,function(e,n,t){return e||d.constraintConditions[t](a,n,o)},!1):n.reduce(l,function(e,n,t){return e&&d.constraintConditions[t](a,n,o)},!0))&&!y?(c.is(".tribe-dropdown, .tribe-ea-dropdown")?(c.select2().data("select2").$container.addClass(p),c.select2().data("select2").$container.is(":hidden")&&c.select2().data("select2").$container.show()):(c.addClass(p),c.is(":hidden")&&c.show()),f&&c.filter(d.selectors.fields).prop("disabled",!1),c.find(d.selectors.fields).not(d.selectors.dependencyManualControl).prop("disabled",!1),void 0!==e().select2&&c.find(".tribe-dropdown, .tribe-ea-dropdown").select2().prop("disabled",!1)):(c.removeClass(p),c.is(":visible")&&c.hide(),c.data("dependency-dont-disable")||c.find(d.selectors.fields).not(d.selectors.dependencyManualControl).prop("disabled",!0),void 0!==e().select2&&c.find(".tribe-dropdown, .tribe-ea-dropdown").select2().prop("disabled",!0),c.is(".tribe-dropdown, .tribe-ea-dropdown")&&c.select2().data("select2").$container.removeClass(p),f&&(c.addClass(p).show(),c.filter(d.selectors.fields).prop("disabled",!0),c.is(".tribe-dropdown, .tribe-ea-dropdown")&&c.select2().data("select2").$container.addClass(p).show()));var m=c.find(d.selectors.dependency);m.length>0&&m.trigger("change")}),o.addClass(d.selectors.dependencyVerified.className()))}},d.setup=function(n){var t=e(d.selectors.dependent);t.length&&t.dependency();var i=e(d.selectors.dependency);i.not(d.selectors.dependencyVerified).length&&i.trigger("verify.dependency")},e.fn.dependency=function(){return this.each(function(){var n=e(this),t=n.data("depends"),i=e(t);i.length&&(i.get(0).created||(i.addClass(d.selectors.dependency.replace(".","")).data("dependent",n),i.get(0).created=!0))})},t.on("setup.dependency",d.setup),t.off("change.dependency verify.dependency",d.selectors.dependency),t.on({"verify.dependency":d.verify,"change.dependency":d.verify},d.selectors.dependency),e(d.setup),i.on("load",d.setup)}(jQuery,window.underscore||window._,{});
1
+ !function(e,n,t){"use strict";var d=e(document),i=e(window);t.selectors={dependent:".tribe-dependent",active:".tribe-active",dependency:".tribe-dependency",dependencyVerified:".tribe-dependency-verified",dependencyManualControl:"[data-dependency-manual-control]",fields:"input, select, textarea",advanced_fields:".select2-container",linked:".tribe-dependent-linked"},t.isNumeric=function(e){return!isNaN(parseFloat(e))&&isFinite(e)},t.constraintConditions={condition:function(e,t){return n.isArray(t)?-1!==t.indexOf(e):e==t},not_condition:function(e,t){return n.isArray(t)?-1===t.indexOf(e):e!=t},is_not_empty:function(e){return""!=e},is_empty:function(e){return""===e},is_numeric:function(e){return t.isNumeric(e)},is_not_numeric:function(e){return!t.isNumeric(e)},is_checked:function(e,n,t){return!(!t.is(":checkbox")&&!t.is(":radio"))&&t.is(":checked")},is_not_checked:function(e,n,t){return!(!t.is(":checkbox")&&!t.is(":radio"))&&!t.is(":checked")}},t.verify=function(i){var o=e(this),c="#"+o.attr("id"),a=o.val();if(c){if(o.is(":radio")){var s=e("[name='"+o.attr("name")+"']");s.not(t.selectors.linked).on("change",function(){s.trigger("verify.dependency")}).addClass(t.selectors.linked.replace(".",""))}var r=d.find('[data-depends="'+c+'"]').not(".select2-container");0!==r.length&&(r.each(function(d,i){var c=e(i);if(c.is("[data-dependent-parent]")){var s=c.data("dependent-parent"),r=c.closest(s);if(0===r.length)return void console.warn("Dependency: `data-dependent-parent` has bad selector",c);c=r.find(i)}var l={condition:!!c.is("[data-condition]")&&c.data("condition"),not_condition:!!c.is("[data-condition-not]")&&c.data("conditionNot"),is_not_empty:c.data("conditionIsNotEmpty")||c.is("[data-condition-is-not-empty]")||c.data("conditionNotEmpty")||c.is("[data-condition-not-empty]"),is_empty:c.data("conditionIsEmpty")||c.is("[data-condition-is-empty]")||c.data("conditionEmpty")||c.is("[data-condition-empty]"),is_numeric:c.data("conditionIsNumeric")||c.is("[data-condition-is-numeric]")||c.data("conditionNumeric")||c.is("[data-condition-numeric]"),is_not_numeric:c.data("conditionIsNotNumeric")||c.is("[data-condition-is-not-numeric]"),is_checked:c.data("conditionIsChecked")||c.is("[data-condition-is-checked]")||c.data("conditionChecked")||c.is("[data-condition-checked]"),is_not_checked:c.data("conditionIsNotChecked")||c.is("[data-condition-is-not-checked]")||c.data("conditionNotChecked")||c.is("[data-condition-not-checked]")},p=t.selectors.active.replace(".",""),u=c.is("[data-dependency-check-disabled]"),f=c.is("[data-dependency-always-visible]"),y=!u&&o.is(":disabled"),h=c.data("condition-relation")||"or";l=n.pick(l,function(e){return!1!==e}),("or"===h?n.reduce(l,function(e,n,d){return e||t.constraintConditions[d](a,n,o)},!1):n.reduce(l,function(e,n,d){return e&&t.constraintConditions[d](a,n,o)},!0))&&!y?(c.is(".tribe-dropdown, .tribe-ea-dropdown")?(c.select2().data("select2").$container.addClass(p),c.select2().data("select2").$container.is(":hidden")&&c.select2().data("select2").$container.show()):(c.addClass(p),c.is(":hidden")&&c.show()),f&&c.filter(t.selectors.fields).prop("disabled",!1),c.find(t.selectors.fields).not(t.selectors.dependencyManualControl).prop("disabled",!1),void 0!==e().select2&&c.find(".tribe-dropdown, .tribe-ea-dropdown").select2().prop("disabled",!1)):(c.removeClass(p),c.is(":visible")&&c.hide(),c.data("dependency-dont-disable")||c.find(t.selectors.fields).not(t.selectors.dependencyManualControl).prop("disabled",!0),void 0!==e().select2&&c.find(".tribe-dropdown, .tribe-ea-dropdown").select2().prop("disabled",!0),c.is(".tribe-dropdown, .tribe-ea-dropdown")&&c.select2().data("select2").$container.removeClass(p),f&&(c.addClass(p).show(),c.filter(t.selectors.fields).prop("disabled",!0),c.is(".tribe-dropdown, .tribe-ea-dropdown")&&c.select2().data("select2").$container.addClass(p).show()));var m=c.find(t.selectors.dependency);m.length>0&&m.trigger("change")}),o.addClass(t.selectors.dependencyVerified.className()))}},t.setup=function(n){var d=e(t.selectors.dependent);d.length&&d.dependency();var i=e(t.selectors.dependency);i.not(t.selectors.dependencyVerified).length&&i.trigger("verify.dependency")},e.fn.dependency=function(){return this.each(function(){var n=e(this),d=n.data("depends"),i=e(d);i.length&&(i.get(0).created||(i.addClass(t.selectors.dependency.replace(".","")).data("dependent",n),i.get(0).created=!0))})},d.on("setup.dependency",t.setup),d.off("change.dependency verify.dependency",t.selectors.dependency),d.on({"verify.dependency":t.verify,"change.dependency":t.verify},t.selectors.dependency),e(t.setup),i.on("load",t.setup)}(jQuery,window.underscore||window._,{});
common/src/resources/js/dialog.js CHANGED
@@ -69,6 +69,6 @@ tribe.dialogs = tribe.dialogs || {};
69
  } );
70
  };
71
 
72
- $document.ready( obj.init );
73
 
74
  } )( jQuery, tribe.dialogs );
69
  } );
70
  };
71
 
72
+ $( obj.init );
73
 
74
  } )( jQuery, tribe.dialogs );
common/src/resources/js/dialog.min.js CHANGED
@@ -1 +1 @@
1
- var tribe=tribe||{};tribe.dialogs=tribe.dialogs||{},function(e,t){"use strict";var o=e(document);t.dialogs=t.dialogs||[],t.events=t.events||{},t.getDialogName=function(e){return"dialog_obj_"+e.id},t.init=function(){t.dialogs.forEach(function(o){var s=t.getDialogName(o),a=new window.A11yDialog({appendTarget:o.appendTarget,bodyLock:o.bodyLock,closeButtonAriaLabel:o.closeButtonAriaLabel,closeButtonClasses:o.closeButtonClasses,contentClasses:o.contentClasses,effect:o.effect,effectEasing:o.effectEasing,effectSpeed:o.effectSpeed,overlayClasses:o.overlayClasses,overlayClickCloses:o.overlayClickCloses,trigger:o.trigger,wrapperClasses:o.wrapperClasses});window[s]=a,o.a11yInstance=a,window[s].on("show",function(s,a){a&&(a.preventDefault(),a.stopPropagation()),e(t.events).trigger(o.showEvent,[s,a])}),window[s].on("hide",function(s,a){a&&(a.preventDefault(),a.stopPropagation()),e(t.events).trigger(o.closeEvent,[s,a])})})},o.ready(t.init)}(jQuery,tribe.dialogs);
1
+ var tribe=tribe||{};tribe.dialogs=tribe.dialogs||{},function(e,t){"use strict";e(document);t.dialogs=t.dialogs||[],t.events=t.events||{},t.getDialogName=function(e){return"dialog_obj_"+e.id},t.init=function(){t.dialogs.forEach(function(o){var s=t.getDialogName(o),a=new window.A11yDialog({appendTarget:o.appendTarget,bodyLock:o.bodyLock,closeButtonAriaLabel:o.closeButtonAriaLabel,closeButtonClasses:o.closeButtonClasses,contentClasses:o.contentClasses,effect:o.effect,effectEasing:o.effectEasing,effectSpeed:o.effectSpeed,overlayClasses:o.overlayClasses,overlayClickCloses:o.overlayClickCloses,trigger:o.trigger,wrapperClasses:o.wrapperClasses});window[s]=a,o.a11yInstance=a,window[s].on("show",function(s,a){a&&(a.preventDefault(),a.stopPropagation()),e(t.events).trigger(o.showEvent,[s,a])}),window[s].on("hide",function(s,a){a&&(a.preventDefault(),a.stopPropagation()),e(t.events).trigger(o.closeEvent,[s,a])})})},e(t.init)}(jQuery,tribe.dialogs);
common/src/resources/js/dropdowns.js CHANGED
@@ -19,7 +19,11 @@ var tribe_dropdowns = window.tribe_dropdowns || {};
19
  };
20
 
21
  obj.freefrom_create_search_choice = function( params ) {
22
- var term = $.trim( params.term );
 
 
 
 
23
 
24
  if ( '' === term ) {
25
  return null;
@@ -79,7 +83,7 @@ var tribe_dropdowns = window.tribe_dropdowns || {};
79
  */
80
  obj.matcher = function ( params, data ) {
81
  // If there are no search terms, return all of the data
82
- if ( $.trim( params.term ) === '') {
83
  return data;
84
  }
85
 
@@ -88,7 +92,7 @@ var tribe_dropdowns = window.tribe_dropdowns || {};
88
  return null;
89
  }
90
 
91
- var term = $.trim( params.term );
92
  var text = data.text;
93
  var $select = $( data.element ).closest( 'select' );
94
  var args = $select.data( 'dropdown' );
19
  };
20
 
21
  obj.freefrom_create_search_choice = function( params ) {
22
+ if ( 'string' !== typeof params.term ) {
23
+ return null;
24
+ }
25
+
26
+ var term = params.term.trim();
27
 
28
  if ( '' === term ) {
29
  return null;
83
  */
84
  obj.matcher = function ( params, data ) {
85
  // If there are no search terms, return all of the data
86
+ if ( 'string' !== typeof params.term || params.term.trim() === '') {
87
  return data;
88
  }
89
 
92
  return null;
93
  }
94
 
95
+ var term = params.term.trim();
96
  var text = data.text;
97
  var $select = $( data.element ).closest( 'select' );
98
  var args = $select.data( 'dropdown' );
common/src/resources/js/dropdowns.min.js CHANGED
@@ -1 +1 @@
1
- var tribe_dropdowns=window.tribe_dropdowns||{};!function(e,t,r){"use strict";t.selector={dropdown:".tribe-dropdown",created:".tribe-dropdown-created",searchField:".select2-search__field"},e.fn.tribe_dropdowns=function(){return t.dropdown(this,{}),this},t.freefrom_create_search_choice=function(t){var a=e.trim(t.term);if(""===a)return null;var o=this.options.options,s=o.$select;if(a.match(o.regexToken)&&(!s.is("[data-int]")||s.is("[data-int]")&&a.match(/\d+/))){var n={id:a,text:a,new:!0};return s.is("[data-create-choice-template]")&&(n.text=r.template(s.data("createChoiceTemplate"))({term:a})),n}return null},t.search_id=function(e){var t=void 0;return void 0!==e.id?t=e.id:void 0!==e.ID?t=e.ID:void 0!==e.value&&(t=e.value),void 0===e?void 0:t},t.matcher=function(a,o){if(""===e.trim(a.term))return o;if(void 0===o.text)return null;var s=e.trim(a.term),n=o.text,i=e(o.element).closest("select").data("dropdown"),d=-1!==n.toUpperCase().indexOf(s.toUpperCase());if(!d&&void 0!==i.tags){var c=r.where(i.tags,{text:n});if(i.tags.length>0&&r.isObject(c))d=-1!==t.search_id(c[0]).toUpperCase().indexOf(s.toUpperCase())}return d},t.init_selection=function(t,a){var o=t.is("[multiple]"),s=t.data("dropdown"),n=(t.data("isEmpty"),t.val().split(s.regexSplit)),i=[];if(e(n).each(function(e,a){var o=function e(t,a){if(!r.isArray(a))return!1;for(var o in a){var s=a[o];if(s.hasOwnProperty("id")&&s.id==t.id)return s;if(s.hasOwnProperty("text")&&s.text==t.text)return s;if(s.hasOwnProperty("children")&&r.isArray(s.children)){var n=e(t,s.children);if(n)return n}}return!1}({id:this,text:this},s.ajax?t.data("options"):s.data);o&&o.selected&&i.push(o)}),i.length&&o)a(i);else{if(!i.length)return void a(!1);a(i[0])}},t.getSelectClasses=function(e){var t=e.attr("class").split(/\s+/);return r.difference(t,["select2-hidden-accessible","hide-before-select2-init"])},t.element=function(a,o){var s,n=e(a);o=e.extend({},o);if(n.addClass(t.selector.created.className()),o.$select=n,o.dropdownAutoWidth=!0,o.width="resolve",o.containerCss={},n.is(":visible")&&(o.containerCss.display="inline-block",o.containerCss.position="relative"),o.dropdownCss={},o.dropdownCss.width="auto",n.is("[data-dropdown-css-width]")&&(o.dropdownCss.width=n.data("dropdown-css-width"),o.dropdownCss.width&&"false"!==o.dropdownCss.width||(delete o.dropdownCss.width,delete o.containerCss)),o.allowClear=!0,n.is("[data-prevent-clear]")&&(o.allowClear=!1),n.is("[data-searching-placeholder]")&&(o.formatSearching=n.data("searching-placeholder")),!n.is("[data-placeholder]")&&n.is("[placeholder]")&&(o.placeholder=n.attr("placeholder")),n.is("[data-options]")&&(o.data=n.data("options")),o.minimumResultsForSearch=10,n.is("[data-hide-search]")&&(o.minimumResultsForSearch=1/0),n.is("[data-force-search]")&&delete o.minimumResultsForSearch,n.is("[data-freeform]")&&(o.createTag=t.freefrom_create_search_choice,o.tags=!0,n.data("tags",!0)),n.is("[multiple]")&&(o.multiple=!0,n.is("[data-maximum-selection-size]")&&(o.maximumSelectionSize=n.data("maximum-selection-size")),n.is("data-separator")||n.data("separator",","),r.isArray(n.data("separator"))?o.tokenSeparators=n.data("separator"):o.tokenSeparators=[n.data("separator")],o.separator=n.data("separator"),o.regexSeparatorElements=["^("],o.regexSplitElements=["(?:"],e.each(o.tokenSeparators,function(e,t){o.regexSeparatorElements.push("[^"+t+"]+"),o.regexSplitElements.push("["+t+"]")}),o.regexSeparatorElements.push(")$"),o.regexSplitElements.push(")"),o.regexSeparatorString=o.regexSeparatorElements.join(""),o.regexSplitString=o.regexSplitElements.join(""),o.regexToken=new RegExp(o.regexSeparatorString,"ig"),o.regexSplit=new RegExp(o.regexSplitString,"ig")),n.is("[data-tags]")&&(o.tags=n.data("tags"),o.createSearchChoice=function(e,t){if(e.match(o.regexToken))return{id:e,text:e}},0===o.tags.length&&(o.formatNoMatches=function(){return n.attr("placeholder")})),n.is("[data-source]")){var i=n.data("source");o.data={results:[]},o.formatResult=function(t,r,a){return void 0!==t.breadcrumbs?e.merge(t.breadcrumbs,[t.text]).join(" &#187; "):t.text},o.ajax={dataType:"json",type:"POST",url:t.ajaxurl(),processResults:function(t,r,a){return e.isPlainObject(t)&&void 0!==t.success?e.isPlainObject(t.data)&&void 0!==t.data.results?t.success?t.data:("string"===e.type(t.data.message)?console.error(t.data.message):console.error("The Select2 search failed in some way... Verify the source."),{results:[]}):(console.error("We received a malformed results array, could not complete the Select2 Search."),{results:[]}):(console.error("We received a malformed Object, could not complete the Select2 Search."),{results:[]})}},o.ajax.data=function(e,t){return{action:"tribe_dropdown",source:i,search:e,page:t,args:n.data("source-args")}}}n.is("[data-attach-container]")&&(n.is("[multiple]")?(e.fn.select2.amd.define("AttachedDropdownAdapter",["select2/utils","select2/dropdown","select2/dropdown/attachContainer"],function(e,t,r){return e.Decorate(t,r)}),o.dropdownAdapter=e.fn.select2.amd.require("AttachedDropdownAdapter")):(e.fn.select2.amd.define("AttachedWithSearchDropdownAdapter",["select2/utils","select2/dropdown","select2/dropdown/search","select2/dropdown/minimumResultsForSearch","select2/dropdown/attachContainer"],function(e,t,r,a,o){var s=e.Decorate(t,o);return s=e.Decorate(s,r),s=e.Decorate(s,a)}),o.dropdownAdapter=e.fn.select2.amd.require("AttachedWithSearchDropdownAdapter"))),n.data("dropdown",o),(s=n.select2(o)).data("select2").$container.addClass(t.getSelectClasses(n).join(" ")),s.data("select2").$container.removeClass("hide-before-select2-init"),s.on("select2:open",t.action_select2_open)},t.ajaxurl=function(){return void 0!==window.ajaxurl?window.ajaxurl:"undefined"!=typeof TEC&&void 0!==TEC.ajaxurl?TEC.ajaxurl:void console.error("Dropdowns framework cannot properly do an AJAX request without the WordPress `ajaxurl` variable setup.")},t.action_select2_open=function(r){var a=e(this),o=(a.data("dropdown"),a.data("select2")),s=o.$dropdown.find(t.selector.searchField);o.$dropdown.addClass(t.selector.dropdown.className()),a.is("[data-search-placeholder]")&&s.attr("placeholder",a.data("searchPlaceholder"))},t.dropdown=function(e,r){var a=e.not(".select2-offscreen, .select2-container, "+t.selector.created.className());return 0===a.length?a:(r||(r={}),a.each(function(e,a){t.element(a,r)}),a)},e(function(){e(t.selector.dropdown).tribe_dropdowns()}),e(window).on("unload",function(){e(t.selector.dropdown).tribe_dropdowns()})}(jQuery,tribe_dropdowns,window.underscore||window._);
1
+ var tribe_dropdowns=window.tribe_dropdowns||{};!function(e,t,r){"use strict";t.selector={dropdown:".tribe-dropdown",created:".tribe-dropdown-created",searchField:".select2-search__field"},e.fn.tribe_dropdowns=function(){return t.dropdown(this,{}),this},t.freefrom_create_search_choice=function(e){if("string"!=typeof e.term)return null;var t=e.term.trim();if(""===t)return null;var a=this.options.options,o=a.$select;if(t.match(a.regexToken)&&(!o.is("[data-int]")||o.is("[data-int]")&&t.match(/\d+/))){var s={id:t,text:t,new:!0};return o.is("[data-create-choice-template]")&&(s.text=r.template(o.data("createChoiceTemplate"))({term:t})),s}return null},t.search_id=function(e){var t=void 0;return void 0!==e.id?t=e.id:void 0!==e.ID?t=e.ID:void 0!==e.value&&(t=e.value),void 0===e?void 0:t},t.matcher=function(a,o){if("string"!=typeof a.term||""===a.term.trim())return o;if(void 0===o.text)return null;var s=a.term.trim(),n=o.text,i=e(o.element).closest("select").data("dropdown"),d=-1!==n.toUpperCase().indexOf(s.toUpperCase());if(!d&&void 0!==i.tags){var c=r.where(i.tags,{text:n});if(i.tags.length>0&&r.isObject(c))d=-1!==t.search_id(c[0]).toUpperCase().indexOf(s.toUpperCase())}return d},t.init_selection=function(t,a){var o=t.is("[multiple]"),s=t.data("dropdown"),n=(t.data("isEmpty"),t.val().split(s.regexSplit)),i=[];if(e(n).each(function(e,a){var o=function e(t,a){if(!r.isArray(a))return!1;for(var o in a){var s=a[o];if(s.hasOwnProperty("id")&&s.id==t.id)return s;if(s.hasOwnProperty("text")&&s.text==t.text)return s;if(s.hasOwnProperty("children")&&r.isArray(s.children)){var n=e(t,s.children);if(n)return n}}return!1}({id:this,text:this},s.ajax?t.data("options"):s.data);o&&o.selected&&i.push(o)}),i.length&&o)a(i);else{if(!i.length)return void a(!1);a(i[0])}},t.getSelectClasses=function(e){var t=e.attr("class").split(/\s+/);return r.difference(t,["select2-hidden-accessible","hide-before-select2-init"])},t.element=function(a,o){var s,n=e(a);o=e.extend({},o);if(n.addClass(t.selector.created.className()),o.$select=n,o.dropdownAutoWidth=!0,o.width="resolve",o.containerCss={},n.is(":visible")&&(o.containerCss.display="inline-block",o.containerCss.position="relative"),o.dropdownCss={},o.dropdownCss.width="auto",n.is("[data-dropdown-css-width]")&&(o.dropdownCss.width=n.data("dropdown-css-width"),o.dropdownCss.width&&"false"!==o.dropdownCss.width||(delete o.dropdownCss.width,delete o.containerCss)),o.allowClear=!0,n.is("[data-prevent-clear]")&&(o.allowClear=!1),n.is("[data-searching-placeholder]")&&(o.formatSearching=n.data("searching-placeholder")),!n.is("[data-placeholder]")&&n.is("[placeholder]")&&(o.placeholder=n.attr("placeholder")),n.is("[data-options]")&&(o.data=n.data("options")),o.minimumResultsForSearch=10,n.is("[data-hide-search]")&&(o.minimumResultsForSearch=1/0),n.is("[data-force-search]")&&delete o.minimumResultsForSearch,n.is("[data-freeform]")&&(o.createTag=t.freefrom_create_search_choice,o.tags=!0,n.data("tags",!0)),n.is("[multiple]")&&(o.multiple=!0,n.is("[data-maximum-selection-size]")&&(o.maximumSelectionSize=n.data("maximum-selection-size")),n.is("data-separator")||n.data("separator",","),r.isArray(n.data("separator"))?o.tokenSeparators=n.data("separator"):o.tokenSeparators=[n.data("separator")],o.separator=n.data("separator"),o.regexSeparatorElements=["^("],o.regexSplitElements=["(?:"],e.each(o.tokenSeparators,function(e,t){o.regexSeparatorElements.push("[^"+t+"]+"),o.regexSplitElements.push("["+t+"]")}),o.regexSeparatorElements.push(")$"),o.regexSplitElements.push(")"),o.regexSeparatorString=o.regexSeparatorElements.join(""),o.regexSplitString=o.regexSplitElements.join(""),o.regexToken=new RegExp(o.regexSeparatorString,"ig"),o.regexSplit=new RegExp(o.regexSplitString,"ig")),n.is("[data-tags]")&&(o.tags=n.data("tags"),o.createSearchChoice=function(e,t){if(e.match(o.regexToken))return{id:e,text:e}},0===o.tags.length&&(o.formatNoMatches=function(){return n.attr("placeholder")})),n.is("[data-source]")){var i=n.data("source");o.data={results:[]},o.formatResult=function(t,r,a){return void 0!==t.breadcrumbs?e.merge(t.breadcrumbs,[t.text]).join(" &#187; "):t.text},o.ajax={dataType:"json",type:"POST",url:t.ajaxurl(),processResults:function(t,r,a){return e.isPlainObject(t)&&void 0!==t.success?e.isPlainObject(t.data)&&void 0!==t.data.results?t.success?t.data:("string"===e.type(t.data.message)?console.error(t.data.message):console.error("The Select2 search failed in some way... Verify the source."),{results:[]}):(console.error("We received a malformed results array, could not complete the Select2 Search."),{results:[]}):(console.error("We received a malformed Object, could not complete the Select2 Search."),{results:[]})}},o.ajax.data=function(e,t){return{action:"tribe_dropdown",source:i,search:e,page:t,args:n.data("source-args")}}}n.is("[data-attach-container]")&&(n.is("[multiple]")?(e.fn.select2.amd.define("AttachedDropdownAdapter",["select2/utils","select2/dropdown","select2/dropdown/attachContainer"],function(e,t,r){return e.Decorate(t,r)}),o.dropdownAdapter=e.fn.select2.amd.require("AttachedDropdownAdapter")):(e.fn.select2.amd.define("AttachedWithSearchDropdownAdapter",["select2/utils","select2/dropdown","select2/dropdown/search","select2/dropdown/minimumResultsForSearch","select2/dropdown/attachContainer"],function(e,t,r,a,o){var s=e.Decorate(t,o);return s=e.Decorate(s,r),s=e.Decorate(s,a)}),o.dropdownAdapter=e.fn.select2.amd.require("AttachedWithSearchDropdownAdapter"))),n.data("dropdown",o),(s=n.select2(o)).data("select2").$container.addClass(t.getSelectClasses(n).join(" ")),s.data("select2").$container.removeClass("hide-before-select2-init"),s.on("select2:open",t.action_select2_open)},t.ajaxurl=function(){return void 0!==window.ajaxurl?window.ajaxurl:"undefined"!=typeof TEC&&void 0!==TEC.ajaxurl?TEC.ajaxurl:void console.error("Dropdowns framework cannot properly do an AJAX request without the WordPress `ajaxurl` variable setup.")},t.action_select2_open=function(r){var a=e(this),o=(a.data("dropdown"),a.data("select2")),s=o.$dropdown.find(t.selector.searchField);o.$dropdown.addClass(t.selector.dropdown.className()),a.is("[data-search-placeholder]")&&s.attr("placeholder",a.data("searchPlaceholder"))},t.dropdown=function(e,r){var a=e.not(".select2-offscreen, .select2-container, "+t.selector.created.className());return 0===a.length?a:(r||(r={}),a.each(function(e,a){t.element(a,r)}),a)},e(function(){e(t.selector.dropdown).tribe_dropdowns()}),e(window).on("unload",function(){e(t.selector.dropdown).tribe_dropdowns()})}(jQuery,tribe_dropdowns,window.underscore||window._);
common/src/resources/js/notice-dismiss.js CHANGED
@@ -3,23 +3,24 @@
3
  */
4
  ( function( $ ) {
5
  // Add / Update a key-value pair in the URL query parameters
6
- function update_query_string(uri, key, value) {
7
- // remove the hash part before operating on the uri
8
- var i = uri.indexOf( '#' );
9
- var hash = i === -1 ? '' : uri.substr(i);
10
- uri = i === -1 ? uri : uri.substr(0, i);
11
 
12
- var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
13
- var separator = uri.indexOf('?') !== -1 ? "&" : "?";
14
- if (uri.match(re)) {
15
- uri = uri.replace(re, '$1' + key + "=" + value + '$2');
16
- } else {
17
- uri = uri + separator + key + "=" + value;
18
- }
19
- return uri + hash; // finally append the hash as well
 
 
20
  }
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
 
3
  */
4
  ( function( $ ) {
5
  // Add / Update a key-value pair in the URL query parameters
6
+ function update_query_string( uri, key, value ) {
7
+ // remove the hash part before operating on the uri
8
+ var i = uri.indexOf( '#' );
9
+ var hash = i === -1 ? '' : uri.substr(i);
 
10
 
11
+ uri = i === -1 ? uri : uri.substr(0, i);
12
+
13
+ var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
14
+ var separator = uri.indexOf('?') !== -1 ? "&" : "?";
15
+ if (uri.match(re)) {
16
+ uri = uri.replace(re, '$1' + key + "=" + value + '$2');
17
+ } else {
18
+ uri = uri + separator + key + "=" + value;
19
+ }
20
+ return uri + hash; // finally append the hash as well
21
  }
22
 
23
+ $( function() {
24
  $( '.tribe-dismiss-notice.is-dismissible' ).on( 'click', '.notice-dismiss', function() {
25
  var dismiss_ajaxurl = update_query_string( ajaxurl, 'tribe-dismiss-notice', $( this ).parents( '.tribe-dismiss-notice' ).data( 'ref' ) );
26
 
common/src/resources/js/notice-dismiss.min.js CHANGED
@@ -1 +1 @@
1
- !function(i){i(document).ready(function(){i(".tribe-dismiss-notice.is-dismissible").on("click",".notice-dismiss",function(){var s=function(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)+n}(ajaxurl,"tribe-dismiss-notice",i(this).parents(".tribe-dismiss-notice").data("ref"));i.ajax(s,{dataType:"json",method:"POST",data:{action:"tribe_notice_dismiss"}})})})}(jQuery);
1
+ !function(i){i(function(){i(".tribe-dismiss-notice.is-dismissible").on("click",".notice-dismiss",function(){var s=function(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)+n}(ajaxurl,"tribe-dismiss-notice",i(this).parents(".tribe-dismiss-notice").data("ref"));i.ajax(s,{dataType:"json",method:"POST",data:{action:"tribe_notice_dismiss"}})})})}(jQuery);
common/src/resources/js/timepicker.js CHANGED
@@ -23,8 +23,8 @@ var tribe_timepickers = tribe_timepickers || {};
23
  };
24
 
25
  obj.timezone = {
26
- link: _.template( '<a href="#" class="tribe-change-timezone"><%= label %> <%= timezone %></a>' )
27
- }
28
 
29
  obj.$ = {};
30
 
@@ -104,5 +104,5 @@ var tribe_timepickers = tribe_timepickers || {};
104
  } );
105
  };
106
 
107
- $( document ).ready( obj.init );
108
  } ( jQuery, tribe_timepickers ) );
23
  };
24
 
25
  obj.timezone = {
26
+ link: _.template( '<a href="#" class="tribe-change-timezone"><%= label %> <%= timezone %></a>' ),
27
+ };
28
 
29
  obj.$ = {};
30
 
104
  } );
105
  };
106
 
107
+ $( obj.init );
108
  } ( jQuery, tribe_timepickers ) );
common/src/resources/js/timepicker.min.js CHANGED
@@ -1 +1 @@
1
- var tribe_timepickers=tribe_timepickers||{};!function(e,t){"use strict";t.selector={container:".tribe-datetime-block",timepicker:".tribe-timepicker",all_day:"#allDayCheckbox",timezone:".tribe-field-timezone",input:"select, input"},t.timepicker={opts:{forceRoundTime:!1,step:30}},t.timezone={link:_.template('<a href="#" class="tribe-change-timezone"><%= label %> <%= timezone %></a>')},t.$={},t.container=function(i,n){var r=e(n),c=r.find(t.selector.all_day),o=r.find(t.selector.timepicker),a=r.find(t.selector.timezone).not(t.selector.input),l=r.find(t.selector.timezone).filter(t.selector.input),m=e(t.timezone.link({label:l.data("timezoneLabel"),timezone:l.data("timezoneValue")}));c.on("change",function(){!0===c.prop("checked")?o.hide():o.show()}).trigger("change"),t.setup_timepickers(o),m.on("click",function(e){a=r.find(t.selector.timezone).filter(".select2-container"),e.preventDefault(),m.hide(),a.show()}),l.before(m)},t.init=function(){t.$.containers=e(t.selector.container),t.$.containers.each(t.container)},t.setup_timepickers=function(i){i.each(function(){var i=e(this),n=e.extend({},t.timepicker.opts);i.data("format")&&(n.timeFormat=i.data("format")),i.data("step")&&(n.step=i.data("step"));var r=i.data("round");r&&0!=r&&"false"!==r&&(n.forceRoundTime=!0),void 0!==e.fn.tribeTimepicker?i.tribeTimepicker(n).trigger("change"):i.timepicker(n).trigger("change")})},e(document).ready(t.init)}(jQuery,tribe_timepickers);
1
+ var tribe_timepickers=tribe_timepickers||{};!function(e,t){"use strict";t.selector={container:".tribe-datetime-block",timepicker:".tribe-timepicker",all_day:"#allDayCheckbox",timezone:".tribe-field-timezone",input:"select, input"},t.timepicker={opts:{forceRoundTime:!1,step:30}},t.timezone={link:_.template('<a href="#" class="tribe-change-timezone"><%= label %> <%= timezone %></a>')},t.$={},t.container=function(i,n){var r=e(n),c=r.find(t.selector.all_day),o=r.find(t.selector.timepicker),a=r.find(t.selector.timezone).not(t.selector.input),l=r.find(t.selector.timezone).filter(t.selector.input),s=e(t.timezone.link({label:l.data("timezoneLabel"),timezone:l.data("timezoneValue")}));c.on("change",function(){!0===c.prop("checked")?o.hide():o.show()}).trigger("change"),t.setup_timepickers(o),s.on("click",function(e){a=r.find(t.selector.timezone).filter(".select2-container"),e.preventDefault(),s.hide(),a.show()}),l.before(s)},t.init=function(){t.$.containers=e(t.selector.container),t.$.containers.each(t.container)},t.setup_timepickers=function(i){i.each(function(){var i=e(this),n=e.extend({},t.timepicker.opts);i.data("format")&&(n.timeFormat=i.data("format")),i.data("step")&&(n.step=i.data("step"));var r=i.data("round");r&&0!=r&&"false"!==r&&(n.forceRoundTime=!0),void 0!==e.fn.tribeTimepicker?i.tribeTimepicker(n).trigger("change"):i.timepicker(n).trigger("change")})},e(t.init)}(jQuery,tribe_timepickers);
common/src/resources/js/tooltip.js CHANGED
@@ -59,6 +59,6 @@ tribe.tooltip = tribe.tooltip || {};
59
  }
60
  };
61
 
62
- $document.ready( obj.setup );
63
 
64
  } )( jQuery, tribe.tooltip );
59
  }
60
  };
61
 
62
+ $( obj.setup );
63
 
64
  } )( jQuery, tribe.tooltip );
common/src/resources/js/tooltip.min.js CHANGED
@@ -1 +1 @@
1
- var tribe=tribe||{};tribe.tooltip=tribe.tooltip||{},function(t,e){"use strict";var o=t(document);e.selectors={tooltip:".tribe-tooltip",active:"active"},e.setup=function(){o.on("click",e.selectors.tooltip,e.onClick),o.on("click",function(o){o.target.closest(e.selectors.tooltip)||t(e.selectors.tooltip).each(function(){t(this).removeClass(e.selectors.active).attr("aria-expanded",!1)})})},e.onClick=function(){var o=t(this).closest(e.selectors.tooltip),s=!o.hasClass(e.selectors.active);t(e.selectors.tooltip).each(function(){t(this).removeClass(e.selectors.active).attr("aria-expanded",!1)}),s&&t(o).addClass(e.selectors.active).attr("aria-expanded",!0)},o.ready(e.setup)}(jQuery,tribe.tooltip);
1
+ var tribe=tribe||{};tribe.tooltip=tribe.tooltip||{},function(t,e){"use strict";var o=t(document);e.selectors={tooltip:".tribe-tooltip",active:"active"},e.setup=function(){o.on("click",e.selectors.tooltip,e.onClick),o.on("click",function(o){o.target.closest(e.selectors.tooltip)||t(e.selectors.tooltip).each(function(){t(this).removeClass(e.selectors.active).attr("aria-expanded",!1)})})},e.onClick=function(){var o=t(this).closest(e.selectors.tooltip),s=!o.hasClass(e.selectors.active);t(e.selectors.tooltip).each(function(){t(this).removeClass(e.selectors.active).attr("aria-expanded",!1)}),s&&t(o).addClass(e.selectors.active).attr("aria-expanded",!0)},t(e.setup)}(jQuery,tribe.tooltip);
common/src/resources/js/validation.js CHANGED
@@ -1,3 +1,5 @@
 
 
1
  /**
2
  * Configures this Object in the Global Tribe variable
3
  *
@@ -14,7 +16,7 @@ tribe.validation = {};
14
  /**
15
  * Object containing all the selectors for Validation
16
  *
17
- * @since 4.7
18
  *
19
  * @type {object}
20
  */
@@ -28,7 +30,7 @@ tribe.validation = {};
28
  notice: '.tribe-notice-validation',
29
  noticeAfter: '.wp-header-end',
30
  noticeFallback: '.wrap > h1',
31
- noticeDismiss: '.notice-dismiss'
32
  };
33
 
34
  /**
@@ -43,7 +45,7 @@ tribe.validation = {};
43
  return '' != value;
44
  },
45
  isGreaterThan: function( value, constraint, $field ) {
46
- var condition = obj.parseConditon( 'isGreaterThan', value, constraint, $field );
47
 
48
  // If we failed to parse Condition we don't check
49
  if ( false === condition ) {
@@ -53,7 +55,7 @@ tribe.validation = {};
53
  return condition.constraint < condition.value;
54
  },
55
  isGreaterOrEqualTo: function( value, constraint, $field ) {
56
- var condition = obj.parseConditon( 'isGreaterOrEqualTo', value, constraint, $field );
57
 
58
  // If we failed to parse Condition we don't check
59
  if ( false === condition ) {
@@ -63,7 +65,7 @@ tribe.validation = {};
63
  return condition.constraint <= condition.value;
64
  },
65
  isLessThan: function( value, constraint, $field ) {
66
- var condition = obj.parseConditon( 'isLessThan', value, constraint, $field );
67
 
68
  // If we failed to parse Condition we don't check
69
  if ( false === condition ) {
@@ -73,7 +75,7 @@ tribe.validation = {};
73
  return condition.constraint > condition.value;
74
  },
75
  isLessOrEqualTo: function( value, constraint, $field ) {
76
- var condition = obj.parseConditon( 'isLessOrEqualTo', value, constraint, $field );
77
 
78
  // If we failed to parse Condition we don't check
79
  if ( false === condition ) {
@@ -83,7 +85,7 @@ tribe.validation = {};
83
  return condition.constraint >= condition.value;
84
  },
85
  isEqualTo: function( value, constraint, $field ) {
86
- var condition = obj.parseConditon( 'isEqualTo', value, constraint, $field );
87
 
88
  // If we failed to parse Condition we don't check
89
  if ( false === condition ) {
@@ -93,7 +95,7 @@ tribe.validation = {};
93
  return condition.constraint == condition.value;
94
  },
95
  isNotEqualTo: function( value, constraint, $field ) {
96
- var condition = obj.parseConditon( 'isNotEqualTo', value, constraint, $field );
97
 
98
  // If we failed to parse Condition we don't check
99
  if ( false === condition ) {
@@ -179,7 +181,7 @@ tribe.validation = {};
179
  *
180
  * @return {object}
181
  */
182
- obj.parseConditon = function( conditional, value, constraint, $field ) {
183
  var type = $field.data( 'validationType' );
184
  var $constraint = null;
185
  var condition = { value: value, constraint: constraint };
@@ -330,8 +332,8 @@ tribe.validation = {};
330
  }
331
 
332
  return value;
333
- }
334
- }
335
 
336
  /**
337
  * FN (prototype) method from jQuery
@@ -349,8 +351,8 @@ tribe.validation = {};
349
  *
350
  * @since 4.7
351
  *
352
- * @param {int} index Field Index
353
- * @param {DOM} item DOM element for the item
354
  *
355
  * @type {function}
356
  */
@@ -431,28 +433,28 @@ tribe.validation = {};
431
  *
432
  * @param {object} $field jQuery Object for the Section been validated
433
  *
434
- * @return {bool}
435
  */
436
  obj.hasErrors = function( $item ) {
437
  var $errors = $item.find( obj.selectors.error ).not( ':disabled' );
438
 
439
  return 0 !== $errors.length;
440
- }
441
 
442
  /**
443
- * Gets which constrains have Passed
444
  *
445
  * @since 4.7
446
  *
447
- * @param {object} $field jQuery Object for the field
448
  *
449
- * @return {object}
450
  */
451
  obj.getConstraints = function( $field ) {
452
  var isDisabled = $field.is( ':disabled' );
453
  var valid = true;
454
 
455
- // Bail if it's a disabled field
456
  if ( isDisabled ) {
457
  return valid;
458
  }
@@ -460,27 +462,27 @@ tribe.validation = {};
460
  var constraints = obj.getConstraintsValue( $field );
461
  var value = $field.val();
462
 
463
- // When we don't have constrains it's always valid
464
  if ( _.isEmpty( constraints ) ) {
465
  return valid;
466
  }
467
 
468
- // Verifies if we have a valid set of constraints
469
  constraints = _.mapObject( constraints, function( constraint, key ) {
470
  return obj.conditions[ key ]( value, constraint, $field );
471
  } );
472
 
473
  return constraints;
474
- }
475
 
476
  /**
477
- * Gets which constrainst have valid values
478
  *
479
  * @since 4.7
480
  *
481
- * @param {object} $constraints Object with all the values for the contraints of a field
482
  *
483
- * @return {object}
484
  */
485
  obj.getConstraintsValue = function( $field ) {
486
  var isDisabled = $field.is( ':disabled' );
@@ -491,7 +493,7 @@ tribe.validation = {};
491
  return constraints;
492
  }
493
 
494
- // Set to all contraints
495
  constraints = obj.constraints;
496
 
497
  // Fetch the values for each one of these
@@ -512,9 +514,9 @@ tribe.validation = {};
512
  *
513
  * @since 4.7
514
  *
515
- * @param {object} $fields jQuery Object for the fields
516
  *
517
- * @return {object}
518
  */
519
  obj.getConstraintsFields = function( $field ) {
520
  var constraints = obj.getConstraintsValue( $field );
@@ -529,19 +531,19 @@ tribe.validation = {};
529
  return $constraint;
530
  } );
531
 
532
- // Check which ones of these are not null
533
  constraints = _.pick( constraints, function( value ) {
534
  return value instanceof jQuery;
535
  } );
536
 
537
- // Turn this into an proper array
538
  constraints = _.values( constraints );
539
 
540
- // Add the current field
541
  constraints.unshift( $field );
542
 
543
- // Conver to jQuery collection
544
- constraints = $( constraints ).map( function () {
545
  return this.get();
546
  } );
547
 
@@ -555,7 +557,7 @@ tribe.validation = {};
555
  *
556
  * @param {object} event JQuery Event
557
  *
558
- * @return {void|false}
559
  */
560
  obj.onValidation = function( event ) {
561
  var $item = $( this );
@@ -642,15 +644,15 @@ tribe.validation = {};
642
  };
643
 
644
  /**
645
- * Validates a single Field
646
  *
647
  * @since 4.7
648
  *
649
- * @param {string} message Message to be Attached
650
- * @param {object} $field jQuery Object for the field
651
- * @param {object} $list jQuery Object for list of Errors
652
  *
653
- * @return {void}
654
  */
655
  obj.addErrorLine = function( message, $field, $list ) {
656
  var $listItem = $( '<li>' ).text( message );
@@ -665,13 +667,13 @@ tribe.validation = {};
665
  };
666
 
667
  /**
668
- * Hooks to the submit and if invalid prevents submit from completing
669
  *
670
  * @since 4.7
671
  *
672
- * @param {object} event JQuery Event
673
  *
674
- * @return {void|false}
675
  */
676
  obj.onSubmit = function( event ) {
677
  var $item = $( this );
@@ -688,12 +690,12 @@ tribe.validation = {};
688
  };
689
 
690
  /**
691
- * Hijack the Browser the Invalidation
692
  *
693
  * Note that it this weird multi-method is required to go around
694
  * the usage of 'invalid' event, which doesn't bubble up to 'form'
695
  * only happens on the Field, which prevents us to use it on
696
- * the ones that are created by JavaScript Templates
697
  *
698
  * @since 4.7
699
  *
@@ -701,7 +703,7 @@ tribe.validation = {};
701
  *
702
  * @param {object} event JQuery Event
703
  *
704
- * @return {void}
705
  */
706
  obj.onClickSubmitButtons = function( event ) {
707
  var $submit = $( this );
@@ -726,15 +728,15 @@ tribe.validation = {};
726
 
727
  /**
728
  * Add a class to mark fields that are invalid and add an one time
729
- * event for these same fields to remove the class on `change`
730
  *
731
  * @since 4.7
732
  *
733
  * @uses obj.onChangeFieldRemoveError
734
  *
735
- * @param {object} event JQuery Event
736
  *
737
- * @return {void|false}
738
  */
739
  obj.onInvalidField = function( event ) {
740
  var $field = $( this );
@@ -758,7 +760,9 @@ tribe.validation = {};
758
  *
759
  * @since 4.7
760
  *
761
- * @return {void}
 
 
762
  */
763
  obj.onChangeFieldRemoveError = function( event ) {
764
  var $field = $( this );
@@ -770,11 +774,13 @@ tribe.validation = {};
770
  };
771
 
772
  /**
773
- * Removes the Notice
774
  *
775
  * @since 4.7
776
  *
777
- * @return {void}
 
 
778
  */
779
  obj.onClickDismissNotice = function( event ) {
780
  var $dismiss = $( this );
@@ -789,9 +795,9 @@ tribe.validation = {};
789
  *
790
  * @since 4.7
791
  *
792
- * @param {object} event jQuery Event
793
  *
794
- * @return {void}
795
  */
796
  obj.onReady = function( event ) {
797
  $( obj.selectors.item ).validation();
@@ -802,7 +808,7 @@ tribe.validation = {};
802
  *
803
  * @since 4.7
804
  *
805
- * @return {void}
806
  */
807
  $.fn.validation = obj.fn;
808
 
@@ -811,5 +817,5 @@ tribe.validation = {};
811
  *
812
  * @since 4.7
813
  */
814
- $document.ready( obj.onReady );
815
  }( tribe.validation, jQuery, window.underscore || window._ ) );
1
+ /* global tribe, console, jQuery */
2
+
3
  /**
4
  * Configures this Object in the Global Tribe variable
5
  *
16
  /**
17
  * Object containing all the selectors for Validation
18
  *
19
+ * @since 4.7.1
20
  *
21
  * @type {object}
22
  */
30
  notice: '.tribe-notice-validation',
31
  noticeAfter: '.wp-header-end',
32
  noticeFallback: '.wrap > h1',
33
+ noticeDismiss: '.notice-dismiss',
34
  };
35
 
36
  /**
45
  return '' != value;
46
  },
47
  isGreaterThan: function( value, constraint, $field ) {
48
+ var condition = obj.parseCondition( 'isGreaterThan', value, constraint, $field );
49
 
50
  // If we failed to parse Condition we don't check
51
  if ( false === condition ) {
55
  return condition.constraint < condition.value;
56
  },
57
  isGreaterOrEqualTo: function( value, constraint, $field ) {
58
+ var condition = obj.parseCondition( 'isGreaterOrEqualTo', value, constraint, $field );
59
 
60
  // If we failed to parse Condition we don't check
61
  if ( false === condition ) {
65
  return condition.constraint <= condition.value;
66
  },
67
  isLessThan: function( value, constraint, $field ) {
68
+ var condition = obj.parseCondition( 'isLessThan', value, constraint, $field );
69
 
70
  // If we failed to parse Condition we don't check
71
  if ( false === condition ) {
75
  return condition.constraint > condition.value;
76
  },
77
  isLessOrEqualTo: function( value, constraint, $field ) {
78
+ var condition = obj.parseCondition( 'isLessOrEqualTo', value, constraint, $field );
79
 
80
  // If we failed to parse Condition we don't check
81
  if ( false === condition ) {
85
  return condition.constraint >= condition.value;
86
  },
87
  isEqualTo: function( value, constraint, $field ) {
88
+ var condition = obj.parseCondition( 'isEqualTo', value, constraint, $field );
89
 
90
  // If we failed to parse Condition we don't check
91
  if ( false === condition ) {
95
  return condition.constraint == condition.value;
96
  },
97
  isNotEqualTo: function( value, constraint, $field ) {
98
+ var condition = obj.parseCondition( 'isNotEqualTo', value, constraint, $field );
99
 
100
  // If we failed to parse Condition we don't check
101
  if ( false === condition ) {
181
  *
182
  * @return {object}
183
  */
184
+ obj.parseCondition = function( conditional, value, constraint, $field ) {
185
  var type = $field.data( 'validationType' );
186
  var $constraint = null;
187
  var condition = { value: value, constraint: constraint };
332
  }
333
 
334
  return value;
335
+ },
336
+ };
337
 
338
  /**
339
  * FN (prototype) method from jQuery
351
  *
352
  * @since 4.7
353
  *
354
+ * @param {int} i Field Index
355
+ * @param {DOM} item DOM element for the item
356
  *
357
  * @type {function}
358
  */
433
  *
434
  * @param {object} $field jQuery Object for the Section been validated
435
  *
436
+ * @return {boolean}
437
  */
438
  obj.hasErrors = function( $item ) {
439
  var $errors = $item.find( obj.selectors.error ).not( ':disabled' );
440
 
441
  return 0 !== $errors.length;
442
+ };
443
 
444
  /**
445
+ * Gets which constrains have Passed.
446
  *
447
  * @since 4.7
448
  *
449
+ * @param {object} $field jQuery Object for the field.
450
  *
451
+ * @return {object} Constraints that have passed.
452
  */
453
  obj.getConstraints = function( $field ) {
454
  var isDisabled = $field.is( ':disabled' );
455
  var valid = true;
456
 
457
+ // Bail if it's a disabled field.
458
  if ( isDisabled ) {
459
  return valid;
460
  }
462
  var constraints = obj.getConstraintsValue( $field );
463
  var value = $field.val();
464
 
465
+ // When we don't have constrains it's always valid.
466
  if ( _.isEmpty( constraints ) ) {
467
  return valid;
468
  }
469
 
470
+ // Verifies if we have a valid set of constraints.
471
  constraints = _.mapObject( constraints, function( constraint, key ) {
472
  return obj.conditions[ key ]( value, constraint, $field );
473
  } );
474
 
475
  return constraints;
476
+ };
477
 
478
  /**
479
+ * Gets which constraint have valid values.
480
  *
481
  * @since 4.7
482
  *
483
+ * @param {object} $field Object with all the values for the constraints of a field.
484
  *
485
+ * @return {object} Specific constraint value.
486
  */
487
  obj.getConstraintsValue = function( $field ) {
488
  var isDisabled = $field.is( ':disabled' );
493
  return constraints;
494
  }
495
 
496
+ // Set to all constraints
497
  constraints = obj.constraints;
498
 
499
  // Fetch the values for each one of these
514
  *
515
  * @since 4.7
516
  *
517
+ * @param {object} $field jQuery Object for the fields
518
  *
519
+ * @return {object} Constraints for validation.
520
  */
521
  obj.getConstraintsFields = function( $field ) {
522
  var constraints = obj.getConstraintsValue( $field );
531
  return $constraint;
532
  } );
533
 
534
+ // Check which ones of these are not null.
535
  constraints = _.pick( constraints, function( value ) {
536
  return value instanceof jQuery;
537
  } );
538
 
539
+ // Turn this into an proper array.
540
  constraints = _.values( constraints );
541
 
542
+ // Add the current field.
543
  constraints.unshift( $field );
544
 
545
+ // Convert to jQuery collection.
546
+ constraints = $( constraints ).map( function() {
547
  return this.get();
548
  } );
549
 
557
  *
558
  * @param {object} event JQuery Event
559
  *
560
+ * @return {void}
561
  */
562
  obj.onValidation = function( event ) {
563
  var $item = $( this );
644
  };
645
 
646
  /**
647
+ * Validates a single Field.
648
  *
649
  * @since 4.7
650
  *
651
+ * @param {string} message Message to be Attached.
652
+ * @param {object} $field jQuery Object for the field.
653
+ * @param {object} $list jQuery Object for list of Errors.
654
  *
655
+ * @return {void} No return.
656
  */
657
  obj.addErrorLine = function( message, $field, $list ) {
658
  var $listItem = $( '<li>' ).text( message );
667
  };
668
 
669
  /**
670
+ * Hooks to the submit and if invalid prevents submit from completing.
671
  *
672
  * @since 4.7
673
  *
674
+ * @param {object} event JQuery Event.
675
  *
676
+ * @return {void|boolean} When invalid it prevent bubble.
677
  */
678
  obj.onSubmit = function( event ) {
679
  var $item = $( this );
690
  };
691
 
692
  /**
693
+ * Hijack the Browser the Invalidation.
694
  *
695
  * Note that it this weird multi-method is required to go around
696
  * the usage of 'invalid' event, which doesn't bubble up to 'form'
697
  * only happens on the Field, which prevents us to use it on
698
+ * the ones that are created by JavaScript Templates.
699
  *
700
  * @since 4.7
701
  *
703
  *
704
  * @param {object} event JQuery Event
705
  *
706
+ * @return {void} No return.
707
  */
708
  obj.onClickSubmitButtons = function( event ) {
709
  var $submit = $( this );
728
 
729
  /**
730
  * Add a class to mark fields that are invalid and add an one time
731
+ * event for these same fields to remove the class on `change`.
732
  *
733
  * @since 4.7
734
  *
735
  * @uses obj.onChangeFieldRemoveError
736
  *
737
+ * @param {Event} event JQuery Event.
738
  *
739
+ * @return {boolean} Return false to avoid bubble up.
740
  */
741
  obj.onInvalidField = function( event ) {
742
  var $field = $( this );
760
  *
761
  * @since 4.7
762
  *
763
+ * @param {Event} event JQuery Event.
764
+ *
765
+ * @return {void} No return.
766
  */
767
  obj.onChangeFieldRemoveError = function( event ) {
768
  var $field = $( this );
774
  };
775
 
776
  /**
777
+ * Removes the Notice.
778
  *
779
  * @since 4.7
780
  *
781
+ * @param {Event} event JQuery Event.
782
+ *
783
+ * @return {void} No return.
784
  */
785
  obj.onClickDismissNotice = function( event ) {
786
  var $dismiss = $( this );
795
  *
796
  * @since 4.7
797
  *
798
+ * @param {Event} event JQuery Event.
799
  *
800
+ * @return {void} No return.
801
  */
802
  obj.onReady = function( event ) {
803
  $( obj.selectors.item ).validation();
808
  *
809
  * @since 4.7
810
  *
811
+ * @return {void} No return.
812
  */
813
  $.fn.validation = obj.fn;
814
 
817
  *
818
  * @since 4.7
819
  */
820
+ $( obj.onReady );
821
  }( tribe.validation, jQuery, window.underscore || window._ ) );
common/src/resources/js/validation.min.js CHANGED
@@ -1 +1 @@
1
- tribe.validation={},function(t,e,a){"use strict";var i=e(document);t.selectors={item:".tribe-validation",fields:"input, select, textarea",submit:".tribe-validation-submit",submitButtons:'input[type="submit"], button[type="submit"]',error:".tribe-validation-error",valid:".tribe-validation-valid",notice:".tribe-notice-validation",noticeAfter:".wp-header-end",noticeFallback:".wrap > h1",noticeDismiss:".notice-dismiss"},t.conditions={isRequired:function(t){return""!=t},isGreaterThan:function(e,a,i){var n=t.parseConditon("isGreaterThan",e,a,i);return!1===n||n.constraint<n.value},isGreaterOrEqualTo:function(e,a,i){var n=t.parseConditon("isGreaterOrEqualTo",e,a,i);return!1===n||n.constraint<=n.value},isLessThan:function(e,a,i){var n=t.parseConditon("isLessThan",e,a,i);return!1===n||n.constraint>n.value},isLessOrEqualTo:function(e,a,i){var n=t.parseConditon("isLessOrEqualTo",e,a,i);return!1===n||n.constraint>=n.value},isEqualTo:function(e,a,i){var n=t.parseConditon("isEqualTo",e,a,i);return!1===n||n.constraint==n.value},isNotEqualTo:function(e,a,i){var n=t.parseConditon("isNotEqualTo",e,a,i);return!1===n||n.constraint!=n.value},matchRegExp:function(t,e,a){return null!==new RegExp(e,"g").exec(t)},notMatchRegExp:function(t,e,a){return null===new RegExp(e,"g").exec(t)}},t.parseType={datepicker:function(t,e,i){var n=["yyyy-mm-dd","m/d/yyyy","mm/dd/yyyy","d/m/yyyy","dd/mm/yyyy","m-d-yyyy","mm-dd-yyyy","d-m-yyyy","dd-mm-yyyy","yyyy.mm.dd","mm.dd.yyyy","dd.mm.yyyy"],r=0;e.length&&e.attr("data-datepicker_format")?r=e.attr("data-datepicker_format"):a.isString(n[e])?r=n[e]:e.parents("[data-datepicker_format]").length&&(r=e.parents("[data-datepicker_format]").eq(0).data("datepicker_format")),void 0!==n[r]&&n[r]||(r=0);var s=n[r].toUpperCase();return t=moment(t,s).format("X")},default:function(t,a,i){return e.isNumeric(t)&&(t=parseFloat(t,10)),t}},t.parseConditon=function(i,n,r,s){var o=s.data("validationType"),l=null,d={value:n,constraint:r};if(o||a.isFunction(t.parseType[o])||(o="default"),!e.isNumeric(r)){if(!(l=e(r)).length)return console.warn("Tribe Validation:","Invalid selector for",s,r),!1;if(!(l=l.not(":disabled")).length)return!1;r=l.val()}return d.constraint=t.parseType[o](r,l,s),d.value=t.parseType[o](n,l,s),d},t.constraints={isRequired:function(t){var e=null;return e=t.data("required")||e,e=t.data("validationRequired")||e,e=t.data("validationIsRequired")||e,e=t.is("[required]")||e,e=t.is("[data-required]")||e,e=t.is("[data-validation-required]")||e,e=t.is("[data-validation-is-required]")||e},isGreaterThan:function(t){var e=null;return t.is("[data-validation-is-greater-than]")&&(e=t.data("validationIsGreaterThan")),e},isGreaterOrEqualTo:function(t){var e=null;return t.is("[data-validation-is-greater-or-equal-to]")&&(e=t.data("validationIsGreaterOrEqualTo")),e},isLessThan:function(t){var e=null;return t.is("[data-validation-is-less-than]")&&(e=t.data("validationIsLessThan")),e},isLessOrEqualTo:function(t){var e=null;return t.is("[data-validation-is-less-or-equal-to]")&&(e=t.data("validationIsLessOrEqualTo")),e},isEqualTo:function(t){var e=null;return t.is("[data-validation-is-equal-to]")&&(e=t.data("validationIsEqualTo")),e},isNotEqualTo:function(t){var e=null;return t.is("[data-validation-is-not-equal-to]")&&(e=t.data("validationIsNotEqualTo")),e},matchRegExp:function(t){var e=null;return t.is("[data-validation-match-regexp]")&&(e=t.data("validationMatchRegexp")),e},notMatchRegExp:function(t){var e=null;return t.is("[data-validation-not-match-regexp]")&&(e=t.data("validationNotMatchRegexp")),e}},t.fn=function(){return this.each(t.setup)},t.setup=function(a,n){var r=e(n);r.addClass(t.selectors.item.className()),r.find(t.selectors.submitButtons).addClass(t.selectors.submit.className()),r.on("submit.tribe",t.onSubmit),r.on("validation.tribe",t.onValidation),r.on("displayErrors.tribe",t.onDisplayErrors),i.on("click.tribe",t.selectors.submit,t.onClickSubmitButtons),i.on("click.tribe",t.selectors.noticeDismiss,t.onClickDismissNotice)},t.validate=function(a,i){var n=e(i);t.isValid(n)||(n.addClass(t.selectors.error.className()),n.one("change",t.onChangeFieldRemoveError))},t.isValid=function(e){var i=t.getConstraints(e);return a.isObject(i)?a.every(i):i},t.hasErrors=function(e){return 0!==e.find(t.selectors.error).not(":disabled").length},t.getConstraints=function(e){if(e.is(":disabled"))return!0;var i=t.getConstraintsValue(e),n=e.val();return!!a.isEmpty(i)||(i=a.mapObject(i,function(a,i){return t.conditions[i](n,a,e)}))},t.getConstraintsValue=function(e){var i={};return e.is(":disabled")?i:(i=t.constraints,i=a.mapObject(i,function(t){return t(e)}),i=a.pick(i,function(t){return null!==t}))},t.getConstraintsFields=function(i){var n=t.getConstraintsValue(i);return n=a.mapObject(n,function(t){var i=null;return a.isNumber(t)||a.isBoolean(t)||(i=e(t)),i}),n=a.pick(n,function(t){return t instanceof jQuery}),(n=a.values(n)).unshift(i),n=e(n).map(function(){return this.get()})},t.onValidation=function(a){var i=e(this),n=i.find(t.selectors.fields);n.removeClass(t.selectors.error.className()),n.each(t.validate),0===i.find(t.selectors.error).not(":disabled").length?i.addClass(t.selectors.valid.className()):i.trigger("displayErrors.tribe")},t.onDisplayErrors=function(n){var r=e(this).find(t.selectors.error).not(":disabled"),s=e("<ul>"),o=e("<span>").addClass(t.selectors.noticeDismiss.className()),l=i.find(t.selectors.notice),d=e("<div>").addClass("notice notice-error is-dismissible tribe-notice").addClass(t.selectors.notice.className()).append(o);if(r.each(function(i,n){var r=e(n),o=r.data("validationError");if(a.isObject(o)){var l={},d=t.getConstraints(r,!1);a.each(o,function(t,e){l[tribe.utils.camelCase(e)]=t}),a.each(d,function(e,a){e||t.addErrorLine(l[a],r,s)})}else t.addErrorLine(o,r,s)}),d.append(s),0===l.length){var u=i.find(t.selectors.noticeAfter);0===u.length&&(u=i.find(t.selectors.noticeFallback)),u.after(d)}else l.replaceWith(d)},t.addErrorLine=function(t,a,i){var n=e("<li>").text(t);n.data("validationField",a),a.data("validationNoticeItem",a),i.append(n)},t.onSubmit=function(a){var i=e(this);if(i.trigger("validation.tribe"),!i.is(t.selectors.valid))return a.preventDefault(),!1},t.onClickSubmitButtons=function(a){var i=e(this).parents(t.selectors.item);if(0!==i.length){i.trigger("validation.tribe");var n=i.find(t.selectors.fields);n.off("invalid.tribe"),n.one("invalid.tribe",t.onInvalidField)}},t.onInvalidField=function(a){var i=e(this),n=i.parents(t.selectors.item);return i.addClass(t.selectors.error.className()),n.trigger("displayErrors.tribe"),i.one("change",t.onChangeFieldRemoveError),a.preventDefault(),!1},t.onChangeFieldRemoveError=function(a){var i=e(this),n=t.getConstraintsFields(i);0!==n.filter(t.selectors.error).length&&n.removeClass(t.selectors.error.className())},t.onClickDismissNotice=function(a){e(this).parents(t.selectors.notice).remove()},t.onReady=function(a){e(t.selectors.item).validation()},e.fn.validation=t.fn,i.ready(t.onReady)}(tribe.validation,jQuery,window.underscore||window._);
1
+ tribe.validation={},function(t,e,i){"use strict";var a=e(document);t.selectors={item:".tribe-validation",fields:"input, select, textarea",submit:".tribe-validation-submit",submitButtons:'input[type="submit"], button[type="submit"]',error:".tribe-validation-error",valid:".tribe-validation-valid",notice:".tribe-notice-validation",noticeAfter:".wp-header-end",noticeFallback:".wrap > h1",noticeDismiss:".notice-dismiss"},t.conditions={isRequired:function(t){return""!=t},isGreaterThan:function(e,i,a){var n=t.parseCondition("isGreaterThan",e,i,a);return!1===n||n.constraint<n.value},isGreaterOrEqualTo:function(e,i,a){var n=t.parseCondition("isGreaterOrEqualTo",e,i,a);return!1===n||n.constraint<=n.value},isLessThan:function(e,i,a){var n=t.parseCondition("isLessThan",e,i,a);return!1===n||n.constraint>n.value},isLessOrEqualTo:function(e,i,a){var n=t.parseCondition("isLessOrEqualTo",e,i,a);return!1===n||n.constraint>=n.value},isEqualTo:function(e,i,a){var n=t.parseCondition("isEqualTo",e,i,a);return!1===n||n.constraint==n.value},isNotEqualTo:function(e,i,a){var n=t.parseCondition("isNotEqualTo",e,i,a);return!1===n||n.constraint!=n.value},matchRegExp:function(t,e,i){return null!==new RegExp(e,"g").exec(t)},notMatchRegExp:function(t,e,i){return null===new RegExp(e,"g").exec(t)}},t.parseType={datepicker:function(t,e,a){var n=["yyyy-mm-dd","m/d/yyyy","mm/dd/yyyy","d/m/yyyy","dd/mm/yyyy","m-d-yyyy","mm-dd-yyyy","d-m-yyyy","dd-mm-yyyy","yyyy.mm.dd","mm.dd.yyyy","dd.mm.yyyy"],r=0;e.length&&e.attr("data-datepicker_format")?r=e.attr("data-datepicker_format"):i.isString(n[e])?r=n[e]:e.parents("[data-datepicker_format]").length&&(r=e.parents("[data-datepicker_format]").eq(0).data("datepicker_format")),void 0!==n[r]&&n[r]||(r=0);var s=n[r].toUpperCase();return t=moment(t,s).format("X")},default:function(t,i,a){return e.isNumeric(t)&&(t=parseFloat(t,10)),t}},t.parseCondition=function(a,n,r,s){var o=s.data("validationType"),l=null,d={value:n,constraint:r};if(o||i.isFunction(t.parseType[o])||(o="default"),!e.isNumeric(r)){if(!(l=e(r)).length)return console.warn("Tribe Validation:","Invalid selector for",s,r),!1;if(!(l=l.not(":disabled")).length)return!1;r=l.val()}return d.constraint=t.parseType[o](r,l,s),d.value=t.parseType[o](n,l,s),d},t.constraints={isRequired:function(t){var e=null;return e=t.data("required")||e,e=t.data("validationRequired")||e,e=t.data("validationIsRequired")||e,e=t.is("[required]")||e,e=t.is("[data-required]")||e,e=t.is("[data-validation-required]")||e,e=t.is("[data-validation-is-required]")||e},isGreaterThan:function(t){var e=null;return t.is("[data-validation-is-greater-than]")&&(e=t.data("validationIsGreaterThan")),e},isGreaterOrEqualTo:function(t){var e=null;return t.is("[data-validation-is-greater-or-equal-to]")&&(e=t.data("validationIsGreaterOrEqualTo")),e},isLessThan:function(t){var e=null;return t.is("[data-validation-is-less-than]")&&(e=t.data("validationIsLessThan")),e},isLessOrEqualTo:function(t){var e=null;return t.is("[data-validation-is-less-or-equal-to]")&&(e=t.data("validationIsLessOrEqualTo")),e},isEqualTo:function(t){var e=null;return t.is("[data-validation-is-equal-to]")&&(e=t.data("validationIsEqualTo")),e},isNotEqualTo:function(t){var e=null;return t.is("[data-validation-is-not-equal-to]")&&(e=t.data("validationIsNotEqualTo")),e},matchRegExp:function(t){var e=null;return t.is("[data-validation-match-regexp]")&&(e=t.data("validationMatchRegexp")),e},notMatchRegExp:function(t){var e=null;return t.is("[data-validation-not-match-regexp]")&&(e=t.data("validationNotMatchRegexp")),e}},t.fn=function(){return this.each(t.setup)},t.setup=function(i,n){var r=e(n);r.addClass(t.selectors.item.className()),r.find(t.selectors.submitButtons).addClass(t.selectors.submit.className()),r.on("submit.tribe",t.onSubmit),r.on("validation.tribe",t.onValidation),r.on("displayErrors.tribe",t.onDisplayErrors),a.on("click.tribe",t.selectors.submit,t.onClickSubmitButtons),a.on("click.tribe",t.selectors.noticeDismiss,t.onClickDismissNotice)},t.validate=function(i,a){var n=e(a);t.isValid(n)||(n.addClass(t.selectors.error.className()),n.one("change",t.onChangeFieldRemoveError))},t.isValid=function(e){var a=t.getConstraints(e);return i.isObject(a)?i.every(a):a},t.hasErrors=function(e){return 0!==e.find(t.selectors.error).not(":disabled").length},t.getConstraints=function(e){if(e.is(":disabled"))return!0;var a=t.getConstraintsValue(e),n=e.val();return!!i.isEmpty(a)||(a=i.mapObject(a,function(i,a){return t.conditions[a](n,i,e)}))},t.getConstraintsValue=function(e){var a={};return e.is(":disabled")?a:(a=t.constraints,a=i.mapObject(a,function(t){return t(e)}),a=i.pick(a,function(t){return null!==t}))},t.getConstraintsFields=function(a){var n=t.getConstraintsValue(a);return n=i.mapObject(n,function(t){var a=null;return i.isNumber(t)||i.isBoolean(t)||(a=e(t)),a}),n=i.pick(n,function(t){return t instanceof jQuery}),(n=i.values(n)).unshift(a),n=e(n).map(function(){return this.get()})},t.onValidation=function(i){var a=e(this),n=a.find(t.selectors.fields);n.removeClass(t.selectors.error.className()),n.each(t.validate),0===a.find(t.selectors.error).not(":disabled").length?a.addClass(t.selectors.valid.className()):a.trigger("displayErrors.tribe")},t.onDisplayErrors=function(n){var r=e(this).find(t.selectors.error).not(":disabled"),s=e("<ul>"),o=e("<span>").addClass(t.selectors.noticeDismiss.className()),l=a.find(t.selectors.notice),d=e("<div>").addClass("notice notice-error is-dismissible tribe-notice").addClass(t.selectors.notice.className()).append(o);if(r.each(function(a,n){var r=e(n),o=r.data("validationError");if(i.isObject(o)){var l={},d=t.getConstraints(r,!1);i.each(o,function(t,e){l[tribe.utils.camelCase(e)]=t}),i.each(d,function(e,i){e||t.addErrorLine(l[i],r,s)})}else t.addErrorLine(o,r,s)}),d.append(s),0===l.length){var u=a.find(t.selectors.noticeAfter);0===u.length&&(u=a.find(t.selectors.noticeFallback)),u.after(d)}else l.replaceWith(d)},t.addErrorLine=function(t,i,a){var n=e("<li>").text(t);n.data("validationField",i),i.data("validationNoticeItem",i),a.append(n)},t.onSubmit=function(i){var a=e(this);if(a.trigger("validation.tribe"),!a.is(t.selectors.valid))return i.preventDefault(),!1},t.onClickSubmitButtons=function(i){var a=e(this).parents(t.selectors.item);if(0!==a.length){a.trigger("validation.tribe");var n=a.find(t.selectors.fields);n.off("invalid.tribe"),n.one("invalid.tribe",t.onInvalidField)}},t.onInvalidField=function(i){var a=e(this),n=a.parents(t.selectors.item);return a.addClass(t.selectors.error.className()),n.trigger("displayErrors.tribe"),a.one("change",t.onChangeFieldRemoveError),i.preventDefault(),!1},t.onChangeFieldRemoveError=function(i){var a=e(this),n=t.getConstraintsFields(a);0!==n.filter(t.selectors.error).length&&n.removeClass(t.selectors.error.className())},t.onClickDismissNotice=function(i){e(this).parents(t.selectors.notice).remove()},t.onReady=function(i){e(t.selectors.item).validation()},e.fn.validation=t.fn,e(t.onReady)}(tribe.validation,jQuery,window.underscore||window._);
common/src/views/dialog/button.php CHANGED
@@ -8,9 +8,10 @@
8
  * @since 4.10.0
9
  * @since 4.12.15 Add data attributes to the button.
10
  * @since 4.12.15 Don't render template if `$button_display` is set to false.
 
11
  *
12
  * @package Tribe
13
- * @version 4.12.15
14
  */
15
 
16
  if ( empty( $button_display ) ) {
@@ -42,4 +43,4 @@ $attributes = $button_attributes ?: [];
42
  <?php if ( ! empty( $button_disabled ) && tribe_is_truthy( $button_disabled ) ) : ?>
43
  <?php tribe_disabled( true ); ?>
44
  <?php endif; ?>
45
- ><?php echo esc_html( $button_text ); ?></button>
8
  * @since 4.10.0
9
  * @since 4.12.15 Add data attributes to the button.
10
  * @since 4.12.15 Don't render template if `$button_display` is set to false.
11
+ * @since 4.12.17 Allow having basic HTMl within the button content so we can add elements with texts for a11y.
12
  *
13
  * @package Tribe
14
+ * @version 4.12.17
15
  */
16
 
17
  if ( empty( $button_display ) ) {
43
  <?php if ( ! empty( $button_disabled ) && tribe_is_truthy( $button_disabled ) ) : ?>
44
  <?php tribe_disabled( true ); ?>
45
  <?php endif; ?>
46
+ ><?php echo wp_kses_post( $button_text ); ?></button>
common/vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit8c2d0aa934f94fd726080a791ddb34b9::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInite0a2325a0c4af72c150bb1561cebce0a::getLoader();
common/vendor/autoload_52.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once dirname(__FILE__) . '/composer'.'/autoload_real_52.php';
6
 
7
- return ComposerAutoloaderInit99029225c2968a7a0a4cb5b4811de16d::getLoader();
4
 
5
  require_once dirname(__FILE__) . '/composer'.'/autoload_real_52.php';
6
 
7
+ return ComposerAutoloaderInitb79b2a6396f989d596b5d39571fdea70::getLoader();
common/vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit8c2d0aa934f94fd726080a791ddb34b9
6
  {
7
  private static $loader;
8
 
@@ -19,15 +19,15 @@ class ComposerAutoloaderInit8c2d0aa934f94fd726080a791ddb34b9
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit8c2d0aa934f94fd726080a791ddb34b9', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit8c2d0aa934f94fd726080a791ddb34b9', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
- call_user_func(\Composer\Autoload\ComposerStaticInit8c2d0aa934f94fd726080a791ddb34b9::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInite0a2325a0c4af72c150bb1561cebce0a
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInite0a2325a0c4af72c150bb1561cebce0a', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInite0a2325a0c4af72c150bb1561cebce0a', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
+ call_user_func(\Composer\Autoload\ComposerStaticInite0a2325a0c4af72c150bb1561cebce0a::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
common/vendor/composer/autoload_real_52.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real_52.php generated by xrstf/composer-php52
4
 
5
- class ComposerAutoloaderInit99029225c2968a7a0a4cb5b4811de16d {
6
  private static $loader;
7
 
8
  public static function loadClassLoader($class) {
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit99029225c2968a7a0a4cb5b4811de16d {
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit99029225c2968a7a0a4cb5b4811de16d', 'loadClassLoader'), true /*, true */);
23
  self::$loader = $loader = new xrstf_Composer52_ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit99029225c2968a7a0a4cb5b4811de16d', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(dirname(__FILE__));
27
  $baseDir = dirname($vendorDir);
2
 
3
  // autoload_real_52.php generated by xrstf/composer-php52
4
 
5
+ class ComposerAutoloaderInitb79b2a6396f989d596b5d39571fdea70 {
6
  private static $loader;
7
 
8
  public static function loadClassLoader($class) {
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInitb79b2a6396f989d596b5d39571fdea70', 'loadClassLoader'), true /*, true */);
23
  self::$loader = $loader = new xrstf_Composer52_ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInitb79b2a6396f989d596b5d39571fdea70', 'loadClassLoader'));
25
 
26
  $vendorDir = dirname(dirname(__FILE__));
27
  $baseDir = dirname($vendorDir);
common/vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit8c2d0aa934f94fd726080a791ddb34b9
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
  'T' =>
@@ -228,10 +228,10 @@ class ComposerStaticInit8c2d0aa934f94fd726080a791ddb34b9
228
  public static function getInitializer(ClassLoader $loader)
229
  {
230
  return \Closure::bind(function () use ($loader) {
231
- $loader->prefixLengthsPsr4 = ComposerStaticInit8c2d0aa934f94fd726080a791ddb34b9::$prefixLengthsPsr4;
232
- $loader->prefixDirsPsr4 = ComposerStaticInit8c2d0aa934f94fd726080a791ddb34b9::$prefixDirsPsr4;
233
- $loader->prefixesPsr0 = ComposerStaticInit8c2d0aa934f94fd726080a791ddb34b9::$prefixesPsr0;
234
- $loader->classMap = ComposerStaticInit8c2d0aa934f94fd726080a791ddb34b9::$classMap;
235
 
236
  }, null, ClassLoader::class);
237
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInite0a2325a0c4af72c150bb1561cebce0a
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
  'T' =>
228
  public static function getInitializer(ClassLoader $loader)
229
  {
230
  return \Closure::bind(function () use ($loader) {
231
+ $loader->prefixLengthsPsr4 = ComposerStaticInite0a2325a0c4af72c150bb1561cebce0a::$prefixLengthsPsr4;
232
+ $loader->prefixDirsPsr4 = ComposerStaticInite0a2325a0c4af72c150bb1561cebce0a::$prefixDirsPsr4;
233
+ $loader->prefixesPsr0 = ComposerStaticInite0a2325a0c4af72c150bb1561cebce0a::$prefixesPsr0;
234
+ $loader->classMap = ComposerStaticInite0a2325a0c4af72c150bb1561cebce0a::$classMap;
235
 
236
  }, null, ClassLoader::class);
237
  }
common/vendor/composer/installed.json CHANGED
@@ -1,16 +1,4 @@
1
  [
2
- {
3
- "name": "faction23/a11y-dialog",
4
- "version": "5.0.4",
5
- "version_normalized": "5.0.4.0",
6
- "source": {
7
- "type": "git",
8
- "url": "https://github.com/faction23/a11y-dialog",
9
- "reference": "master"
10
- },
11
- "type": "library",
12
- "installation-source": "source"
13
- },
14
  {
15
  "name": "firebase/php-jwt",
16
  "version": "v5.0.0",
@@ -186,23 +174,23 @@
186
  },
187
  {
188
  "name": "psr/log",
189
- "version": "1.1.4",
190
- "version_normalized": "1.1.4.0",
191
  "source": {
192
  "type": "git",
193
  "url": "https://github.com/php-fig/log.git",
194
- "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
195
  },
196
  "dist": {
197
  "type": "zip",
198
- "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
199
- "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
200
  "shasum": ""
201
  },
202
  "require": {
203
  "php": ">=5.3.0"
204
  },
205
- "time": "2021-05-03T11:20:27+00:00",
206
  "type": "library",
207
  "extra": {
208
  "branch-alias": {
@@ -222,7 +210,7 @@
222
  "authors": [
223
  {
224
  "name": "PHP-FIG",
225
- "homepage": "https://www.php-fig.org/"
226
  }
227
  ],
228
  "description": "Common interface for logging libraries",
1
  [
 
 
 
 
 
 
 
 
 
 
 
 
2
  {
3
  "name": "firebase/php-jwt",
4
  "version": "v5.0.0",
174
  },
175
  {
176
  "name": "psr/log",
177
+ "version": "1.1.3",
178
+ "version_normalized": "1.1.3.0",
179
  "source": {
180
  "type": "git",
181
  "url": "https://github.com/php-fig/log.git",
182
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
183
  },
184
  "dist": {
185
  "type": "zip",
186
+ "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
187
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
188
  "shasum": ""
189
  },
190
  "require": {
191
  "php": ">=5.3.0"
192
  },
193
+ "time": "2020-03-23T09:12:05+00:00",
194
  "type": "library",
195
  "extra": {
196
  "branch-alias": {
210
  "authors": [
211
  {
212
  "name": "PHP-FIG",
213
+ "homepage": "http://www.php-fig.org/"
214
  }
215
  ],
216
  "description": "Common interface for logging libraries",
common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_asc.png RENAMED
File without changes
common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_asc_disabled.png RENAMED
File without changes
common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_both.png RENAMED
File without changes
common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_desc.png RENAMED
File without changes
common/vendor/datatables/{DataTables → DataTables-1.10.23}/images/sort_desc_disabled.png RENAMED
File without changes
common/vendor/datatables/datatables.css ADDED
@@ -0,0 +1,1279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * This combined file was created by the DataTables downloader builder:
3
+ * https://datatables.net/download
4
+ *
5
+ * To rebuild or modify this file with the latest versions of the included
6
+ * software please visit:
7
+ * https://datatables.net/download/#dt/dt-1.10.23/af-2.3.5/b-1.6.5/cr-1.5.3/fc-3.3.2/fh-3.1.8/kt-2.6.1/r-2.2.7/rr-1.2.7/sc-2.0.3/sl-1.3.1
8
+ *
9
+ * Included libraries:
10
+ * DataTables 1.10.23, AutoFill 2.3.5, Buttons 1.6.5, ColReorder 1.5.3, FixedColumns 3.3.2, FixedHeader 3.1.8, KeyTable 2.6.1, Responsive 2.2.7, RowReorder 1.2.7, Scroller 2.0.3, Select 1.3.1
11
+ */
12
+
13
+ /*
14
+ * Table styles
15
+ */
16
+ table.dataTable {
17
+ width: 100%;
18
+ margin: 0 auto;
19
+ clear: both;
20
+ border-collapse: separate;
21
+ border-spacing: 0;
22
+ /*
23
+ * Header and footer styles
24
+ */
25
+ /*
26
+ * Body styles
27
+ */
28
+ }
29
+ table.dataTable thead th,
30
+ table.dataTable tfoot th {
31
+ font-weight: bold;
32
+ }
33
+ table.dataTable thead th,
34
+ table.dataTable thead td {
35
+ padding: 10px 18px;
36
+ border-bottom: 1px solid #111;
37
+ }
38
+ table.dataTable thead th:active,
39
+ table.dataTable thead td:active {
40
+ outline: none;
41
+ }
42
+ table.dataTable tfoot th,
43
+ table.dataTable tfoot td {
44
+ padding: 10px 18px 6px 18px;
45
+ border-top: 1px solid #111;
46
+ }
47
+ table.dataTable thead .sorting,
48
+ table.dataTable thead .sorting_asc,
49
+ table.dataTable thead .sorting_desc,
50
+ table.dataTable thead .sorting_asc_disabled,
51
+ table.dataTable thead .sorting_desc_disabled {
52
+ cursor: pointer;
53
+ *cursor: hand;
54
+ background-repeat: no-repeat;
55
+ background-position: center right;
56
+ }
57
+ table.dataTable thead .sorting {
58
+ background-image: url("DataTables-1.10.23/images/sort_both.png");
59
+ }
60
+ table.dataTable thead .sorting_asc {
61
+ background-image: url("DataTables-1.10.23/images/sort_asc.png");
62
+ }
63
+ table.dataTable thead .sorting_desc {
64
+ background-image: url("DataTables-1.10.23/images/sort_desc.png");
65
+ }
66
+ table.dataTable thead .sorting_asc_disabled {
67
+ background-image: url("DataTables-1.10.23/images/sort_asc_disabled.png");
68
+ }
69
+ table.dataTable thead .sorting_desc_disabled {
70
+ background-image: url("DataTables-1.10.23/images/sort_desc_disabled.png");
71
+ }
72
+ table.dataTable tbody tr {
73
+ background-color: #ffffff;
74
+ }
75
+ table.dataTable tbody tr.selected {
76
+ background-color: #B0BED9;
77
+ }
78
+ table.dataTable tbody th,
79
+ table.dataTable tbody td {
80
+ padding: 8px 10px;
81
+ }
82
+ table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td {
83
+ border-top: 1px solid #ddd;
84
+ }
85
+ table.dataTable.row-border tbody tr:first-child th,
86
+ table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th,
87
+ table.dataTable.display tbody tr:first-child td {
88
+ border-top: none;
89
+ }
90
+ table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td {
91
+ border-top: 1px solid #ddd;
92
+ border-right: 1px solid #ddd;
93
+ }
94
+ table.dataTable.cell-border tbody tr th:first-child,
95
+ table.dataTable.cell-border tbody tr td:first-child {
96
+ border-left: 1px solid #ddd;
97
+ }
98
+ table.dataTable.cell-border tbody tr:first-child th,
99
+ table.dataTable.cell-border tbody tr:first-child td {
100
+ border-top: none;
101
+ }
102
+ table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd {
103
+ background-color: #f9f9f9;
104
+ }
105
+ table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected {
106
+ background-color: #acbad4;
107
+ }
108
+ table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover {
109
+ background-color: #f6f6f6;
110
+ }
111
+ table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected {
112
+ background-color: #aab7d1;
113
+ }
114
+ table.dataTable.order-column tbody tr > .sorting_1,
115
+ table.dataTable.order-column tbody tr > .sorting_2,
116
+ table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1,
117
+ table.dataTable.display tbody tr > .sorting_2,
118
+ table.dataTable.display tbody tr > .sorting_3 {
119
+ background-color: #fafafa;
120
+ }
121
+ table.dataTable.order-column tbody tr.selected > .sorting_1,
122
+ table.dataTable.order-column tbody tr.selected > .sorting_2,
123
+ table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1,
124
+ table.dataTable.display tbody tr.selected > .sorting_2,
125
+ table.dataTable.display tbody tr.selected > .sorting_3 {
126
+ background-color: #acbad5;
127
+ }
128
+ table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
129
+ background-color: #f1f1f1;
130
+ }
131
+ table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
132
+ background-color: #f3f3f3;
133
+ }
134
+ table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
135
+ background-color: whitesmoke;
136
+ }
137
+ table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
138
+ background-color: #a6b4cd;
139
+ }
140
+ table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
141
+ background-color: #a8b5cf;
142
+ }
143
+ table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
144
+ background-color: #a9b7d1;
145
+ }
146
+ table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
147
+ background-color: #fafafa;
148
+ }
149
+ table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
150
+ background-color: #fcfcfc;
151
+ }
152
+ table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
153
+ background-color: #fefefe;
154
+ }
155
+ table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
156
+ background-color: #acbad5;
157
+ }
158
+ table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
159
+ background-color: #aebcd6;
160
+ }
161
+ table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
162
+ background-color: #afbdd8;
163
+ }
164
+ table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {
165
+ background-color: #eaeaea;
166
+ }
167
+ table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {
168
+ background-color: #ececec;
169
+ }
170
+ table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {
171
+ background-color: #efefef;
172
+ }
173
+ table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {
174
+ background-color: #a2aec7;
175
+ }
176
+ table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {
177
+ background-color: #a3b0c9;
178
+ }
179
+ table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {
180
+ background-color: #a5b2cb;
181
+ }
182
+ table.dataTable.no-footer {
183
+ border-bottom: 1px solid #111;
184
+ }
185
+ table.dataTable.nowrap th, table.dataTable.nowrap td {
186
+ white-space: nowrap;
187
+ }
188
+ table.dataTable.compact thead th,
189
+ table.dataTable.compact thead td {
190
+ padding: 4px 17px;
191
+ }
192
+ table.dataTable.compact tfoot th,
193
+ table.dataTable.compact tfoot td {
194
+ padding: 4px;
195
+ }
196
+ table.dataTable.compact tbody th,
197
+ table.dataTable.compact tbody td {
198
+ padding: 4px;
199
+ }
200
+ table.dataTable th.dt-left,
201
+ table.dataTable td.dt-left {
202
+ text-align: left;
203
+ }
204
+ table.dataTable th.dt-center,
205
+ table.dataTable td.dt-center,
206
+ table.dataTable td.dataTables_empty {
207
+ text-align: center;
208
+ }
209
+ table.dataTable th.dt-right,
210
+ table.dataTable td.dt-right {
211
+ text-align: right;
212
+ }
213
+ table.dataTable th.dt-justify,
214
+ table.dataTable td.dt-justify {
215
+ text-align: justify;
216
+ }
217
+ table.dataTable th.dt-nowrap,
218
+ table.dataTable td.dt-nowrap {
219
+ white-space: nowrap;
220
+ }
221
+ table.dataTable thead th.dt-head-left,
222
+ table.dataTable thead td.dt-head-left,
223
+ table.dataTable tfoot th.dt-head-left,
224
+ table.dataTable tfoot td.dt-head-left {
225
+ text-align: left;
226
+ }
227
+ table.dataTable thead th.dt-head-center,
228
+ table.dataTable thead td.dt-head-center,
229
+ table.dataTable tfoot th.dt-head-center,
230
+ table.dataTable tfoot td.dt-head-center {
231
+ text-align: center;
232
+ }
233
+ table.dataTable thead th.dt-head-right,
234
+ table.dataTable thead td.dt-head-right,
235
+ table.dataTable tfoot th.dt-head-right,
236
+ table.dataTable tfoot td.dt-head-right {
237
+ text-align: right;
238
+ }
239
+ table.dataTable thead th.dt-head-justify,
240
+ table.dataTable thead td.dt-head-justify,
241
+ table.dataTable tfoot th.dt-head-justify,
242
+ table.dataTable tfoot td.dt-head-justify {
243
+ text-align: justify;
244
+ }
245
+ table.dataTable thead th.dt-head-nowrap,
246
+ table.dataTable thead td.dt-head-nowrap,
247
+ table.dataTable tfoot th.dt-head-nowrap,
248
+ table.dataTable tfoot td.dt-head-nowrap {
249
+ white-space: nowrap;
250
+ }
251
+ table.dataTable tbody th.dt-body-left,
252
+ table.dataTable tbody td.dt-body-left {
253
+ text-align: left;
254
+ }
255
+ table.dataTable tbody th.dt-body-center,
256
+ table.dataTable tbody td.dt-body-center {
257
+ text-align: center;
258
+ }
259
+ table.dataTable tbody th.dt-body-right,
260
+ table.dataTable tbody td.dt-body-right {
261
+ text-align: right;
262
+ }
263
+ table.dataTable tbody th.dt-body-justify,
264
+ table.dataTable tbody td.dt-body-justify {
265
+ text-align: justify;
266
+ }
267
+ table.dataTable tbody th.dt-body-nowrap,
268
+ table.dataTable tbody td.dt-body-nowrap {
269
+ white-space: nowrap;
270
+ }
271
+
272
+ table.dataTable,
273
+ table.dataTable th,
274
+ table.dataTable td {
275
+ box-sizing: content-box;
276
+ }
277
+
278
+ /*
279
+ * Control feature layout
280
+ */
281
+ .dataTables_wrapper {
282
+ position: relative;
283
+ clear: both;
284
+ *zoom: 1;
285
+ zoom: 1;
286
+ }
287
+ .dataTables_wrapper .dataTables_length {
288
+ float: left;
289
+ }
290
+ .dataTables_wrapper .dataTables_length select {
291
+ border: 1px solid #aaa;
292
+ border-radius: 3px;
293
+ padding: 5px;
294
+ background-color: transparent;
295
+ padding: 4px;
296
+ }
297
+ .dataTables_wrapper .dataTables_filter {
298
+ float: right;
299
+ text-align: right;
300
+ }
301
+ .dataTables_wrapper .dataTables_filter input {
302
+ border: 1px solid #aaa;
303
+ border-radius: 3px;
304
+ padding: 5px;
305
+ background-color: transparent;
306
+ margin-left: 3px;
307
+ }
308
+ .dataTables_wrapper .dataTables_info {
309
+ clear: both;
310
+ float: left;
311
+ padding-top: 0.755em;
312
+ }
313
+ .dataTables_wrapper .dataTables_paginate {
314
+ float: right;
315
+ text-align: right;
316
+ padding-top: 0.25em;
317
+ }
318
+ .dataTables_wrapper .dataTables_paginate .paginate_button {
319
+ box-sizing: border-box;
320
+ display: inline-block;
321
+ min-width: 1.5em;
322
+ padding: 0.5em 1em;
323
+ margin-left: 2px;
324
+ text-align: center;
325
+ text-decoration: none !important;
326
+ cursor: pointer;
327
+ *cursor: hand;
328
+ color: #333 !important;
329
+ border: 1px solid transparent;
330
+ border-radius: 2px;
331
+ }
332
+ .dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
333
+ color: #333 !important;
334
+ border: 1px solid #979797;
335
+ background-color: white;
336
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, #dcdcdc));
337
+ /* Chrome,Safari4+ */
338
+ background: -webkit-linear-gradient(top, white 0%, #dcdcdc 100%);
339
+ /* Chrome10+,Safari5.1+ */
340
+ background: -moz-linear-gradient(top, white 0%, #dcdcdc 100%);
341
+ /* FF3.6+ */
342
+ background: -ms-linear-gradient(top, white 0%, #dcdcdc 100%);
343
+ /* IE10+ */
344
+ background: -o-linear-gradient(top, white 0%, #dcdcdc 100%);
345
+ /* Opera 11.10+ */
346
+ background: linear-gradient(to bottom, white 0%, #dcdcdc 100%);
347
+ /* W3C */
348
+ }
349
+ .dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
350
+ cursor: default;
351
+ color: #666 !important;
352
+ border: 1px solid transparent;
353
+ background: transparent;
354
+ box-shadow: none;
355
+ }
356
+ .dataTables_wrapper .dataTables_paginate .paginate_button:hover {
357
+ color: white !important;
358
+ border: 1px solid #111;
359
+ background-color: #585858;
360
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));
361
+ /* Chrome,Safari4+ */
362
+ background: -webkit-linear-gradient(top, #585858 0%, #111 100%);
363
+ /* Chrome10+,Safari5.1+ */
364
+ background: -moz-linear-gradient(top, #585858 0%, #111 100%);
365
+ /* FF3.6+ */
366
+ background: -ms-linear-gradient(top, #585858 0%, #111 100%);
367
+ /* IE10+ */
368
+ background: -o-linear-gradient(top, #585858 0%, #111 100%);
369
+ /* Opera 11.10+ */
370
+ background: linear-gradient(to bottom, #585858 0%, #111 100%);
371
+ /* W3C */
372
+ }
373
+ .dataTables_wrapper .dataTables_paginate .paginate_button:active {
374
+ outline: none;
375
+ background-color: #2b2b2b;
376
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));
377
+ /* Chrome,Safari4+ */
378
+ background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
379
+ /* Chrome10+,Safari5.1+ */
380
+ background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
381
+ /* FF3.6+ */
382
+ background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
383
+ /* IE10+ */
384
+ background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
385
+ /* Opera 11.10+ */
386
+ background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);
387
+ /* W3C */
388
+ box-shadow: inset 0 0 3px #111;
389
+ }
390
+ .dataTables_wrapper .dataTables_paginate .ellipsis {
391
+ padding: 0 1em;
392
+ }
393
+ .dataTables_wrapper .dataTables_processing {
394
+ position: absolute;
395
+ top: 50%;
396
+ left: 50%;
397
+ width: 100%;
398
+ height: 40px;
399
+ margin-left: -50%;
400
+ margin-top: -25px;
401
+ padding-top: 20px;
402
+ text-align: center;
403
+ font-size: 1.2em;
404
+ background-color: white;
405
+ 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)));
406
+ 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%);
407
+ 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%);
408
+ 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%);
409
+ 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%);
410
+ 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%);
411
+ }
412
+ .dataTables_wrapper .dataTables_length,
413
+ .dataTables_wrapper .dataTables_filter,
414
+ .dataTables_wrapper .dataTables_info,
415
+ .dataTables_wrapper .dataTables_processing,
416
+ .dataTables_wrapper .dataTables_paginate {
417
+ color: #333;
418
+ }
419
+ .dataTables_wrapper .dataTables_scroll {
420
+ clear: both;
421
+ }
422
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
423
+ *margin-top: -1px;
424
+ -webkit-overflow-scrolling: touch;
425
+ }
426
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td {
427
+ vertical-align: middle;
428
+ }
429
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing,
430
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing,
431
+ .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing {
432
+ height: 0;
433
+ overflow: hidden;
434
+ margin: 0 !important;
435
+ padding: 0 !important;
436
+ }
437
+ .dataTables_wrapper.no-footer .dataTables_scrollBody {
438
+ border-bottom: 1px solid #111;
439
+ }
440
+ .dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable,
441
+ .dataTables_wrapper.no-footer div.dataTables_scrollBody > table {
442
+ border-bottom: none;
443
+ }
444
+ .dataTables_wrapper:after {
445
+ visibility: hidden;
446
+ display: block;
447
+ content: "";
448
+ clear: both;
449
+ height: 0;
450
+ }
451
+
452
+ @media screen and (max-width: 767px) {
453
+ .dataTables_wrapper .dataTables_info,
454
+ .dataTables_wrapper .dataTables_paginate {
455
+ float: none;
456
+ text-align: center;
457
+ }
458
+ .dataTables_wrapper .dataTables_paginate {
459
+ margin-top: 0.5em;
460
+ }
461
+ }
462
+ @media screen and (max-width: 640px) {
463
+ .dataTables_wrapper .dataTables_length,
464
+ .dataTables_wrapper .dataTables_filter {
465
+ float: none;
466
+ text-align: center;
467
+ }
468
+ .dataTables_wrapper .dataTables_filter {
469
+ margin-top: 0.5em;
470
+ }
471
+ }
472
+
473
+
474
+ div.dt-autofill-handle{position:absolute;height:8px;width:8px;z-index:102;box-sizing:border-box;background:#3366ff;cursor:pointer}div.dtk-focus-alt div.dt-autofill-handle{background:#ff8b33}div.dt-autofill-select{position:absolute;z-index:1001;background-color:#4989de;background-image:repeating-linear-gradient(45deg, transparent, transparent 5px, rgba(255,255,255,0.5) 5px, rgba(255,255,255,0.5) 10px)}div.dt-autofill-select.top,div.dt-autofill-select.bottom{height:3px;margin-top:-1px}div.dt-autofill-select.left,div.dt-autofill-select.right{width:3px;margin-left:-1px}div.dt-autofill-list{position:fixed;top:50%;left:50%;width:500px;margin-left:-250px;background-color:white;border-radius:6px;box-shadow:0 0 5px #555;border:2px solid #444;z-index:11;box-sizing:border-box;padding:1.5em 2em}div.dt-autofill-list ul{display:table;margin:0;padding:0;list-style:none;width:100%}div.dt-autofill-list ul li{display:table-row}div.dt-autofill-list ul li:last-child div.dt-autofill-question,div.dt-autofill-list ul li:last-child div.dt-autofill-button{border-bottom:none}div.dt-autofill-list ul li:hover{background-color:#f6f6f6}div.dt-autofill-list div.dt-autofill-question{display:table-cell;padding:0.5em 0;border-bottom:1px solid #ccc}div.dt-autofill-list div.dt-autofill-question input[type=number]{padding:6px;width:30px;margin:-2px 0}div.dt-autofill-list div.dt-autofill-button{display:table-cell;padding:0.5em 0;border-bottom:1px solid #ccc}div.dt-autofill-list div.dt-autofill-button button{color:white;margin:0;padding:6px 12px;text-align:center;border:1px solid #2e6da4;background-color:#337ab7;border-radius:4px;cursor:pointer;vertical-align:middle}div.dt-autofill-background{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);background:radial-gradient(ellipse farthest-corner at center, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0.7) 100%);z-index:10}
475
+
476
+
477
+ @keyframes dtb-spinner {
478
+ 100% {
479
+ transform: rotate(360deg);
480
+ }
481
+ }
482
+ @-o-keyframes dtb-spinner {
483
+ 100% {
484
+ -o-transform: rotate(360deg);
485
+ transform: rotate(360deg);
486
+ }
487
+ }
488
+ @-ms-keyframes dtb-spinner {
489
+ 100% {
490
+ -ms-transform: rotate(360deg);
491
+ transform: rotate(360deg);
492
+ }
493
+ }
494
+ @-webkit-keyframes dtb-spinner {
495
+ 100% {
496
+ -webkit-transform: rotate(360deg);
497
+ transform: rotate(360deg);
498
+ }
499
+ }
500
+ @-moz-keyframes dtb-spinner {
501
+ 100% {
502
+ -moz-transform: rotate(360deg);
503
+ transform: rotate(360deg);
504
+ }
505
+ }
506
+ div.dt-button-info {
507
+ position: fixed;
508
+ top: 50%;
509
+ left: 50%;
510
+ width: 400px;
511
+ margin-top: -100px;
512
+ margin-left: -200px;
513
+ background-color: white;
514
+ border: 2px solid #111;
515
+ box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.3);
516
+ border-radius: 3px;
517
+ text-align: center;
518
+ z-index: 21;
519
+ }
520
+ div.dt-button-info h2 {
521
+ padding: 0.5em;
522
+ margin: 0;
523
+ font-weight: normal;
524
+ border-bottom: 1px solid #ddd;
525
+ background-color: #f3f3f3;
526
+ }
527
+ div.dt-button-info > div {
528
+ padding: 1em;
529
+ }
530
+
531
+ div.dt-button-collection-title {
532
+ text-align: center;
533
+ padding: 0.3em 0 0.5em;
534
+ font-size: 0.9em;
535
+ }
536
+
537
+ div.dt-button-collection-title:empty {
538
+ display: none;
539
+ }
540
+
541
+ button.dt-button,
542
+ div.dt-button,
543
+ a.dt-button,
544
+ input.dt-button {
545
+ position: relative;
546
+ display: inline-block;
547
+ box-sizing: border-box;
548
+ margin-right: 0.333em;
549
+ margin-bottom: 0.333em;
550
+ padding: 0.5em 1em;
551
+ border: 1px solid rgba(0, 0, 0, 0.3);
552
+ border-radius: 2px;
553
+ cursor: pointer;
554
+ font-size: 0.88em;
555
+ line-height: 1.6em;
556
+ color: black;
557
+ white-space: nowrap;
558
+ overflow: hidden;
559
+ background-color: rgba(0, 0, 0, 0.1);
560
+ /* Fallback */
561
+ background: -webkit-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
562
+ /* Chrome 10+, Saf5.1+, iOS 5+ */
563
+ background: -moz-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
564
+ /* FF3.6 */
565
+ background: -ms-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
566
+ /* IE10 */
567
+ background: -o-linear-gradient(top, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
568
+ /* Opera 11.10+ */
569
+ background: linear-gradient(to bottom, rgba(230, 230, 230, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
570
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='rgba(230, 230, 230, 0.1)', EndColorStr='rgba(0, 0, 0, 0.1)');
571
+ -webkit-user-select: none;
572
+ -moz-user-select: none;
573
+ -ms-user-select: none;
574
+ user-select: none;
575
+ text-decoration: none;
576
+ outline: none;
577
+ text-overflow: ellipsis;
578
+ }
579
+ button.dt-button.disabled,
580
+ div.dt-button.disabled,
581
+ a.dt-button.disabled,
582
+ input.dt-button.disabled {
583
+ cursor: default;
584
+ opacity: 0.4;
585
+ }
586
+ button.dt-button:active:not(.disabled), button.dt-button.active:not(.disabled),
587
+ div.dt-button:active:not(.disabled),
588
+ div.dt-button.active:not(.disabled),
589
+ a.dt-button:active:not(.disabled),
590
+ a.dt-button.active:not(.disabled),
591
+ input.dt-button:active:not(.disabled),
592
+ input.dt-button.active:not(.disabled) {
593
+ background-color: rgba(0, 0, 0, 0.1);
594
+ /* Fallback */
595
+ background: -webkit-linear-gradient(top, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
596
+ /* Chrome 10+, Saf5.1+, iOS 5+ */
597
+ background: -moz-linear-gradient(top, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
598
+ /* FF3.6 */
599
+ background: -ms-linear-gradient(top, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
600
+ /* IE10 */
601
+ background: -o-linear-gradient(top, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
602
+ /* Opera 11.10+ */
603
+ background: linear-gradient(to bottom, rgba(179, 179, 179, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
604
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='rgba(179, 179, 179, 0.1)', EndColorStr='rgba(0, 0, 0, 0.1)');
605
+ box-shadow: inset 1px 1px 3px #999999;
606
+ }
607
+ button.dt-button:active:not(.disabled):hover:not(.disabled), button.dt-button.active:not(.disabled):hover:not(.disabled),
608
+ div.dt-button:active:not(.disabled):hover:not(.disabled),
609
+ div.dt-button.active:not(.disabled):hover:not(.disabled),
610
+ a.dt-button:active:not(.disabled):hover:not(.disabled),
611
+ a.dt-button.active:not(.disabled):hover:not(.disabled),
612
+ input.dt-button:active:not(.disabled):hover:not(.disabled),
613
+ input.dt-button.active:not(.disabled):hover:not(.disabled) {
614
+ box-shadow: inset 1px 1px 3px #999999;
615
+ background-color: rgba(0, 0, 0, 0.1);
616
+ /* Fallback */
617
+ background: -webkit-linear-gradient(top, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
618
+ /* Chrome 10+, Saf5.1+, iOS 5+ */
619
+ background: -moz-linear-gradient(top, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
620
+ /* FF3.6 */
621
+ background: -ms-linear-gradient(top, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
622
+ /* IE10 */
623
+ background: -o-linear-gradient(top, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
624
+ /* Opera 11.10+ */
625
+ background: linear-gradient(to bottom, rgba(128, 128, 128, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
626
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='rgba(128, 128, 128, 0.1)', EndColorStr='rgba(0, 0, 0, 0.1)');
627
+ }
628
+ button.dt-button:hover,
629
+ div.dt-button:hover,
630
+ a.dt-button:hover,
631
+ input.dt-button:hover {
632
+ text-decoration: none;
633
+ }
634
+ button.dt-button:hover:not(.disabled),
635
+ div.dt-button:hover:not(.disabled),
636
+ a.dt-button:hover:not(.disabled),
637
+ input.dt-button:hover:not(.disabled) {
638
+ border: 1px solid #666;
639
+ background-color: rgba(0, 0, 0, 0.1);
640
+ /* Fallback */
641
+ background: -webkit-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
642
+ /* Chrome 10+, Saf5.1+, iOS 5+ */
643
+ background: -moz-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
644
+ /* FF3.6 */
645
+ background: -ms-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
646
+ /* IE10 */
647
+ background: -o-linear-gradient(top, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
648
+ /* Opera 11.10+ */
649
+ background: linear-gradient(to bottom, rgba(153, 153, 153, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%);
650
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='rgba(153, 153, 153, 0.1)', EndColorStr='rgba(0, 0, 0, 0.1)');
651
+ }
652
+ button.dt-button:focus:not(.disabled),
653
+ div.dt-button:focus:not(.disabled),
654
+ a.dt-button:focus:not(.disabled),
655
+ input.dt-button:focus:not(.disabled) {
656
+ border: 1px solid #426c9e;
657
+ text-shadow: 0 1px 0 #c4def1;
658
+ outline: none;
659
+ background-color: #79ace9;
660
+ /* Fallback */
661
+ background: -webkit-linear-gradient(top, #d1e2f7 0%, #79ace9 100%);
662
+ /* Chrome 10+, Saf5.1+, iOS 5+ */
663
+ background: -moz-linear-gradient(top, #d1e2f7 0%, #79ace9 100%);
664
+ /* FF3.6 */
665
+ background: -ms-linear-gradient(top, #d1e2f7 0%, #79ace9 100%);
666
+ /* IE10 */
667
+ background: -o-linear-gradient(top, #d1e2f7 0%, #79ace9 100%);
668
+ /* Opera 11.10+ */
669
+ background: linear-gradient(to bottom, #d1e2f7 0%, #79ace9 100%);
670
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#d1e2f7', EndColorStr='#79ace9');
671
+ }
672
+
673
+ .dt-button embed {
674
+ outline: none;
675
+ }
676
+
677
+ div.dt-buttons {
678
+ position: relative;
679
+ float: left;
680
+ }
681
+ div.dt-buttons.buttons-right {
682
+ float: right;
683
+ }
684
+
685
+ div.dataTables_layout_cell div.dt-buttons {
686
+ float: none;
687
+ }
688
+ div.dataTables_layout_cell div.dt-buttons.buttons-right {
689
+ float: none;
690
+ }
691
+
692
+ div.dt-button-collection {
693
+ position: absolute;
694
+ top: 0;
695
+ left: 0;
696
+ width: 150px;
697
+ margin-top: 3px;
698
+ padding: 8px 8px 4px 8px;
699
+ border: 1px solid #ccc;
700
+ border: 1px solid rgba(0, 0, 0, 0.4);
701
+ background-color: white;
702
+ overflow: hidden;
703
+ z-index: 2002;
704
+ border-radius: 5px;
705
+ box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.3);
706
+ box-sizing: border-box;
707
+ }
708
+ div.dt-button-collection button.dt-button,
709
+ div.dt-button-collection div.dt-button,
710
+ div.dt-button-collection a.dt-button {
711
+ position: relative;
712
+ left: 0;
713
+ right: 0;
714
+ width: 100%;
715
+ display: block;
716
+ float: none;
717
+ margin-bottom: 4px;
718
+ margin-right: 0;
719
+ }
720
+ div.dt-button-collection button.dt-button:active:not(.disabled), div.dt-button-collection button.dt-button.active:not(.disabled),
721
+ div.dt-button-collection div.dt-button:active:not(.disabled),
722
+ div.dt-button-collection div.dt-button.active:not(.disabled),
723
+ div.dt-button-collection a.dt-button:active:not(.disabled),
724
+ div.dt-button-collection a.dt-button.active:not(.disabled) {
725
+ background-color: #dadada;
726
+ /* Fallback */
727
+ background: -webkit-linear-gradient(top, #f0f0f0 0%, #dadada 100%);
728
+ /* Chrome 10+, Saf5.1+, iOS 5+ */
729
+ background: -moz-linear-gradient(top, #f0f0f0 0%, #dadada 100%);
730
+ /* FF3.6 */
731
+ background: -ms-linear-gradient(top, #f0f0f0 0%, #dadada 100%);
732
+ /* IE10 */
733
+ background: -o-linear-gradient(top, #f0f0f0 0%, #dadada 100%);
734
+ /* Opera 11.10+ */
735
+ background: linear-gradient(to bottom, #f0f0f0 0%, #dadada 100%);
736
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#f0f0f0', EndColorStr='#dadada');
737
+ box-shadow: inset 1px 1px 3px #666;
738
+ }
739
+ div.dt-button-collection.fixed {
740
+ position: fixed;
741
+ top: 50%;
742
+ left: 50%;
743
+ margin-left: -75px;
744
+ border-radius: 0;
745
+ }
746
+ div.dt-button-collection.fixed.two-column {
747
+ margin-left: -200px;
748
+ }
749
+ div.dt-button-collection.fixed.three-column {
750
+ margin-left: -225px;
751
+ }
752
+ div.dt-button-collection.fixed.four-column {
753
+ margin-left: -300px;
754
+ }
755
+ div.dt-button-collection > :last-child {
756
+ display: block !important;
757
+ -webkit-column-gap: 8px;
758
+ -moz-column-gap: 8px;
759
+ -ms-column-gap: 8px;
760
+ -o-column-gap: 8px;
761
+ column-gap: 8px;
762
+ }
763
+ div.dt-button-collection > :last-child > * {
764
+ -webkit-column-break-inside: avoid;
765
+ break-inside: avoid;
766
+ }
767
+ div.dt-button-collection.two-column {
768
+ width: 400px;
769
+ }
770
+ div.dt-button-collection.two-column > :last-child {
771
+ padding-bottom: 1px;
772
+ -webkit-column-count: 2;
773
+ -moz-column-count: 2;
774
+ -ms-column-count: 2;
775
+ -o-column-count: 2;
776
+ column-count: 2;
777
+ }
778
+ div.dt-button-collection.three-column {
779
+ width: 450px;
780
+ }
781
+ div.dt-button-collection.three-column > :last-child {
782
+ padding-bottom: 1px;
783
+ -webkit-column-count: 3;
784
+ -moz-column-count: 3;
785
+ -ms-column-count: 3;
786
+ -o-column-count: 3;
787
+ column-count: 3;
788
+ }
789
+ div.dt-button-collection.four-column {
790
+ width: 600px;
791
+ }
792
+ div.dt-button-collection.four-column > :last-child {
793
+ padding-bottom: 1px;
794
+ -webkit-column-count: 4;
795
+ -moz-column-count: 4;
796
+ -ms-column-count: 4;
797
+ -o-column-count: 4;
798
+ column-count: 4;
799
+ }
800
+ div.dt-button-collection .dt-button {
801
+ border-radius: 0;
802
+ }
803
+
804
+ div.dt-button-background {
805
+ position: fixed;
806
+ top: 0;
807
+ left: 0;
808
+ width: 100%;
809
+ height: 100%;
810
+ background: rgba(0, 0, 0, 0.7);
811
+ /* Fallback */
812
+ background: -ms-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%);
813
+ /* IE10 Consumer Preview */
814
+ background: -moz-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%);
815
+ /* Firefox */
816
+ background: -o-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%);
817
+ /* Opera */
818
+ background: -webkit-gradient(radial, center center, 0, center center, 497, color-stop(0, rgba(0, 0, 0, 0.3)), color-stop(1, rgba(0, 0, 0, 0.7)));
819
+ /* Webkit (Safari/Chrome 10) */
820
+ background: -webkit-radial-gradient(center, ellipse farthest-corner, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%);
821
+ /* Webkit (Chrome 11+) */
822
+ background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%);
823
+ /* W3C Markup, IE10 Release Preview */
824
+ z-index: 2001;
825
+ }
826
+
827
+ @media screen and (max-width: 640px) {
828
+ div.dt-buttons {
829
+ float: none !important;
830
+ text-align: center;
831
+ }
832
+ }
833
+ button.dt-button.processing,
834
+ div.dt-button.processing,
835
+ a.dt-button.processing {
836
+ color: rgba(0, 0, 0, 0.2);
837
+ }
838
+ button.dt-button.processing:after,
839
+ div.dt-button.processing:after,
840
+ a.dt-button.processing:after {
841
+ position: absolute;
842
+ top: 50%;
843
+ left: 50%;
844
+ width: 16px;
845
+ height: 16px;
846
+ margin: -8px 0 0 -8px;
847
+ box-sizing: border-box;
848
+ display: block;
849
+ content: ' ';
850
+ border: 2px solid #282828;
851
+ border-radius: 50%;
852
+ border-left-color: transparent;
853
+ border-right-color: transparent;
854
+ animation: dtb-spinner 1500ms infinite linear;
855
+ -o-animation: dtb-spinner 1500ms infinite linear;
856
+ -ms-animation: dtb-spinner 1500ms infinite linear;
857
+ -webkit-animation: dtb-spinner 1500ms infinite linear;
858
+ -moz-animation: dtb-spinner 1500ms infinite linear;
859
+ }
860
+
861
+
862
+ table.DTCR_clonedTable.dataTable {
863
+ position: absolute !important;
864
+ background-color: rgba(255, 255, 255, 0.7);
865
+ z-index: 202;
866
+ }
867
+
868
+ div.DTCR_pointer {
869
+ width: 1px;
870
+ background-color: #0259C4;
871
+ z-index: 201;
872
+ }
873
+
874
+
875
+ table.DTFC_Cloned thead,
876
+ table.DTFC_Cloned tfoot {
877
+ background-color: white;
878
+ }
879
+
880
+ div.DTFC_Blocker {
881
+ background-color: white;
882
+ }
883
+
884
+ div.DTFC_LeftWrapper table.dataTable,
885
+ div.DTFC_RightWrapper table.dataTable {
886
+ margin-bottom: 0;
887
+ z-index: 2;
888
+ }
889
+ div.DTFC_LeftWrapper table.dataTable.no-footer,
890
+ div.DTFC_RightWrapper table.dataTable.no-footer {
891
+ border-bottom: none;
892
+ }
893
+
894
+ table.dataTable.display tbody tr.DTFC_NoData {
895
+ background-color: transparent;
896
+ }
897
+
898
+
899
+ table.fixedHeader-floating {
900
+ position: fixed !important;
901
+ background-color: white;
902
+ }
903
+
904
+ table.fixedHeader-floating.no-footer {
905
+ border-bottom-width: 0;
906
+ }
907
+
908
+ table.fixedHeader-locked {
909
+ position: absolute !important;
910
+ background-color: white;
911
+ }
912
+
913
+ @media print {
914
+ table.fixedHeader-floating {
915
+ display: none;
916
+ }
917
+ }
918
+
919
+
920
+ table.dataTable tbody th.focus,
921
+ table.dataTable tbody td.focus {
922
+ box-shadow: inset 0 0 1px 2px #3366ff;
923
+ }
924
+
925
+ div.dtk-focus-alt table.dataTable tbody th.focus,
926
+ div.dtk-focus-alt table.dataTable tbody td.focus {
927
+ box-shadow: inset 0 0 1px 2px #ff8b33;
928
+ }
929
+
930
+
931
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.child,
932
+ table.dataTable.dtr-inline.collapsed > tbody > tr > th.child,
933
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty {
934
+ cursor: default !important;
935
+ }
936
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.child:before,
937
+ table.dataTable.dtr-inline.collapsed > tbody > tr > th.child:before,
938
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty:before {
939
+ display: none !important;
940
+ }
941
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control,
942
+ table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control {
943
+ position: relative;
944
+ padding-left: 30px;
945
+ cursor: pointer;
946
+ }
947
+ table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control:before,
948
+ table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control:before {
949
+ top: 50%;
950
+ left: 5px;
951
+ height: 1em;
952
+ width: 1em;
953
+ margin-top: -9px;
954
+ display: block;
955
+ position: absolute;
956
+ color: white;
957
+ border: 0.15em solid white;
958
+ border-radius: 1em;
959
+ box-shadow: 0 0 0.2em #444;
960
+ box-sizing: content-box;
961
+ text-align: center;
962
+ text-indent: 0 !important;
963
+ font-family: "Courier New", Courier, monospace;
964
+ line-height: 1em;
965
+ content: "+";
966
+ background-color: #31b131;
967
+ }
968
+ table.dataTable.dtr-inline.collapsed > tbody > tr.parent > td.dtr-control:before,
969
+ table.dataTable.dtr-inline.collapsed > tbody > tr.parent > th.dtr-control:before {
970
+ content: "-";
971
+ background-color: #d33333;
972
+ }
973
+ table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td.dtr-control,
974
+ table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th.dtr-control {
975
+ padding-left: 27px;
976
+ }
977
+ table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td.dtr-control:before,
978
+ table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th.dtr-control:before {
979
+ left: 4px;
980
+ height: 14px;
981
+ width: 14px;
982
+ border-radius: 14px;
983
+ line-height: 14px;
984
+ text-indent: 3px;
985
+ }
986
+ table.dataTable.dtr-column > tbody > tr > td.dtr-control,
987
+ table.dataTable.dtr-column > tbody > tr > th.dtr-control,
988
+ table.dataTable.dtr-column > tbody > tr > td.control,
989
+ table.dataTable.dtr-column > tbody > tr > th.control {
990
+ position: relative;
991
+ cursor: pointer;
992
+ }
993
+ table.dataTable.dtr-column > tbody > tr > td.dtr-control:before,
994
+ table.dataTable.dtr-column > tbody > tr > th.dtr-control:before,
995
+ table.dataTable.dtr-column > tbody > tr > td.control:before,
996
+ table.dataTable.dtr-column > tbody > tr > th.control:before {
997
+ top: 50%;
998
+ left: 50%;
999
+ height: 0.8em;
1000
+ width: 0.8em;
1001
+ margin-top: -0.5em;
1002
+ margin-left: -0.5em;
1003
+ display: block;
1004
+ position: absolute;
1005
+ color: white;
1006
+ border: 0.15em solid white;
1007
+ border-radius: 1em;
1008
+ box-shadow: 0 0 0.2em #444;
1009
+ box-sizing: content-box;
1010
+ text-align: center;
1011
+ text-indent: 0 !important;
1012
+ font-family: "Courier New", Courier, monospace;
1013
+ line-height: 1em;
1014
+ content: "+";
1015
+ background-color: #31b131;
1016
+ }
1017
+ table.dataTable.dtr-column > tbody > tr.parent td.dtr-control:before,
1018
+ table.dataTable.dtr-column > tbody > tr.parent th.dtr-control:before,
1019
+ table.dataTable.dtr-column > tbody > tr.parent td.control:before,
1020
+ table.dataTable.dtr-column > tbody > tr.parent th.control:before {
1021
+ content: "-";
1022
+ background-color: #d33333;
1023
+ }
1024
+ table.dataTable > tbody > tr.child {
1025
+ padding: 0.5em 1em;
1026
+ }
1027
+ table.dataTable > tbody > tr.child:hover {
1028
+ background: transparent !important;
1029
+ }
1030
+ table.dataTable > tbody > tr.child ul.dtr-details {
1031
+ display: inline-block;
1032
+ list-style-type: none;
1033
+ margin: 0;
1034
+ padding: 0;
1035
+ }
1036
+ table.dataTable > tbody > tr.child ul.dtr-details > li {
1037
+ border-bottom: 1px solid #efefef;
1038
+ padding: 0.5em 0;
1039
+ }
1040
+ table.dataTable > tbody > tr.child ul.dtr-details > li:first-child {
1041
+ padding-top: 0;
1042
+ }
1043
+ table.dataTable > tbody > tr.child ul.dtr-details > li:last-child {
1044
+ border-bottom: none;
1045
+ }
1046
+ table.dataTable > tbody > tr.child span.dtr-title {
1047
+ display: inline-block;
1048
+ min-width: 75px;
1049
+ font-weight: bold;
1050
+ }
1051
+ div.dtr-modal {
1052
+ position: fixed;
1053
+ box-sizing: border-box;
1054
+ top: 0;
1055
+ left: 0;
1056
+ height: 100%;
1057
+ width: 100%;
1058
+ z-index: 100;
1059
+ padding: 10em 1em;
1060
+ }
1061
+ div.dtr-modal div.dtr-modal-display {
1062
+ position: absolute;
1063
+ top: 0;
1064
+ left: 0;
1065
+ bottom: 0;
1066
+ right: 0;
1067
+ width: 50%;
1068
+ height: 50%;
1069
+ overflow: auto;
1070
+ margin: auto;
1071
+ z-index: 102;
1072
+ overflow: auto;
1073
+ background-color: #f5f5f7;
1074
+ border: 1px solid black;
1075
+ border-radius: 0.5em;
1076
+ box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6);
1077
+ }
1078
+ div.dtr-modal div.dtr-modal-content {
1079
+ position: relative;
1080
+ padding: 1em;
1081
+ }
1082
+ div.dtr-modal div.dtr-modal-close {
1083
+ position: absolute;
1084
+ top: 6px;
1085
+ right: 6px;
1086
+ width: 22px;
1087
+ height: 22px;
1088
+ border: 1px solid #eaeaea;
1089
+ background-color: #f9f9f9;
1090
+ text-align: center;
1091
+ border-radius: 3px;
1092
+ cursor: pointer;
1093
+ z-index: 12;
1094
+ }
1095
+ div.dtr-modal div.dtr-modal-close:hover {
1096
+ background-color: #eaeaea;
1097
+ }
1098
+ div.dtr-modal div.dtr-modal-background {
1099
+ position: fixed;
1100
+ top: 0;
1101
+ left: 0;
1102
+ right: 0;
1103
+ bottom: 0;
1104
+ z-index: 101;
1105
+ background: rgba(0, 0, 0, 0.6);
1106
+ }
1107
+
1108
+ @media screen and (max-width: 767px) {
1109
+ div.dtr-modal div.dtr-modal-display {
1110
+ width: 95%;
1111
+ }
1112
+ }
1113
+
1114
+
1115
+ table.dt-rowReorder-float {
1116
+ position: absolute !important;
1117
+ opacity: 0.8;
1118
+ table-layout: fixed;
1119
+ outline: 2px solid #888;
1120
+ outline-offset: -2px;
1121
+ z-index: 2001;
1122
+ }
1123
+
1124
+ tr.dt-rowReorder-moving {
1125
+ outline: 2px solid #555;
1126
+ outline-offset: -2px;
1127
+ }
1128
+
1129
+ body.dt-rowReorder-noOverflow {
1130
+ overflow-x: hidden;
1131
+ }
1132
+
1133
+ table.dataTable td.reorder {
1134
+ text-align: center;
1135
+ cursor: move;
1136
+ }
1137
+
1138
+
1139
+ div.dts {
1140
+ display: block !important;
1141
+ }
1142
+ div.dts tbody th,
1143
+ div.dts tbody td {
1144
+ white-space: nowrap;
1145
+ }
1146
+ div.dts div.dts_loading {
1147
+ z-index: 1;
1148
+ }
1149
+ div.dts div.dts_label {
1150
+ position: absolute;
1151
+ right: 10px;
1152
+ background: rgba(0, 0, 0, 0.8);
1153
+ color: white;
1154
+ box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.5);
1155
+ text-align: right;
1156
+ border-radius: 3px;
1157
+ padding: 0.4em;
1158
+ z-index: 2;
1159
+ display: none;
1160
+ }
1161
+ div.dts div.dataTables_scrollBody {
1162
+ background: repeating-linear-gradient(45deg, #edeeff, #edeeff 10px, white 10px, white 20px);
1163
+ }
1164
+ div.dts div.dataTables_scrollBody table {
1165
+ z-index: 2;
1166
+ }
1167
+ div.dts div.dataTables_paginate,
1168
+ div.dts div.dataTables_length {
1169
+ display: none;
1170
+ }
1171
+
1172
+
1173
+ table.dataTable tbody > tr.selected,
1174
+ table.dataTable tbody > tr > .selected {
1175
+ background-color: #B0BED9;
1176
+ }
1177
+ table.dataTable.stripe tbody > tr.odd.selected,
1178
+ table.dataTable.stripe tbody > tr.odd > .selected, table.dataTable.display tbody > tr.odd.selected,
1179
+ table.dataTable.display tbody > tr.odd > .selected {
1180
+ background-color: #acbad4;
1181
+ }
1182
+ table.dataTable.hover tbody > tr.selected:hover,
1183
+ table.dataTable.hover tbody > tr > .selected:hover, table.dataTable.display tbody > tr.selected:hover,
1184
+ table.dataTable.display tbody > tr > .selected:hover {
1185
+ background-color: #aab7d1;
1186
+ }
1187
+ table.dataTable.order-column tbody > tr.selected > .sorting_1,
1188
+ table.dataTable.order-column tbody > tr.selected > .sorting_2,
1189
+ table.dataTable.order-column tbody > tr.selected > .sorting_3,
1190
+ table.dataTable.order-column tbody > tr > .selected, table.dataTable.display tbody > tr.selected > .sorting_1,
1191
+ table.dataTable.display tbody > tr.selected > .sorting_2,
1192
+ table.dataTable.display tbody > tr.selected > .sorting_3,
1193
+ table.dataTable.display tbody > tr > .selected {
1194
+ background-color: #acbad5;
1195
+ }
1196
+ table.dataTable.display tbody > tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_1 {
1197
+ background-color: #a6b4cd;
1198
+ }
1199
+ table.dataTable.display tbody > tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_2 {
1200
+ background-color: #a8b5cf;
1201
+ }
1202
+ table.dataTable.display tbody > tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_3 {
1203
+ background-color: #a9b7d1;
1204
+ }
1205
+ table.dataTable.display tbody > tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_1 {
1206
+ background-color: #acbad5;
1207
+ }
1208
+ table.dataTable.display tbody > tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_2 {
1209
+ background-color: #aebcd6;
1210
+ }
1211
+ table.dataTable.display tbody > tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_3 {
1212
+ background-color: #afbdd8;
1213
+ }
1214
+ table.dataTable.display tbody > tr.odd > .selected, table.dataTable.order-column.stripe tbody > tr.odd > .selected {
1215
+ background-color: #a6b4cd;
1216
+ }
1217
+ table.dataTable.display tbody > tr.even > .selected, table.dataTable.order-column.stripe tbody > tr.even > .selected {
1218
+ background-color: #acbad5;
1219
+ }
1220
+ table.dataTable.display tbody > tr.selected:hover > .sorting_1, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_1 {
1221
+ background-color: #a2aec7;
1222
+ }
1223
+ table.dataTable.display tbody > tr.selected:hover > .sorting_2, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_2 {
1224
+ background-color: #a3b0c9;
1225
+ }
1226
+ table.dataTable.display tbody > tr.selected:hover > .sorting_3, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_3 {
1227
+ background-color: #a5b2cb;
1228
+ }
1229
+ table.dataTable.display tbody > tr:hover > .selected,
1230
+ table.dataTable.display tbody > tr > .selected:hover, table.dataTable.order-column.hover tbody > tr:hover > .selected,
1231
+ table.dataTable.order-column.hover tbody > tr > .selected:hover {
1232
+ background-color: #a2aec7;
1233
+ }
1234
+ table.dataTable tbody td.select-checkbox,
1235
+ table.dataTable tbody th.select-checkbox {
1236
+ position: relative;
1237
+ }
1238
+ table.dataTable tbody td.select-checkbox:before, table.dataTable tbody td.select-checkbox:after,
1239
+ table.dataTable tbody th.select-checkbox:before,
1240
+ table.dataTable tbody th.select-checkbox:after {
1241
+ display: block;
1242
+ position: absolute;
1243
+ top: 1.2em;
1244
+ left: 50%;
1245
+ width: 12px;
1246
+ height: 12px;
1247
+ box-sizing: border-box;
1248
+ }
1249
+ table.dataTable tbody td.select-checkbox:before,
1250
+ table.dataTable tbody th.select-checkbox:before {
1251
+ content: ' ';
1252
+ margin-top: -6px;
1253
+ margin-left: -6px;
1254
+ border: 1px solid black;
1255
+ border-radius: 3px;
1256
+ }
1257
+ table.dataTable tr.selected td.select-checkbox:after,
1258
+ table.dataTable tr.selected th.select-checkbox:after {
1259
+ content: '\2714';
1260
+ margin-top: -11px;
1261
+ margin-left: -4px;
1262
+ text-align: center;
1263
+ text-shadow: 1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9;
1264
+ }
1265
+
1266
+ div.dataTables_wrapper span.select-info,
1267
+ div.dataTables_wrapper span.select-item {
1268
+ margin-left: 0.5em;
1269
+ }
1270
+
1271
+ @media screen and (max-width: 640px) {
1272
+ div.dataTables_wrapper span.select-info,
1273
+ div.dataTables_wrapper span.select-item {
1274
+ margin-left: 0;
1275
+ display: block;
1276
+ }
1277
+ }
1278
+
1279
+
common/vendor/datatables/datatables.js ADDED
@@ -0,0 +1,28818 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * This combined file was created by the DataTables downloader builder:
3
+ * https://datatables.net/download
4
+ *
5
+ * To rebuild or modify this file with the latest versions of the included
6
+ * software please visit:
7
+ * https://datatables.net/download/#dt/dt-1.10.23/af-2.3.5/b-1.6.5/cr-1.5.3/fc-3.3.2/fh-3.1.8/kt-2.6.1/r-2.2.7/rr-1.2.7/sc-2.0.3/sl-1.3.1
8
+ *
9
+ * Included libraries:
10
+ * DataTables 1.10.23, AutoFill 2.3.5, Buttons 1.6.5, ColReorder 1.5.3, FixedColumns 3.3.2, FixedHeader 3.1.8, KeyTable 2.6.1, Responsive 2.2.7, RowReorder 1.2.7, Scroller 2.0.3, Select 1.3.1
11
+ */
12
+
13
+ /*! DataTables 1.10.23
14
+ * ©2008-2020 SpryMedia Ltd - datatables.net/license
15
+ */
16
+
17
+ /**
18
+ * @summary DataTables
19
+ * @description Paginate, search and order HTML tables
20
+ * @version 1.10.23
21
+ * @file jquery.dataTables.js
22
+ * @author SpryMedia Ltd
23
+ * @contact www.datatables.net
24
+ * @copyright Copyright 2008-2020 SpryMedia Ltd.
25
+ *
26
+ * This source file is free software, available under the following license:
27
+ * MIT license - http://datatables.net/license
28
+ *
29
+ * This source file is distributed in the hope that it will be useful, but
30
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
31
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
32
+ *
33
+ * For details please refer to: http://www.datatables.net
34
+ */
35
+
36
+ /*jslint evil: true, undef: true, browser: true */
37
+ /*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*/
38
+
39
+ (function( factory ) {
40
+ "use strict";
41
+
42
+ if ( typeof define === 'function' && define.amd ) {
43
+ // AMD
44
+ define( ['jquery'], function ( $ ) {
45
+ return factory( $, window, document );
46
+ } );
47
+ }
48
+ else if ( typeof exports === 'object' ) {
49
+ // CommonJS
50
+ module.exports = function (root, $) {
51
+ if ( ! root ) {
52
+ // CommonJS environments without a window global must pass a
53
+ // root. This will give an error otherwise
54
+ root = window;
55
+ }
56
+
57
+ if ( ! $ ) {
58
+ $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
59
+ require('jquery') :
60
+ require('jquery')( root );
61
+ }
62
+
63
+ return factory( $, root, root.document );
64
+ };
65
+ }
66
+ else {
67
+ // Browser
68
+ factory( jQuery, window, document );
69
+ }
70
+ }
71
+ (function( $, window, document, undefined ) {
72
+ "use strict";
73
+
74
+ /**
75
+ * DataTables is a plug-in for the jQuery Javascript library. It is a highly
76
+ * flexible tool, based upon the foundations of progressive enhancement,
77
+ * which will add advanced interaction controls to any HTML table. For a
78
+ * full list of features please refer to
79
+ * [DataTables.net](href="http://datatables.net).
80
+ *
81
+ * Note that the `DataTable` object is not a global variable but is aliased
82
+ * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
83
+ * be accessed.
84
+ *
85
+ * @class
86
+ * @param {object} [init={}] Configuration object for DataTables. Options
87
+ * are defined by {@link DataTable.defaults}
88
+ * @requires jQuery 1.7+
89
+ *
90
+ * @example
91
+ * // Basic initialisation
92
+ * $(document).ready( function {
93
+ * $('#example').dataTable();
94
+ * } );
95
+ *
96
+ * @example
97
+ * // Initialisation with configuration options - in this case, disable
98
+ * // pagination and sorting.
99
+ * $(document).ready( function {
100
+ * $('#example').dataTable( {
101
+ * "paginate": false,
102
+ * "sort": false
103
+ * } );
104
+ * } );
105
+ */
106
+ var DataTable = function ( options )
107
+ {
108
+ /**
109
+ * Perform a jQuery selector action on the table's TR elements (from the tbody) and
110
+ * return the resulting jQuery object.
111
+ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
112
+ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
113
+ * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
114
+ * criterion ("applied") or all TR elements (i.e. no filter).
115
+ * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
116
+ * Can be either 'current', whereby the current sorting of the table is used, or
117
+ * 'original' whereby the original order the data was read into the table is used.
118
+ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
119
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
120
+ * 'current' and filter is 'applied', regardless of what they might be given as.
121
+ * @returns {object} jQuery object, filtered by the given selector.
122
+ * @dtopt API
123
+ * @deprecated Since v1.10
124
+ *
125
+ * @example
126
+ * $(document).ready(function() {
127
+ * var oTable = $('#example').dataTable();
128
+ *
129
+ * // Highlight every second row
130
+ * oTable.$('tr:odd').css('backgroundColor', 'blue');
131
+ * } );
132
+ *
133
+ * @example
134
+ * $(document).ready(function() {
135
+ * var oTable = $('#example').dataTable();
136
+ *
137
+ * // Filter to rows with 'Webkit' in them, add a background colour and then
138
+ * // remove the filter, thus highlighting the 'Webkit' rows only.
139
+ * oTable.fnFilter('Webkit');
140
+ * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
141
+ * oTable.fnFilter('');
142
+ * } );
143
+ */
144
+ this.$ = function ( sSelector, oOpts )
145
+ {
146
+ return this.api(true).$( sSelector, oOpts );
147
+ };
148
+
149
+
150
+ /**
151
+ * Almost identical to $ in operation, but in this case returns the data for the matched
152
+ * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
153
+ * rather than any descendants, so the data can be obtained for the row/cell. If matching
154
+ * rows are found, the data returned is the original data array/object that was used to
155
+ * create the row (or a generated array if from a DOM source).
156
+ *
157
+ * This method is often useful in-combination with $ where both functions are given the
158
+ * same parameters and the array indexes will match identically.
159
+ * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
160
+ * @param {object} [oOpts] Optional parameters for modifying the rows to be included
161
+ * @param {string} [oOpts.filter=none] Select elements that meet the current filter
162
+ * criterion ("applied") or all elements (i.e. no filter).
163
+ * @param {string} [oOpts.order=current] Order of the data in the processed array.
164
+ * Can be either 'current', whereby the current sorting of the table is used, or
165
+ * 'original' whereby the original order the data was read into the table is used.
166
+ * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
167
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
168
+ * 'current' and filter is 'applied', regardless of what they might be given as.
169
+ * @returns {array} Data for the matched elements. If any elements, as a result of the
170
+ * selector, were not TR, TD or TH elements in the DataTable, they will have a null
171
+ * entry in the array.
172
+ * @dtopt API
173
+ * @deprecated Since v1.10
174
+ *
175
+ * @example
176
+ * $(document).ready(function() {
177
+ * var oTable = $('#example').dataTable();
178
+ *
179
+ * // Get the data from the first row in the table
180
+ * var data = oTable._('tr:first');
181
+ *
182
+ * // Do something useful with the data
183
+ * alert( "First cell is: "+data[0] );
184
+ * } );
185
+ *
186
+ * @example
187
+ * $(document).ready(function() {
188
+ * var oTable = $('#example').dataTable();
189
+ *
190
+ * // Filter to 'Webkit' and get all data for
191
+ * oTable.fnFilter('Webkit');
192
+ * var data = oTable._('tr', {"search": "applied"});
193
+ *
194
+ * // Do something with the data
195
+ * alert( data.length+" rows matched the search" );
196
+ * } );
197
+ */
198
+ this._ = function ( sSelector, oOpts )
199
+ {
200
+ return this.api(true).rows( sSelector, oOpts ).data();
201
+ };
202
+
203
+
204
+ /**
205
+ * Create a DataTables Api instance, with the currently selected tables for
206
+ * the Api's context.
207
+ * @param {boolean} [traditional=false] Set the API instance's context to be
208
+ * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
209
+ * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
210
+ * or if all tables captured in the jQuery object should be used.
211
+ * @return {DataTables.Api}
212
+ */
213
+ this.api = function ( traditional )
214
+ {
215
+ return traditional ?
216
+ new _Api(
217
+ _fnSettingsFromNode( this[ _ext.iApiIndex ] )
218
+ ) :
219
+ new _Api( this );
220
+ };
221
+
222
+
223
+ /**
224
+ * Add a single new row or multiple rows of data to the table. Please note
225
+ * that this is suitable for client-side processing only - if you are using
226
+ * server-side processing (i.e. "bServerSide": true), then to add data, you
227
+ * must add it to the data source, i.e. the server-side, through an Ajax call.
228
+ * @param {array|object} data The data to be added to the table. This can be:
229
+ * <ul>
230
+ * <li>1D array of data - add a single row with the data provided</li>
231
+ * <li>2D array of arrays - add multiple rows in a single call</li>
232
+ * <li>object - data object when using <i>mData</i></li>
233
+ * <li>array of objects - multiple data objects when using <i>mData</i></li>
234
+ * </ul>
235
+ * @param {bool} [redraw=true] redraw the table or not
236
+ * @returns {array} An array of integers, representing the list of indexes in
237
+ * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
238
+ * the table.
239
+ * @dtopt API
240
+ * @deprecated Since v1.10
241
+ *
242
+ * @example
243
+ * // Global var for counter
244
+ * var giCount = 2;
245
+ *
246
+ * $(document).ready(function() {
247
+ * $('#example').dataTable();
248
+ * } );
249
+ *
250
+ * function fnClickAddRow() {
251
+ * $('#example').dataTable().fnAddData( [
252
+ * giCount+".1",
253
+ * giCount+".2",
254
+ * giCount+".3",
255
+ * giCount+".4" ]
256
+ * );
257
+ *
258
+ * giCount++;
259
+ * }
260
+ */
261
+ this.fnAddData = function( data, redraw )
262
+ {
263
+ var api = this.api( true );
264
+
265
+ /* Check if we want to add multiple rows or not */
266
+ var rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
267
+ api.rows.add( data ) :
268
+ api.row.add( data );
269
+
270
+ if ( redraw === undefined || redraw ) {
271
+ api.draw();
272
+ }
273
+
274
+ return rows.flatten().toArray();
275
+ };
276
+
277
+
278
+ /**
279
+ * This function will make DataTables recalculate the column sizes, based on the data
280
+ * contained in the table and the sizes applied to the columns (in the DOM, CSS or
281
+ * through the sWidth parameter). This can be useful when the width of the table's
282
+ * parent element changes (for example a window resize).
283
+ * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
284
+ * @dtopt API
285
+ * @deprecated Since v1.10
286
+ *
287
+ * @example
288
+ * $(document).ready(function() {
289
+ * var oTable = $('#example').dataTable( {
290
+ * "sScrollY": "200px",
291
+ * "bPaginate": false
292
+ * } );
293
+ *
294
+ * $(window).on('resize', function () {
295
+ * oTable.fnAdjustColumnSizing();
296
+ * } );
297
+ * } );
298
+ */
299
+ this.fnAdjustColumnSizing = function ( bRedraw )
300
+ {
301
+ var api = this.api( true ).columns.adjust();
302
+ var settings = api.settings()[0];
303
+ var scroll = settings.oScroll;
304
+
305
+ if ( bRedraw === undefined || bRedraw ) {
306
+ api.draw( false );
307
+ }
308
+ else if ( scroll.sX !== "" || scroll.sY !== "" ) {
309
+ /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
310
+ _fnScrollDraw( settings );
311
+ }
312
+ };
313
+
314
+
315
+ /**
316
+ * Quickly and simply clear a table
317
+ * @param {bool} [bRedraw=true] redraw the table or not
318
+ * @dtopt API
319
+ * @deprecated Since v1.10
320
+ *
321
+ * @example
322
+ * $(document).ready(function() {
323
+ * var oTable = $('#example').dataTable();
324
+ *
325
+ * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
326
+ * oTable.fnClearTable();
327
+ * } );
328
+ */
329
+ this.fnClearTable = function( bRedraw )
330
+ {
331
+ var api = this.api( true ).clear();
332
+
333
+ if ( bRedraw === undefined || bRedraw ) {
334
+ api.draw();
335
+ }
336
+ };
337
+
338
+
339
+ /**
340
+ * The exact opposite of 'opening' a row, this function will close any rows which
341
+ * are currently 'open'.
342
+ * @param {node} nTr the table row to 'close'
343
+ * @returns {int} 0 on success, or 1 if failed (can't find the row)
344
+ * @dtopt API
345
+ * @deprecated Since v1.10
346
+ *
347
+ * @example
348
+ * $(document).ready(function() {
349
+ * var oTable;
350
+ *
351
+ * // 'open' an information row when a row is clicked on
352
+ * $('#example tbody tr').click( function () {
353
+ * if ( oTable.fnIsOpen(this) ) {
354
+ * oTable.fnClose( this );
355
+ * } else {
356
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
357
+ * }
358
+ * } );
359
+ *
360
+ * oTable = $('#example').dataTable();
361
+ * } );
362
+ */
363
+ this.fnClose = function( nTr )
364
+ {
365
+ this.api( true ).row( nTr ).child.hide();
366
+ };
367
+
368
+
369
+ /**
370
+ * Remove a row for the table
371
+ * @param {mixed} target The index of the row from aoData to be deleted, or
372
+ * the TR element you want to delete
373
+ * @param {function|null} [callBack] Callback function
374
+ * @param {bool} [redraw=true] Redraw the table or not
375
+ * @returns {array} The row that was deleted
376
+ * @dtopt API
377
+ * @deprecated Since v1.10
378
+ *
379
+ * @example
380
+ * $(document).ready(function() {
381
+ * var oTable = $('#example').dataTable();
382
+ *
383
+ * // Immediately remove the first row
384
+ * oTable.fnDeleteRow( 0 );
385
+ * } );
386
+ */
387
+ this.fnDeleteRow = function( target, callback, redraw )
388
+ {
389
+ var api = this.api( true );
390
+ var rows = api.rows( target );
391
+ var settings = rows.settings()[0];
392
+ var data = settings.aoData[ rows[0][0] ];
393
+
394
+ rows.remove();
395
+
396
+ if ( callback ) {
397
+ callback.call( this, settings, data );
398
+ }
399
+
400
+ if ( redraw === undefined || redraw ) {
401
+ api.draw();
402
+ }
403
+
404
+ return data;
405
+ };
406
+
407
+
408
+ /**
409
+ * Restore the table to it's original state in the DOM by removing all of DataTables
410
+ * enhancements, alterations to the DOM structure of the table and event listeners.
411
+ * @param {boolean} [remove=false] Completely remove the table from the DOM
412
+ * @dtopt API
413
+ * @deprecated Since v1.10
414
+ *
415
+ * @example
416
+ * $(document).ready(function() {
417
+ * // This example is fairly pointless in reality, but shows how fnDestroy can be used
418
+ * var oTable = $('#example').dataTable();
419
+ * oTable.fnDestroy();
420
+ * } );
421
+ */
422
+ this.fnDestroy = function ( remove )
423
+ {
424
+ this.api( true ).destroy( remove );
425
+ };
426
+
427
+
428
+ /**
429
+ * Redraw the table
430
+ * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
431
+ * @dtopt API
432
+ * @deprecated Since v1.10
433
+ *
434
+ * @example
435
+ * $(document).ready(function() {
436
+ * var oTable = $('#example').dataTable();
437
+ *
438
+ * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
439
+ * oTable.fnDraw();
440
+ * } );
441
+ */
442
+ this.fnDraw = function( complete )
443
+ {
444
+ // Note that this isn't an exact match to the old call to _fnDraw - it takes
445
+ // into account the new data, but can hold position.
446
+ this.api( true ).draw( complete );
447
+ };
448
+
449
+
450
+ /**
451
+ * Filter the input based on data
452
+ * @param {string} sInput String to filter the table on
453
+ * @param {int|null} [iColumn] Column to limit filtering to
454
+ * @param {bool} [bRegex=false] Treat as regular expression or not
455
+ * @param {bool} [bSmart=true] Perform smart filtering or not
456
+ * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
457
+ * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
458
+ * @dtopt API
459
+ * @deprecated Since v1.10
460
+ *
461
+ * @example
462
+ * $(document).ready(function() {
463
+ * var oTable = $('#example').dataTable();
464
+ *
465
+ * // Sometime later - filter...
466
+ * oTable.fnFilter( 'test string' );
467
+ * } );
468
+ */
469
+ this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
470
+ {
471
+ var api = this.api( true );
472
+
473
+ if ( iColumn === null || iColumn === undefined ) {
474
+ api.search( sInput, bRegex, bSmart, bCaseInsensitive );
475
+ }
476
+ else {
477
+ api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
478
+ }
479
+
480
+ api.draw();
481
+ };
482
+
483
+
484
+ /**
485
+ * Get the data for the whole table, an individual row or an individual cell based on the
486
+ * provided parameters.
487
+ * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
488
+ * a TR node then the data source for the whole row will be returned. If given as a
489
+ * TD/TH cell node then iCol will be automatically calculated and the data for the
490
+ * cell returned. If given as an integer, then this is treated as the aoData internal
491
+ * data index for the row (see fnGetPosition) and the data for that row used.
492
+ * @param {int} [col] Optional column index that you want the data of.
493
+ * @returns {array|object|string} If mRow is undefined, then the data for all rows is
494
+ * returned. If mRow is defined, just data for that row, and is iCol is
495
+ * defined, only data for the designated cell is returned.
496
+ * @dtopt API
497
+ * @deprecated Since v1.10
498
+ *
499
+ * @example
500
+ * // Row data
501
+ * $(document).ready(function() {
502
+ * oTable = $('#example').dataTable();
503
+ *
504
+ * oTable.$('tr').click( function () {
505
+ * var data = oTable.fnGetData( this );
506
+ * // ... do something with the array / object of data for the row
507
+ * } );
508
+ * } );
509
+ *
510
+ * @example
511
+ * // Individual cell data
512
+ * $(document).ready(function() {
513
+ * oTable = $('#example').dataTable();
514
+ *
515
+ * oTable.$('td').click( function () {
516
+ * var sData = oTable.fnGetData( this );
517
+ * alert( 'The cell clicked on had the value of '+sData );
518
+ * } );
519
+ * } );
520
+ */
521
+ this.fnGetData = function( src, col )
522
+ {
523
+ var api = this.api( true );
524
+
525
+ if ( src !== undefined ) {
526
+ var type = src.nodeName ? src.nodeName.toLowerCase() : '';
527
+
528
+ return col !== undefined || type == 'td' || type == 'th' ?
529
+ api.cell( src, col ).data() :
530
+ api.row( src ).data() || null;
531
+ }
532
+
533
+ return api.data().toArray();
534
+ };
535
+
536
+
537
+ /**
538
+ * Get an array of the TR nodes that are used in the table's body. Note that you will
539
+ * typically want to use the '$' API method in preference to this as it is more
540
+ * flexible.
541
+ * @param {int} [iRow] Optional row index for the TR element you want
542
+ * @returns {array|node} If iRow is undefined, returns an array of all TR elements
543
+ * in the table's body, or iRow is defined, just the TR element requested.
544
+ * @dtopt API
545
+ * @deprecated Since v1.10
546
+ *
547
+ * @example
548
+ * $(document).ready(function() {
549
+ * var oTable = $('#example').dataTable();
550
+ *
551
+ * // Get the nodes from the table
552
+ * var nNodes = oTable.fnGetNodes( );
553
+ * } );
554
+ */
555
+ this.fnGetNodes = function( iRow )
556
+ {
557
+ var api = this.api( true );
558
+
559
+ return iRow !== undefined ?
560
+ api.row( iRow ).node() :
561
+ api.rows().nodes().flatten().toArray();
562
+ };
563
+
564
+
565
+ /**
566
+ * Get the array indexes of a particular cell from it's DOM element
567
+ * and column index including hidden columns
568
+ * @param {node} node this can either be a TR, TD or TH in the table's body
569
+ * @returns {int} If nNode is given as a TR, then a single index is returned, or
570
+ * if given as a cell, an array of [row index, column index (visible),
571
+ * column index (all)] is given.
572
+ * @dtopt API
573
+ * @deprecated Since v1.10
574
+ *
575
+ * @example
576
+ * $(document).ready(function() {
577
+ * $('#example tbody td').click( function () {
578
+ * // Get the position of the current data from the node
579
+ * var aPos = oTable.fnGetPosition( this );
580
+ *
581
+ * // Get the data array for this row
582
+ * var aData = oTable.fnGetData( aPos[0] );
583
+ *
584
+ * // Update the data array and return the value
585
+ * aData[ aPos[1] ] = 'clicked';
586
+ * this.innerHTML = 'clicked';
587
+ * } );
588
+ *
589
+ * // Init DataTables
590
+ * oTable = $('#example').dataTable();
591
+ * } );
592
+ */
593
+ this.fnGetPosition = function( node )
594
+ {
595
+ var api = this.api( true );
596
+ var nodeName = node.nodeName.toUpperCase();
597
+
598
+ if ( nodeName == 'TR' ) {
599
+ return api.row( node ).index();
600
+ }
601
+ else if ( nodeName == 'TD' || nodeName == 'TH' ) {
602
+ var cell = api.cell( node ).index();
603
+
604
+ return [
605
+ cell.row,
606
+ cell.columnVisible,
607
+ cell.column
608
+ ];
609
+ }
610
+ return null;
611
+ };
612
+
613
+
614
+ /**
615
+ * Check to see if a row is 'open' or not.
616
+ * @param {node} nTr the table row to check
617
+ * @returns {boolean} true if the row is currently open, false otherwise
618
+ * @dtopt API
619
+ * @deprecated Since v1.10
620
+ *
621
+ * @example
622
+ * $(document).ready(function() {
623
+ * var oTable;
624
+ *
625
+ * // 'open' an information row when a row is clicked on
626
+ * $('#example tbody tr').click( function () {
627
+ * if ( oTable.fnIsOpen(this) ) {
628
+ * oTable.fnClose( this );
629
+ * } else {
630
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
631
+ * }
632
+ * } );
633
+ *
634
+ * oTable = $('#example').dataTable();
635
+ * } );
636
+ */
637
+ this.fnIsOpen = function( nTr )
638
+ {
639
+ return this.api( true ).row( nTr ).child.isShown();
640
+ };
641
+
642
+
643
+ /**
644
+ * This function will place a new row directly after a row which is currently
645
+ * on display on the page, with the HTML contents that is passed into the
646
+ * function. This can be used, for example, to ask for confirmation that a
647
+ * particular record should be deleted.
648
+ * @param {node} nTr The table row to 'open'
649
+ * @param {string|node|jQuery} mHtml The HTML to put into the row
650
+ * @param {string} sClass Class to give the new TD cell
651
+ * @returns {node} The row opened. Note that if the table row passed in as the
652
+ * first parameter, is not found in the table, this method will silently
653
+ * return.
654
+ * @dtopt API
655
+ * @deprecated Since v1.10
656
+ *
657
+ * @example
658
+ * $(document).ready(function() {
659
+ * var oTable;
660
+ *
661
+ * // 'open' an information row when a row is clicked on
662
+ * $('#example tbody tr').click( function () {
663
+ * if ( oTable.fnIsOpen(this) ) {
664
+ * oTable.fnClose( this );
665
+ * } else {
666
+ * oTable.fnOpen( this, "Temporary row opened", "info_row" );
667
+ * }
668
+ * } );
669
+ *
670
+ * oTable = $('#example').dataTable();
671
+ * } );
672
+ */
673
+ this.fnOpen = function( nTr, mHtml, sClass )
674
+ {
675
+ return this.api( true )
676
+ .row( nTr )
677
+ .child( mHtml, sClass )
678
+ .show()
679
+ .child()[0];
680
+ };
681
+
682
+
683
+ /**
684
+ * Change the pagination - provides the internal logic for pagination in a simple API
685
+ * function. With this function you can have a DataTables table go to the next,
686
+ * previous, first or last pages.
687
+ * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
688
+ * or page number to jump to (integer), note that page 0 is the first page.
689
+ * @param {bool} [bRedraw=true] Redraw the table or not
690
+ * @dtopt API
691
+ * @deprecated Since v1.10
692
+ *
693
+ * @example
694
+ * $(document).ready(function() {
695
+ * var oTable = $('#example').dataTable();
696
+ * oTable.fnPageChange( 'next' );
697
+ * } );
698
+ */
699
+ this.fnPageChange = function ( mAction, bRedraw )
700
+ {
701
+ var api = this.api( true ).page( mAction );
702
+
703
+ if ( bRedraw === undefined || bRedraw ) {
704
+ api.draw(false);
705
+ }
706
+ };
707
+
708
+
709
+ /**
710
+ * Show a particular column
711
+ * @param {int} iCol The column whose display should be changed
712
+ * @param {bool} bShow Show (true) or hide (false) the column
713
+ * @param {bool} [bRedraw=true] Redraw the table or not
714
+ * @dtopt API
715
+ * @deprecated Since v1.10
716
+ *
717
+ * @example
718
+ * $(document).ready(function() {
719
+ * var oTable = $('#example').dataTable();
720
+ *
721
+ * // Hide the second column after initialisation
722
+ * oTable.fnSetColumnVis( 1, false );
723
+ * } );
724
+ */
725
+ this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
726
+ {
727
+ var api = this.api( true ).column( iCol ).visible( bShow );
728
+
729
+ if ( bRedraw === undefined || bRedraw ) {
730
+ api.columns.adjust().draw();
731
+ }
732
+ };
733
+
734
+
735
+ /**
736
+ * Get the settings for a particular table for external manipulation
737
+ * @returns {object} DataTables settings object. See
738
+ * {@link DataTable.models.oSettings}
739
+ * @dtopt API
740
+ * @deprecated Since v1.10
741
+ *
742
+ * @example
743
+ * $(document).ready(function() {
744
+ * var oTable = $('#example').dataTable();
745
+ * var oSettings = oTable.fnSettings();
746
+ *
747
+ * // Show an example parameter from the settings
748
+ * alert( oSettings._iDisplayStart );
749
+ * } );
750
+ */
751
+ this.fnSettings = function()
752
+ {
753
+ return _fnSettingsFromNode( this[_ext.iApiIndex] );
754
+ };
755
+
756
+
757
+ /**
758
+ * Sort the table by a particular column
759
+ * @param {int} iCol the data index to sort on. Note that this will not match the
760
+ * 'display index' if you have hidden data entries
761
+ * @dtopt API
762
+ * @deprecated Since v1.10
763
+ *
764
+ * @example
765
+ * $(document).ready(function() {
766
+ * var oTable = $('#example').dataTable();
767
+ *
768
+ * // Sort immediately with columns 0 and 1
769
+ * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
770
+ * } );
771
+ */
772
+ this.fnSort = function( aaSort )
773
+ {
774
+ this.api( true ).order( aaSort ).draw();
775
+ };
776
+
777
+
778
+ /**
779
+ * Attach a sort listener to an element for a given column
780
+ * @param {node} nNode the element to attach the sort listener to
781
+ * @param {int} iColumn the column that a click on this node will sort on
782
+ * @param {function} [fnCallback] callback function when sort is run
783
+ * @dtopt API
784
+ * @deprecated Since v1.10
785
+ *
786
+ * @example
787
+ * $(document).ready(function() {
788
+ * var oTable = $('#example').dataTable();
789
+ *
790
+ * // Sort on column 1, when 'sorter' is clicked on
791
+ * oTable.fnSortListener( document.getElementById('sorter'), 1 );
792
+ * } );
793
+ */
794
+ this.fnSortListener = function( nNode, iColumn, fnCallback )
795
+ {
796
+ this.api( true ).order.listener( nNode, iColumn, fnCallback );
797
+ };
798
+
799
+
800
+ /**
801
+ * Update a table cell or row - this method will accept either a single value to
802
+ * update the cell with, an array of values with one element for each column or
803
+ * an object in the same format as the original data source. The function is
804
+ * self-referencing in order to make the multi column updates easier.
805
+ * @param {object|array|string} mData Data to update the cell/row with
806
+ * @param {node|int} mRow TR element you want to update or the aoData index
807
+ * @param {int} [iColumn] The column to update, give as null or undefined to
808
+ * update a whole row.
809
+ * @param {bool} [bRedraw=true] Redraw the table or not
810
+ * @param {bool} [bAction=true] Perform pre-draw actions or not
811
+ * @returns {int} 0 on success, 1 on error
812
+ * @dtopt API
813
+ * @deprecated Since v1.10
814
+ *
815
+ * @example
816
+ * $(document).ready(function() {
817
+ * var oTable = $('#example').dataTable();
818
+ * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
819
+ * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
820
+ * } );
821
+ */
822
+ this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
823
+ {
824
+ var api = this.api( true );
825
+
826
+ if ( iColumn === undefined || iColumn === null ) {
827
+ api.row( mRow ).data( mData );
828
+ }
829
+ else {
830
+ api.cell( mRow, iColumn ).data( mData );
831
+ }
832
+
833
+ if ( bAction === undefined || bAction ) {
834
+ api.columns.adjust();
835
+ }
836
+
837
+ if ( bRedraw === undefined || bRedraw ) {
838
+ api.draw();
839
+ }
840
+ return 0;
841
+ };
842
+
843
+
844
+ /**
845
+ * Provide a common method for plug-ins to check the version of DataTables being used, in order
846
+ * to ensure compatibility.
847
+ * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
848
+ * formats "X" and "X.Y" are also acceptable.
849
+ * @returns {boolean} true if this version of DataTables is greater or equal to the required
850
+ * version, or false if this version of DataTales is not suitable
851
+ * @method
852
+ * @dtopt API
853
+ * @deprecated Since v1.10
854
+ *
855
+ * @example
856
+ * $(document).ready(function() {
857
+ * var oTable = $('#example').dataTable();
858
+ * alert( oTable.fnVersionCheck( '1.9.0' ) );
859
+ * } );
860
+ */
861
+ this.fnVersionCheck = _ext.fnVersionCheck;
862
+
863
+
864
+ var _that = this;
865
+ var emptyInit = options === undefined;
866
+ var len = this.length;
867
+
868
+ if ( emptyInit ) {
869
+ options = {};
870
+ }
871
+
872
+ this.oApi = this.internal = _ext.internal;
873
+
874
+ // Extend with old style plug-in API methods
875
+ for ( var fn in DataTable.ext.internal ) {
876
+ if ( fn ) {
877
+ this[fn] = _fnExternApiFunc(fn);
878
+ }
879
+ }
880
+
881
+ this.each(function() {
882
+ // For each initialisation we want to give it a clean initialisation
883
+ // object that can be bashed around
884
+ var o = {};
885
+ var oInit = len > 1 ? // optimisation for single table case
886
+ _fnExtend( o, options, true ) :
887
+ options;
888
+
889
+ /*global oInit,_that,emptyInit*/
890
+ var i=0, iLen, j, jLen, k, kLen;
891
+ var sId = this.getAttribute( 'id' );
892
+ var bInitHandedOff = false;
893
+ var defaults = DataTable.defaults;
894
+ var $this = $(this);
895
+
896
+
897
+ /* Sanity check */
898
+ if ( this.nodeName.toLowerCase() != 'table' )
899
+ {
900
+ _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
901
+ return;
902
+ }
903
+
904
+ /* Backwards compatibility for the defaults */
905
+ _fnCompatOpts( defaults );
906
+ _fnCompatCols( defaults.column );
907
+
908
+ /* Convert the camel-case defaults to Hungarian */
909
+ _fnCamelToHungarian( defaults, defaults, true );
910
+ _fnCamelToHungarian( defaults.column, defaults.column, true );
911
+
912
+ /* Setting up the initialisation object */
913
+ _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );
914
+
915
+
916
+
917
+ /* Check to see if we are re-initialising a table */
918
+ var allSettings = DataTable.settings;
919
+ for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
920
+ {
921
+ var s = allSettings[i];
922
+
923
+ /* Base check on table node */
924
+ if (
925
+ s.nTable == this ||
926
+ (s.nTHead && s.nTHead.parentNode == this) ||
927
+ (s.nTFoot && s.nTFoot.parentNode == this)
928
+ ) {
929
+ var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
930
+ var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
931
+
932
+ if ( emptyInit || bRetrieve )
933
+ {
934
+ return s.oInstance;
935
+ }
936
+ else if ( bDestroy )
937
+ {
938
+ s.oInstance.fnDestroy();
939
+ break;
940
+ }
941
+ else
942
+ {
943
+ _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
944
+ return;
945
+ }
946
+ }
947
+
948
+ /* If the element we are initialising has the same ID as a table which was previously
949
+ * initialised, but the table nodes don't match (from before) then we destroy the old
950
+ * instance by simply deleting it. This is under the assumption that the table has been
951
+ * destroyed by other methods. Anyone using non-id selectors will need to do this manually
952
+ */
953
+ if ( s.sTableId == this.id )
954
+ {
955
+ allSettings.splice( i, 1 );
956
+ break;
957
+ }
958
+ }
959
+
960
+ /* Ensure the table has an ID - required for accessibility */
961
+ if ( sId === null || sId === "" )
962
+ {
963
+ sId = "DataTables_Table_"+(DataTable.ext._unique++);
964
+ this.id = sId;
965
+ }
966
+
967
+ /* Create the settings object for this table and set some of the default parameters */
968
+ var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
969
+ "sDestroyWidth": $this[0].style.width,
970
+ "sInstance": sId,
971
+ "sTableId": sId
972
+ } );
973
+ oSettings.nTable = this;
974
+ oSettings.oApi = _that.internal;
975
+ oSettings.oInit = oInit;
976
+
977
+ allSettings.push( oSettings );
978
+
979
+ // Need to add the instance after the instance after the settings object has been added
980
+ // to the settings array, so we can self reference the table instance if more than one
981
+ oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
982
+
983
+ // Backwards compatibility, before we apply all the defaults
984
+ _fnCompatOpts( oInit );
985
+ _fnLanguageCompat( oInit.oLanguage );
986
+
987
+ // If the length menu is given, but the init display length is not, use the length menu
988
+ if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
989
+ {
990
+ oInit.iDisplayLength = Array.isArray( oInit.aLengthMenu[0] ) ?
991
+ oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
992
+ }
993
+
994
+ // Apply the defaults and init options to make a single init object will all
995
+ // options defined from defaults and instance options.
996
+ oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
997
+
998
+
999
+ // Map the initialisation options onto the settings object
1000
+ _fnMap( oSettings.oFeatures, oInit, [
1001
+ "bPaginate",
1002
+ "bLengthChange",
1003
+ "bFilter",
1004
+ "bSort",
1005
+ "bSortMulti",
1006
+ "bInfo",
1007
+ "bProcessing",
1008
+ "bAutoWidth",
1009
+ "bSortClasses",
1010
+ "bServerSide",
1011
+ "bDeferRender"
1012
+ ] );
1013
+ _fnMap( oSettings, oInit, [
1014
+ "asStripeClasses",
1015
+ "ajax",
1016
+ "fnServerData",
1017
+ "fnFormatNumber",
1018
+ "sServerMethod",
1019
+ "aaSorting",
1020
+ "aaSortingFixed",
1021
+ "aLengthMenu",
1022
+ "sPaginationType",
1023
+ "sAjaxSource",
1024
+ "sAjaxDataProp",
1025
+ "iStateDuration",
1026
+ "sDom",
1027
+ "bSortCellsTop",
1028
+ "iTabIndex",
1029
+ "fnStateLoadCallback",
1030
+ "fnStateSaveCallback",
1031
+ "renderer",
1032
+ "searchDelay",
1033
+ "rowId",
1034
+ [ "iCookieDuration", "iStateDuration" ], // backwards compat
1035
+ [ "oSearch", "oPreviousSearch" ],
1036
+ [ "aoSearchCols", "aoPreSearchCols" ],
1037
+ [ "iDisplayLength", "_iDisplayLength" ]
1038
+ ] );
1039
+ _fnMap( oSettings.oScroll, oInit, [
1040
+ [ "sScrollX", "sX" ],
1041
+ [ "sScrollXInner", "sXInner" ],
1042
+ [ "sScrollY", "sY" ],
1043
+ [ "bScrollCollapse", "bCollapse" ]
1044
+ ] );
1045
+ _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
1046
+
1047
+ /* Callback functions which are array driven */
1048
+ _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
1049
+ _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
1050
+ _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
1051
+ _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
1052
+ _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
1053
+ _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
1054
+ _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
1055
+ _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
1056
+ _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
1057
+ _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
1058
+ _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
1059
+
1060
+ oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
1061
+
1062
+ /* Browser support detection */
1063
+ _fnBrowserDetect( oSettings );
1064
+
1065
+ var oClasses = oSettings.oClasses;
1066
+
1067
+ $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
1068
+ $this.addClass( oClasses.sTable );
1069
+
1070
+
1071
+ if ( oSettings.iInitDisplayStart === undefined )
1072
+ {
1073
+ /* Display start point, taking into account the save saving */
1074
+ oSettings.iInitDisplayStart = oInit.iDisplayStart;
1075
+ oSettings._iDisplayStart = oInit.iDisplayStart;
1076
+ }
1077
+
1078
+ if ( oInit.iDeferLoading !== null )
1079
+ {
1080
+ oSettings.bDeferLoading = true;
1081
+ var tmp = Array.isArray( oInit.iDeferLoading );
1082
+ oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
1083
+ oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
1084
+ }
1085
+
1086
+ /* Language definitions */
1087
+ var oLanguage = oSettings.oLanguage;
1088
+ $.extend( true, oLanguage, oInit.oLanguage );
1089
+
1090
+ if ( oLanguage.sUrl )
1091
+ {
1092
+ /* Get the language definitions from a file - because this Ajax call makes the language
1093
+ * get async to the remainder of this function we use bInitHandedOff to indicate that
1094
+ * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
1095
+ */
1096
+ $.ajax( {
1097
+ dataType: 'json',
1098
+ url: oLanguage.sUrl,
1099
+ success: function ( json ) {
1100
+ _fnLanguageCompat( json );
1101
+ _fnCamelToHungarian( defaults.oLanguage, json );
1102
+ $.extend( true, oLanguage, json );
1103
+ _fnInitialise( oSettings );
1104
+ },
1105
+ error: function () {
1106
+ // Error occurred loading language file, continue on as best we can
1107
+ _fnInitialise( oSettings );
1108
+ }
1109
+ } );
1110
+ bInitHandedOff = true;
1111
+ }
1112
+
1113
+ /*
1114
+ * Stripes
1115
+ */
1116
+ if ( oInit.asStripeClasses === null )
1117
+ {
1118
+ oSettings.asStripeClasses =[
1119
+ oClasses.sStripeOdd,
1120
+ oClasses.sStripeEven
1121
+ ];
1122
+ }
1123
+
1124
+ /* Remove row stripe classes if they are already on the table row */
1125
+ var stripeClasses = oSettings.asStripeClasses;
1126
+ var rowOne = $this.children('tbody').find('tr').eq(0);
1127
+ if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
1128
+ return rowOne.hasClass(el);
1129
+ } ) ) !== -1 ) {
1130
+ $('tbody tr', this).removeClass( stripeClasses.join(' ') );
1131
+ oSettings.asDestroyStripes = stripeClasses.slice();
1132
+ }
1133
+
1134
+ /*
1135
+ * Columns
1136
+ * See if we should load columns automatically or use defined ones
1137
+ */
1138
+ var anThs = [];
1139
+ var aoColumnsInit;
1140
+ var nThead = this.getElementsByTagName('thead');
1141
+ if ( nThead.length !== 0 )
1142
+ {
1143
+ _fnDetectHeader( oSettings.aoHeader, nThead[0] );
1144
+ anThs = _fnGetUniqueThs( oSettings );
1145
+ }
1146
+
1147
+ /* If not given a column array, generate one with nulls */
1148
+ if ( oInit.aoColumns === null )
1149
+ {
1150
+ aoColumnsInit = [];
1151
+ for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
1152
+ {
1153
+ aoColumnsInit.push( null );
1154
+ }
1155
+ }
1156
+ else
1157
+ {
1158
+ aoColumnsInit = oInit.aoColumns;
1159
+ }
1160
+
1161
+ /* Add the columns */
1162
+ for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
1163
+ {
1164
+ _fnAddColumn( oSettings, anThs ? anThs[i] : null );
1165
+ }
1166
+
1167
+ /* Apply the column definitions */
1168
+ _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
1169
+ _fnColumnOptions( oSettings, iCol, oDef );
1170
+ } );
1171
+
1172
+ /* HTML5 attribute detection - build an mData object automatically if the
1173
+ * attributes are found
1174
+ */
1175
+ if ( rowOne.length ) {
1176
+ var a = function ( cell, name ) {
1177
+ return cell.getAttribute( 'data-'+name ) !== null ? name : null;
1178
+ };
1179
+
1180
+ $( rowOne[0] ).children('th, td').each( function (i, cell) {
1181
+ var col = oSettings.aoColumns[i];
1182
+
1183
+ if ( col.mData === i ) {
1184
+ var sort = a( cell, 'sort' ) || a( cell, 'order' );
1185
+ var filter = a( cell, 'filter' ) || a( cell, 'search' );
1186
+
1187
+ if ( sort !== null || filter !== null ) {
1188
+ col.mData = {
1189
+ _: i+'.display',
1190
+ sort: sort !== null ? i+'.@data-'+sort : undefined,
1191
+ type: sort !== null ? i+'.@data-'+sort : undefined,
1192
+ filter: filter !== null ? i+'.@data-'+filter : undefined
1193
+ };
1194
+
1195
+ _fnColumnOptions( oSettings, i );
1196
+ }
1197
+ }
1198
+ } );
1199
+ }
1200
+
1201
+ var features = oSettings.oFeatures;
1202
+ var loadedInit = function () {
1203
+ /*
1204
+ * Sorting
1205
+ * @todo For modularisation (1.11) this needs to do into a sort start up handler
1206
+ */
1207
+
1208
+ // If aaSorting is not defined, then we use the first indicator in asSorting
1209
+ // in case that has been altered, so the default sort reflects that option
1210
+ if ( oInit.aaSorting === undefined ) {
1211
+ var sorting = oSettings.aaSorting;
1212
+ for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
1213
+ sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
1214
+ }
1215
+ }
1216
+
1217
+ /* Do a first pass on the sorting classes (allows any size changes to be taken into
1218
+ * account, and also will apply sorting disabled classes if disabled
1219
+ */
1220
+ _fnSortingClasses( oSettings );
1221
+
1222
+ if ( features.bSort ) {
1223
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1224
+ if ( oSettings.bSorted ) {
1225
+ var aSort = _fnSortFlatten( oSettings );
1226
+ var sortedColumns = {};
1227
+
1228
+ $.each( aSort, function (i, val) {
1229
+ sortedColumns[ val.src ] = val.dir;
1230
+ } );
1231
+
1232
+ _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
1233
+ _fnSortAria( oSettings );
1234
+ }
1235
+ } );
1236
+ }
1237
+
1238
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1239
+ if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
1240
+ _fnSortingClasses( oSettings );
1241
+ }
1242
+ }, 'sc' );
1243
+
1244
+
1245
+ /*
1246
+ * Final init
1247
+ * Cache the header, body and footer as required, creating them if needed
1248
+ */
1249
+
1250
+ // Work around for Webkit bug 83867 - store the caption-side before removing from doc
1251
+ var captions = $this.children('caption').each( function () {
1252
+ this._captionSide = $(this).css('caption-side');
1253
+ } );
1254
+
1255
+ var thead = $this.children('thead');
1256
+ if ( thead.length === 0 ) {
1257
+ thead = $('<thead/>').appendTo($this);
1258
+ }
1259
+ oSettings.nTHead = thead[0];
1260
+
1261
+ var tbody = $this.children('tbody');
1262
+ if ( tbody.length === 0 ) {
1263
+ tbody = $('<tbody/>').appendTo($this);
1264
+ }
1265
+ oSettings.nTBody = tbody[0];
1266
+
1267
+ var tfoot = $this.children('tfoot');
1268
+ if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
1269
+ // If we are a scrolling table, and no footer has been given, then we need to create
1270
+ // a tfoot element for the caption element to be appended to
1271
+ tfoot = $('<tfoot/>').appendTo($this);
1272
+ }
1273
+
1274
+ if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
1275
+ $this.addClass( oClasses.sNoFooter );
1276
+ }
1277
+ else if ( tfoot.length > 0 ) {
1278
+ oSettings.nTFoot = tfoot[0];
1279
+ _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
1280
+ }
1281
+
1282
+ /* Check if there is data passing into the constructor */
1283
+ if ( oInit.aaData ) {
1284
+ for ( i=0 ; i<oInit.aaData.length ; i++ ) {
1285
+ _fnAddData( oSettings, oInit.aaData[ i ] );
1286
+ }
1287
+ }
1288
+ else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
1289
+ /* Grab the data from the page - only do this when deferred loading or no Ajax
1290
+ * source since there is no point in reading the DOM data if we are then going
1291
+ * to replace it with Ajax data
1292
+ */
1293
+ _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
1294
+ }
1295
+
1296
+ /* Copy the data index array */
1297
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
1298
+
1299
+ /* Initialisation complete - table can be drawn */
1300
+ oSettings.bInitialised = true;
1301
+
1302
+ /* Check if we need to initialise the table (it might not have been handed off to the
1303
+ * language processor)
1304
+ */
1305
+ if ( bInitHandedOff === false ) {
1306
+ _fnInitialise( oSettings );
1307
+ }
1308
+ };
1309
+
1310
+ /* Must be done after everything which can be overridden by the state saving! */
1311
+ if ( oInit.bStateSave )
1312
+ {
1313
+ features.bStateSave = true;
1314
+ _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
1315
+ _fnLoadState( oSettings, oInit, loadedInit );
1316
+ }
1317
+ else {
1318
+ loadedInit();
1319
+ }
1320
+
1321
+ } );
1322
+ _that = null;
1323
+ return this;
1324
+ };
1325
+
1326
+
1327
+ /*
1328
+ * It is useful to have variables which are scoped locally so only the
1329
+ * DataTables functions can access them and they don't leak into global space.
1330
+ * At the same time these functions are often useful over multiple files in the
1331
+ * core and API, so we list, or at least document, all variables which are used
1332
+ * by DataTables as private variables here. This also ensures that there is no
1333
+ * clashing of variable names and that they can easily referenced for reuse.
1334
+ */
1335
+
1336
+
1337
+ // Defined else where
1338
+ // _selector_run
1339
+ // _selector_opts
1340
+ // _selector_first
1341
+ // _selector_row_indexes
1342
+
1343
+ var _ext; // DataTable.ext
1344
+ var _Api; // DataTable.Api
1345
+ var _api_register; // DataTable.Api.register
1346
+ var _api_registerPlural; // DataTable.Api.registerPlural
1347
+
1348
+ var _re_dic = {};
1349
+ var _re_new_lines = /[\r\n\u2028]/g;
1350
+ var _re_html = /<.*?>/g;
1351
+
1352
+ // This is not strict ISO8601 - Date.parse() is quite lax, although
1353
+ // implementations differ between browsers.
1354
+ var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
1355
+
1356
+ // Escape regular expression special characters
1357
+ var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
1358
+
1359
+ // http://en.wikipedia.org/wiki/Foreign_exchange_market
1360
+ // - \u20BD - Russian ruble.
1361
+ // - \u20a9 - South Korean Won
1362
+ // - \u20BA - Turkish Lira
1363
+ // - \u20B9 - Indian Rupee
1364
+ // - R - Brazil (R$) and South Africa
1365
+ // - fr - Swiss Franc
1366
+ // - kr - Swedish krona, Norwegian krone and Danish krone
1367
+ // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
1368
+ // - Ƀ - Bitcoin
1369
+ // - Ξ - Ethereum
1370
+ // standards as thousands separators.
1371
+ var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
1372
+
1373
+
1374
+ var _empty = function ( d ) {
1375
+ return !d || d === true || d === '-' ? true : false;
1376
+ };
1377
+
1378
+
1379
+ var _intVal = function ( s ) {
1380
+ var integer = parseInt( s, 10 );
1381
+ return !isNaN(integer) && isFinite(s) ? integer : null;
1382
+ };
1383
+
1384
+ // Convert from a formatted number with characters other than `.` as the
1385
+ // decimal place, to a Javascript number
1386
+ var _numToDecimal = function ( num, decimalPoint ) {
1387
+ // Cache created regular expressions for speed as this function is called often
1388
+ if ( ! _re_dic[ decimalPoint ] ) {
1389
+ _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
1390
+ }
1391
+ return typeof num === 'string' && decimalPoint !== '.' ?
1392
+ num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
1393
+ num;
1394
+ };
1395
+
1396
+
1397
+ var _isNumber = function ( d, decimalPoint, formatted ) {
1398
+ var strType = typeof d === 'string';
1399
+
1400
+ // If empty return immediately so there must be a number if it is a
1401
+ // formatted string (this stops the string "k", or "kr", etc being detected
1402
+ // as a formatted number for currency
1403
+ if ( _empty( d ) ) {
1404
+ return true;
1405
+ }
1406
+
1407
+ if ( decimalPoint && strType ) {
1408
+ d = _numToDecimal( d, decimalPoint );
1409
+ }
1410
+
1411
+ if ( formatted && strType ) {
1412
+ d = d.replace( _re_formatted_numeric, '' );
1413
+ }
1414
+
1415
+ return !isNaN( parseFloat(d) ) && isFinite( d );
1416
+ };
1417
+
1418
+
1419
+ // A string without HTML in it can be considered to be HTML still
1420
+ var _isHtml = function ( d ) {
1421
+ return _empty( d ) || typeof d === 'string';
1422
+ };
1423
+
1424
+
1425
+ var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1426
+ if ( _empty( d ) ) {
1427
+ return true;
1428
+ }
1429
+
1430
+ var html = _isHtml( d );
1431
+ return ! html ?
1432
+ null :
1433
+ _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1434
+ true :
1435
+ null;
1436
+ };
1437
+
1438
+
1439
+ var _pluck = function ( a, prop, prop2 ) {
1440
+ var out = [];
1441
+ var i=0, ien=a.length;
1442
+
1443
+ // Could have the test in the loop for slightly smaller code, but speed
1444
+ // is essential here
1445
+ if ( prop2 !== undefined ) {
1446
+ for ( ; i<ien ; i++ ) {
1447
+ if ( a[i] && a[i][ prop ] ) {
1448
+ out.push( a[i][ prop ][ prop2 ] );
1449
+ }
1450
+ }
1451
+ }
1452
+ else {
1453
+ for ( ; i<ien ; i++ ) {
1454
+ if ( a[i] ) {
1455
+ out.push( a[i][ prop ] );
1456
+ }
1457
+ }
1458
+ }
1459
+
1460
+ return out;
1461
+ };
1462
+
1463
+
1464
+ // Basically the same as _pluck, but rather than looping over `a` we use `order`
1465
+ // as the indexes to pick from `a`
1466
+ var _pluck_order = function ( a, order, prop, prop2 )
1467
+ {
1468
+ var out = [];
1469
+ var i=0, ien=order.length;
1470
+
1471
+ // Could have the test in the loop for slightly smaller code, but speed
1472
+ // is essential here
1473
+ if ( prop2 !== undefined ) {
1474
+ for ( ; i<ien ; i++ ) {
1475
+ if ( a[ order[i] ][ prop ] ) {
1476
+ out.push( a[ order[i] ][ prop ][ prop2 ] );
1477
+ }
1478
+ }
1479
+ }
1480
+ else {
1481
+ for ( ; i<ien ; i++ ) {
1482
+ out.push( a[ order[i] ][ prop ] );
1483
+ }
1484
+ }
1485
+
1486
+ return out;
1487
+ };
1488
+
1489
+
1490
+ var _range = function ( len, start )
1491
+ {
1492
+ var out = [];
1493
+ var end;
1494
+
1495
+ if ( start === undefined ) {
1496
+ start = 0;
1497
+ end = len;
1498
+ }
1499
+ else {
1500
+ end = start;
1501
+ start = len;
1502
+ }
1503
+
1504
+ for ( var i=start ; i<end ; i++ ) {
1505
+ out.push( i );
1506
+ }
1507
+
1508
+ return out;
1509
+ };
1510
+
1511
+
1512
+ var _removeEmpty = function ( a )
1513
+ {
1514
+ var out = [];
1515
+
1516
+ for ( var i=0, ien=a.length ; i<ien ; i++ ) {
1517
+ if ( a[i] ) { // careful - will remove all falsy values!
1518
+ out.push( a[i] );
1519
+ }
1520
+ }
1521
+
1522
+ return out;
1523
+ };
1524
+
1525
+
1526
+ var _stripHtml = function ( d ) {
1527
+ return d.replace( _re_html, '' );
1528
+ };
1529
+
1530
+
1531
+ /**
1532
+ * Determine if all values in the array are unique. This means we can short
1533
+ * cut the _unique method at the cost of a single loop. A sorted array is used
1534
+ * to easily check the values.
1535
+ *
1536
+ * @param {array} src Source array
1537
+ * @return {boolean} true if all unique, false otherwise
1538
+ * @ignore
1539
+ */
1540
+ var _areAllUnique = function ( src ) {
1541
+ if ( src.length < 2 ) {
1542
+ return true;
1543
+ }
1544
+
1545
+ var sorted = src.slice().sort();
1546
+ var last = sorted[0];
1547
+
1548
+ for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
1549
+ if ( sorted[i] === last ) {
1550
+ return false;
1551
+ }
1552
+
1553
+ last = sorted[i];
1554
+ }
1555
+
1556
+ return true;
1557
+ };
1558
+
1559
+
1560
+ /**
1561
+ * Find the unique elements in a source array.
1562
+ *
1563
+ * @param {array} src Source array
1564
+ * @return {array} Array of unique items
1565
+ * @ignore
1566
+ */
1567
+ var _unique = function ( src )
1568
+ {
1569
+ if ( _areAllUnique( src ) ) {
1570
+ return src.slice();
1571
+ }
1572
+
1573
+ // A faster unique method is to use object keys to identify used values,
1574
+ // but this doesn't work with arrays or objects, which we must also
1575
+ // consider. See jsperf.com/compare-array-unique-versions/4 for more
1576
+ // information.
1577
+ var
1578
+ out = [],
1579
+ val,
1580
+ i, ien=src.length,
1581
+ j, k=0;
1582
+
1583
+ again: for ( i=0 ; i<ien ; i++ ) {
1584
+ val = src[i];
1585
+
1586
+ for ( j=0 ; j<k ; j++ ) {
1587
+ if ( out[j] === val ) {
1588
+ continue again;
1589
+ }
1590
+ }
1591
+
1592
+ out.push( val );
1593
+ k++;
1594
+ }
1595
+
1596
+ return out;
1597
+ };
1598
+
1599
+ // Surprisingly this is faster than [].concat.apply
1600
+ // https://jsperf.com/flatten-an-array-loop-vs-reduce/2
1601
+ var _flatten = function (out, val) {
1602
+ if (Array.isArray(val)) {
1603
+ for (var i=0 ; i<val.length ; i++) {
1604
+ _flatten(out, val[i]);
1605
+ }
1606
+ }
1607
+ else {
1608
+ out.push(val);
1609
+ }
1610
+
1611
+ return out;
1612
+ }
1613
+
1614
+ // Array.isArray polyfill.
1615
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
1616
+ if (! Array.isArray) {
1617
+ Array.isArray = function(arg) {
1618
+ return Object.prototype.toString.call(arg) === '[object Array]';
1619
+ };
1620
+ }
1621
+
1622
+ // .trim() polyfill
1623
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim
1624
+ if (!String.prototype.trim) {
1625
+ String.prototype.trim = function () {
1626
+ return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
1627
+ };
1628
+ }
1629
+
1630
+ /**
1631
+ * DataTables utility methods
1632
+ *
1633
+ * This namespace provides helper methods that DataTables uses internally to
1634
+ * create a DataTable, but which are not exclusively used only for DataTables.
1635
+ * These methods can be used by extension authors to save the duplication of
1636
+ * code.
1637
+ *
1638
+ * @namespace
1639
+ */
1640
+ DataTable.util = {
1641
+ /**
1642
+ * Throttle the calls to a function. Arguments and context are maintained
1643
+ * for the throttled function.
1644
+ *
1645
+ * @param {function} fn Function to be called
1646
+ * @param {integer} freq Call frequency in mS
1647
+ * @return {function} Wrapped function
1648
+ */
1649
+ throttle: function ( fn, freq ) {
1650
+ var
1651
+ frequency = freq !== undefined ? freq : 200,
1652
+ last,
1653
+ timer;
1654
+
1655
+ return function () {
1656
+ var
1657
+ that = this,
1658
+ now = +new Date(),
1659
+ args = arguments;
1660
+
1661
+ if ( last && now < last + frequency ) {
1662
+ clearTimeout( timer );
1663
+
1664
+ timer = setTimeout( function () {
1665
+ last = undefined;
1666
+ fn.apply( that, args );
1667
+ }, frequency );
1668
+ }
1669
+ else {
1670
+ last = now;
1671
+ fn.apply( that, args );
1672
+ }
1673
+ };
1674
+ },
1675
+
1676
+
1677
+ /**
1678
+ * Escape a string such that it can be used in a regular expression
1679
+ *
1680
+ * @param {string} val string to escape
1681
+ * @returns {string} escaped string
1682
+ */
1683
+ escapeRegex: function ( val ) {
1684
+ return val.replace( _re_escape_regex, '\\$1' );
1685
+ }
1686
+ };
1687
+
1688
+
1689
+
1690
+ /**
1691
+ * Create a mapping object that allows camel case parameters to be looked up
1692
+ * for their Hungarian counterparts. The mapping is stored in a private
1693
+ * parameter called `_hungarianMap` which can be accessed on the source object.
1694
+ * @param {object} o
1695
+ * @memberof DataTable#oApi
1696
+ */
1697
+ function _fnHungarianMap ( o )
1698
+ {
1699
+ var
1700
+ hungarian = 'a aa ai ao as b fn i m o s ',
1701
+ match,
1702
+ newKey,
1703
+ map = {};
1704
+
1705
+ $.each( o, function (key, val) {
1706
+ match = key.match(/^([^A-Z]+?)([A-Z])/);
1707
+
1708
+ if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
1709
+ {
1710
+ newKey = key.replace( match[0], match[2].toLowerCase() );
1711
+ map[ newKey ] = key;
1712
+
1713
+ if ( match[1] === 'o' )
1714
+ {
1715
+ _fnHungarianMap( o[key] );
1716
+ }
1717
+ }
1718
+ } );
1719
+
1720
+ o._hungarianMap = map;
1721
+ }
1722
+
1723
+
1724
+ /**
1725
+ * Convert from camel case parameters to Hungarian, based on a Hungarian map
1726
+ * created by _fnHungarianMap.
1727
+ * @param {object} src The model object which holds all parameters that can be
1728
+ * mapped.
1729
+ * @param {object} user The object to convert from camel case to Hungarian.
1730
+ * @param {boolean} force When set to `true`, properties which already have a
1731
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
1732
+ * won't be.
1733
+ * @memberof DataTable#oApi
1734
+ */
1735
+ function _fnCamelToHungarian ( src, user, force )
1736
+ {
1737
+ if ( ! src._hungarianMap ) {
1738
+ _fnHungarianMap( src );
1739
+ }
1740
+
1741
+ var hungarianKey;
1742
+
1743
+ $.each( user, function (key, val) {
1744
+ hungarianKey = src._hungarianMap[ key ];
1745
+
1746
+ if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
1747
+ {
1748
+ // For objects, we need to buzz down into the object to copy parameters
1749
+ if ( hungarianKey.charAt(0) === 'o' )
1750
+ {
1751
+ // Copy the camelCase options over to the hungarian
1752
+ if ( ! user[ hungarianKey ] ) {
1753
+ user[ hungarianKey ] = {};
1754
+ }
1755
+ $.extend( true, user[hungarianKey], user[key] );
1756
+
1757
+ _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
1758
+ }
1759
+ else {
1760
+ user[hungarianKey] = user[ key ];
1761
+ }
1762
+ }
1763
+ } );
1764
+ }
1765
+
1766
+
1767
+ /**
1768
+ * Language compatibility - when certain options are given, and others aren't, we
1769
+ * need to duplicate the values over, in order to provide backwards compatibility
1770
+ * with older language files.
1771
+ * @param {object} oSettings dataTables settings object
1772
+ * @memberof DataTable#oApi
1773
+ */
1774
+ function _fnLanguageCompat( lang )
1775
+ {
1776
+ // Note the use of the Hungarian notation for the parameters in this method as
1777
+ // this is called after the mapping of camelCase to Hungarian
1778
+ var defaults = DataTable.defaults.oLanguage;
1779
+
1780
+ // Default mapping
1781
+ var defaultDecimal = defaults.sDecimal;
1782
+ if ( defaultDecimal ) {
1783
+ _addNumericSort( defaultDecimal );
1784
+ }
1785
+
1786
+ if ( lang ) {
1787
+ var zeroRecords = lang.sZeroRecords;
1788
+
1789
+ // Backwards compatibility - if there is no sEmptyTable given, then use the same as
1790
+ // sZeroRecords - assuming that is given.
1791
+ if ( ! lang.sEmptyTable && zeroRecords &&
1792
+ defaults.sEmptyTable === "No data available in table" )
1793
+ {
1794
+ _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
1795
+ }
1796
+
1797
+ // Likewise with loading records
1798
+ if ( ! lang.sLoadingRecords && zeroRecords &&
1799
+ defaults.sLoadingRecords === "Loading..." )
1800
+ {
1801
+ _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
1802
+ }
1803
+
1804
+ // Old parameter name of the thousands separator mapped onto the new
1805
+ if ( lang.sInfoThousands ) {
1806
+ lang.sThousands = lang.sInfoThousands;
1807
+ }
1808
+
1809
+ var decimal = lang.sDecimal;
1810
+ if ( decimal && defaultDecimal !== decimal ) {
1811
+ _addNumericSort( decimal );
1812
+ }
1813
+ }
1814
+ }
1815
+
1816
+
1817
+ /**
1818
+ * Map one parameter onto another
1819
+ * @param {object} o Object to map
1820
+ * @param {*} knew The new parameter name
1821
+ * @param {*} old The old parameter name
1822
+ */
1823
+ var _fnCompatMap = function ( o, knew, old ) {
1824
+ if ( o[ knew ] !== undefined ) {
1825
+ o[ old ] = o[ knew ];
1826
+ }
1827
+ };
1828
+
1829
+
1830
+ /**
1831
+ * Provide backwards compatibility for the main DT options. Note that the new
1832
+ * options are mapped onto the old parameters, so this is an external interface
1833
+ * change only.
1834
+ * @param {object} init Object to map
1835
+ */
1836
+ function _fnCompatOpts ( init )
1837
+ {
1838
+ _fnCompatMap( init, 'ordering', 'bSort' );
1839
+ _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
1840
+ _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
1841
+ _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
1842
+ _fnCompatMap( init, 'order', 'aaSorting' );
1843
+ _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
1844
+ _fnCompatMap( init, 'paging', 'bPaginate' );
1845
+ _fnCompatMap( init, 'pagingType', 'sPaginationType' );
1846
+ _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
1847
+ _fnCompatMap( init, 'searching', 'bFilter' );
1848
+
1849
+ // Boolean initialisation of x-scrolling
1850
+ if ( typeof init.sScrollX === 'boolean' ) {
1851
+ init.sScrollX = init.sScrollX ? '100%' : '';
1852
+ }
1853
+ if ( typeof init.scrollX === 'boolean' ) {
1854
+ init.scrollX = init.scrollX ? '100%' : '';
1855
+ }
1856
+
1857
+ // Column search objects are in an array, so it needs to be converted
1858
+ // element by element
1859
+ var searchCols = init.aoSearchCols;
1860
+
1861
+ if ( searchCols ) {
1862
+ for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
1863
+ if ( searchCols[i] ) {
1864
+ _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
1865
+ }
1866
+ }
1867
+ }
1868
+ }
1869
+
1870
+
1871
+ /**
1872
+ * Provide backwards compatibility for column options. Note that the new options
1873
+ * are mapped onto the old parameters, so this is an external interface change
1874
+ * only.
1875
+ * @param {object} init Object to map
1876
+ */
1877
+ function _fnCompatCols ( init )
1878
+ {
1879
+ _fnCompatMap( init, 'orderable', 'bSortable' );
1880
+ _fnCompatMap( init, 'orderData', 'aDataSort' );
1881
+ _fnCompatMap( init, 'orderSequence', 'asSorting' );
1882
+ _fnCompatMap( init, 'orderDataType', 'sortDataType' );
1883
+
1884
+ // orderData can be given as an integer
1885
+ var dataSort = init.aDataSort;
1886
+ if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) {
1887
+ init.aDataSort = [ dataSort ];
1888
+ }
1889
+ }
1890
+
1891
+
1892
+ /**
1893
+ * Browser feature detection for capabilities, quirks
1894
+ * @param {object} settings dataTables settings object
1895
+ * @memberof DataTable#oApi
1896
+ */
1897
+ function _fnBrowserDetect( settings )
1898
+ {
1899
+ // We don't need to do this every time DataTables is constructed, the values
1900
+ // calculated are specific to the browser and OS configuration which we
1901
+ // don't expect to change between initialisations
1902
+ if ( ! DataTable.__browser ) {
1903
+ var browser = {};
1904
+ DataTable.__browser = browser;
1905
+
1906
+ // Scrolling feature / quirks detection
1907
+ var n = $('<div/>')
1908
+ .css( {
1909
+ position: 'fixed',
1910
+ top: 0,
1911
+ left: $(window).scrollLeft()*-1, // allow for scrolling
1912
+ height: 1,
1913
+ width: 1,
1914
+ overflow: 'hidden'
1915
+ } )
1916
+ .append(
1917
+ $('<div/>')
1918
+ .css( {
1919
+ position: 'absolute',
1920
+ top: 1,
1921
+ left: 1,
1922
+ width: 100,
1923
+ overflow: 'scroll'
1924
+ } )
1925
+ .append(
1926
+ $('<div/>')
1927
+ .css( {
1928
+ width: '100%',
1929
+ height: 10
1930
+ } )
1931
+ )
1932
+ )
1933
+ .appendTo( 'body' );
1934
+
1935
+ var outer = n.children();
1936
+ var inner = outer.children();
1937
+
1938
+ // Numbers below, in order, are:
1939
+ // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
1940
+ //
1941
+ // IE6 XP: 100 100 100 83
1942
+ // IE7 Vista: 100 100 100 83
1943
+ // IE 8+ Windows: 83 83 100 83
1944
+ // Evergreen Windows: 83 83 100 83
1945
+ // Evergreen Mac with scrollbars: 85 85 100 85
1946
+ // Evergreen Mac without scrollbars: 100 100 100 100
1947
+
1948
+ // Get scrollbar width
1949
+ browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
1950
+
1951
+ // IE6/7 will oversize a width 100% element inside a scrolling element, to
1952
+ // include the width of the scrollbar, while other browsers ensure the inner
1953
+ // element is contained without forcing scrolling
1954
+ browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
1955
+
1956
+ // In rtl text layout, some browsers (most, but not all) will place the
1957
+ // scrollbar on the left, rather than the right.
1958
+ browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
1959
+
1960
+ // IE8- don't provide height and width for getBoundingClientRect
1961
+ browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
1962
+
1963
+ n.remove();
1964
+ }
1965
+
1966
+ $.extend( settings.oBrowser, DataTable.__browser );
1967
+ settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
1968
+ }
1969
+
1970
+
1971
+ /**
1972
+ * Array.prototype reduce[Right] method, used for browsers which don't support
1973
+ * JS 1.6. Done this way to reduce code size, since we iterate either way
1974
+ * @param {object} settings dataTables settings object
1975
+ * @memberof DataTable#oApi
1976
+ */
1977
+ function _fnReduce ( that, fn, init, start, end, inc )
1978
+ {
1979
+ var
1980
+ i = start,
1981
+ value,
1982
+ isSet = false;
1983
+
1984
+ if ( init !== undefined ) {
1985
+ value = init;
1986
+ isSet = true;
1987
+ }
1988
+
1989
+ while ( i !== end ) {
1990
+ if ( ! that.hasOwnProperty(i) ) {
1991
+ continue;
1992
+ }
1993
+
1994
+ value = isSet ?
1995
+ fn( value, that[i], i, that ) :
1996
+ that[i];
1997
+
1998
+ isSet = true;
1999
+ i += inc;
2000
+ }
2001
+
2002
+ return value;
2003
+ }
2004
+
2005
+ /**
2006
+ * Add a column to the list used for the table with default values
2007
+ * @param {object} oSettings dataTables settings object
2008
+ * @param {node} nTh The th element for this column
2009
+ * @memberof DataTable#oApi
2010
+ */
2011
+ function _fnAddColumn( oSettings, nTh )
2012
+ {
2013
+ // Add column to aoColumns array
2014
+ var oDefaults = DataTable.defaults.column;
2015
+ var iCol = oSettings.aoColumns.length;
2016
+ var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
2017
+ "nTh": nTh ? nTh : document.createElement('th'),
2018
+ "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
2019
+ "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
2020
+ "mData": oDefaults.mData ? oDefaults.mData : iCol,
2021
+ idx: iCol
2022
+ } );
2023
+ oSettings.aoColumns.push( oCol );
2024
+
2025
+ // Add search object for column specific search. Note that the `searchCols[ iCol ]`
2026
+ // passed into extend can be undefined. This allows the user to give a default
2027
+ // with only some of the parameters defined, and also not give a default
2028
+ var searchCols = oSettings.aoPreSearchCols;
2029
+ searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
2030
+
2031
+ // Use the default column options function to initialise classes etc
2032
+ _fnColumnOptions( oSettings, iCol, $(nTh).data() );
2033
+ }
2034
+
2035
+
2036
+ /**
2037
+ * Apply options for a column
2038
+ * @param {object} oSettings dataTables settings object
2039
+ * @param {int} iCol column index to consider
2040
+ * @param {object} oOptions object with sType, bVisible and bSearchable etc
2041
+ * @memberof DataTable#oApi
2042
+ */
2043
+ function _fnColumnOptions( oSettings, iCol, oOptions )
2044
+ {
2045
+ var oCol = oSettings.aoColumns[ iCol ];
2046
+ var oClasses = oSettings.oClasses;
2047
+ var th = $(oCol.nTh);
2048
+
2049
+ // Try to get width information from the DOM. We can't get it from CSS
2050
+ // as we'd need to parse the CSS stylesheet. `width` option can override
2051
+ if ( ! oCol.sWidthOrig ) {
2052
+ // Width attribute
2053
+ oCol.sWidthOrig = th.attr('width') || null;
2054
+
2055
+ // Style attribute
2056
+ var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
2057
+ if ( t ) {
2058
+ oCol.sWidthOrig = t[1];
2059
+ }
2060
+ }
2061
+
2062
+ /* User specified column options */
2063
+ if ( oOptions !== undefined && oOptions !== null )
2064
+ {
2065
+ // Backwards compatibility
2066
+ _fnCompatCols( oOptions );
2067
+
2068
+ // Map camel case parameters to their Hungarian counterparts
2069
+ _fnCamelToHungarian( DataTable.defaults.column, oOptions, true );
2070
+
2071
+ /* Backwards compatibility for mDataProp */
2072
+ if ( oOptions.mDataProp !== undefined && !oOptions.mData )
2073
+ {
2074
+ oOptions.mData = oOptions.mDataProp;
2075
+ }
2076
+
2077
+ if ( oOptions.sType )
2078
+ {
2079
+ oCol._sManualType = oOptions.sType;
2080
+ }
2081
+
2082
+ // `class` is a reserved word in Javascript, so we need to provide
2083
+ // the ability to use a valid name for the camel case input
2084
+ if ( oOptions.className && ! oOptions.sClass )
2085
+ {
2086
+ oOptions.sClass = oOptions.className;
2087
+ }
2088
+ if ( oOptions.sClass ) {
2089
+ th.addClass( oOptions.sClass );
2090
+ }
2091
+
2092
+ $.extend( oCol, oOptions );
2093
+ _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2094
+
2095
+ /* iDataSort to be applied (backwards compatibility), but aDataSort will take
2096
+ * priority if defined
2097
+ */
2098
+ if ( oOptions.iDataSort !== undefined )
2099
+ {
2100
+ oCol.aDataSort = [ oOptions.iDataSort ];
2101
+ }
2102
+ _fnMap( oCol, oOptions, "aDataSort" );
2103
+ }
2104
+
2105
+ /* Cache the data get and set functions for speed */
2106
+ var mDataSrc = oCol.mData;
2107
+ var mData = _fnGetObjectDataFn( mDataSrc );
2108
+ var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
2109
+
2110
+ var attrTest = function( src ) {
2111
+ return typeof src === 'string' && src.indexOf('@') !== -1;
2112
+ };
2113
+ oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
2114
+ attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
2115
+ );
2116
+ oCol._setter = null;
2117
+
2118
+ oCol.fnGetData = function (rowData, type, meta) {
2119
+ var innerData = mData( rowData, type, undefined, meta );
2120
+
2121
+ return mRender && type ?
2122
+ mRender( innerData, type, rowData, meta ) :
2123
+ innerData;
2124
+ };
2125
+ oCol.fnSetData = function ( rowData, val, meta ) {
2126
+ return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
2127
+ };
2128
+
2129
+ // Indicate if DataTables should read DOM data as an object or array
2130
+ // Used in _fnGetRowElements
2131
+ if ( typeof mDataSrc !== 'number' ) {
2132
+ oSettings._rowReadObject = true;
2133
+ }
2134
+
2135
+ /* Feature sorting overrides column specific when off */
2136
+ if ( !oSettings.oFeatures.bSort )
2137
+ {
2138
+ oCol.bSortable = false;
2139
+ th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
2140
+ }
2141
+
2142
+ /* Check that the class assignment is correct for sorting */
2143
+ var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
2144
+ var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
2145
+ if ( !oCol.bSortable || (!bAsc && !bDesc) )
2146
+ {
2147
+ oCol.sSortingClass = oClasses.sSortableNone;
2148
+ oCol.sSortingClassJUI = "";
2149
+ }
2150
+ else if ( bAsc && !bDesc )
2151
+ {
2152
+ oCol.sSortingClass = oClasses.sSortableAsc;
2153
+ oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
2154
+ }
2155
+ else if ( !bAsc && bDesc )
2156
+ {
2157
+ oCol.sSortingClass = oClasses.sSortableDesc;
2158
+ oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
2159
+ }
2160
+ else
2161
+ {
2162
+ oCol.sSortingClass = oClasses.sSortable;
2163
+ oCol.sSortingClassJUI = oClasses.sSortJUI;
2164
+ }
2165
+ }
2166
+
2167
+
2168
+ /**
2169
+ * Adjust the table column widths for new data. Note: you would probably want to
2170
+ * do a redraw after calling this function!
2171
+ * @param {object} settings dataTables settings object
2172
+ * @memberof DataTable#oApi
2173
+ */
2174
+ function _fnAdjustColumnSizing ( settings )
2175
+ {
2176
+ /* Not interested in doing column width calculation if auto-width is disabled */
2177
+ if ( settings.oFeatures.bAutoWidth !== false )
2178
+ {
2179
+ var columns = settings.aoColumns;
2180
+
2181
+ _fnCalculateColumnWidths( settings );
2182
+ for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
2183
+ {
2184
+ columns[i].nTh.style.width = columns[i].sWidth;
2185
+ }
2186
+ }
2187
+
2188
+ var scroll = settings.oScroll;
2189
+ if ( scroll.sY !== '' || scroll.sX !== '')
2190
+ {
2191
+ _fnScrollDraw( settings );
2192
+ }
2193
+
2194
+ _fnCallbackFire( settings, null, 'column-sizing', [settings] );
2195
+ }
2196
+
2197
+
2198
+ /**
2199
+ * Covert the index of a visible column to the index in the data array (take account
2200
+ * of hidden columns)
2201
+ * @param {object} oSettings dataTables settings object
2202
+ * @param {int} iMatch Visible column index to lookup
2203
+ * @returns {int} i the data index
2204
+ * @memberof DataTable#oApi
2205
+ */
2206
+ function _fnVisibleToColumnIndex( oSettings, iMatch )
2207
+ {
2208
+ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2209
+
2210
+ return typeof aiVis[iMatch] === 'number' ?
2211
+ aiVis[iMatch] :
2212
+ null;
2213
+ }
2214
+
2215
+
2216
+ /**
2217
+ * Covert the index of an index in the data array and convert it to the visible
2218
+ * column index (take account of hidden columns)
2219
+ * @param {int} iMatch Column index to lookup
2220
+ * @param {object} oSettings dataTables settings object
2221
+ * @returns {int} i the data index
2222
+ * @memberof DataTable#oApi
2223
+ */
2224
+ function _fnColumnIndexToVisible( oSettings, iMatch )
2225
+ {
2226
+ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2227
+ var iPos = $.inArray( iMatch, aiVis );
2228
+
2229
+ return iPos !== -1 ? iPos : null;
2230
+ }
2231
+
2232
+
2233
+ /**
2234
+ * Get the number of visible columns
2235
+ * @param {object} oSettings dataTables settings object
2236
+ * @returns {int} i the number of visible columns
2237
+ * @memberof DataTable#oApi
2238
+ */
2239
+ function _fnVisbleColumns( oSettings )
2240
+ {
2241
+ var vis = 0;
2242
+
2243
+ // No reduce in IE8, use a loop for now
2244
+ $.each( oSettings.aoColumns, function ( i, col ) {
2245
+ if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
2246
+ vis++;
2247
+ }
2248
+ } );
2249
+
2250
+ return vis;
2251
+ }
2252
+
2253
+
2254
+ /**
2255
+ * Get an array of column indexes that match a given property
2256
+ * @param {object} oSettings dataTables settings object
2257
+ * @param {string} sParam Parameter in aoColumns to look for - typically
2258
+ * bVisible or bSearchable
2259
+ * @returns {array} Array of indexes with matched properties
2260
+ * @memberof DataTable#oApi
2261
+ */
2262
+ function _fnGetColumns( oSettings, sParam )
2263
+ {
2264
+ var a = [];
2265
+
2266
+ $.map( oSettings.aoColumns, function(val, i) {
2267
+ if ( val[sParam] ) {
2268
+ a.push( i );
2269
+ }
2270
+ } );
2271
+
2272
+ return a;
2273
+ }
2274
+
2275
+
2276
+ /**
2277
+ * Calculate the 'type' of a column
2278
+ * @param {object} settings dataTables settings object
2279
+ * @memberof DataTable#oApi
2280
+ */
2281
+ function _fnColumnTypes ( settings )
2282
+ {
2283
+ var columns = settings.aoColumns;
2284
+ var data = settings.aoData;
2285
+ var types = DataTable.ext.type.detect;
2286
+ var i, ien, j, jen, k, ken;
2287
+ var col, cell, detectedType, cache;
2288
+
2289
+ // For each column, spin over the
2290
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2291
+ col = columns[i];
2292
+ cache = [];
2293
+
2294
+ if ( ! col.sType && col._sManualType ) {
2295
+ col.sType = col._sManualType;
2296
+ }
2297
+ else if ( ! col.sType ) {
2298
+ for ( j=0, jen=types.length ; j<jen ; j++ ) {
2299
+ for ( k=0, ken=data.length ; k<ken ; k++ ) {
2300
+ // Use a cache array so we only need to get the type data
2301
+ // from the formatter once (when using multiple detectors)
2302
+ if ( cache[k] === undefined ) {
2303
+ cache[k] = _fnGetCellData( settings, k, i, 'type' );
2304
+ }
2305
+
2306
+ detectedType = types[j]( cache[k], settings );
2307
+
2308
+ // If null, then this type can't apply to this column, so
2309
+ // rather than testing all cells, break out. There is an
2310
+ // exception for the last type which is `html`. We need to
2311
+ // scan all rows since it is possible to mix string and HTML
2312
+ // types
2313
+ if ( ! detectedType && j !== types.length-1 ) {
2314
+ break;
2315
+ }
2316
+
2317
+ // Only a single match is needed for html type since it is
2318
+ // bottom of the pile and very similar to string
2319
+ if ( detectedType === 'html' ) {
2320
+ break;
2321
+ }
2322
+ }
2323
+
2324
+ // Type is valid for all data points in the column - use this
2325
+ // type
2326
+ if ( detectedType ) {
2327
+ col.sType = detectedType;
2328
+ break;
2329
+ }
2330
+ }
2331
+
2332
+ // Fall back - if no type was detected, always use string
2333
+ if ( ! col.sType ) {
2334
+ col.sType = 'string';
2335
+ }
2336
+ }
2337
+ }
2338
+ }
2339
+
2340
+
2341
+ /**
2342
+ * Take the column definitions and static columns arrays and calculate how
2343
+ * they relate to column indexes. The callback function will then apply the
2344
+ * definition found for a column to a suitable configuration object.
2345
+ * @param {object} oSettings dataTables settings object
2346
+ * @param {array} aoColDefs The aoColumnDefs array that is to be applied
2347
+ * @param {array} aoCols The aoColumns array that defines columns individually
2348
+ * @param {function} fn Callback function - takes two parameters, the calculated
2349
+ * column index and the definition for that column.
2350
+ * @memberof DataTable#oApi
2351
+ */
2352
+ function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
2353
+ {
2354
+ var i, iLen, j, jLen, k, kLen, def;
2355
+ var columns = oSettings.aoColumns;
2356
+
2357
+ // Column definitions with aTargets
2358
+ if ( aoColDefs )
2359
+ {
2360
+ /* Loop over the definitions array - loop in reverse so first instance has priority */
2361
+ for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
2362
+ {
2363
+ def = aoColDefs[i];
2364
+
2365
+ /* Each definition can target multiple columns, as it is an array */
2366
+ var aTargets = def.targets !== undefined ?
2367
+ def.targets :
2368
+ def.aTargets;
2369
+
2370
+ if ( ! Array.isArray( aTargets ) )
2371
+ {
2372
+ aTargets = [ aTargets ];
2373
+ }
2374
+
2375
+ for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
2376
+ {
2377
+ if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
2378
+ {
2379
+ /* Add columns that we don't yet know about */
2380
+ while( columns.length <= aTargets[j] )
2381
+ {
2382
+ _fnAddColumn( oSettings );
2383
+ }
2384
+
2385
+ /* Integer, basic index */
2386
+ fn( aTargets[j], def );
2387
+ }
2388
+ else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
2389
+ {
2390
+ /* Negative integer, right to left column counting */
2391
+ fn( columns.length+aTargets[j], def );
2392
+ }
2393
+ else if ( typeof aTargets[j] === 'string' )
2394
+ {
2395
+ /* Class name matching on TH element */
2396
+ for ( k=0, kLen=columns.length ; k<kLen ; k++ )
2397
+ {
2398
+ if ( aTargets[j] == "_all" ||
2399
+ $(columns[k].nTh).hasClass( aTargets[j] ) )
2400
+ {
2401
+ fn( k, def );
2402
+ }
2403
+ }
2404
+ }
2405
+ }
2406
+ }
2407
+ }
2408
+
2409
+ // Statically defined columns array
2410
+ if ( aoCols )
2411
+ {
2412
+ for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
2413
+ {
2414
+ fn( i, aoCols[i] );
2415
+ }
2416
+ }
2417
+ }
2418
+
2419
+ /**
2420
+ * Add a data array to the table, creating DOM node etc. This is the parallel to
2421
+ * _fnGatherData, but for adding rows from a Javascript source, rather than a
2422
+ * DOM source.
2423
+ * @param {object} oSettings dataTables settings object
2424
+ * @param {array} aData data array to be added
2425
+ * @param {node} [nTr] TR element to add to the table - optional. If not given,
2426
+ * DataTables will create a row automatically
2427
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
2428
+ * if nTr is.
2429
+ * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
2430
+ * @memberof DataTable#oApi
2431
+ */
2432
+ function _fnAddData ( oSettings, aDataIn, nTr, anTds )
2433
+ {
2434
+ /* Create the object for storing information about this new row */
2435
+ var iRow = oSettings.aoData.length;
2436
+ var oData = $.extend( true, {}, DataTable.models.oRow, {
2437
+ src: nTr ? 'dom' : 'data',
2438
+ idx: iRow
2439
+ } );
2440
+
2441
+ oData._aData = aDataIn;
2442
+ oSettings.aoData.push( oData );
2443
+
2444
+ /* Create the cells */
2445
+ var nTd, sThisType;
2446
+ var columns = oSettings.aoColumns;
2447
+
2448
+ // Invalidate the column types as the new data needs to be revalidated
2449
+ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
2450
+ {
2451
+ columns[i].sType = null;
2452
+ }
2453
+
2454
+ /* Add to the display array */
2455
+ oSettings.aiDisplayMaster.push( iRow );
2456
+
2457
+ var id = oSettings.rowIdFn( aDataIn );
2458
+ if ( id !== undefined ) {
2459
+ oSettings.aIds[ id ] = oData;
2460
+ }
2461
+
2462
+ /* Create the DOM information, or register it if already present */
2463
+ if ( nTr || ! oSettings.oFeatures.bDeferRender )
2464
+ {
2465
+ _fnCreateTr( oSettings, iRow, nTr, anTds );
2466
+ }
2467
+
2468
+ return iRow;
2469
+ }
2470
+
2471
+
2472
+ /**
2473
+ * Add one or more TR elements to the table. Generally we'd expect to
2474
+ * use this for reading data from a DOM sourced table, but it could be
2475
+ * used for an TR element. Note that if a TR is given, it is used (i.e.
2476
+ * it is not cloned).
2477
+ * @param {object} settings dataTables settings object
2478
+ * @param {array|node|jQuery} trs The TR element(s) to add to the table
2479
+ * @returns {array} Array of indexes for the added rows
2480
+ * @memberof DataTable#oApi
2481
+ */
2482
+ function _fnAddTr( settings, trs )
2483
+ {
2484
+ var row;
2485
+
2486
+ // Allow an individual node to be passed in
2487
+ if ( ! (trs instanceof $) ) {
2488
+ trs = $(trs);
2489
+ }
2490
+
2491
+ return trs.map( function (i, el) {
2492
+ row = _fnGetRowElements( settings, el );
2493
+ return _fnAddData( settings, row.data, el, row.cells );
2494
+ } );
2495
+ }
2496
+
2497
+
2498
+ /**
2499
+ * Take a TR element and convert it to an index in aoData
2500
+ * @param {object} oSettings dataTables settings object
2501
+ * @param {node} n the TR element to find
2502
+ * @returns {int} index if the node is found, null if not
2503
+ * @memberof DataTable#oApi
2504
+ */
2505
+ function _fnNodeToDataIndex( oSettings, n )
2506
+ {
2507
+ return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
2508
+ }
2509
+
2510
+
2511
+ /**
2512
+ * Take a TD element and convert it into a column data index (not the visible index)
2513
+ * @param {object} oSettings dataTables settings object
2514
+ * @param {int} iRow The row number the TD/TH can be found in
2515
+ * @param {node} n The TD/TH element to find
2516
+ * @returns {int} index if the node is found, -1 if not
2517
+ * @memberof DataTable#oApi
2518
+ */
2519
+ function _fnNodeToColumnIndex( oSettings, iRow, n )
2520
+ {
2521
+ return $.inArray( n, oSettings.aoData[ iRow ].anCells );
2522
+ }
2523
+
2524
+
2525
+ /**
2526
+ * Get the data for a given cell from the internal cache, taking into account data mapping
2527
+ * @param {object} settings dataTables settings object
2528
+ * @param {int} rowIdx aoData row id
2529
+ * @param {int} colIdx Column index
2530
+ * @param {string} type data get type ('display', 'type' 'filter' 'sort')
2531
+ * @returns {*} Cell data
2532
+ * @memberof DataTable#oApi
2533
+ */
2534
+ function _fnGetCellData( settings, rowIdx, colIdx, type )
2535
+ {
2536
+ var draw = settings.iDraw;
2537
+ var col = settings.aoColumns[colIdx];
2538
+ var rowData = settings.aoData[rowIdx]._aData;
2539
+ var defaultContent = col.sDefaultContent;
2540
+ var cellData = col.fnGetData( rowData, type, {
2541
+ settings: settings,
2542
+ row: rowIdx,
2543
+ col: colIdx
2544
+ } );
2545
+
2546
+ if ( cellData === undefined ) {
2547
+ if ( settings.iDrawError != draw && defaultContent === null ) {
2548
+ _fnLog( settings, 0, "Requested unknown parameter "+
2549
+ (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
2550
+ " for row "+rowIdx+", column "+colIdx, 4 );
2551
+ settings.iDrawError = draw;
2552
+ }
2553
+ return defaultContent;
2554
+ }
2555
+
2556
+ // When the data source is null and a specific data type is requested (i.e.
2557
+ // not the original data), we can use default column data
2558
+ if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
2559
+ cellData = defaultContent;
2560
+ }
2561
+ else if ( typeof cellData === 'function' ) {
2562
+ // If the data source is a function, then we run it and use the return,
2563
+ // executing in the scope of the data object (for instances)
2564
+ return cellData.call( rowData );
2565
+ }
2566
+
2567
+ if ( cellData === null && type == 'display' ) {
2568
+ return '';
2569
+ }
2570
+ return cellData;
2571
+ }
2572
+
2573
+
2574
+ /**
2575
+ * Set the value for a specific cell, into the internal data cache
2576
+ * @param {object} settings dataTables settings object
2577
+ * @param {int} rowIdx aoData row id
2578
+ * @param {int} colIdx Column index
2579
+ * @param {*} val Value to set
2580
+ * @memberof DataTable#oApi
2581
+ */
2582
+ function _fnSetCellData( settings, rowIdx, colIdx, val )
2583
+ {
2584
+ var col = settings.aoColumns[colIdx];
2585
+ var rowData = settings.aoData[rowIdx]._aData;
2586
+
2587
+ col.fnSetData( rowData, val, {
2588
+ settings: settings,
2589
+ row: rowIdx,
2590
+ col: colIdx
2591
+ } );
2592
+ }
2593
+
2594
+
2595
+ // Private variable that is used to match action syntax in the data property object
2596
+ var __reArray = /\[.*?\]$/;
2597
+ var __reFn = /\(\)$/;
2598
+
2599
+ /**
2600
+ * Split string on periods, taking into account escaped periods
2601
+ * @param {string} str String to split
2602
+ * @return {array} Split string
2603
+ */
2604
+ function _fnSplitObjNotation( str )
2605
+ {
2606
+ return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
2607
+ return s.replace(/\\\./g, '.');
2608
+ } );
2609
+ }
2610
+
2611
+
2612
+ /**
2613
+ * Return a function that can be used to get data from a source object, taking
2614
+ * into account the ability to use nested objects as a source
2615
+ * @param {string|int|function} mSource The data source for the object
2616
+ * @returns {function} Data get function
2617
+ * @memberof DataTable#oApi
2618
+ */
2619
+ function _fnGetObjectDataFn( mSource )
2620
+ {
2621
+ if ( $.isPlainObject( mSource ) )
2622
+ {
2623
+ /* Build an object of get functions, and wrap them in a single call */
2624
+ var o = {};
2625
+ $.each( mSource, function (key, val) {
2626
+ if ( val ) {
2627
+ o[key] = _fnGetObjectDataFn( val );
2628
+ }
2629
+ } );
2630
+
2631
+ return function (data, type, row, meta) {
2632
+ var t = o[type] || o._;
2633
+ return t !== undefined ?
2634
+ t(data, type, row, meta) :
2635
+ data;
2636
+ };
2637
+ }
2638
+ else if ( mSource === null )
2639
+ {
2640
+ /* Give an empty string for rendering / sorting etc */
2641
+ return function (data) { // type, row and meta also passed, but not used
2642
+ return data;
2643
+ };
2644
+ }
2645
+ else if ( typeof mSource === 'function' )
2646
+ {
2647
+ return function (data, type, row, meta) {
2648
+ return mSource( data, type, row, meta );
2649
+ };
2650
+ }
2651
+ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2652
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2653
+ {
2654
+ /* If there is a . in the source string then the data source is in a
2655
+ * nested object so we loop over the data for each level to get the next
2656
+ * level down. On each loop we test for undefined, and if found immediately
2657
+ * return. This allows entire objects to be missing and sDefaultContent to
2658
+ * be used if defined, rather than throwing an error
2659
+ */
2660
+ var fetchData = function (data, type, src) {
2661
+ var arrayNotation, funcNotation, out, innerSrc;
2662
+
2663
+ if ( src !== "" )
2664
+ {
2665
+ var a = _fnSplitObjNotation( src );
2666
+
2667
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2668
+ {
2669
+ // Check if we are dealing with special notation
2670
+ arrayNotation = a[i].match(__reArray);
2671
+ funcNotation = a[i].match(__reFn);
2672
+
2673
+ if ( arrayNotation )
2674
+ {
2675
+ // Array notation
2676
+ a[i] = a[i].replace(__reArray, '');
2677
+
2678
+ // Condition allows simply [] to be passed in
2679
+ if ( a[i] !== "" ) {
2680
+ data = data[ a[i] ];
2681
+ }
2682
+ out = [];
2683
+
2684
+ // Get the remainder of the nested object to get
2685
+ a.splice( 0, i+1 );
2686
+ innerSrc = a.join('.');
2687
+
2688
+ // Traverse each entry in the array getting the properties requested
2689
+ if ( Array.isArray( data ) ) {
2690
+ for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
2691
+ out.push( fetchData( data[j], type, innerSrc ) );
2692
+ }
2693
+ }
2694
+
2695
+ // If a string is given in between the array notation indicators, that
2696
+ // is used to join the strings together, otherwise an array is returned
2697
+ var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
2698
+ data = (join==="") ? out : out.join(join);
2699
+
2700
+ // The inner call to fetchData has already traversed through the remainder
2701
+ // of the source requested, so we exit from the loop
2702
+ break;
2703
+ }
2704
+ else if ( funcNotation )
2705
+ {
2706
+ // Function call
2707
+ a[i] = a[i].replace(__reFn, '');
2708
+ data = data[ a[i] ]();
2709
+ continue;
2710
+ }
2711
+
2712
+ if ( data === null || data[ a[i] ] === undefined )
2713
+ {
2714
+ return undefined;
2715
+ }
2716
+ data = data[ a[i] ];
2717
+ }
2718
+ }
2719
+
2720
+ return data;
2721
+ };
2722
+
2723
+ return function (data, type) { // row and meta also passed, but not used
2724
+ return fetchData( data, type, mSource );
2725
+ };
2726
+ }
2727
+ else
2728
+ {
2729
+ /* Array or flat object mapping */
2730
+ return function (data, type) { // row and meta also passed, but not used
2731
+ return data[mSource];
2732
+ };
2733
+ }
2734
+ }
2735
+
2736
+
2737
+ /**
2738
+ * Return a function that can be used to set data from a source object, taking
2739
+ * into account the ability to use nested objects as a source
2740
+ * @param {string|int|function} mSource The data source for the object
2741
+ * @returns {function} Data set function
2742
+ * @memberof DataTable#oApi
2743
+ */
2744
+ function _fnSetObjectDataFn( mSource )
2745
+ {
2746
+ if ( $.isPlainObject( mSource ) )
2747
+ {
2748
+ /* Unlike get, only the underscore (global) option is used for for
2749
+ * setting data since we don't know the type here. This is why an object
2750
+ * option is not documented for `mData` (which is read/write), but it is
2751
+ * for `mRender` which is read only.
2752
+ */
2753
+ return _fnSetObjectDataFn( mSource._ );
2754
+ }
2755
+ else if ( mSource === null )
2756
+ {
2757
+ /* Nothing to do when the data source is null */
2758
+ return function () {};
2759
+ }
2760
+ else if ( typeof mSource === 'function' )
2761
+ {
2762
+ return function (data, val, meta) {
2763
+ mSource( data, 'set', val, meta );
2764
+ };
2765
+ }
2766
+ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2767
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2768
+ {
2769
+ /* Like the get, we need to get data from a nested object */
2770
+ var setData = function (data, val, src) {
2771
+ var a = _fnSplitObjNotation( src ), b;
2772
+ var aLast = a[a.length-1];
2773
+ var arrayNotation, funcNotation, o, innerSrc;
2774
+
2775
+ for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
2776
+ {
2777
+ // Protect against prototype pollution
2778
+ if (a[i] === '__proto__' || a[i] === 'constructor') {
2779
+ throw new Error('Cannot set prototype values');
2780
+ }
2781
+
2782
+ // Check if we are dealing with an array notation request
2783
+ arrayNotation = a[i].match(__reArray);
2784
+ funcNotation = a[i].match(__reFn);
2785
+
2786
+ if ( arrayNotation )
2787
+ {
2788
+ a[i] = a[i].replace(__reArray, '');
2789
+ data[ a[i] ] = [];
2790
+
2791
+ // Get the remainder of the nested object to set so we can recurse
2792
+ b = a.slice();
2793
+ b.splice( 0, i+1 );
2794
+ innerSrc = b.join('.');
2795
+
2796
+ // Traverse each entry in the array setting the properties requested
2797
+ if ( Array.isArray( val ) )
2798
+ {
2799
+ for ( var j=0, jLen=val.length ; j<jLen ; j++ )
2800
+ {
2801
+ o = {};
2802
+ setData( o, val[j], innerSrc );
2803
+ data[ a[i] ].push( o );
2804
+ }
2805
+ }
2806
+ else
2807
+ {
2808
+ // We've been asked to save data to an array, but it
2809
+ // isn't array data to be saved. Best that can be done
2810
+ // is to just save the value.
2811
+ data[ a[i] ] = val;
2812
+ }
2813
+
2814
+ // The inner call to setData has already traversed through the remainder
2815
+ // of the source and has set the data, thus we can exit here
2816
+ return;
2817
+ }
2818
+ else if ( funcNotation )
2819
+ {
2820
+ // Function call
2821
+ a[i] = a[i].replace(__reFn, '');
2822
+ data = data[ a[i] ]( val );
2823
+ }
2824
+
2825
+ // If the nested object doesn't currently exist - since we are
2826
+ // trying to set the value - create it
2827
+ if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
2828
+ {
2829
+ data[ a[i] ] = {};
2830
+ }
2831
+ data = data[ a[i] ];
2832
+ }
2833
+
2834
+ // Last item in the input - i.e, the actual set
2835
+ if ( aLast.match(__reFn ) )
2836
+ {
2837
+ // Function call
2838
+ data = data[ aLast.replace(__reFn, '') ]( val );
2839
+ }
2840
+ else
2841
+ {
2842
+ // If array notation is used, we just want to strip it and use the property name
2843
+ // and assign the value. If it isn't used, then we get the result we want anyway
2844
+ data[ aLast.replace(__reArray, '') ] = val;
2845
+ }
2846
+ };
2847
+
2848
+ return function (data, val) { // meta is also passed in, but not used
2849
+ return setData( data, val, mSource );
2850
+ };
2851
+ }
2852
+ else
2853
+ {
2854
+ /* Array or flat object mapping */
2855
+ return function (data, val) { // meta is also passed in, but not used
2856
+ data[mSource] = val;
2857
+ };
2858
+ }
2859
+ }
2860
+
2861
+
2862
+ /**
2863
+ * Return an array with the full table data
2864
+ * @param {object} oSettings dataTables settings object
2865
+ * @returns array {array} aData Master data array
2866
+ * @memberof DataTable#oApi
2867
+ */
2868
+ function _fnGetDataMaster ( settings )
2869
+ {
2870
+ return _pluck( settings.aoData, '_aData' );
2871
+ }
2872
+
2873
+
2874
+ /**
2875
+ * Nuke the table
2876
+ * @param {object} oSettings dataTables settings object
2877
+ * @memberof DataTable#oApi
2878
+ */
2879
+ function _fnClearTable( settings )
2880
+ {
2881
+ settings.aoData.length = 0;
2882
+ settings.aiDisplayMaster.length = 0;
2883
+ settings.aiDisplay.length = 0;
2884
+ settings.aIds = {};
2885
+ }
2886
+
2887
+
2888
+ /**
2889
+ * Take an array of integers (index array) and remove a target integer (value - not
2890
+ * the key!)
2891
+ * @param {array} a Index array to target
2892
+ * @param {int} iTarget value to find
2893
+ * @memberof DataTable#oApi
2894
+ */
2895
+ function _fnDeleteIndex( a, iTarget, splice )
2896
+ {
2897
+ var iTargetIndex = -1;
2898
+
2899
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2900
+ {
2901
+ if ( a[i] == iTarget )
2902
+ {
2903
+ iTargetIndex = i;
2904
+ }
2905
+ else if ( a[i] > iTarget )
2906
+ {
2907
+ a[i]--;
2908
+ }
2909
+ }
2910
+
2911
+ if ( iTargetIndex != -1 && splice === undefined )
2912
+ {
2913
+ a.splice( iTargetIndex, 1 );
2914
+ }
2915
+ }
2916
+
2917
+
2918
+ /**
2919
+ * Mark cached data as invalid such that a re-read of the data will occur when
2920
+ * the cached data is next requested. Also update from the data source object.
2921
+ *
2922
+ * @param {object} settings DataTables settings object
2923
+ * @param {int} rowIdx Row index to invalidate
2924
+ * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
2925
+ * or 'data'
2926
+ * @param {int} [colIdx] Column index to invalidate. If undefined the whole
2927
+ * row will be invalidated
2928
+ * @memberof DataTable#oApi
2929
+ *
2930
+ * @todo For the modularisation of v1.11 this will need to become a callback, so
2931
+ * the sort and filter methods can subscribe to it. That will required
2932
+ * initialisation options for sorting, which is why it is not already baked in
2933
+ */
2934
+ function _fnInvalidate( settings, rowIdx, src, colIdx )
2935
+ {
2936
+ var row = settings.aoData[ rowIdx ];
2937
+ var i, ien;
2938
+ var cellWrite = function ( cell, col ) {
2939
+ // This is very frustrating, but in IE if you just write directly
2940
+ // to innerHTML, and elements that are overwritten are GC'ed,
2941
+ // even if there is a reference to them elsewhere
2942
+ while ( cell.childNodes.length ) {
2943
+ cell.removeChild( cell.firstChild );
2944
+ }
2945
+
2946
+ cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
2947
+ };
2948
+
2949
+ // Are we reading last data from DOM or the data object?
2950
+ if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
2951
+ // Read the data from the DOM
2952
+ row._aData = _fnGetRowElements(
2953
+ settings, row, colIdx, colIdx === undefined ? undefined : row._aData
2954
+ )
2955
+ .data;
2956
+ }
2957
+ else {
2958
+ // Reading from data object, update the DOM
2959
+ var cells = row.anCells;
2960
+
2961
+ if ( cells ) {
2962
+ if ( colIdx !== undefined ) {
2963
+ cellWrite( cells[colIdx], colIdx );
2964
+ }
2965
+ else {
2966
+ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
2967
+ cellWrite( cells[i], i );
2968
+ }
2969
+ }
2970
+ }
2971
+ }
2972
+
2973
+ // For both row and cell invalidation, the cached data for sorting and
2974
+ // filtering is nulled out
2975
+ row._aSortData = null;
2976
+ row._aFilterData = null;
2977
+
2978
+ // Invalidate the type for a specific column (if given) or all columns since
2979
+ // the data might have changed
2980
+ var cols = settings.aoColumns;
2981
+ if ( colIdx !== undefined ) {
2982
+ cols[ colIdx ].sType = null;
2983
+ }
2984
+ else {
2985
+ for ( i=0, ien=cols.length ; i<ien ; i++ ) {
2986
+ cols[i].sType = null;
2987
+ }
2988
+
2989
+ // Update DataTables special `DT_*` attributes for the row
2990
+ _fnRowAttributes( settings, row );
2991
+ }
2992
+ }
2993
+
2994
+
2995
+ /**
2996
+ * Build a data source object from an HTML row, reading the contents of the
2997
+ * cells that are in the row.
2998
+ *
2999
+ * @param {object} settings DataTables settings object
3000
+ * @param {node|object} TR element from which to read data or existing row
3001
+ * object from which to re-read the data from the cells
3002
+ * @param {int} [colIdx] Optional column index
3003
+ * @param {array|object} [d] Data source object. If `colIdx` is given then this
3004
+ * parameter should also be given and will be used to write the data into.
3005
+ * Only the column in question will be written
3006
+ * @returns {object} Object with two parameters: `data` the data read, in
3007
+ * document order, and `cells` and array of nodes (they can be useful to the
3008
+ * caller, so rather than needing a second traversal to get them, just return
3009
+ * them from here).
3010
+ * @memberof DataTable#oApi
3011
+ */
3012
+ function _fnGetRowElements( settings, row, colIdx, d )
3013
+ {
3014
+ var
3015
+ tds = [],
3016
+ td = row.firstChild,
3017
+ name, col, o, i=0, contents,
3018
+ columns = settings.aoColumns,
3019
+ objectRead = settings._rowReadObject;
3020
+
3021
+ // Allow the data object to be passed in, or construct
3022
+ d = d !== undefined ?
3023
+ d :
3024
+ objectRead ?
3025
+ {} :
3026
+ [];
3027
+
3028
+ var attr = function ( str, td ) {
3029
+ if ( typeof str === 'string' ) {
3030
+ var idx = str.indexOf('@');
3031
+
3032
+ if ( idx !== -1 ) {
3033
+ var attr = str.substring( idx+1 );
3034
+ var setter = _fnSetObjectDataFn( str );
3035
+ setter( d, td.getAttribute( attr ) );
3036
+ }
3037
+ }
3038
+ };
3039
+
3040
+ // Read data from a cell and store into the data object
3041
+ var cellProcess = function ( cell ) {
3042
+ if ( colIdx === undefined || colIdx === i ) {
3043
+ col = columns[i];
3044
+ contents = (cell.innerHTML).trim();
3045
+
3046
+ if ( col && col._bAttrSrc ) {
3047
+ var setter = _fnSetObjectDataFn( col.mData._ );
3048
+ setter( d, contents );
3049
+
3050
+ attr( col.mData.sort, cell );
3051
+ attr( col.mData.type, cell );
3052
+ attr( col.mData.filter, cell );
3053
+ }
3054
+ else {
3055
+ // Depending on the `data` option for the columns the data can
3056
+ // be read to either an object or an array.
3057
+ if ( objectRead ) {
3058
+ if ( ! col._setter ) {
3059
+ // Cache the setter function
3060
+ col._setter = _fnSetObjectDataFn( col.mData );
3061
+ }
3062
+ col._setter( d, contents );
3063
+ }
3064
+ else {
3065
+ d[i] = contents;
3066
+ }
3067
+ }
3068
+ }
3069
+
3070
+ i++;
3071
+ };
3072
+
3073
+ if ( td ) {
3074
+ // `tr` element was passed in
3075
+ while ( td ) {
3076
+ name = td.nodeName.toUpperCase();
3077
+
3078
+ if ( name == "TD" || name == "TH" ) {
3079
+ cellProcess( td );
3080
+ tds.push( td );
3081
+ }
3082
+
3083
+ td = td.nextSibling;
3084
+ }
3085
+ }
3086
+ else {
3087
+ // Existing row object passed in
3088
+ tds = row.anCells;
3089
+
3090
+ for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
3091
+ cellProcess( tds[j] );
3092
+ }
3093
+ }
3094
+
3095
+ // Read the ID from the DOM if present
3096
+ var rowNode = row.firstChild ? row : row.nTr;
3097
+
3098
+ if ( rowNode ) {
3099
+ var id = rowNode.getAttribute( 'id' );
3100
+
3101
+ if ( id ) {
3102
+ _fnSetObjectDataFn( settings.rowId )( d, id );
3103
+ }
3104
+ }
3105
+
3106
+ return {
3107
+ data: d,
3108
+ cells: tds
3109
+ };
3110
+ }
3111
+ /**
3112
+ * Create a new TR element (and it's TD children) for a row
3113
+ * @param {object} oSettings dataTables settings object
3114
+ * @param {int} iRow Row to consider
3115
+ * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
3116
+ * DataTables will create a row automatically
3117
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
3118
+ * if nTr is.
3119
+ * @memberof DataTable#oApi
3120
+ */
3121
+ function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
3122
+ {
3123
+ var
3124
+ row = oSettings.aoData[iRow],
3125
+ rowData = row._aData,
3126
+ cells = [],
3127
+ nTr, nTd, oCol,
3128
+ i, iLen, create;
3129
+
3130
+ if ( row.nTr === null )
3131
+ {
3132
+ nTr = nTrIn || document.createElement('tr');
3133
+
3134
+ row.nTr = nTr;
3135
+ row.anCells = cells;
3136
+
3137
+ /* Use a private property on the node to allow reserve mapping from the node
3138
+ * to the aoData array for fast look up
3139
+ */
3140
+ nTr._DT_RowIndex = iRow;
3141
+
3142
+ /* Special parameters can be given by the data source to be used on the row */
3143
+ _fnRowAttributes( oSettings, row );
3144
+
3145
+ /* Process each column */
3146
+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3147
+ {
3148
+ oCol = oSettings.aoColumns[i];
3149
+ create = nTrIn ? false : true;
3150
+
3151
+ nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
3152
+ nTd._DT_CellIndex = {
3153
+ row: iRow,
3154
+ column: i
3155
+ };
3156
+
3157
+ cells.push( nTd );
3158
+
3159
+ // Need to create the HTML if new, or if a rendering function is defined
3160
+ if ( create || ((oCol.mRender || oCol.mData !== i) &&
3161
+ (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3162
+ )) {
3163
+ nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
3164
+ }
3165
+
3166
+ /* Add user defined class */
3167
+ if ( oCol.sClass )
3168
+ {
3169
+ nTd.className += ' '+oCol.sClass;
3170
+ }
3171
+
3172
+ // Visibility - add or remove as required
3173
+ if ( oCol.bVisible && ! nTrIn )
3174
+ {
3175
+ nTr.appendChild( nTd );
3176
+ }
3177
+ else if ( ! oCol.bVisible && nTrIn )
3178
+ {
3179
+ nTd.parentNode.removeChild( nTd );
3180
+ }
3181
+
3182
+ if ( oCol.fnCreatedCell )
3183
+ {
3184
+ oCol.fnCreatedCell.call( oSettings.oInstance,
3185
+ nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
3186
+ );
3187
+ }
3188
+ }
3189
+
3190
+ _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] );
3191
+ }
3192
+ }
3193
+
3194
+
3195
+ /**
3196
+ * Add attributes to a row based on the special `DT_*` parameters in a data
3197
+ * source object.
3198
+ * @param {object} settings DataTables settings object
3199
+ * @param {object} DataTables row object for the row to be modified
3200
+ * @memberof DataTable#oApi
3201
+ */
3202
+ function _fnRowAttributes( settings, row )
3203
+ {
3204
+ var tr = row.nTr;
3205
+ var data = row._aData;
3206
+
3207
+ if ( tr ) {
3208
+ var id = settings.rowIdFn( data );
3209
+
3210
+ if ( id ) {
3211
+ tr.id = id;
3212
+ }
3213
+
3214
+ if ( data.DT_RowClass ) {
3215
+ // Remove any classes added by DT_RowClass before
3216
+ var a = data.DT_RowClass.split(' ');
3217
+ row.__rowc = row.__rowc ?
3218
+ _unique( row.__rowc.concat( a ) ) :
3219
+ a;
3220
+
3221
+ $(tr)
3222
+ .removeClass( row.__rowc.join(' ') )
3223
+ .addClass( data.DT_RowClass );
3224
+ }
3225
+
3226
+ if ( data.DT_RowAttr ) {
3227
+ $(tr).attr( data.DT_RowAttr );
3228
+ }
3229
+
3230
+ if ( data.DT_RowData ) {
3231
+ $(tr).data( data.DT_RowData );
3232
+ }
3233
+ }
3234
+ }
3235
+
3236
+
3237
+ /**
3238
+ * Create the HTML header for the table
3239
+ * @param {object} oSettings dataTables settings object
3240
+ * @memberof DataTable#oApi
3241
+ */
3242
+ function _fnBuildHead( oSettings )
3243
+ {
3244
+ var i, ien, cell, row, column;
3245
+ var thead = oSettings.nTHead;
3246
+ var tfoot = oSettings.nTFoot;
3247
+ var createHeader = $('th, td', thead).length === 0;
3248
+ var classes = oSettings.oClasses;
3249
+ var columns = oSettings.aoColumns;
3250
+
3251
+ if ( createHeader ) {
3252
+ row = $('<tr/>').appendTo( thead );
3253
+ }
3254
+
3255
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
3256
+ column = columns[i];
3257
+ cell = $( column.nTh ).addClass( column.sClass );
3258
+
3259
+ if ( createHeader ) {
3260
+ cell.appendTo( row );
3261
+ }
3262
+
3263
+ // 1.11 move into sorting
3264
+ if ( oSettings.oFeatures.bSort ) {
3265
+ cell.addClass( column.sSortingClass );
3266
+
3267
+ if ( column.bSortable !== false ) {
3268
+ cell
3269
+ .attr( 'tabindex', oSettings.iTabIndex )
3270
+ .attr( 'aria-controls', oSettings.sTableId );
3271
+
3272
+ _fnSortAttachListener( oSettings, column.nTh, i );
3273
+ }
3274
+ }
3275
+
3276
+ if ( column.sTitle != cell[0].innerHTML ) {
3277
+ cell.html( column.sTitle );
3278
+ }
3279
+
3280
+ _fnRenderer( oSettings, 'header' )(
3281
+ oSettings, cell, column, classes
3282
+ );
3283
+ }
3284
+
3285
+ if ( createHeader ) {
3286
+ _fnDetectHeader( oSettings.aoHeader, thead );
3287
+ }
3288
+
3289
+ /* ARIA role for the rows */
3290
+ $(thead).children('tr').attr('role', 'row');
3291
+
3292
+ /* Deal with the footer - add classes if required */
3293
+ $(thead).children('tr').children('th, td').addClass( classes.sHeaderTH );
3294
+ $(tfoot).children('tr').children('th, td').addClass( classes.sFooterTH );
3295
+
3296
+ // Cache the footer cells. Note that we only take the cells from the first
3297
+ // row in the footer. If there is more than one row the user wants to
3298
+ // interact with, they need to use the table().foot() method. Note also this
3299
+ // allows cells to be used for multiple columns using colspan
3300
+ if ( tfoot !== null ) {
3301
+ var cells = oSettings.aoFooter[0];
3302
+
3303
+ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
3304
+ column = columns[i];
3305
+ column.nTf = cells[i].cell;
3306
+
3307
+ if ( column.sClass ) {
3308
+ $(column.nTf).addClass( column.sClass );
3309
+ }
3310
+ }
3311
+ }
3312
+ }
3313
+
3314
+
3315
+ /**
3316
+ * Draw the header (or footer) element based on the column visibility states. The
3317
+ * methodology here is to use the layout array from _fnDetectHeader, modified for
3318
+ * the instantaneous column visibility, to construct the new layout. The grid is
3319
+ * traversed over cell at a time in a rows x columns grid fashion, although each
3320
+ * cell insert can cover multiple elements in the grid - which is tracks using the
3321
+ * aApplied array. Cell inserts in the grid will only occur where there isn't
3322
+ * already a cell in that position.
3323
+ * @param {object} oSettings dataTables settings object
3324
+ * @param array {objects} aoSource Layout array from _fnDetectHeader
3325
+ * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
3326
+ * @memberof DataTable#oApi
3327
+ */
3328
+ function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
3329
+ {
3330
+ var i, iLen, j, jLen, k, kLen, n, nLocalTr;
3331
+ var aoLocal = [];
3332
+ var aApplied = [];
3333
+ var iColumns = oSettings.aoColumns.length;
3334
+ var iRowspan, iColspan;
3335
+
3336
+ if ( ! aoSource )
3337
+ {
3338
+ return;
3339
+ }
3340
+
3341
+ if ( bIncludeHidden === undefined )
3342
+ {
3343
+ bIncludeHidden = false;
3344
+ }
3345
+
3346
+ /* Make a copy of the master layout array, but without the visible columns in it */
3347
+ for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
3348
+ {
3349
+ aoLocal[i] = aoSource[i].slice();
3350
+ aoLocal[i].nTr = aoSource[i].nTr;
3351
+
3352
+ /* Remove any columns which are currently hidden */
3353
+ for ( j=iColumns-1 ; j>=0 ; j-- )
3354
+ {
3355
+ if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
3356
+ {
3357
+ aoLocal[i].splice( j, 1 );
3358
+ }
3359
+ }
3360
+
3361
+ /* Prep the applied array - it needs an element for each row */
3362
+ aApplied.push( [] );
3363
+ }
3364
+
3365
+ for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
3366
+ {
3367
+ nLocalTr = aoLocal[i].nTr;
3368
+
3369
+ /* All cells are going to be replaced, so empty out the row */
3370
+ if ( nLocalTr )
3371
+ {
3372
+ while( (n = nLocalTr.firstChild) )
3373
+ {
3374
+ nLocalTr.removeChild( n );
3375
+ }
3376
+ }
3377
+
3378
+ for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
3379
+ {
3380
+ iRowspan = 1;
3381
+ iColspan = 1;
3382
+
3383
+ /* Check to see if there is already a cell (row/colspan) covering our target
3384
+ * insert point. If there is, then there is nothing to do.
3385
+ */
3386
+ if ( aApplied[i][j] === undefined )
3387
+ {
3388
+ nLocalTr.appendChild( aoLocal[i][j].cell );
3389
+ aApplied[i][j] = 1;
3390
+
3391
+ /* Expand the cell to cover as many rows as needed */
3392
+ while ( aoLocal[i+iRowspan] !== undefined &&
3393
+ aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
3394
+ {
3395
+ aApplied[i+iRowspan][j] = 1;
3396
+ iRowspan++;
3397
+ }
3398
+
3399
+ /* Expand the cell to cover as many columns as needed */
3400
+ while ( aoLocal[i][j+iColspan] !== undefined &&
3401
+ aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
3402
+ {
3403
+ /* Must update the applied array over the rows for the columns */
3404
+ for ( k=0 ; k<iRowspan ; k++ )
3405
+ {
3406
+ aApplied[i+k][j+iColspan] = 1;
3407
+ }
3408
+ iColspan++;
3409
+ }
3410
+
3411
+ /* Do the actual expansion in the DOM */
3412
+ $(aoLocal[i][j].cell)
3413
+ .attr('rowspan', iRowspan)
3414
+ .attr('colspan', iColspan);
3415
+ }
3416
+ }
3417
+ }
3418
+ }
3419
+
3420
+
3421
+ /**
3422
+ * Insert the required TR nodes into the table for display
3423
+ * @param {object} oSettings dataTables settings object
3424
+ * @memberof DataTable#oApi
3425
+ */
3426
+ function _fnDraw( oSettings )
3427
+ {
3428
+ /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
3429
+ var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
3430
+ if ( $.inArray( false, aPreDraw ) !== -1 )
3431
+ {
3432
+ _fnProcessingDisplay( oSettings, false );
3433
+ return;
3434
+ }
3435
+
3436
+ var i, iLen, n;
3437
+ var anRows = [];
3438
+ var iRowCount = 0;
3439
+ var asStripeClasses = oSettings.asStripeClasses;
3440
+ var iStripes = asStripeClasses.length;
3441
+ var iOpenRows = oSettings.aoOpenRows.length;
3442
+ var oLang = oSettings.oLanguage;
3443
+ var iInitDisplayStart = oSettings.iInitDisplayStart;
3444
+ var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3445
+ var aiDisplay = oSettings.aiDisplay;
3446
+
3447
+ oSettings.bDrawing = true;
3448
+
3449
+ /* Check and see if we have an initial draw position from state saving */
3450
+ if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
3451
+ {
3452
+ oSettings._iDisplayStart = bServerSide ?
3453
+ iInitDisplayStart :
3454
+ iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
3455
+ 0 :
3456
+ iInitDisplayStart;
3457
+
3458
+ oSettings.iInitDisplayStart = -1;
3459
+ }
3460
+
3461
+ var iDisplayStart = oSettings._iDisplayStart;
3462
+ var iDisplayEnd = oSettings.fnDisplayEnd();
3463
+
3464
+ /* Server-side processing draw intercept */
3465
+ if ( oSettings.bDeferLoading )
3466
+ {
3467
+ oSettings.bDeferLoading = false;
3468
+ oSettings.iDraw++;
3469
+ _fnProcessingDisplay( oSettings, false );
3470
+ }
3471
+ else if ( !bServerSide )
3472
+ {
3473
+ oSettings.iDraw++;
3474
+ }
3475
+ else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
3476
+ {
3477
+ return;
3478
+ }
3479
+
3480
+ if ( aiDisplay.length !== 0 )
3481
+ {
3482
+ var iStart = bServerSide ? 0 : iDisplayStart;
3483
+ var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
3484
+
3485
+ for ( var j=iStart ; j<iEnd ; j++ )
3486
+ {
3487
+ var iDataIndex = aiDisplay[j];
3488
+ var aoData = oSettings.aoData[ iDataIndex ];
3489
+ if ( aoData.nTr === null )
3490
+ {
3491
+ _fnCreateTr( oSettings, iDataIndex );
3492
+ }
3493
+
3494
+ var nRow = aoData.nTr;
3495
+
3496
+ /* Remove the old striping classes and then add the new one */
3497
+ if ( iStripes !== 0 )
3498
+ {
3499
+ var sStripe = asStripeClasses[ iRowCount % iStripes ];
3500
+ if ( aoData._sRowStripe != sStripe )
3501
+ {
3502
+ $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
3503
+ aoData._sRowStripe = sStripe;
3504
+ }
3505
+ }
3506
+
3507
+ // Row callback functions - might want to manipulate the row
3508
+ // iRowCount and j are not currently documented. Are they at all
3509
+ // useful?
3510
+ _fnCallbackFire( oSettings, 'aoRowCallback', null,
3511
+ [nRow, aoData._aData, iRowCount, j, iDataIndex] );
3512
+
3513
+ anRows.push( nRow );
3514
+ iRowCount++;
3515
+ }
3516
+ }
3517
+ else
3518
+ {
3519
+ /* Table is empty - create a row with an empty message in it */
3520
+ var sZero = oLang.sZeroRecords;
3521
+ if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
3522
+ {
3523
+ sZero = oLang.sLoadingRecords;
3524
+ }
3525
+ else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
3526
+ {
3527
+ sZero = oLang.sEmptyTable;
3528
+ }
3529
+
3530
+ anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
3531
+ .append( $('<td />', {
3532
+ 'valign': 'top',
3533
+ 'colSpan': _fnVisbleColumns( oSettings ),
3534
+ 'class': oSettings.oClasses.sRowEmpty
3535
+ } ).html( sZero ) )[0];
3536
+ }
3537
+
3538
+ /* Header and footer callbacks */
3539
+ _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
3540
+ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3541
+
3542
+ _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
3543
+ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3544
+
3545
+ var body = $(oSettings.nTBody);
3546
+
3547
+ body.children().detach();
3548
+ body.append( $(anRows) );
3549
+
3550
+ /* Call all required callback functions for the end of a draw */
3551
+ _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
3552
+
3553
+ /* Draw is complete, sorting and filtering must be as well */
3554
+ oSettings.bSorted = false;
3555
+ oSettings.bFiltered = false;
3556
+ oSettings.bDrawing = false;
3557
+ }
3558
+
3559
+
3560
+ /**
3561
+ * Redraw the table - taking account of the various features which are enabled
3562
+ * @param {object} oSettings dataTables settings object
3563
+ * @param {boolean} [holdPosition] Keep the current paging position. By default
3564
+ * the paging is reset to the first page
3565
+ * @memberof DataTable#oApi
3566
+ */
3567
+ function _fnReDraw( settings, holdPosition )
3568
+ {
3569
+ var
3570
+ features = settings.oFeatures,
3571
+ sort = features.bSort,
3572
+ filter = features.bFilter;
3573
+
3574
+ if ( sort ) {
3575
+ _fnSort( settings );
3576
+ }
3577
+
3578
+ if ( filter ) {
3579
+ _fnFilterComplete( settings, settings.oPreviousSearch );
3580
+ }
3581
+ else {
3582
+ // No filtering, so we want to just use the display master
3583
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
3584
+ }
3585
+
3586
+ if ( holdPosition !== true ) {
3587
+ settings._iDisplayStart = 0;
3588
+ }
3589
+
3590
+ // Let any modules know about the draw hold position state (used by
3591
+ // scrolling internally)
3592
+ settings._drawHold = holdPosition;
3593
+
3594
+ _fnDraw( settings );
3595
+
3596
+ settings._drawHold = false;
3597
+ }
3598
+
3599
+
3600
+ /**
3601
+ * Add the options to the page HTML for the table
3602
+ * @param {object} oSettings dataTables settings object
3603
+ * @memberof DataTable#oApi
3604
+ */
3605
+ function _fnAddOptionsHtml ( oSettings )
3606
+ {
3607
+ var classes = oSettings.oClasses;
3608
+ var table = $(oSettings.nTable);
3609
+ var holding = $('<div/>').insertBefore( table ); // Holding element for speed
3610
+ var features = oSettings.oFeatures;
3611
+
3612
+ // All DataTables are wrapped in a div
3613
+ var insert = $('<div/>', {
3614
+ id: oSettings.sTableId+'_wrapper',
3615
+ 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
3616
+ } );
3617
+
3618
+ oSettings.nHolding = holding[0];
3619
+ oSettings.nTableWrapper = insert[0];
3620
+ oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
3621
+
3622
+ /* Loop over the user set positioning and place the elements as needed */
3623
+ var aDom = oSettings.sDom.split('');
3624
+ var featureNode, cOption, nNewNode, cNext, sAttr, j;
3625
+ for ( var i=0 ; i<aDom.length ; i++ )
3626
+ {
3627
+ featureNode = null;
3628
+ cOption = aDom[i];
3629
+
3630
+ if ( cOption == '<' )
3631
+ {
3632
+ /* New container div */
3633
+ nNewNode = $('<div/>')[0];
3634
+
3635
+ /* Check to see if we should append an id and/or a class name to the container */
3636
+ cNext = aDom[i+1];
3637
+ if ( cNext == "'" || cNext == '"' )
3638
+ {
3639
+ sAttr = "";
3640
+ j = 2;
3641
+ while ( aDom[i+j] != cNext )
3642
+ {
3643
+ sAttr += aDom[i+j];
3644
+ j++;
3645
+ }
3646
+
3647
+ /* Replace jQuery UI constants @todo depreciated */
3648
+ if ( sAttr == "H" )
3649
+ {
3650
+ sAttr = classes.sJUIHeader;
3651
+ }
3652
+ else if ( sAttr == "F" )
3653
+ {
3654
+ sAttr = classes.sJUIFooter;
3655
+ }
3656
+
3657
+ /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3658
+ * breaks the string into parts and applies them as needed
3659
+ */
3660
+ if ( sAttr.indexOf('.') != -1 )
3661
+ {
3662
+ var aSplit = sAttr.split('.');
3663
+ nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
3664
+ nNewNode.className = aSplit[1];
3665
+ }
3666
+ else if ( sAttr.charAt(0) == "#" )
3667
+ {
3668
+ nNewNode.id = sAttr.substr(1, sAttr.length-1);
3669
+ }
3670
+ else
3671
+ {
3672
+ nNewNode.className = sAttr;
3673
+ }
3674
+
3675
+ i += j; /* Move along the position array */
3676
+ }
3677
+
3678
+ insert.append( nNewNode );
3679
+ insert = $(nNewNode);
3680
+ }
3681
+ else if ( cOption == '>' )
3682
+ {
3683
+ /* End container div */
3684
+ insert = insert.parent();
3685
+ }
3686
+ // @todo Move options into their own plugins?
3687
+ else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
3688
+ {
3689
+ /* Length */
3690
+ featureNode = _fnFeatureHtmlLength( oSettings );
3691
+ }
3692
+ else if ( cOption == 'f' && features.bFilter )
3693
+ {
3694
+ /* Filter */
3695
+ featureNode = _fnFeatureHtmlFilter( oSettings );
3696
+ }
3697
+ else if ( cOption == 'r' && features.bProcessing )
3698
+ {
3699
+ /* pRocessing */
3700
+ featureNode = _fnFeatureHtmlProcessing( oSettings );
3701
+ }
3702
+ else if ( cOption == 't' )
3703
+ {
3704
+ /* Table */
3705
+ featureNode = _fnFeatureHtmlTable( oSettings );
3706
+ }
3707
+ else if ( cOption == 'i' && features.bInfo )
3708
+ {
3709
+ /* Info */
3710
+ featureNode = _fnFeatureHtmlInfo( oSettings );
3711
+ }
3712
+ else if ( cOption == 'p' && features.bPaginate )
3713
+ {
3714
+ /* Pagination */
3715
+ featureNode = _fnFeatureHtmlPaginate( oSettings );
3716
+ }
3717
+ else if ( DataTable.ext.feature.length !== 0 )
3718
+ {
3719
+ /* Plug-in features */
3720
+ var aoFeatures = DataTable.ext.feature;
3721
+ for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
3722
+ {
3723
+ if ( cOption == aoFeatures[k].cFeature )
3724
+ {
3725
+ featureNode = aoFeatures[k].fnInit( oSettings );
3726
+ break;
3727
+ }
3728
+ }
3729
+ }
3730
+
3731
+ /* Add to the 2D features array */
3732
+ if ( featureNode )
3733
+ {
3734
+ var aanFeatures = oSettings.aanFeatures;
3735
+
3736
+ if ( ! aanFeatures[cOption] )
3737
+ {
3738
+ aanFeatures[cOption] = [];
3739
+ }
3740
+
3741
+ aanFeatures[cOption].push( featureNode );
3742
+ insert.append( featureNode );
3743
+ }
3744
+ }
3745
+
3746
+ /* Built our DOM structure - replace the holding div with what we want */
3747
+ holding.replaceWith( insert );
3748
+ oSettings.nHolding = null;
3749
+ }
3750
+
3751
+
3752
+ /**
3753
+ * Use the DOM source to create up an array of header cells. The idea here is to
3754
+ * create a layout grid (array) of rows x columns, which contains a reference
3755
+ * to the cell that that point in the grid (regardless of col/rowspan), such that
3756
+ * any column / row could be removed and the new grid constructed
3757
+ * @param array {object} aLayout Array to store the calculated layout in
3758
+ * @param {node} nThead The header/footer element for the table
3759
+ * @memberof DataTable#oApi
3760
+ */
3761
+ function _fnDetectHeader ( aLayout, nThead )
3762
+ {
3763
+ var nTrs = $(nThead).children('tr');
3764
+ var nTr, nCell;
3765
+ var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
3766
+ var bUnique;
3767
+ var fnShiftCol = function ( a, i, j ) {
3768
+ var k = a[i];
3769
+ while ( k[j] ) {
3770
+ j++;
3771
+ }
3772
+ return j;
3773
+ };
3774
+
3775
+ aLayout.splice( 0, aLayout.length );
3776
+
3777
+ /* We know how many rows there are in the layout - so prep it */
3778
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3779
+ {
3780
+ aLayout.push( [] );
3781
+ }
3782
+
3783
+ /* Calculate a layout array */
3784
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3785
+ {
3786
+ nTr = nTrs[i];
3787
+ iColumn = 0;
3788
+
3789
+ /* For every cell in the row... */
3790
+ nCell = nTr.firstChild;
3791
+ while ( nCell ) {
3792
+ if ( nCell.nodeName.toUpperCase() == "TD" ||
3793
+ nCell.nodeName.toUpperCase() == "TH" )
3794
+ {
3795
+ /* Get the col and rowspan attributes from the DOM and sanitise them */
3796
+ iColspan = nCell.getAttribute('colspan') * 1;
3797
+ iRowspan = nCell.getAttribute('rowspan') * 1;
3798
+ iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
3799
+ iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
3800
+
3801
+ /* There might be colspan cells already in this row, so shift our target
3802
+ * accordingly
3803
+ */
3804
+ iColShifted = fnShiftCol( aLayout, i, iColumn );
3805
+
3806
+ /* Cache calculation for unique columns */
3807
+ bUnique = iColspan === 1 ? true : false;
3808
+
3809
+ /* If there is col / rowspan, copy the information into the layout grid */
3810
+ for ( l=0 ; l<iColspan ; l++ )
3811
+ {
3812
+ for ( k=0 ; k<iRowspan ; k++ )
3813
+ {
3814
+ aLayout[i+k][iColShifted+l] = {
3815
+ "cell": nCell,
3816
+ "unique": bUnique
3817
+ };
3818
+ aLayout[i+k].nTr = nTr;
3819
+ }
3820
+ }
3821
+ }
3822
+ nCell = nCell.nextSibling;
3823
+ }
3824
+ }
3825
+ }
3826
+
3827
+
3828
+ /**
3829
+ * Get an array of unique th elements, one for each column
3830
+ * @param {object} oSettings dataTables settings object
3831
+ * @param {node} nHeader automatically detect the layout from this node - optional
3832
+ * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
3833
+ * @returns array {node} aReturn list of unique th's
3834
+ * @memberof DataTable#oApi
3835
+ */
3836
+ function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
3837
+ {
3838
+ var aReturn = [];
3839
+ if ( !aLayout )
3840
+ {
3841
+ aLayout = oSettings.aoHeader;
3842
+ if ( nHeader )
3843
+ {
3844
+ aLayout = [];
3845
+ _fnDetectHeader( aLayout, nHeader );
3846
+ }
3847
+ }
3848
+
3849
+ for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
3850
+ {
3851
+ for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
3852
+ {
3853
+ if ( aLayout[i][j].unique &&
3854
+ (!aReturn[j] || !oSettings.bSortCellsTop) )
3855
+ {
3856
+ aReturn[j] = aLayout[i][j].cell;
3857
+ }
3858
+ }
3859
+ }
3860
+
3861
+ return aReturn;
3862
+ }
3863
+
3864
+ /**
3865
+ * Create an Ajax call based on the table's settings, taking into account that
3866
+ * parameters can have multiple forms, and backwards compatibility.
3867
+ *
3868
+ * @param {object} oSettings dataTables settings object
3869
+ * @param {array} data Data to send to the server, required by
3870
+ * DataTables - may be augmented by developer callbacks
3871
+ * @param {function} fn Callback function to run when data is obtained
3872
+ */
3873
+ function _fnBuildAjax( oSettings, data, fn )
3874
+ {
3875
+ // Compatibility with 1.9-, allow fnServerData and event to manipulate
3876
+ _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
3877
+
3878
+ // Convert to object based for 1.10+ if using the old array scheme which can
3879
+ // come from server-side processing or serverParams
3880
+ if ( data && Array.isArray(data) ) {
3881
+ var tmp = {};
3882
+ var rbracket = /(.*?)\[\]$/;
3883
+
3884
+ $.each( data, function (key, val) {
3885
+ var match = val.name.match(rbracket);
3886
+
3887
+ if ( match ) {
3888
+ // Support for arrays
3889
+ var name = match[0];
3890
+
3891
+ if ( ! tmp[ name ] ) {
3892
+ tmp[ name ] = [];
3893
+ }
3894
+ tmp[ name ].push( val.value );
3895
+ }
3896
+ else {
3897
+ tmp[val.name] = val.value;
3898
+ }
3899
+ } );
3900
+ data = tmp;
3901
+ }
3902
+
3903
+ var ajaxData;
3904
+ var ajax = oSettings.ajax;
3905
+ var instance = oSettings.oInstance;
3906
+ var callback = function ( json ) {
3907
+ _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
3908
+ fn( json );
3909
+ };
3910
+
3911
+ if ( $.isPlainObject( ajax ) && ajax.data )
3912
+ {
3913
+ ajaxData = ajax.data;
3914
+
3915
+ var newData = typeof ajaxData === 'function' ?
3916
+ ajaxData( data, oSettings ) : // fn can manipulate data or return
3917
+ ajaxData; // an object object or array to merge
3918
+
3919
+ // If the function returned something, use that alone
3920
+ data = typeof ajaxData === 'function' && newData ?
3921
+ newData :
3922
+ $.extend( true, data, newData );
3923
+
3924
+ // Remove the data property as we've resolved it already and don't want
3925
+ // jQuery to do it again (it is restored at the end of the function)
3926
+ delete ajax.data;
3927
+ }
3928
+
3929
+ var baseAjax = {
3930
+ "data": data,
3931
+ "success": function (json) {
3932
+ var error = json.error || json.sError;
3933
+ if ( error ) {
3934
+ _fnLog( oSettings, 0, error );
3935
+ }
3936
+
3937
+ oSettings.json = json;
3938
+ callback( json );
3939
+ },
3940
+ "dataType": "json",
3941
+ "cache": false,
3942
+ "type": oSettings.sServerMethod,
3943
+ "error": function (xhr, error, thrown) {
3944
+ var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
3945
+
3946
+ if ( $.inArray( true, ret ) === -1 ) {
3947
+ if ( error == "parsererror" ) {
3948
+ _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
3949
+ }
3950
+ else if ( xhr.readyState === 4 ) {
3951
+ _fnLog( oSettings, 0, 'Ajax error', 7 );
3952
+ }
3953
+ }
3954
+
3955
+ _fnProcessingDisplay( oSettings, false );
3956
+ }
3957
+ };
3958
+
3959
+ // Store the data submitted for the API
3960
+ oSettings.oAjaxData = data;
3961
+
3962
+ // Allow plug-ins and external processes to modify the data
3963
+ _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
3964
+
3965
+ if ( oSettings.fnServerData )
3966
+ {
3967
+ // DataTables 1.9- compatibility
3968
+ oSettings.fnServerData.call( instance,
3969
+ oSettings.sAjaxSource,
3970
+ $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
3971
+ return { name: key, value: val };
3972
+ } ),
3973
+ callback,
3974
+ oSettings
3975
+ );
3976
+ }
3977
+ else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
3978
+ {
3979
+ // DataTables 1.9- compatibility
3980
+ oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
3981
+ url: ajax || oSettings.sAjaxSource
3982
+ } ) );
3983
+ }
3984
+ else if ( typeof ajax === 'function' )
3985
+ {
3986
+ // Is a function - let the caller define what needs to be done
3987
+ oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
3988
+ }
3989
+ else
3990
+ {
3991
+ // Object to extend the base settings
3992
+ oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
3993
+
3994
+ // Restore for next time around
3995
+ ajax.data = ajaxData;
3996
+ }
3997
+ }
3998
+
3999
+
4000
+ /**
4001
+ * Update the table using an Ajax call
4002
+ * @param {object} settings dataTables settings object
4003
+ * @returns {boolean} Block the table drawing or not
4004
+ * @memberof DataTable#oApi
4005
+ */
4006
+ function _fnAjaxUpdate( settings )
4007
+ {
4008
+ if ( settings.bAjaxDataGet ) {
4009
+ settings.iDraw++;
4010
+ _fnProcessingDisplay( settings, true );
4011
+
4012
+ _fnBuildAjax(
4013
+ settings,
4014
+ _fnAjaxParameters( settings ),
4015
+ function(json) {
4016
+ _fnAjaxUpdateDraw( settings, json );
4017
+ }
4018
+ );
4019
+
4020
+ return false;
4021
+ }
4022
+ return true;
4023
+ }
4024
+
4025
+
4026
+ /**
4027
+ * Build up the parameters in an object needed for a server-side processing
4028
+ * request. Note that this is basically done twice, is different ways - a modern
4029
+ * method which is used by default in DataTables 1.10 which uses objects and
4030
+ * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
4031
+ * the sAjaxSource option is used in the initialisation, or the legacyAjax
4032
+ * option is set.
4033
+ * @param {object} oSettings dataTables settings object
4034
+ * @returns {bool} block the table drawing or not
4035
+ * @memberof DataTable#oApi
4036
+ */
4037
+ function _fnAjaxParameters( settings )
4038
+ {
4039
+ var
4040
+ columns = settings.aoColumns,
4041
+ columnCount = columns.length,
4042
+ features = settings.oFeatures,
4043
+ preSearch = settings.oPreviousSearch,
4044
+ preColSearch = settings.aoPreSearchCols,
4045
+ i, data = [], dataProp, column, columnSearch,
4046
+ sort = _fnSortFlatten( settings ),
4047
+ displayStart = settings._iDisplayStart,
4048
+ displayLength = features.bPaginate !== false ?
4049
+ settings._iDisplayLength :
4050
+ -1;
4051
+
4052
+ var param = function ( name, value ) {
4053
+ data.push( { 'name': name, 'value': value } );
4054
+ };
4055
+
4056
+ // DataTables 1.9- compatible method
4057
+ param( 'sEcho', settings.iDraw );
4058
+ param( 'iColumns', columnCount );
4059
+ param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
4060
+ param( 'iDisplayStart', displayStart );
4061
+ param( 'iDisplayLength', displayLength );
4062
+
4063
+ // DataTables 1.10+ method
4064
+ var d = {
4065
+ draw: settings.iDraw,
4066
+ columns: [],
4067
+ order: [],
4068
+ start: displayStart,
4069
+ length: displayLength,
4070
+ search: {
4071
+ value: preSearch.sSearch,
4072
+ regex: preSearch.bRegex
4073
+ }
4074
+ };
4075
+
4076
+ for ( i=0 ; i<columnCount ; i++ ) {
4077
+ column = columns[i];
4078
+ columnSearch = preColSearch[i];
4079
+ dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
4080
+
4081
+ d.columns.push( {
4082
+ data: dataProp,
4083
+ name: column.sName,
4084
+ searchable: column.bSearchable,
4085
+ orderable: column.bSortable,
4086
+ search: {
4087
+ value: columnSearch.sSearch,
4088
+ regex: columnSearch.bRegex
4089
+ }
4090
+ } );
4091
+
4092
+ param( "mDataProp_"+i, dataProp );
4093
+
4094
+ if ( features.bFilter ) {
4095
+ param( 'sSearch_'+i, columnSearch.sSearch );
4096
+ param( 'bRegex_'+i, columnSearch.bRegex );
4097
+ param( 'bSearchable_'+i, column.bSearchable );
4098
+ }
4099
+
4100
+ if ( features.bSort ) {
4101
+ param( 'bSortable_'+i, column.bSortable );
4102
+ }
4103
+ }
4104
+
4105
+ if ( features.bFilter ) {
4106
+ param( 'sSearch', preSearch.sSearch );
4107
+ param( 'bRegex', preSearch.bRegex );
4108
+ }
4109
+
4110
+ if ( features.bSort ) {
4111
+ $.each( sort, function ( i, val ) {
4112
+ d.order.push( { column: val.col, dir: val.dir } );
4113
+
4114
+ param( 'iSortCol_'+i, val.col );
4115
+ param( 'sSortDir_'+i, val.dir );
4116
+ } );
4117
+
4118
+ param( 'iSortingCols', sort.length );
4119
+ }
4120
+
4121
+ // If the legacy.ajax parameter is null, then we automatically decide which
4122
+ // form to use, based on sAjaxSource
4123
+ var legacy = DataTable.ext.legacy.ajax;
4124
+ if ( legacy === null ) {
4125
+ return settings.sAjaxSource ? data : d;
4126
+ }
4127
+
4128
+ // Otherwise, if legacy has been specified then we use that to decide on the
4129
+ // form
4130
+ return legacy ? data : d;
4131
+ }
4132
+
4133
+
4134
+ /**
4135
+ * Data the data from the server (nuking the old) and redraw the table
4136
+ * @param {object} oSettings dataTables settings object
4137
+ * @param {object} json json data return from the server.
4138
+ * @param {string} json.sEcho Tracking flag for DataTables to match requests
4139
+ * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
4140
+ * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
4141
+ * @param {array} json.aaData The data to display on this page
4142
+ * @param {string} [json.sColumns] Column ordering (sName, comma separated)
4143
+ * @memberof DataTable#oApi
4144
+ */
4145
+ function _fnAjaxUpdateDraw ( settings, json )
4146
+ {
4147
+ // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
4148
+ // Support both
4149
+ var compat = function ( old, modern ) {
4150
+ return json[old] !== undefined ? json[old] : json[modern];
4151
+ };
4152
+
4153
+ var data = _fnAjaxDataSrc( settings, json );
4154
+ var draw = compat( 'sEcho', 'draw' );
4155
+ var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
4156
+ var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
4157
+
4158
+ if ( draw !== undefined ) {
4159
+ // Protect against out of sequence returns
4160
+ if ( draw*1 < settings.iDraw ) {
4161
+ return;
4162
+ }
4163
+ settings.iDraw = draw * 1;
4164
+ }
4165
+
4166
+ _fnClearTable( settings );
4167
+ settings._iRecordsTotal = parseInt(recordsTotal, 10);
4168
+ settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
4169
+
4170
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
4171
+ _fnAddData( settings, data[i] );
4172
+ }
4173
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
4174
+
4175
+ settings.bAjaxDataGet = false;
4176
+ _fnDraw( settings );
4177
+
4178
+ if ( ! settings._bInitComplete ) {
4179
+ _fnInitComplete( settings, json );
4180
+ }
4181
+
4182
+ settings.bAjaxDataGet = true;
4183
+ _fnProcessingDisplay( settings, false );
4184
+ }
4185
+
4186
+
4187
+ /**
4188
+ * Get the data from the JSON data source to use for drawing a table. Using
4189
+ * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
4190
+ * source object, or from a processing function.
4191
+ * @param {object} oSettings dataTables settings object
4192
+ * @param {object} json Data source object / array from the server
4193
+ * @return {array} Array of data to use
4194
+ */
4195
+ function _fnAjaxDataSrc ( oSettings, json )
4196
+ {
4197
+ var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
4198
+ oSettings.ajax.dataSrc :
4199
+ oSettings.sAjaxDataProp; // Compatibility with 1.9-.
4200
+
4201
+ // Compatibility with 1.9-. In order to read from aaData, check if the
4202
+ // default has been changed, if not, check for aaData
4203
+ if ( dataSrc === 'data' ) {
4204
+ return json.aaData || json[dataSrc];
4205
+ }
4206
+
4207
+ return dataSrc !== "" ?
4208
+ _fnGetObjectDataFn( dataSrc )( json ) :
4209
+ json;
4210
+ }
4211
+
4212
+ /**
4213
+ * Generate the node required for filtering text
4214
+ * @returns {node} Filter control element
4215
+ * @param {object} oSettings dataTables settings object
4216
+ * @memberof DataTable#oApi
4217
+ */
4218
+ function _fnFeatureHtmlFilter ( settings )
4219
+ {
4220
+ var classes = settings.oClasses;
4221
+ var tableId = settings.sTableId;
4222
+ var language = settings.oLanguage;
4223
+ var previousSearch = settings.oPreviousSearch;
4224
+ var features = settings.aanFeatures;
4225
+ var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
4226
+
4227
+ var str = language.sSearch;
4228
+ str = str.match(/_INPUT_/) ?
4229
+ str.replace('_INPUT_', input) :
4230
+ str+input;
4231
+
4232
+ var filter = $('<div/>', {
4233
+ 'id': ! features.f ? tableId+'_filter' : null,
4234
+ 'class': classes.sFilter
4235
+ } )
4236
+ .append( $('<label/>' ).append( str ) );
4237
+
4238
+ var searchFn = function() {
4239
+ /* Update all other filter input elements for the new display */
4240
+ var n = features.f;
4241
+ var val = !this.value ? "" : this.value; // mental IE8 fix :-(
4242
+
4243
+ /* Now do the filter */
4244
+ if ( val != previousSearch.sSearch ) {
4245
+ _fnFilterComplete( settings, {
4246
+ "sSearch": val,
4247
+ "bRegex": previousSearch.bRegex,
4248
+ "bSmart": previousSearch.bSmart ,
4249
+ "bCaseInsensitive": previousSearch.bCaseInsensitive
4250
+ } );
4251
+
4252
+ // Need to redraw, without resorting
4253
+ settings._iDisplayStart = 0;
4254
+ _fnDraw( settings );
4255
+ }
4256
+ };
4257
+
4258
+ var searchDelay = settings.searchDelay !== null ?
4259
+ settings.searchDelay :
4260
+ _fnDataSource( settings ) === 'ssp' ?
4261
+ 400 :
4262
+ 0;
4263
+
4264
+ var jqFilter = $('input', filter)
4265
+ .val( previousSearch.sSearch )
4266
+ .attr( 'placeholder', language.sSearchPlaceholder )
4267
+ .on(
4268
+ 'keyup.DT search.DT input.DT paste.DT cut.DT',
4269
+ searchDelay ?
4270
+ _fnThrottle( searchFn, searchDelay ) :
4271
+ searchFn
4272
+ )
4273
+ .on( 'mouseup', function(e) {
4274
+ // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
4275
+ // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
4276
+ // checks the value to see if it has changed. In other browsers it won't have.
4277
+ setTimeout( function () {
4278
+ searchFn.call(jqFilter[0]);
4279
+ }, 10);
4280
+ } )
4281
+ .on( 'keypress.DT', function(e) {
4282
+ /* Prevent form submission */
4283
+ if ( e.keyCode == 13 ) {
4284
+ return false;
4285
+ }
4286
+ } )
4287
+ .attr('aria-controls', tableId);
4288
+
4289
+ // Update the input elements whenever the table is filtered
4290
+ $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
4291
+ if ( settings === s ) {
4292
+ // IE9 throws an 'unknown error' if document.activeElement is used
4293
+ // inside an iframe or frame...
4294
+ try {
4295
+ if ( jqFilter[0] !== document.activeElement ) {
4296
+ jqFilter.val( previousSearch.sSearch );
4297
+ }
4298
+ }
4299
+ catch ( e ) {}
4300
+ }
4301
+ } );
4302
+
4303
+ return filter[0];
4304
+ }
4305
+
4306
+
4307
+ /**
4308
+ * Filter the table using both the global filter and column based filtering
4309
+ * @param {object} oSettings dataTables settings object
4310
+ * @param {object} oSearch search information
4311
+ * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
4312
+ * @memberof DataTable#oApi
4313
+ */
4314
+ function _fnFilterComplete ( oSettings, oInput, iForce )
4315
+ {
4316
+ var oPrevSearch = oSettings.oPreviousSearch;
4317
+ var aoPrevSearch = oSettings.aoPreSearchCols;
4318
+ var fnSaveFilter = function ( oFilter ) {
4319
+ /* Save the filtering values */
4320
+ oPrevSearch.sSearch = oFilter.sSearch;
4321
+ oPrevSearch.bRegex = oFilter.bRegex;
4322
+ oPrevSearch.bSmart = oFilter.bSmart;
4323
+ oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
4324
+ };
4325
+ var fnRegex = function ( o ) {
4326
+ // Backwards compatibility with the bEscapeRegex option
4327
+ return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
4328
+ };
4329
+
4330
+ // Resolve any column types that are unknown due to addition or invalidation
4331
+ // @todo As per sort - can this be moved into an event handler?
4332
+ _fnColumnTypes( oSettings );
4333
+
4334
+ /* In server-side processing all filtering is done by the server, so no point hanging around here */
4335
+ if ( _fnDataSource( oSettings ) != 'ssp' )
4336
+ {
4337
+ /* Global filter */
4338
+ _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
4339
+ fnSaveFilter( oInput );
4340
+
4341
+ /* Now do the individual column filter */
4342
+ for ( var i=0 ; i<aoPrevSearch.length ; i++ )
4343
+ {
4344
+ _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
4345
+ aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
4346
+ }
4347
+
4348
+ /* Custom filtering */
4349
+ _fnFilterCustom( oSettings );
4350
+ }
4351
+ else
4352
+ {
4353
+ fnSaveFilter( oInput );
4354
+ }
4355
+
4356
+ /* Tell the draw function we have been filtering */
4357
+ oSettings.bFiltered = true;
4358
+ _fnCallbackFire( oSettings, null, 'search', [oSettings] );
4359
+ }
4360
+
4361
+
4362
+ /**
4363
+ * Apply custom filtering functions
4364
+ * @param {object} oSettings dataTables settings object
4365
+ * @memberof DataTable#oApi
4366
+ */
4367
+ function _fnFilterCustom( settings )
4368
+ {
4369
+ var filters = DataTable.ext.search;
4370
+ var displayRows = settings.aiDisplay;
4371
+ var row, rowIdx;
4372
+
4373
+ for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
4374
+ var rows = [];
4375
+
4376
+ // Loop over each row and see if it should be included
4377
+ for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
4378
+ rowIdx = displayRows[ j ];
4379
+ row = settings.aoData[ rowIdx ];
4380
+
4381
+ if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
4382
+ rows.push( rowIdx );
4383
+ }
4384
+ }
4385
+
4386
+ // So the array reference doesn't break set the results into the
4387
+ // existing array
4388
+ displayRows.length = 0;
4389
+ $.merge( displayRows, rows );
4390
+ }
4391
+ }
4392
+
4393
+
4394
+ /**
4395
+ * Filter the table on a per-column basis
4396
+ * @param {object} oSettings dataTables settings object
4397
+ * @param {string} sInput string to filter on
4398
+ * @param {int} iColumn column to filter
4399
+ * @param {bool} bRegex treat search string as a regular expression or not
4400
+ * @param {bool} bSmart use smart filtering or not
4401
+ * @param {bool} bCaseInsensitive Do case insenstive matching or not
4402
+ * @memberof DataTable#oApi
4403
+ */
4404
+ function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
4405
+ {
4406
+ if ( searchStr === '' ) {
4407
+ return;
4408
+ }
4409
+
4410
+ var data;
4411
+ var out = [];
4412
+ var display = settings.aiDisplay;
4413
+ var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
4414
+
4415
+ for ( var i=0 ; i<display.length ; i++ ) {
4416
+ data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
4417
+
4418
+ if ( rpSearch.test( data ) ) {
4419
+ out.push( display[i] );
4420
+ }
4421
+ }
4422
+
4423
+ settings.aiDisplay = out;
4424
+ }
4425
+
4426
+
4427
+ /**
4428
+ * Filter the data table based on user input and draw the table
4429
+ * @param {object} settings dataTables settings object
4430
+ * @param {string} input string to filter on
4431
+ * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
4432
+ * @param {bool} regex treat as a regular expression or not
4433
+ * @param {bool} smart perform smart filtering or not
4434
+ * @param {bool} caseInsensitive Do case insenstive matching or not
4435
+ * @memberof DataTable#oApi
4436
+ */
4437
+ function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
4438
+ {
4439
+ var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
4440
+ var prevSearch = settings.oPreviousSearch.sSearch;
4441
+ var displayMaster = settings.aiDisplayMaster;
4442
+ var display, invalidated, i;
4443
+ var filtered = [];
4444
+
4445
+ // Need to take account of custom filtering functions - always filter
4446
+ if ( DataTable.ext.search.length !== 0 ) {
4447
+ force = true;
4448
+ }
4449
+
4450
+ // Check if any of the rows were invalidated
4451
+ invalidated = _fnFilterData( settings );
4452
+
4453
+ // If the input is blank - we just want the full data set
4454
+ if ( input.length <= 0 ) {
4455
+ settings.aiDisplay = displayMaster.slice();
4456
+ }
4457
+ else {
4458
+ // New search - start from the master array
4459
+ if ( invalidated ||
4460
+ force ||
4461
+ regex ||
4462
+ prevSearch.length > input.length ||
4463
+ input.indexOf(prevSearch) !== 0 ||
4464
+ settings.bSorted // On resort, the display master needs to be
4465
+ // re-filtered since indexes will have changed
4466
+ ) {
4467
+ settings.aiDisplay = displayMaster.slice();
4468
+ }
4469
+
4470
+ // Search the display array
4471
+ display = settings.aiDisplay;
4472
+
4473
+ for ( i=0 ; i<display.length ; i++ ) {
4474
+ if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
4475
+ filtered.push( display[i] );
4476
+ }
4477
+ }
4478
+
4479
+ settings.aiDisplay = filtered;
4480
+ }
4481
+ }
4482
+
4483
+
4484
+ /**
4485
+ * Build a regular expression object suitable for searching a table
4486
+ * @param {string} sSearch string to search for
4487
+ * @param {bool} bRegex treat as a regular expression or not
4488
+ * @param {bool} bSmart perform smart filtering or not
4489
+ * @param {bool} bCaseInsensitive Do case insensitive matching or not
4490
+ * @returns {RegExp} constructed object
4491
+ * @memberof DataTable#oApi
4492
+ */
4493
+ function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
4494
+ {
4495
+ search = regex ?
4496
+ search :
4497
+ _fnEscapeRegex( search );
4498
+
4499
+ if ( smart ) {
4500
+ /* For smart filtering we want to allow the search to work regardless of
4501
+ * word order. We also want double quoted text to be preserved, so word
4502
+ * order is important - a la google. So this is what we want to
4503
+ * generate:
4504
+ *
4505
+ * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
4506
+ */
4507
+ var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
4508
+ if ( word.charAt(0) === '"' ) {
4509
+ var m = word.match( /^"(.*)"$/ );
4510
+ word = m ? m[1] : word;
4511
+ }
4512
+
4513
+ return word.replace('"', '');
4514
+ } );
4515
+
4516
+ search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
4517
+ }
4518
+
4519
+ return new RegExp( search, caseInsensitive ? 'i' : '' );
4520
+ }
4521
+
4522
+
4523
+ /**
4524
+ * Escape a string such that it can be used in a regular expression
4525
+ * @param {string} sVal string to escape
4526
+ * @returns {string} escaped string
4527
+ * @memberof DataTable#oApi
4528
+ */
4529
+ var _fnEscapeRegex = DataTable.util.escapeRegex;
4530
+
4531
+ var __filter_div = $('<div>')[0];
4532
+ var __filter_div_textContent = __filter_div.textContent !== undefined;
4533
+
4534
+ // Update the filtering data for each row if needed (by invalidation or first run)
4535
+ function _fnFilterData ( settings )
4536
+ {
4537
+ var columns = settings.aoColumns;
4538
+ var column;
4539
+ var i, j, ien, jen, filterData, cellData, row;
4540
+ var fomatters = DataTable.ext.type.search;
4541
+ var wasInvalidated = false;
4542
+
4543
+ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4544
+ row = settings.aoData[i];
4545
+
4546
+ if ( ! row._aFilterData ) {
4547
+ filterData = [];
4548
+
4549
+ for ( j=0, jen=columns.length ; j<jen ; j++ ) {
4550
+ column = columns[j];
4551
+
4552
+ if ( column.bSearchable ) {
4553
+ cellData = _fnGetCellData( settings, i, j, 'filter' );
4554
+
4555
+ if ( fomatters[ column.sType ] ) {
4556
+ cellData = fomatters[ column.sType ]( cellData );
4557
+ }
4558
+
4559
+ // Search in DataTables 1.10 is string based. In 1.11 this
4560
+ // should be altered to also allow strict type checking.
4561
+ if ( cellData === null ) {
4562
+ cellData = '';
4563
+ }
4564
+
4565
+ if ( typeof cellData !== 'string' && cellData.toString ) {
4566
+ cellData = cellData.toString();
4567
+ }
4568
+ }
4569
+ else {
4570
+ cellData = '';
4571
+ }
4572
+
4573
+ // If it looks like there is an HTML entity in the string,
4574
+ // attempt to decode it so sorting works as expected. Note that
4575
+ // we could use a single line of jQuery to do this, but the DOM
4576
+ // method used here is much faster http://jsperf.com/html-decode
4577
+ if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
4578
+ __filter_div.innerHTML = cellData;
4579
+ cellData = __filter_div_textContent ?
4580
+ __filter_div.textContent :
4581
+ __filter_div.innerText;
4582
+ }
4583
+
4584
+ if ( cellData.replace ) {
4585
+ cellData = cellData.replace(/[\r\n\u2028]/g, '');
4586
+ }
4587
+
4588
+ filterData.push( cellData );
4589
+ }
4590
+
4591
+ row._aFilterData = filterData;
4592
+ row._sFilterRow = filterData.join(' ');
4593
+ wasInvalidated = true;
4594
+ }
4595
+ }
4596
+
4597
+ return wasInvalidated;
4598
+ }
4599
+
4600
+
4601
+ /**
4602
+ * Convert from the internal Hungarian notation to camelCase for external
4603
+ * interaction
4604
+ * @param {object} obj Object to convert
4605
+ * @returns {object} Inverted object
4606
+ * @memberof DataTable#oApi
4607
+ */
4608
+ function _fnSearchToCamel ( obj )
4609
+ {
4610
+ return {
4611
+ search: obj.sSearch,
4612
+ smart: obj.bSmart,
4613
+ regex: obj.bRegex,
4614
+ caseInsensitive: obj.bCaseInsensitive
4615
+ };
4616
+ }
4617
+
4618
+
4619
+
4620
+ /**
4621
+ * Convert from camelCase notation to the internal Hungarian. We could use the
4622
+ * Hungarian convert function here, but this is cleaner
4623
+ * @param {object} obj Object to convert
4624
+ * @returns {object} Inverted object
4625
+ * @memberof DataTable#oApi
4626
+ */
4627
+ function _fnSearchToHung ( obj )
4628
+ {
4629
+ return {
4630
+ sSearch: obj.search,
4631
+ bSmart: obj.smart,
4632
+ bRegex: obj.regex,
4633
+ bCaseInsensitive: obj.caseInsensitive
4634
+ };
4635
+ }
4636
+
4637
+ /**
4638
+ * Generate the node required for the info display
4639
+ * @param {object} oSettings dataTables settings object
4640
+ * @returns {node} Information element
4641
+ * @memberof DataTable#oApi
4642
+ */
4643
+ function _fnFeatureHtmlInfo ( settings )
4644
+ {
4645
+ var
4646
+ tid = settings.sTableId,
4647
+ nodes = settings.aanFeatures.i,
4648
+ n = $('<div/>', {
4649
+ 'class': settings.oClasses.sInfo,
4650
+ 'id': ! nodes ? tid+'_info' : null
4651
+ } );
4652
+
4653
+ if ( ! nodes ) {
4654
+ // Update display on each draw
4655
+ settings.aoDrawCallback.push( {
4656
+ "fn": _fnUpdateInfo,
4657
+ "sName": "information"
4658
+ } );
4659
+
4660
+ n
4661
+ .attr( 'role', 'status' )
4662
+ .attr( 'aria-live', 'polite' );
4663
+
4664
+ // Table is described by our info div
4665
+ $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
4666
+ }
4667
+
4668
+ return n[0];
4669
+ }
4670
+
4671
+
4672
+ /**
4673
+ * Update the information elements in the display
4674
+ * @param {object} settings dataTables settings object
4675
+ * @memberof DataTable#oApi
4676
+ */
4677
+ function _fnUpdateInfo ( settings )
4678
+ {
4679
+ /* Show information about the table */
4680
+ var nodes = settings.aanFeatures.i;
4681
+ if ( nodes.length === 0 ) {
4682
+ return;
4683
+ }
4684
+
4685
+ var
4686
+ lang = settings.oLanguage,
4687
+ start = settings._iDisplayStart+1,
4688
+ end = settings.fnDisplayEnd(),
4689
+ max = settings.fnRecordsTotal(),
4690
+ total = settings.fnRecordsDisplay(),
4691
+ out = total ?
4692
+ lang.sInfo :
4693
+ lang.sInfoEmpty;
4694
+
4695
+ if ( total !== max ) {
4696
+ /* Record set after filtering */
4697
+ out += ' ' + lang.sInfoFiltered;
4698
+ }
4699
+
4700
+ // Convert the macros
4701
+ out += lang.sInfoPostFix;
4702
+ out = _fnInfoMacros( settings, out );
4703
+
4704
+ var callback = lang.fnInfoCallback;
4705
+ if ( callback !== null ) {
4706
+ out = callback.call( settings.oInstance,
4707
+ settings, start, end, max, total, out
4708
+ );
4709
+ }
4710
+
4711
+ $(nodes).html( out );
4712
+ }
4713
+
4714
+
4715
+ function _fnInfoMacros ( settings, str )
4716
+ {
4717
+ // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
4718
+ // internally
4719
+ var
4720
+ formatter = settings.fnFormatNumber,
4721
+ start = settings._iDisplayStart+1,
4722
+ len = settings._iDisplayLength,
4723
+ vis = settings.fnRecordsDisplay(),
4724
+ all = len === -1;
4725
+
4726
+ return str.
4727
+ replace(/_START_/g, formatter.call( settings, start ) ).
4728
+ replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
4729
+ replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
4730
+ replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
4731
+ replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
4732
+ replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
4733
+ }
4734
+
4735
+
4736
+
4737
+ /**
4738
+ * Draw the table for the first time, adding all required features
4739
+ * @param {object} settings dataTables settings object
4740
+ * @memberof DataTable#oApi
4741
+ */
4742
+ function _fnInitialise ( settings )
4743
+ {
4744
+ var i, iLen, iAjaxStart=settings.iInitDisplayStart;
4745
+ var columns = settings.aoColumns, column;
4746
+ var features = settings.oFeatures;
4747
+ var deferLoading = settings.bDeferLoading; // value modified by the draw
4748
+
4749
+ /* Ensure that the table data is fully initialised */
4750
+ if ( ! settings.bInitialised ) {
4751
+ setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4752
+ return;
4753
+ }
4754
+
4755
+ /* Show the display HTML options */
4756
+ _fnAddOptionsHtml( settings );
4757
+
4758
+ /* Build and draw the header / footer for the table */
4759
+ _fnBuildHead( settings );
4760
+ _fnDrawHead( settings, settings.aoHeader );
4761
+ _fnDrawHead( settings, settings.aoFooter );
4762
+
4763
+ /* Okay to show that something is going on now */
4764
+ _fnProcessingDisplay( settings, true );
4765
+
4766
+ /* Calculate sizes for columns */
4767
+ if ( features.bAutoWidth ) {
4768
+ _fnCalculateColumnWidths( settings );
4769
+ }
4770
+
4771
+ for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
4772
+ column = columns[i];
4773
+
4774
+ if ( column.sWidth ) {
4775
+ column.nTh.style.width = _fnStringToCss( column.sWidth );
4776
+ }
4777
+ }
4778
+
4779
+ _fnCallbackFire( settings, null, 'preInit', [settings] );
4780
+
4781
+ // If there is default sorting required - let's do it. The sort function
4782
+ // will do the drawing for us. Otherwise we draw the table regardless of the
4783
+ // Ajax source - this allows the table to look initialised for Ajax sourcing
4784
+ // data (show 'loading' message possibly)
4785
+ _fnReDraw( settings );
4786
+
4787
+ // Server-side processing init complete is done by _fnAjaxUpdateDraw
4788
+ var dataSrc = _fnDataSource( settings );
4789
+ if ( dataSrc != 'ssp' || deferLoading ) {
4790
+ // if there is an ajax source load the data
4791
+ if ( dataSrc == 'ajax' ) {
4792
+ _fnBuildAjax( settings, [], function(json) {
4793
+ var aData = _fnAjaxDataSrc( settings, json );
4794
+
4795
+ // Got the data - add it to the table
4796
+ for ( i=0 ; i<aData.length ; i++ ) {
4797
+ _fnAddData( settings, aData[i] );
4798
+ }
4799
+
4800
+ // Reset the init display for cookie saving. We've already done
4801
+ // a filter, and therefore cleared it before. So we need to make
4802
+ // it appear 'fresh'
4803
+ settings.iInitDisplayStart = iAjaxStart;
4804
+
4805
+ _fnReDraw( settings );
4806
+
4807
+ _fnProcessingDisplay( settings, false );
4808
+ _fnInitComplete( settings, json );
4809
+ }, settings );
4810
+ }
4811
+ else {
4812
+ _fnProcessingDisplay( settings, false );
4813
+ _fnInitComplete( settings );
4814
+ }
4815
+ }
4816
+ }
4817
+
4818
+
4819
+ /**
4820
+ * Draw the table for the first time, adding all required features
4821
+ * @param {object} oSettings dataTables settings object
4822
+ * @param {object} [json] JSON from the server that completed the table, if using Ajax source
4823
+ * with client-side processing (optional)
4824
+ * @memberof DataTable#oApi
4825
+ */
4826
+ function _fnInitComplete ( settings, json )
4827
+ {
4828
+ settings._bInitComplete = true;
4829
+
4830
+ // When data was added after the initialisation (data or Ajax) we need to
4831
+ // calculate the column sizing
4832
+ if ( json || settings.oInit.aaData ) {
4833
+ _fnAdjustColumnSizing( settings );
4834
+ }
4835
+
4836
+ _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
4837
+ _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
4838
+ }
4839
+
4840
+
4841
+ function _fnLengthChange ( settings, val )
4842
+ {
4843
+ var len = parseInt( val, 10 );
4844
+ settings._iDisplayLength = len;
4845
+
4846
+ _fnLengthOverflow( settings );
4847
+
4848
+ // Fire length change event
4849
+ _fnCallbackFire( settings, null, 'length', [settings, len] );
4850
+ }
4851
+
4852
+
4853
+ /**
4854
+ * Generate the node required for user display length changing
4855
+ * @param {object} settings dataTables settings object
4856
+ * @returns {node} Display length feature node
4857
+ * @memberof DataTable#oApi
4858
+ */
4859
+ function _fnFeatureHtmlLength ( settings )
4860
+ {
4861
+ var
4862
+ classes = settings.oClasses,
4863
+ tableId = settings.sTableId,
4864
+ menu = settings.aLengthMenu,
4865
+ d2 = Array.isArray( menu[0] ),
4866
+ lengths = d2 ? menu[0] : menu,
4867
+ language = d2 ? menu[1] : menu;
4868
+
4869
+ var select = $('<select/>', {
4870
+ 'name': tableId+'_length',
4871
+ 'aria-controls': tableId,
4872
+ 'class': classes.sLengthSelect
4873
+ } );
4874
+
4875
+ for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
4876
+ select[0][ i ] = new Option(
4877
+ typeof language[i] === 'number' ?
4878
+ settings.fnFormatNumber( language[i] ) :
4879
+ language[i],
4880
+ lengths[i]
4881
+ );
4882
+ }
4883
+
4884
+ var div = $('<div><label/></div>').addClass( classes.sLength );
4885
+ if ( ! settings.aanFeatures.l ) {
4886
+ div[0].id = tableId+'_length';
4887
+ }
4888
+
4889
+ div.children().append(
4890
+ settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
4891
+ );
4892
+
4893
+ // Can't use `select` variable as user might provide their own and the
4894
+ // reference is broken by the use of outerHTML
4895
+ $('select', div)
4896
+ .val( settings._iDisplayLength )
4897
+ .on( 'change.DT', function(e) {
4898
+ _fnLengthChange( settings, $(this).val() );
4899
+ _fnDraw( settings );
4900
+ } );
4901
+
4902
+ // Update node value whenever anything changes the table's length
4903
+ $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
4904
+ if ( settings === s ) {
4905
+ $('select', div).val( len );
4906
+ }
4907
+ } );
4908
+
4909
+ return div[0];
4910
+ }
4911
+
4912
+
4913
+
4914
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4915
+ * Note that most of the paging logic is done in
4916
+ * DataTable.ext.pager
4917
+ */
4918
+
4919
+ /**
4920
+ * Generate the node required for default pagination
4921
+ * @param {object} oSettings dataTables settings object
4922
+ * @returns {node} Pagination feature node
4923
+ * @memberof DataTable#oApi
4924
+ */
4925
+ function _fnFeatureHtmlPaginate ( settings )
4926
+ {
4927
+ var
4928
+ type = settings.sPaginationType,
4929
+ plugin = DataTable.ext.pager[ type ],
4930
+ modern = typeof plugin === 'function',
4931
+ redraw = function( settings ) {
4932
+ _fnDraw( settings );
4933
+ },
4934
+ node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
4935
+ features = settings.aanFeatures;
4936
+
4937
+ if ( ! modern ) {
4938
+ plugin.fnInit( settings, node, redraw );
4939
+ }
4940
+
4941
+ /* Add a draw callback for the pagination on first instance, to update the paging display */
4942
+ if ( ! features.p )
4943
+ {
4944
+ node.id = settings.sTableId+'_paginate';
4945
+
4946
+ settings.aoDrawCallback.push( {
4947
+ "fn": function( settings ) {
4948
+ if ( modern ) {
4949
+ var
4950
+ start = settings._iDisplayStart,
4951
+ len = settings._iDisplayLength,
4952
+ visRecords = settings.fnRecordsDisplay(),
4953
+ all = len === -1,
4954
+ page = all ? 0 : Math.ceil( start / len ),
4955
+ pages = all ? 1 : Math.ceil( visRecords / len ),
4956
+ buttons = plugin(page, pages),
4957
+ i, ien;
4958
+
4959
+ for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
4960
+ _fnRenderer( settings, 'pageButton' )(
4961
+ settings, features.p[i], i, buttons, page, pages
4962
+ );
4963
+ }
4964
+ }
4965
+ else {
4966
+ plugin.fnUpdate( settings, redraw );
4967
+ }
4968
+ },
4969
+ "sName": "pagination"
4970
+ } );
4971
+ }
4972
+
4973
+ return node;
4974
+ }
4975
+
4976
+
4977
+ /**
4978
+ * Alter the display settings to change the page
4979
+ * @param {object} settings DataTables settings object
4980
+ * @param {string|int} action Paging action to take: "first", "previous",
4981
+ * "next" or "last" or page number to jump to (integer)
4982
+ * @param [bool] redraw Automatically draw the update or not
4983
+ * @returns {bool} true page has changed, false - no change
4984
+ * @memberof DataTable#oApi
4985
+ */
4986
+ function _fnPageChange ( settings, action, redraw )
4987
+ {
4988
+ var
4989
+ start = settings._iDisplayStart,
4990
+ len = settings._iDisplayLength,
4991
+ records = settings.fnRecordsDisplay();
4992
+
4993
+ if ( records === 0 || len === -1 )
4994
+ {
4995
+ start = 0;
4996
+ }
4997
+ else if ( typeof action === "number" )
4998
+ {
4999
+ start = action * len;
5000
+
5001
+ if ( start > records )
5002
+ {
5003
+ start = 0;
5004
+ }
5005
+ }
5006
+ else if ( action == "first" )
5007
+ {
5008
+ start = 0;
5009
+ }
5010
+ else if ( action == "previous" )
5011
+ {
5012
+ start = len >= 0 ?
5013
+ start - len :
5014
+ 0;
5015
+
5016
+ if ( start < 0 )
5017
+ {
5018
+ start = 0;
5019
+ }
5020
+ }
5021
+ else if ( action == "next" )
5022
+ {
5023
+ if ( start + len < records )
5024
+ {
5025
+ start += len;
5026
+ }
5027
+ }
5028
+ else if ( action == "last" )
5029
+ {
5030
+ start = Math.floor( (records-1) / len) * len;
5031
+ }
5032
+ else
5033
+ {
5034
+ _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
5035
+ }
5036
+
5037
+ var changed = settings._iDisplayStart !== start;
5038
+ settings._iDisplayStart = start;
5039
+
5040
+ if ( changed ) {
5041
+ _fnCallbackFire( settings, null, 'page', [settings] );
5042
+
5043
+ if ( redraw ) {
5044
+ _fnDraw( settings );
5045
+ }
5046
+ }
5047
+
5048
+ return changed;
5049
+ }
5050
+
5051
+
5052
+
5053
+ /**
5054
+ * Generate the node required for the processing node
5055
+ * @param {object} settings dataTables settings object
5056
+ * @returns {node} Processing element
5057
+ * @memberof DataTable#oApi
5058
+ */
5059
+ function _fnFeatureHtmlProcessing ( settings )
5060
+ {
5061
+ return $('<div/>', {
5062
+ 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
5063
+ 'class': settings.oClasses.sProcessing
5064
+ } )
5065
+ .html( settings.oLanguage.sProcessing )
5066
+ .insertBefore( settings.nTable )[0];
5067
+ }
5068
+
5069
+
5070
+ /**
5071
+ * Display or hide the processing indicator
5072
+ * @param {object} settings dataTables settings object
5073
+ * @param {bool} show Show the processing indicator (true) or not (false)
5074
+ * @memberof DataTable#oApi
5075
+ */
5076
+ function _fnProcessingDisplay ( settings, show )
5077
+ {
5078
+ if ( settings.oFeatures.bProcessing ) {
5079
+ $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
5080
+ }
5081
+
5082
+ _fnCallbackFire( settings, null, 'processing', [settings, show] );
5083
+ }
5084
+
5085
+ /**
5086
+ * Add any control elements for the table - specifically scrolling
5087
+ * @param {object} settings dataTables settings object
5088
+ * @returns {node} Node to add to the DOM
5089
+ * @memberof DataTable#oApi
5090
+ */
5091
+ function _fnFeatureHtmlTable ( settings )
5092
+ {
5093
+ var table = $(settings.nTable);
5094
+
5095
+ // Add the ARIA grid role to the table
5096
+ table.attr( 'role', 'grid' );
5097
+
5098
+ // Scrolling from here on in
5099
+ var scroll = settings.oScroll;
5100
+
5101
+ if ( scroll.sX === '' && scroll.sY === '' ) {
5102
+ return settings.nTable;
5103
+ }
5104
+
5105
+ var scrollX = scroll.sX;
5106
+ var scrollY = scroll.sY;
5107
+ var classes = settings.oClasses;
5108
+ var caption = table.children('caption');
5109
+ var captionSide = caption.length ? caption[0]._captionSide : null;
5110
+ var headerClone = $( table[0].cloneNode(false) );
5111
+ var footerClone = $( table[0].cloneNode(false) );
5112
+ var footer = table.children('tfoot');
5113
+ var _div = '<div/>';
5114
+ var size = function ( s ) {
5115
+ return !s ? null : _fnStringToCss( s );
5116
+ };
5117
+
5118
+ if ( ! footer.length ) {
5119
+ footer = null;
5120
+ }
5121
+
5122
+ /*
5123
+ * The HTML structure that we want to generate in this function is:
5124
+ * div - scroller
5125
+ * div - scroll head
5126
+ * div - scroll head inner
5127
+ * table - scroll head table
5128
+ * thead - thead
5129
+ * div - scroll body
5130
+ * table - table (master table)
5131
+ * thead - thead clone for sizing
5132
+ * tbody - tbody
5133
+ * div - scroll foot
5134
+ * div - scroll foot inner
5135
+ * table - scroll foot table
5136
+ * tfoot - tfoot
5137
+ */
5138
+ var scroller = $( _div, { 'class': classes.sScrollWrapper } )
5139
+ .append(
5140
+ $(_div, { 'class': classes.sScrollHead } )
5141
+ .css( {
5142
+ overflow: 'hidden',
5143
+ position: 'relative',
5144
+ border: 0,
5145
+ width: scrollX ? size(scrollX) : '100%'
5146
+ } )
5147
+ .append(
5148
+ $(_div, { 'class': classes.sScrollHeadInner } )
5149
+ .css( {
5150
+ 'box-sizing': 'content-box',
5151
+ width: scroll.sXInner || '100%'
5152
+ } )
5153
+ .append(
5154
+ headerClone
5155
+ .removeAttr('id')
5156
+ .css( 'margin-left', 0 )
5157
+ .append( captionSide === 'top' ? caption : null )
5158
+ .append(
5159
+ table.children('thead')
5160
+ )
5161
+ )
5162
+ )
5163
+ )
5164
+ .append(
5165
+ $(_div, { 'class': classes.sScrollBody } )
5166
+ .css( {
5167
+ position: 'relative',
5168
+ overflow: 'auto',
5169
+ width: size( scrollX )
5170
+ } )
5171
+ .append( table )
5172
+ );
5173
+
5174
+ if ( footer ) {
5175
+ scroller.append(
5176
+ $(_div, { 'class': classes.sScrollFoot } )
5177
+ .css( {
5178
+ overflow: 'hidden',
5179
+ border: 0,
5180
+ width: scrollX ? size(scrollX) : '100%'
5181
+ } )
5182
+ .append(
5183
+ $(_div, { 'class': classes.sScrollFootInner } )
5184
+ .append(
5185
+ footerClone
5186
+ .removeAttr('id')
5187
+ .css( 'margin-left', 0 )
5188
+ .append( captionSide === 'bottom' ? caption : null )
5189
+ .append(
5190
+ table.children('tfoot')
5191
+ )
5192
+ )
5193
+ )
5194
+ );
5195
+ }
5196
+
5197
+ var children = scroller.children();
5198
+ var scrollHead = children[0];
5199
+ var scrollBody = children[1];
5200
+ var scrollFoot = footer ? children[2] : null;
5201
+
5202
+ // When the body is scrolled, then we also want to scroll the headers
5203
+ if ( scrollX ) {
5204
+ $(scrollBody).on( 'scroll.DT', function (e) {
5205
+ var scrollLeft = this.scrollLeft;
5206
+
5207
+ scrollHead.scrollLeft = scrollLeft;
5208
+
5209
+ if ( footer ) {
5210
+ scrollFoot.scrollLeft = scrollLeft;
5211
+ }
5212
+ } );
5213
+ }
5214
+
5215
+ $(scrollBody).css('max-height', scrollY);
5216
+ if (! scroll.bCollapse) {
5217
+ $(scrollBody).css('height', scrollY);
5218
+ }
5219
+
5220
+ settings.nScrollHead = scrollHead;
5221
+ settings.nScrollBody = scrollBody;
5222
+ settings.nScrollFoot = scrollFoot;
5223
+
5224
+ // On redraw - align columns
5225
+ settings.aoDrawCallback.push( {
5226
+ "fn": _fnScrollDraw,
5227
+ "sName": "scrolling"
5228
+ } );
5229
+
5230
+ return scroller[0];
5231
+ }
5232
+
5233
+
5234
+
5235
+ /**
5236
+ * Update the header, footer and body tables for resizing - i.e. column
5237
+ * alignment.
5238
+ *
5239
+ * Welcome to the most horrible function DataTables. The process that this
5240
+ * function follows is basically:
5241
+ * 1. Re-create the table inside the scrolling div
5242
+ * 2. Take live measurements from the DOM
5243
+ * 3. Apply the measurements to align the columns
5244
+ * 4. Clean up
5245
+ *
5246
+ * @param {object} settings dataTables settings object
5247
+ * @memberof DataTable#oApi
5248
+ */
5249
+ function _fnScrollDraw ( settings )
5250
+ {
5251
+ // Given that this is such a monster function, a lot of variables are use
5252
+ // to try and keep the minimised size as small as possible
5253
+ var
5254
+ scroll = settings.oScroll,
5255
+ scrollX = scroll.sX,
5256
+ scrollXInner = scroll.sXInner,
5257
+ scrollY = scroll.sY,
5258
+ barWidth = scroll.iBarWidth,
5259
+ divHeader = $(settings.nScrollHead),
5260
+ divHeaderStyle = divHeader[0].style,
5261
+ divHeaderInner = divHeader.children('div'),
5262
+ divHeaderInnerStyle = divHeaderInner[0].style,
5263
+ divHeaderTable = divHeaderInner.children('table'),
5264
+ divBodyEl = settings.nScrollBody,
5265
+ divBody = $(divBodyEl),
5266
+ divBodyStyle = divBodyEl.style,
5267
+ divFooter = $(settings.nScrollFoot),
5268
+ divFooterInner = divFooter.children('div'),
5269
+ divFooterTable = divFooterInner.children('table'),
5270
+ header = $(settings.nTHead),
5271
+ table = $(settings.nTable),
5272
+ tableEl = table[0],
5273
+ tableStyle = tableEl.style,
5274
+ footer = settings.nTFoot ? $(settings.nTFoot) : null,
5275
+ browser = settings.oBrowser,
5276
+ ie67 = browser.bScrollOversize,
5277
+ dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
5278
+ headerTrgEls, footerTrgEls,
5279
+ headerSrcEls, footerSrcEls,
5280
+ headerCopy, footerCopy,
5281
+ headerWidths=[], footerWidths=[],
5282
+ headerContent=[], footerContent=[],
5283
+ idx, correction, sanityWidth,
5284
+ zeroOut = function(nSizer) {
5285
+ var style = nSizer.style;
5286
+ style.paddingTop = "0";
5287
+ style.paddingBottom = "0";
5288
+ style.borderTopWidth = "0";
5289
+ style.borderBottomWidth = "0";
5290
+ style.height = 0;
5291
+ };
5292
+
5293
+ // If the scrollbar visibility has changed from the last draw, we need to
5294
+ // adjust the column sizes as the table width will have changed to account
5295
+ // for the scrollbar
5296
+ var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
5297
+
5298
+ if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
5299
+ settings.scrollBarVis = scrollBarVis;
5300
+ _fnAdjustColumnSizing( settings );
5301
+ return; // adjust column sizing will call this function again
5302
+ }
5303
+ else {
5304
+ settings.scrollBarVis = scrollBarVis;
5305
+ }
5306
+
5307
+ /*
5308
+ * 1. Re-create the table inside the scrolling div
5309
+ */
5310
+
5311
+ // Remove the old minimised thead and tfoot elements in the inner table
5312
+ table.children('thead, tfoot').remove();
5313
+
5314
+ if ( footer ) {
5315
+ footerCopy = footer.clone().prependTo( table );
5316
+ footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
5317
+ footerSrcEls = footerCopy.find('tr');
5318
+ }
5319
+
5320
+ // Clone the current header and footer elements and then place it into the inner table
5321
+ headerCopy = header.clone().prependTo( table );
5322
+ headerTrgEls = header.find('tr'); // original header is in its own table
5323
+ headerSrcEls = headerCopy.find('tr');
5324
+ headerCopy.find('th, td').removeAttr('tabindex');
5325
+
5326
+
5327
+ /*
5328
+ * 2. Take live measurements from the DOM - do not alter the DOM itself!
5329
+ */
5330
+
5331
+ // Remove old sizing and apply the calculated column widths
5332
+ // Get the unique column headers in the newly created (cloned) header. We want to apply the
5333
+ // calculated sizes to this header
5334
+ if ( ! scrollX )
5335
+ {
5336
+ divBodyStyle.width = '100%';
5337
+ divHeader[0].style.width = '100%';
5338
+ }
5339
+
5340
+ $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
5341
+ idx = _fnVisibleToColumnIndex( settings, i );
5342
+ el.style.width = settings.aoColumns[idx].sWidth;
5343
+ } );
5344
+
5345
+ if ( footer ) {
5346
+ _fnApplyToChildren( function(n) {
5347
+ n.style.width = "";
5348
+ }, footerSrcEls );
5349
+ }
5350
+
5351
+ // Size the table as a whole
5352
+ sanityWidth = table.outerWidth();
5353
+ if ( scrollX === "" ) {
5354
+ // No x scrolling
5355
+ tableStyle.width = "100%";
5356
+
5357
+ // IE7 will make the width of the table when 100% include the scrollbar
5358
+ // - which is shouldn't. When there is a scrollbar we need to take this
5359
+ // into account.
5360
+ if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
5361
+ divBody.css('overflow-y') == "scroll")
5362
+ ) {
5363
+ tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
5364
+ }
5365
+
5366
+ // Recalculate the sanity width
5367
+ sanityWidth = table.outerWidth();
5368
+ }
5369
+ else if ( scrollXInner !== "" ) {
5370
+ // legacy x scroll inner has been given - use it
5371
+ tableStyle.width = _fnStringToCss(scrollXInner);
5372
+
5373
+ // Recalculate the sanity width
5374
+ sanityWidth = table.outerWidth();
5375
+ }
5376
+
5377
+ // Hidden header should have zero height, so remove padding and borders. Then
5378
+ // set the width based on the real headers
5379
+
5380
+ // Apply all styles in one pass
5381
+ _fnApplyToChildren( zeroOut, headerSrcEls );
5382
+
5383
+ // Read all widths in next pass
5384
+ _fnApplyToChildren( function(nSizer) {
5385
+ headerContent.push( nSizer.innerHTML );
5386
+ headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5387
+ }, headerSrcEls );
5388
+
5389
+ // Apply all widths in final pass
5390
+ _fnApplyToChildren( function(nToSize, i) {
5391
+ // Only apply widths to the DataTables detected header cells - this
5392
+ // prevents complex headers from having contradictory sizes applied
5393
+ if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
5394
+ nToSize.style.width = headerWidths[i];
5395
+ }
5396
+ }, headerTrgEls );
5397
+
5398
+ $(headerSrcEls).height(0);
5399
+
5400
+ /* Same again with the footer if we have one */
5401
+ if ( footer )
5402
+ {
5403
+ _fnApplyToChildren( zeroOut, footerSrcEls );
5404
+
5405
+ _fnApplyToChildren( function(nSizer) {
5406
+ footerContent.push( nSizer.innerHTML );
5407
+ footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5408
+ }, footerSrcEls );
5409
+
5410
+ _fnApplyToChildren( function(nToSize, i) {
5411
+ nToSize.style.width = footerWidths[i];
5412
+ }, footerTrgEls );
5413
+
5414
+ $(footerSrcEls).height(0);
5415
+ }
5416
+
5417
+
5418
+ /*
5419
+ * 3. Apply the measurements
5420
+ */
5421
+
5422
+ // "Hide" the header and footer that we used for the sizing. We need to keep
5423
+ // the content of the cell so that the width applied to the header and body
5424
+ // both match, but we want to hide it completely. We want to also fix their
5425
+ // width to what they currently are
5426
+ _fnApplyToChildren( function(nSizer, i) {
5427
+ nSizer.innerHTML = '<div class="dataTables_sizing">'+headerContent[i]+'</div>';
5428
+ nSizer.childNodes[0].style.height = "0";
5429
+ nSizer.childNodes[0].style.overflow = "hidden";
5430
+ nSizer.style.width = headerWidths[i];
5431
+ }, headerSrcEls );
5432
+
5433
+ if ( footer )
5434
+ {
5435
+ _fnApplyToChildren( function(nSizer, i) {
5436
+ nSizer.innerHTML = '<div class="dataTables_sizing">'+footerContent[i]+'</div>';
5437
+ nSizer.childNodes[0].style.height = "0";
5438
+ nSizer.childNodes[0].style.overflow = "hidden";
5439
+ nSizer.style.width = footerWidths[i];
5440
+ }, footerSrcEls );
5441
+ }
5442
+
5443
+ // Sanity check that the table is of a sensible width. If not then we are going to get
5444
+ // misalignment - try to prevent this by not allowing the table to shrink below its min width
5445
+ if ( table.outerWidth() < sanityWidth )
5446
+ {
5447
+ // The min width depends upon if we have a vertical scrollbar visible or not */
5448
+ correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
5449
+ divBody.css('overflow-y') == "scroll")) ?
5450
+ sanityWidth+barWidth :
5451
+ sanityWidth;
5452
+
5453
+ // IE6/7 are a law unto themselves...
5454
+ if ( ie67 && (divBodyEl.scrollHeight >
5455
+ divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
5456
+ ) {
5457
+ tableStyle.width = _fnStringToCss( correction-barWidth );
5458
+ }
5459
+
5460
+ // And give the user a warning that we've stopped the table getting too small
5461
+ if ( scrollX === "" || scrollXInner !== "" ) {
5462
+ _fnLog( settings, 1, 'Possible column misalignment', 6 );
5463
+ }
5464
+ }
5465
+ else
5466
+ {
5467
+ correction = '100%';
5468
+ }
5469
+
5470
+ // Apply to the container elements
5471
+ divBodyStyle.width = _fnStringToCss( correction );
5472
+ divHeaderStyle.width = _fnStringToCss( correction );
5473
+
5474
+ if ( footer ) {
5475
+ settings.nScrollFoot.style.width = _fnStringToCss( correction );
5476
+ }
5477
+
5478
+
5479
+ /*
5480
+ * 4. Clean up
5481
+ */
5482
+ if ( ! scrollY ) {
5483
+ /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
5484
+ * the scrollbar height from the visible display, rather than adding it on. We need to
5485
+ * set the height in order to sort this. Don't want to do it in any other browsers.
5486
+ */
5487
+ if ( ie67 ) {
5488
+ divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
5489
+ }
5490
+ }
5491
+
5492
+ /* Finally set the width's of the header and footer tables */
5493
+ var iOuterWidth = table.outerWidth();
5494
+ divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
5495
+ divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
5496
+
5497
+ // Figure out if there are scrollbar present - if so then we need a the header and footer to
5498
+ // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5499
+ var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
5500
+ var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
5501
+ divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
5502
+
5503
+ if ( footer ) {
5504
+ divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
5505
+ divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
5506
+ divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
5507
+ }
5508
+
5509
+ // Correct DOM ordering for colgroup - comes before the thead
5510
+ table.children('colgroup').insertBefore( table.children('thead') );
5511
+
5512
+ /* Adjust the position of the header in case we loose the y-scrollbar */
5513
+ divBody.trigger('scroll');
5514
+
5515
+ // If sorting or filtering has occurred, jump the scrolling back to the top
5516
+ // only if we aren't holding the position
5517
+ if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
5518
+ divBodyEl.scrollTop = 0;
5519
+ }
5520
+ }
5521
+
5522
+
5523
+
5524
+ /**
5525
+ * Apply a given function to the display child nodes of an element array (typically
5526
+ * TD children of TR rows
5527
+ * @param {function} fn Method to apply to the objects
5528
+ * @param array {nodes} an1 List of elements to look through for display children
5529
+ * @param array {nodes} an2 Another list (identical structure to the first) - optional
5530
+ * @memberof DataTable#oApi
5531
+ */
5532
+ function _fnApplyToChildren( fn, an1, an2 )
5533
+ {
5534
+ var index=0, i=0, iLen=an1.length;
5535
+ var nNode1, nNode2;
5536
+
5537
+ while ( i < iLen ) {
5538
+ nNode1 = an1[i].firstChild;
5539
+ nNode2 = an2 ? an2[i].firstChild : null;
5540
+
5541
+ while ( nNode1 ) {
5542
+ if ( nNode1.nodeType === 1 ) {
5543
+ if ( an2 ) {
5544
+ fn( nNode1, nNode2, index );
5545
+ }
5546
+ else {
5547
+ fn( nNode1, index );
5548
+ }
5549
+
5550
+ index++;
5551
+ }
5552
+
5553
+ nNode1 = nNode1.nextSibling;
5554
+ nNode2 = an2 ? nNode2.nextSibling : null;
5555
+ }
5556
+
5557
+ i++;
5558
+ }
5559
+ }
5560
+
5561
+
5562
+
5563
+ var __re_html_remove = /<.*?>/g;
5564
+
5565
+
5566
+ /**
5567
+ * Calculate the width of columns for the table
5568
+ * @param {object} oSettings dataTables settings object
5569
+ * @memberof DataTable#oApi
5570
+ */
5571
+ function _fnCalculateColumnWidths ( oSettings )
5572
+ {
5573
+ var
5574
+ table = oSettings.nTable,
5575
+ columns = oSettings.aoColumns,
5576
+ scroll = oSettings.oScroll,
5577
+ scrollY = scroll.sY,
5578
+ scrollX = scroll.sX,
5579
+ scrollXInner = scroll.sXInner,
5580
+ columnCount = columns.length,
5581
+ visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
5582
+ headerCells = $('th', oSettings.nTHead),
5583
+ tableWidthAttr = table.getAttribute('width'), // from DOM element
5584
+ tableContainer = table.parentNode,
5585
+ userInputs = false,
5586
+ i, column, columnIdx, width, outerWidth,
5587
+ browser = oSettings.oBrowser,
5588
+ ie67 = browser.bScrollOversize;
5589
+
5590
+ var styleWidth = table.style.width;
5591
+ if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5592
+ tableWidthAttr = styleWidth;
5593
+ }
5594
+
5595
+ /* Convert any user input sizes into pixel sizes */
5596
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5597
+ column = columns[ visibleColumns[i] ];
5598
+
5599
+ if ( column.sWidth !== null ) {
5600
+ column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
5601
+
5602
+ userInputs = true;
5603
+ }
5604
+ }
5605
+
5606
+ /* If the number of columns in the DOM equals the number that we have to
5607
+ * process in DataTables, then we can use the offsets that are created by
5608
+ * the web- browser. No custom sizes can be set in order for this to happen,
5609
+ * nor scrolling used
5610
+ */
5611
+ if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
5612
+ columnCount == _fnVisbleColumns( oSettings ) &&
5613
+ columnCount == headerCells.length
5614
+ ) {
5615
+ for ( i=0 ; i<columnCount ; i++ ) {
5616
+ var colIdx = _fnVisibleToColumnIndex( oSettings, i );
5617
+
5618
+ if ( colIdx !== null ) {
5619
+ columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5620
+ }
5621
+ }
5622
+ }
5623
+ else
5624
+ {
5625
+ // Otherwise construct a single row, worst case, table with the widest
5626
+ // node in the data, assign any user defined widths, then insert it into
5627
+ // the DOM and allow the browser to do all the hard work of calculating
5628
+ // table widths
5629
+ var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
5630
+ .css( 'visibility', 'hidden' )
5631
+ .removeAttr( 'id' );
5632
+
5633
+ // Clean up the table body
5634
+ tmpTable.find('tbody tr').remove();
5635
+ var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
5636
+
5637
+ // Clone the table header and footer - we can't use the header / footer
5638
+ // from the cloned table, since if scrolling is active, the table's
5639
+ // real header and footer are contained in different table tags
5640
+ tmpTable.find('thead, tfoot').remove();
5641
+ tmpTable
5642
+ .append( $(oSettings.nTHead).clone() )
5643
+ .append( $(oSettings.nTFoot).clone() );
5644
+
5645
+ // Remove any assigned widths from the footer (from scrolling)
5646
+ tmpTable.find('tfoot th, tfoot td').css('width', '');
5647
+
5648
+ // Apply custom sizing to the cloned header
5649
+ headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
5650
+
5651
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5652
+ column = columns[ visibleColumns[i] ];
5653
+
5654
+ headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
5655
+ _fnStringToCss( column.sWidthOrig ) :
5656
+ '';
5657
+
5658
+ // For scrollX we need to force the column width otherwise the
5659
+ // browser will collapse it. If this width is smaller than the
5660
+ // width the column requires, then it will have no effect
5661
+ if ( column.sWidthOrig && scrollX ) {
5662
+ $( headerCells[i] ).append( $('<div/>').css( {
5663
+ width: column.sWidthOrig,
5664
+ margin: 0,
5665
+ padding: 0,
5666
+ border: 0,
5667
+ height: 1
5668
+ } ) );
5669
+ }
5670
+ }
5671
+
5672
+ // Find the widest cell for each column and put it into the table
5673
+ if ( oSettings.aoData.length ) {
5674
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5675
+ columnIdx = visibleColumns[i];
5676
+ column = columns[ columnIdx ];
5677
+
5678
+ $( _fnGetWidestNode( oSettings, columnIdx ) )
5679
+ .clone( false )
5680
+ .append( column.sContentPadding )
5681
+ .appendTo( tr );
5682
+ }
5683
+ }
5684
+
5685
+ // Tidy the temporary table - remove name attributes so there aren't
5686
+ // duplicated in the dom (radio elements for example)
5687
+ $('[name]', tmpTable).removeAttr('name');
5688
+
5689
+ // Table has been built, attach to the document so we can work with it.
5690
+ // A holding element is used, positioned at the top of the container
5691
+ // with minimal height, so it has no effect on if the container scrolls
5692
+ // or not. Otherwise it might trigger scrolling when it actually isn't
5693
+ // needed
5694
+ var holder = $('<div/>').css( scrollX || scrollY ?
5695
+ {
5696
+ position: 'absolute',
5697
+ top: 0,
5698
+ left: 0,
5699
+ height: 1,
5700
+ right: 0,
5701
+ overflow: 'hidden'
5702
+ } :
5703
+ {}
5704
+ )
5705
+ .append( tmpTable )
5706
+ .appendTo( tableContainer );
5707
+
5708
+ // When scrolling (X or Y) we want to set the width of the table as
5709
+ // appropriate. However, when not scrolling leave the table width as it
5710
+ // is. This results in slightly different, but I think correct behaviour
5711
+ if ( scrollX && scrollXInner ) {
5712
+ tmpTable.width( scrollXInner );
5713
+ }
5714
+ else if ( scrollX ) {
5715
+ tmpTable.css( 'width', 'auto' );
5716
+ tmpTable.removeAttr('width');
5717
+
5718
+ // If there is no width attribute or style, then allow the table to
5719
+ // collapse
5720
+ if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
5721
+ tmpTable.width( tableContainer.clientWidth );
5722
+ }
5723
+ }
5724
+ else if ( scrollY ) {
5725
+ tmpTable.width( tableContainer.clientWidth );
5726
+ }
5727
+ else if ( tableWidthAttr ) {
5728
+ tmpTable.width( tableWidthAttr );
5729
+ }
5730
+
5731
+ // Get the width of each column in the constructed table - we need to
5732
+ // know the inner width (so it can be assigned to the other table's
5733
+ // cells) and the outer width so we can calculate the full width of the
5734
+ // table. This is safe since DataTables requires a unique cell for each
5735
+ // column, but if ever a header can span multiple columns, this will
5736
+ // need to be modified.
5737
+ var total = 0;
5738
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
5739
+ var cell = $(headerCells[i]);
5740
+ var border = cell.outerWidth() - cell.width();
5741
+
5742
+ // Use getBounding... where possible (not IE8-) because it can give
5743
+ // sub-pixel accuracy, which we then want to round up!
5744
+ var bounding = browser.bBounding ?
5745
+ Math.ceil( headerCells[i].getBoundingClientRect().width ) :
5746
+ cell.outerWidth();
5747
+
5748
+ // Total is tracked to remove any sub-pixel errors as the outerWidth
5749
+ // of the table might not equal the total given here (IE!).
5750
+ total += bounding;
5751
+
5752
+ // Width for each column to use
5753
+ columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
5754
+ }
5755
+
5756
+ table.style.width = _fnStringToCss( total );
5757
+
5758
+ // Finished with the table - ditch it
5759
+ holder.remove();
5760
+ }
5761
+
5762
+ // If there is a width attr, we want to attach an event listener which
5763
+ // allows the table sizing to automatically adjust when the window is
5764
+ // resized. Use the width attr rather than CSS, since we can't know if the
5765
+ // CSS is a relative value or absolute - DOM read is always px.
5766
+ if ( tableWidthAttr ) {
5767
+ table.style.width = _fnStringToCss( tableWidthAttr );
5768
+ }
5769
+
5770
+ if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
5771
+ var bindResize = function () {
5772
+ $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
5773
+ _fnAdjustColumnSizing( oSettings );
5774
+ } ) );
5775
+ };
5776
+
5777
+ // IE6/7 will crash if we bind a resize event handler on page load.
5778
+ // To be removed in 1.11 which drops IE6/7 support
5779
+ if ( ie67 ) {
5780
+ setTimeout( bindResize, 1000 );
5781
+ }
5782
+ else {
5783
+ bindResize();
5784
+ }
5785
+
5786
+ oSettings._reszEvt = true;
5787
+ }
5788
+ }
5789
+
5790
+
5791
+ /**
5792
+ * Throttle the calls to a function. Arguments and context are maintained for
5793
+ * the throttled function
5794
+ * @param {function} fn Function to be called
5795
+ * @param {int} [freq=200] call frequency in mS
5796
+ * @returns {function} wrapped function
5797
+ * @memberof DataTable#oApi
5798
+ */
5799
+ var _fnThrottle = DataTable.util.throttle;
5800
+
5801
+
5802
+ /**
5803
+ * Convert a CSS unit width to pixels (e.g. 2em)
5804
+ * @param {string} width width to be converted
5805
+ * @param {node} parent parent to get the with for (required for relative widths) - optional
5806
+ * @returns {int} width in pixels
5807
+ * @memberof DataTable#oApi
5808
+ */
5809
+ function _fnConvertToWidth ( width, parent )
5810
+ {
5811
+ if ( ! width ) {
5812
+ return 0;
5813
+ }
5814
+
5815
+ var n = $('<div/>')
5816
+ .css( 'width', _fnStringToCss( width ) )
5817
+ .appendTo( parent || document.body );
5818
+
5819
+ var val = n[0].offsetWidth;
5820
+ n.remove();
5821
+
5822
+ return val;
5823
+ }
5824
+
5825
+
5826
+ /**
5827
+ * Get the widest node
5828
+ * @param {object} settings dataTables settings object
5829
+ * @param {int} colIdx column of interest
5830
+ * @returns {node} widest table node
5831
+ * @memberof DataTable#oApi
5832
+ */
5833
+ function _fnGetWidestNode( settings, colIdx )
5834
+ {
5835
+ var idx = _fnGetMaxLenString( settings, colIdx );
5836
+ if ( idx < 0 ) {
5837
+ return null;
5838
+ }
5839
+
5840
+ var data = settings.aoData[ idx ];
5841
+ return ! data.nTr ? // Might not have been created when deferred rendering
5842
+ $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
5843
+ data.anCells[ colIdx ];
5844
+ }
5845
+
5846
+
5847
+ /**
5848
+ * Get the maximum strlen for each data column
5849
+ * @param {object} settings dataTables settings object
5850
+ * @param {int} colIdx column of interest
5851
+ * @returns {string} max string length for each column
5852
+ * @memberof DataTable#oApi
5853
+ */
5854
+ function _fnGetMaxLenString( settings, colIdx )
5855
+ {
5856
+ var s, max=-1, maxIdx = -1;
5857
+
5858
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
5859
+ s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
5860
+ s = s.replace( __re_html_remove, '' );
5861
+ s = s.replace( /&nbsp;/g, ' ' );
5862
+
5863
+ if ( s.length > max ) {
5864
+ max = s.length;
5865
+ maxIdx = i;
5866
+ }
5867
+ }
5868
+
5869
+ return maxIdx;
5870
+ }
5871
+
5872
+
5873
+ /**
5874
+ * Append a CSS unit (only if required) to a string
5875
+ * @param {string} value to css-ify
5876
+ * @returns {string} value with css unit
5877
+ * @memberof DataTable#oApi
5878
+ */
5879
+ function _fnStringToCss( s )
5880
+ {
5881
+ if ( s === null ) {
5882
+ return '0px';
5883
+ }
5884
+
5885
+ if ( typeof s == 'number' ) {
5886
+ return s < 0 ?
5887
+ '0px' :
5888
+ s+'px';
5889
+ }
5890
+
5891
+ // Check it has a unit character already
5892
+ return s.match(/\d$/) ?
5893
+ s+'px' :
5894
+ s;
5895
+ }
5896
+
5897
+
5898
+
5899
+ function _fnSortFlatten ( settings )
5900
+ {
5901
+ var
5902
+ i, iLen, k, kLen,
5903
+ aSort = [],
5904
+ aiOrig = [],
5905
+ aoColumns = settings.aoColumns,
5906
+ aDataSort, iCol, sType, srcCol,
5907
+ fixed = settings.aaSortingFixed,
5908
+ fixedObj = $.isPlainObject( fixed ),
5909
+ nestedSort = [],
5910
+ add = function ( a ) {
5911
+ if ( a.length && ! Array.isArray( a[0] ) ) {
5912
+ // 1D array
5913
+ nestedSort.push( a );
5914
+ }
5915
+ else {
5916
+ // 2D array
5917
+ $.merge( nestedSort, a );
5918
+ }
5919
+ };
5920
+
5921
+ // Build the sort array, with pre-fix and post-fix options if they have been
5922
+ // specified
5923
+ if ( Array.isArray( fixed ) ) {
5924
+ add( fixed );
5925
+ }
5926
+
5927
+ if ( fixedObj && fixed.pre ) {
5928
+ add( fixed.pre );
5929
+ }
5930
+
5931
+ add( settings.aaSorting );
5932
+
5933
+ if (fixedObj && fixed.post ) {
5934
+ add( fixed.post );
5935
+ }
5936
+
5937
+ for ( i=0 ; i<nestedSort.length ; i++ )
5938
+ {
5939
+ srcCol = nestedSort[i][0];
5940
+ aDataSort = aoColumns[ srcCol ].aDataSort;
5941
+
5942
+ for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
5943
+ {
5944
+ iCol = aDataSort[k];
5945
+ sType = aoColumns[ iCol ].sType || 'string';
5946
+
5947
+ if ( nestedSort[i]._idx === undefined ) {
5948
+ nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
5949
+ }
5950
+
5951
+ aSort.push( {
5952
+ src: srcCol,
5953
+ col: iCol,
5954
+ dir: nestedSort[i][1],
5955
+ index: nestedSort[i]._idx,
5956
+ type: sType,
5957
+ formatter: DataTable.ext.type.order[ sType+"-pre" ]
5958
+ } );
5959
+ }
5960
+ }
5961
+
5962
+ return aSort;
5963
+ }
5964
+
5965
+ /**
5966
+ * Change the order of the table
5967
+ * @param {object} oSettings dataTables settings object
5968
+ * @memberof DataTable#oApi
5969
+ * @todo This really needs split up!
5970
+ */
5971
+ function _fnSort ( oSettings )
5972
+ {
5973
+ var
5974
+ i, ien, iLen, j, jLen, k, kLen,
5975
+ sDataType, nTh,
5976
+ aiOrig = [],
5977
+ oExtSort = DataTable.ext.type.order,
5978
+ aoData = oSettings.aoData,
5979
+ aoColumns = oSettings.aoColumns,
5980
+ aDataSort, data, iCol, sType, oSort,
5981
+ formatters = 0,
5982
+ sortCol,
5983
+ displayMaster = oSettings.aiDisplayMaster,
5984
+ aSort;
5985
+
5986
+ // Resolve any column types that are unknown due to addition or invalidation
5987
+ // @todo Can this be moved into a 'data-ready' handler which is called when
5988
+ // data is going to be used in the table?
5989
+ _fnColumnTypes( oSettings );
5990
+
5991
+ aSort = _fnSortFlatten( oSettings );
5992
+
5993
+ for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
5994
+ sortCol = aSort[i];
5995
+
5996
+ // Track if we can use the fast sort algorithm
5997
+ if ( sortCol.formatter ) {
5998
+ formatters++;
5999
+ }
6000
+
6001
+ // Load the data needed for the sort, for each cell
6002
+ _fnSortData( oSettings, sortCol.col );
6003
+ }
6004
+
6005
+ /* No sorting required if server-side or no sorting array */
6006
+ if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
6007
+ {
6008
+ // Create a value - key array of the current row positions such that we can use their
6009
+ // current position during the sort, if values match, in order to perform stable sorting
6010
+ for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
6011
+ aiOrig[ displayMaster[i] ] = i;
6012
+ }
6013
+
6014
+ /* Do the sort - here we want multi-column sorting based on a given data source (column)
6015
+ * and sorting function (from oSort) in a certain direction. It's reasonably complex to
6016
+ * follow on it's own, but this is what we want (example two column sorting):
6017
+ * fnLocalSorting = function(a,b){
6018
+ * var iTest;
6019
+ * iTest = oSort['string-asc']('data11', 'data12');
6020
+ * if (iTest !== 0)
6021
+ * return iTest;
6022
+ * iTest = oSort['numeric-desc']('data21', 'data22');
6023
+ * if (iTest !== 0)
6024
+ * return iTest;
6025
+ * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
6026
+ * }
6027
+ * Basically we have a test for each sorting column, if the data in that column is equal,
6028
+ * test the next column. If all columns match, then we use a numeric sort on the row
6029
+ * positions in the original data array to provide a stable sort.
6030
+ *
6031
+ * Note - I know it seems excessive to have two sorting methods, but the first is around
6032
+ * 15% faster, so the second is only maintained for backwards compatibility with sorting
6033
+ * methods which do not have a pre-sort formatting function.
6034
+ */
6035
+ if ( formatters === aSort.length ) {
6036
+ // All sort types have formatting functions
6037
+ displayMaster.sort( function ( a, b ) {
6038
+ var
6039
+ x, y, k, test, sort,
6040
+ len=aSort.length,
6041
+ dataA = aoData[a]._aSortData,
6042
+ dataB = aoData[b]._aSortData;
6043
+
6044
+ for ( k=0 ; k<len ; k++ ) {
6045
+ sort = aSort[k];
6046
+
6047
+ x = dataA[ sort.col ];
6048
+ y = dataB[ sort.col ];
6049
+
6050
+ test = x<y ? -1 : x>y ? 1 : 0;
6051
+ if ( test !== 0 ) {
6052
+ return sort.dir === 'asc' ? test : -test;
6053
+ }
6054
+ }
6055
+
6056
+ x = aiOrig[a];
6057
+ y = aiOrig[b];
6058
+ return x<y ? -1 : x>y ? 1 : 0;
6059
+ } );
6060
+ }
6061
+ else {
6062
+ // Depreciated - remove in 1.11 (providing a plug-in option)
6063
+ // Not all sort types have formatting methods, so we have to call their sorting
6064
+ // methods.
6065
+ displayMaster.sort( function ( a, b ) {
6066
+ var
6067
+ x, y, k, l, test, sort, fn,
6068
+ len=aSort.length,
6069
+ dataA = aoData[a]._aSortData,
6070
+ dataB = aoData[b]._aSortData;
6071
+
6072
+ for ( k=0 ; k<len ; k++ ) {
6073
+ sort = aSort[k];
6074
+
6075
+ x = dataA[ sort.col ];
6076
+ y = dataB[ sort.col ];
6077
+
6078
+ fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
6079
+ test = fn( x, y );
6080
+ if ( test !== 0 ) {
6081
+ return test;
6082
+ }
6083
+ }
6084
+
6085
+ x = aiOrig[a];
6086
+ y = aiOrig[b];
6087
+ return x<y ? -1 : x>y ? 1 : 0;
6088
+ } );
6089
+ }
6090
+ }
6091
+
6092
+ /* Tell the draw function that we have sorted the data */
6093
+ oSettings.bSorted = true;
6094
+ }
6095
+
6096
+
6097
+ function _fnSortAria ( settings )
6098
+ {
6099
+ var label;
6100
+ var nextSort;
6101
+ var columns = settings.aoColumns;
6102
+ var aSort = _fnSortFlatten( settings );
6103
+ var oAria = settings.oLanguage.oAria;
6104
+
6105
+ // ARIA attributes - need to loop all columns, to update all (removing old
6106
+ // attributes as needed)
6107
+ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
6108
+ {
6109
+ var col = columns[i];
6110
+ var asSorting = col.asSorting;
6111
+ var sTitle = col.sTitle.replace( /<.*?>/g, "" );
6112
+ var th = col.nTh;
6113
+
6114
+ // IE7 is throwing an error when setting these properties with jQuery's
6115
+ // attr() and removeAttr() methods...
6116
+ th.removeAttribute('aria-sort');
6117
+
6118
+ /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
6119
+ if ( col.bSortable ) {
6120
+ if ( aSort.length > 0 && aSort[0].col == i ) {
6121
+ th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
6122
+ nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
6123
+ }
6124
+ else {
6125
+ nextSort = asSorting[0];
6126
+ }
6127
+
6128
+ label = sTitle + ( nextSort === "asc" ?
6129
+ oAria.sSortAscending :
6130
+ oAria.sSortDescending
6131
+ );
6132
+ }
6133
+ else {
6134
+ label = sTitle;
6135
+ }
6136
+
6137
+ th.setAttribute('aria-label', label);
6138
+ }
6139
+ }
6140
+
6141
+
6142
+ /**
6143
+ * Function to run on user sort request
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 {boolean} [append=false] Append the requested sort to the existing
6148
+ * sort if true (i.e. multi-column sort)
6149
+ * @param {function} [callback] callback function
6150
+ * @memberof DataTable#oApi
6151
+ */
6152
+ function _fnSortListener ( settings, colIdx, append, callback )
6153
+ {
6154
+ var col = settings.aoColumns[ colIdx ];
6155
+ var sorting = settings.aaSorting;
6156
+ var asSorting = col.asSorting;
6157
+ var nextSortIdx;
6158
+ var next = function ( a, overflow ) {
6159
+ var idx = a._idx;
6160
+ if ( idx === undefined ) {
6161
+ idx = $.inArray( a[1], asSorting );
6162
+ }
6163
+
6164
+ return idx+1 < asSorting.length ?
6165
+ idx+1 :
6166
+ overflow ?
6167
+ null :
6168
+ 0;
6169
+ };
6170
+
6171
+ // Convert to 2D array if needed
6172
+ if ( typeof sorting[0] === 'number' ) {
6173
+ sorting = settings.aaSorting = [ sorting ];
6174
+ }
6175
+
6176
+ // If appending the sort then we are multi-column sorting
6177
+ if ( append && settings.oFeatures.bSortMulti ) {
6178
+ // Are we already doing some kind of sort on this column?
6179
+ var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
6180
+
6181
+ if ( sortIdx !== -1 ) {
6182
+ // Yes, modify the sort
6183
+ nextSortIdx = next( sorting[sortIdx], true );
6184
+
6185
+ if ( nextSortIdx === null && sorting.length === 1 ) {
6186
+ nextSortIdx = 0; // can't remove sorting completely
6187
+ }
6188
+
6189
+ if ( nextSortIdx === null ) {
6190
+ sorting.splice( sortIdx, 1 );
6191
+ }
6192
+ else {
6193
+ sorting[sortIdx][1] = asSorting[ nextSortIdx ];
6194
+ sorting[sortIdx]._idx = nextSortIdx;
6195
+ }
6196
+ }
6197
+ else {
6198
+ // No sort on this column yet
6199
+ sorting.push( [ colIdx, asSorting[0], 0 ] );
6200
+ sorting[sorting.length-1]._idx = 0;
6201
+ }
6202
+ }
6203
+ else if ( sorting.length && sorting[0][0] == colIdx ) {
6204
+ // Single column - already sorting on this column, modify the sort
6205
+ nextSortIdx = next( sorting[0] );
6206
+
6207
+ sorting.length = 1;
6208
+ sorting[0][1] = asSorting[ nextSortIdx ];
6209
+ sorting[0]._idx = nextSortIdx;
6210
+ }
6211
+ else {
6212
+ // Single column - sort only on this column
6213
+ sorting.length = 0;
6214
+ sorting.push( [ colIdx, asSorting[0] ] );
6215
+ sorting[0]._idx = 0;
6216
+ }
6217
+
6218
+ // Run the sort by calling a full redraw
6219
+ _fnReDraw( settings );
6220
+
6221
+ // callback used for async user interaction
6222
+ if ( typeof callback == 'function' ) {
6223
+ callback( settings );
6224
+ }
6225
+ }
6226
+
6227
+
6228
+ /**
6229
+ * Attach a sort handler (click) to a node
6230
+ * @param {object} settings dataTables settings object
6231
+ * @param {node} attachTo node to attach the handler to
6232
+ * @param {int} colIdx column sorting index
6233
+ * @param {function} [callback] callback function
6234
+ * @memberof DataTable#oApi
6235
+ */
6236
+ function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
6237
+ {
6238
+ var col = settings.aoColumns[ colIdx ];
6239
+
6240
+ _fnBindAction( attachTo, {}, function (e) {
6241
+ /* If the column is not sortable - don't to anything */
6242
+ if ( col.bSortable === false ) {
6243
+ return;
6244
+ }
6245
+
6246
+ // If processing is enabled use a timeout to allow the processing
6247
+ // display to be shown - otherwise to it synchronously
6248
+ if ( settings.oFeatures.bProcessing ) {
6249
+ _fnProcessingDisplay( settings, true );
6250
+
6251
+ setTimeout( function() {
6252
+ _fnSortListener( settings, colIdx, e.shiftKey, callback );
6253
+
6254
+ // In server-side processing, the draw callback will remove the
6255
+ // processing display
6256
+ if ( _fnDataSource( settings ) !== 'ssp' ) {
6257
+ _fnProcessingDisplay( settings, false );
6258
+ }
6259
+ }, 0 );
6260
+ }
6261
+ else {
6262
+ _fnSortListener( settings, colIdx, e.shiftKey, callback );
6263
+ }
6264
+ } );
6265
+ }
6266
+
6267
+
6268
+ /**
6269
+ * Set the sorting classes on table's body, Note: it is safe to call this function
6270
+ * when bSort and bSortClasses are false
6271
+ * @param {object} oSettings dataTables settings object
6272
+ * @memberof DataTable#oApi
6273
+ */
6274
+ function _fnSortingClasses( settings )
6275
+ {
6276
+ var oldSort = settings.aLastSort;
6277
+ var sortClass = settings.oClasses.sSortColumn;
6278
+ var sort = _fnSortFlatten( settings );
6279
+ var features = settings.oFeatures;
6280
+ var i, ien, colIdx;
6281
+
6282
+ if ( features.bSort && features.bSortClasses ) {
6283
+ // Remove old sorting classes
6284
+ for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
6285
+ colIdx = oldSort[i].src;
6286
+
6287
+ // Remove column sorting
6288
+ $( _pluck( settings.aoData, 'anCells', colIdx ) )
6289
+ .removeClass( sortClass + (i<2 ? i+1 : 3) );
6290
+ }
6291
+
6292
+ // Add new column sorting
6293
+ for ( i=0, ien=sort.length ; i<ien ; i++ ) {
6294
+ colIdx = sort[i].src;
6295
+
6296
+ $( _pluck( settings.aoData, 'anCells', colIdx ) )
6297
+ .addClass( sortClass + (i<2 ? i+1 : 3) );
6298
+ }
6299
+ }
6300
+
6301
+ settings.aLastSort = sort;
6302
+ }
6303
+
6304
+
6305
+ // Get the data to sort a column, be it from cache, fresh (populating the
6306
+ // cache), or from a sort formatter
6307
+ function _fnSortData( settings, idx )
6308
+ {
6309
+ // Custom sorting function - provided by the sort data type
6310
+ var column = settings.aoColumns[ idx ];
6311
+ var customSort = DataTable.ext.order[ column.sSortDataType ];
6312
+ var customData;
6313
+
6314
+ if ( customSort ) {
6315
+ customData = customSort.call( settings.oInstance, settings, idx,
6316
+ _fnColumnIndexToVisible( settings, idx )
6317
+ );
6318
+ }
6319
+
6320
+ // Use / populate cache
6321
+ var row, cellData;
6322
+ var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
6323
+
6324
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
6325
+ row = settings.aoData[i];
6326
+
6327
+ if ( ! row._aSortData ) {
6328
+ row._aSortData = [];
6329
+ }
6330
+
6331
+ if ( ! row._aSortData[idx] || customSort ) {
6332
+ cellData = customSort ?
6333
+ customData[i] : // If there was a custom sort function, use data from there
6334
+ _fnGetCellData( settings, i, idx, 'sort' );
6335
+
6336
+ row._aSortData[ idx ] = formatter ?
6337
+ formatter( cellData ) :
6338
+ cellData;
6339
+ }
6340
+ }
6341
+ }
6342
+
6343
+
6344
+
6345
+ /**
6346
+ * Save the state of a table
6347
+ * @param {object} oSettings dataTables settings object
6348
+ * @memberof DataTable#oApi
6349
+ */
6350
+ function _fnSaveState ( settings )
6351
+ {
6352
+ if ( !settings.oFeatures.bStateSave || settings.bDestroying )
6353
+ {
6354
+ return;
6355
+ }
6356
+
6357
+ /* Store the interesting variables */
6358
+ var state = {
6359
+ time: +new Date(),
6360
+ start: settings._iDisplayStart,
6361
+ length: settings._iDisplayLength,
6362
+ order: $.extend( true, [], settings.aaSorting ),
6363
+ search: _fnSearchToCamel( settings.oPreviousSearch ),
6364
+ columns: $.map( settings.aoColumns, function ( col, i ) {
6365
+ return {
6366
+ visible: col.bVisible,
6367
+ search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
6368
+ };
6369
+ } )
6370
+ };
6371
+
6372
+ _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
6373
+
6374
+ settings.oSavedState = state;
6375
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
6376
+ }
6377
+
6378
+
6379
+ /**
6380
+ * Attempt to load a saved table state
6381
+ * @param {object} oSettings dataTables settings object
6382
+ * @param {object} oInit DataTables init object so we can override settings
6383
+ * @param {function} callback Callback to execute when the state has been loaded
6384
+ * @memberof DataTable#oApi
6385
+ */
6386
+ function _fnLoadState ( settings, oInit, callback )
6387
+ {
6388
+ var i, ien;
6389
+ var columns = settings.aoColumns;
6390
+ var loaded = function ( s ) {
6391
+ if ( ! s || ! s.time ) {
6392
+ callback();
6393
+ return;
6394
+ }
6395
+
6396
+ // Allow custom and plug-in manipulation functions to alter the saved data set and
6397
+ // cancelling of loading by returning false
6398
+ var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
6399
+ if ( $.inArray( false, abStateLoad ) !== -1 ) {
6400
+ callback();
6401
+ return;
6402
+ }
6403
+
6404
+ // Reject old data
6405
+ var duration = settings.iStateDuration;
6406
+ if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
6407
+ callback();
6408
+ return;
6409
+ }
6410
+
6411
+ // Number of columns have changed - all bets are off, no restore of settings
6412
+ if ( s.columns && columns.length !== s.columns.length ) {
6413
+ callback();
6414
+ return;
6415
+ }
6416
+
6417
+ // Store the saved state so it might be accessed at any time
6418
+ settings.oLoadedState = $.extend( true, {}, s );
6419
+
6420
+ // Restore key features - todo - for 1.11 this needs to be done by
6421
+ // subscribed events
6422
+ if ( s.start !== undefined ) {
6423
+ settings._iDisplayStart = s.start;
6424
+ settings.iInitDisplayStart = s.start;
6425
+ }
6426
+ if ( s.length !== undefined ) {
6427
+ settings._iDisplayLength = s.length;
6428
+ }
6429
+
6430
+ // Order
6431
+ if ( s.order !== undefined ) {
6432
+ settings.aaSorting = [];
6433
+ $.each( s.order, function ( i, col ) {
6434
+ settings.aaSorting.push( col[0] >= columns.length ?
6435
+ [ 0, col[1] ] :
6436
+ col
6437
+ );
6438
+ } );
6439
+ }
6440
+
6441
+ // Search
6442
+ if ( s.search !== undefined ) {
6443
+ $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
6444
+ }
6445
+
6446
+ // Columns
6447
+ //
6448
+ if ( s.columns ) {
6449
+ for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6450
+ var col = s.columns[i];
6451
+
6452
+ // Visibility
6453
+ if ( col.visible !== undefined ) {
6454
+ columns[i].bVisible = col.visible;
6455
+ }
6456
+
6457
+ // Search
6458
+ if ( col.search !== undefined ) {
6459
+ $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
6460
+ }
6461
+ }
6462
+ }
6463
+
6464
+ _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
6465
+ callback();
6466
+ };
6467
+
6468
+ if ( ! settings.oFeatures.bStateSave ) {
6469
+ callback();
6470
+ return;
6471
+ }
6472
+
6473
+ var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
6474
+
6475
+ if ( state !== undefined ) {
6476
+ loaded( state );
6477
+ }
6478
+ // otherwise, wait for the loaded callback to be executed
6479
+ }
6480
+
6481
+
6482
+ /**
6483
+ * Return the settings object for a particular table
6484
+ * @param {node} table table we are using as a dataTable
6485
+ * @returns {object} Settings object - or null if not found
6486
+ * @memberof DataTable#oApi
6487
+ */
6488
+ function _fnSettingsFromNode ( table )
6489
+ {
6490
+ var settings = DataTable.settings;
6491
+ var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
6492
+
6493
+ return idx !== -1 ?
6494
+ settings[ idx ] :
6495
+ null;
6496
+ }
6497
+
6498
+
6499
+ /**
6500
+ * Log an error message
6501
+ * @param {object} settings dataTables settings object
6502
+ * @param {int} level log error messages, or display them to the user
6503
+ * @param {string} msg error message
6504
+ * @param {int} tn Technical note id to get more information about the error.
6505
+ * @memberof DataTable#oApi
6506
+ */
6507
+ function _fnLog( settings, level, msg, tn )
6508
+ {
6509
+ msg = 'DataTables warning: '+
6510
+ (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
6511
+
6512
+ if ( tn ) {
6513
+ msg += '. For more information about this error, please see '+
6514
+ 'http://datatables.net/tn/'+tn;
6515
+ }
6516
+
6517
+ if ( ! level ) {
6518
+ // Backwards compatibility pre 1.10
6519
+ var ext = DataTable.ext;
6520
+ var type = ext.sErrMode || ext.errMode;
6521
+
6522
+ if ( settings ) {
6523
+ _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
6524
+ }
6525
+
6526
+ if ( type == 'alert' ) {
6527
+ alert( msg );
6528
+ }
6529
+ else if ( type == 'throw' ) {
6530
+ throw new Error(msg);
6531
+ }
6532
+ else if ( typeof type == 'function' ) {
6533
+ type( settings, tn, msg );
6534
+ }
6535
+ }
6536
+ else if ( window.console && console.log ) {
6537
+ console.log( msg );
6538
+ }
6539
+ }
6540
+
6541
+
6542
+ /**
6543
+ * See if a property is defined on one object, if so assign it to the other object
6544
+ * @param {object} ret target object
6545
+ * @param {object} src source object
6546
+ * @param {string} name property
6547
+ * @param {string} [mappedName] name to map too - optional, name used if not given
6548
+ * @memberof DataTable#oApi
6549
+ */
6550
+ function _fnMap( ret, src, name, mappedName )
6551
+ {
6552
+ if ( Array.isArray( name ) ) {
6553
+ $.each( name, function (i, val) {
6554
+ if ( Array.isArray( val ) ) {
6555
+ _fnMap( ret, src, val[0], val[1] );
6556
+ }
6557
+ else {
6558
+ _fnMap( ret, src, val );
6559
+ }
6560
+ } );
6561
+
6562
+ return;
6563
+ }
6564
+
6565
+ if ( mappedName === undefined ) {
6566
+ mappedName = name;
6567
+ }
6568
+
6569
+ if ( src[name] !== undefined ) {
6570
+ ret[mappedName] = src[name];
6571
+ }
6572
+ }
6573
+
6574
+
6575
+ /**
6576
+ * Extend objects - very similar to jQuery.extend, but deep copy objects, and
6577
+ * shallow copy arrays. The reason we need to do this, is that we don't want to
6578
+ * deep copy array init values (such as aaSorting) since the dev wouldn't be
6579
+ * able to override them, but we do want to deep copy arrays.
6580
+ * @param {object} out Object to extend
6581
+ * @param {object} extender Object from which the properties will be applied to
6582
+ * out
6583
+ * @param {boolean} breakRefs If true, then arrays will be sliced to take an
6584
+ * independent copy with the exception of the `data` or `aaData` parameters
6585
+ * if they are present. This is so you can pass in a collection to
6586
+ * DataTables and have that used as your data source without breaking the
6587
+ * references
6588
+ * @returns {object} out Reference, just for convenience - out === the return.
6589
+ * @memberof DataTable#oApi
6590
+ * @todo This doesn't take account of arrays inside the deep copied objects.
6591
+ */
6592
+ function _fnExtend( out, extender, breakRefs )
6593
+ {
6594
+ var val;
6595
+
6596
+ for ( var prop in extender ) {
6597
+ if ( extender.hasOwnProperty(prop) ) {
6598
+ val = extender[prop];
6599
+
6600
+ if ( $.isPlainObject( val ) ) {
6601
+ if ( ! $.isPlainObject( out[prop] ) ) {
6602
+ out[prop] = {};
6603
+ }
6604
+ $.extend( true, out[prop], val );
6605
+ }
6606
+ else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) {
6607
+ out[prop] = val.slice();
6608
+ }
6609
+ else {
6610
+ out[prop] = val;
6611
+ }
6612
+ }
6613
+ }
6614
+
6615
+ return out;
6616
+ }
6617
+
6618
+
6619
+ /**
6620
+ * Bind an event handers to allow a click or return key to activate the callback.
6621
+ * This is good for accessibility since a return on the keyboard will have the
6622
+ * same effect as a click, if the element has focus.
6623
+ * @param {element} n Element to bind the action to
6624
+ * @param {object} oData Data object to pass to the triggered function
6625
+ * @param {function} fn Callback function for when the event is triggered
6626
+ * @memberof DataTable#oApi
6627
+ */
6628
+ function _fnBindAction( n, oData, fn )
6629
+ {
6630
+ $(n)
6631
+ .on( 'click.DT', oData, function (e) {
6632
+ $(n).trigger('blur'); // Remove focus outline for mouse users
6633
+ fn(e);
6634
+ } )
6635
+ .on( 'keypress.DT', oData, function (e){
6636
+ if ( e.which === 13 ) {
6637
+ e.preventDefault();
6638
+ fn(e);
6639
+ }
6640
+ } )
6641
+ .on( 'selectstart.DT', function () {
6642
+ /* Take the brutal approach to cancelling text selection */
6643
+ return false;
6644
+ } );
6645
+ }
6646
+
6647
+
6648
+ /**
6649
+ * Register a callback function. Easily allows a callback function to be added to
6650
+ * an array store of callback functions that can then all be called together.
6651
+ * @param {object} oSettings dataTables settings object
6652
+ * @param {string} sStore Name of the array storage for the callbacks in oSettings
6653
+ * @param {function} fn Function to be called back
6654
+ * @param {string} sName Identifying name for the callback (i.e. a label)
6655
+ * @memberof DataTable#oApi
6656
+ */
6657
+ function _fnCallbackReg( oSettings, sStore, fn, sName )
6658
+ {
6659
+ if ( fn )
6660
+ {
6661
+ oSettings[sStore].push( {
6662
+ "fn": fn,
6663
+ "sName": sName
6664
+ } );
6665
+ }
6666
+ }
6667
+
6668
+
6669
+ /**
6670
+ * Fire callback functions and trigger events. Note that the loop over the
6671
+ * callback array store is done backwards! Further note that you do not want to
6672
+ * fire off triggers in time sensitive applications (for example cell creation)
6673
+ * as its slow.
6674
+ * @param {object} settings dataTables settings object
6675
+ * @param {string} callbackArr Name of the array storage for the callbacks in
6676
+ * oSettings
6677
+ * @param {string} eventName Name of the jQuery custom event to trigger. If
6678
+ * null no trigger is fired
6679
+ * @param {array} args Array of arguments to pass to the callback function /
6680
+ * trigger
6681
+ * @memberof DataTable#oApi
6682
+ */
6683
+ function _fnCallbackFire( settings, callbackArr, eventName, args )
6684
+ {
6685
+ var ret = [];
6686
+
6687
+ if ( callbackArr ) {
6688
+ ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
6689
+ return val.fn.apply( settings.oInstance, args );
6690
+ } );
6691
+ }
6692
+
6693
+ if ( eventName !== null ) {
6694
+ var e = $.Event( eventName+'.dt' );
6695
+
6696
+ $(settings.nTable).trigger( e, args );
6697
+
6698
+ ret.push( e.result );
6699
+ }
6700
+
6701
+ return ret;
6702
+ }
6703
+
6704
+
6705
+ function _fnLengthOverflow ( settings )
6706
+ {
6707
+ var
6708
+ start = settings._iDisplayStart,
6709
+ end = settings.fnDisplayEnd(),
6710
+ len = settings._iDisplayLength;
6711
+
6712
+ /* If we have space to show extra rows (backing up from the end point - then do so */
6713
+ if ( start >= end )
6714
+ {
6715
+ start = end - len;
6716
+ }
6717
+
6718
+ // Keep the start record on the current page
6719
+ start -= (start % len);
6720
+
6721
+ if ( len === -1 || start < 0 )
6722
+ {
6723
+ start = 0;
6724
+ }
6725
+
6726
+ settings._iDisplayStart = start;
6727
+ }
6728
+
6729
+
6730
+ function _fnRenderer( settings, type )
6731
+ {
6732
+ var renderer = settings.renderer;
6733
+ var host = DataTable.ext.renderer[type];
6734
+
6735
+ if ( $.isPlainObject( renderer ) && renderer[type] ) {
6736
+ // Specific renderer for this type. If available use it, otherwise use
6737
+ // the default.
6738
+ return host[renderer[type]] || host._;
6739
+ }
6740
+ else if ( typeof renderer === 'string' ) {
6741
+ // Common renderer - if there is one available for this type use it,
6742
+ // otherwise use the default
6743
+ return host[renderer] || host._;
6744
+ }
6745
+
6746
+ // Use the default
6747
+ return host._;
6748
+ }
6749
+
6750
+
6751
+ /**
6752
+ * Detect the data source being used for the table. Used to simplify the code
6753
+ * a little (ajax) and to make it compress a little smaller.
6754
+ *
6755
+ * @param {object} settings dataTables settings object
6756
+ * @returns {string} Data source
6757
+ * @memberof DataTable#oApi
6758
+ */
6759
+ function _fnDataSource ( settings )
6760
+ {
6761
+ if ( settings.oFeatures.bServerSide ) {
6762
+ return 'ssp';
6763
+ }
6764
+ else if ( settings.ajax || settings.sAjaxSource ) {
6765
+ return 'ajax';
6766
+ }
6767
+ return 'dom';
6768
+ }
6769
+
6770
+
6771
+
6772
+
6773
+ /**
6774
+ * Computed structure of the DataTables API, defined by the options passed to
6775
+ * `DataTable.Api.register()` when building the API.
6776
+ *
6777
+ * The structure is built in order to speed creation and extension of the Api
6778
+ * objects since the extensions are effectively pre-parsed.
6779
+ *
6780
+ * The array is an array of objects with the following structure, where this
6781
+ * base array represents the Api prototype base:
6782
+ *
6783
+ * [
6784
+ * {
6785
+ * name: 'data' -- string - Property name
6786
+ * val: function () {}, -- function - Api method (or undefined if just an object
6787
+ * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
6788
+ * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
6789
+ * },
6790
+ * {
6791
+ * name: 'row'
6792
+ * val: {},
6793
+ * methodExt: [ ... ],
6794
+ * propExt: [
6795
+ * {
6796
+ * name: 'data'
6797
+ * val: function () {},
6798
+ * methodExt: [ ... ],
6799
+ * propExt: [ ... ]
6800
+ * },
6801
+ * ...
6802
+ * ]
6803
+ * }
6804
+ * ]
6805
+ *
6806
+ * @type {Array}
6807
+ * @ignore
6808
+ */
6809
+ var __apiStruct = [];
6810
+
6811
+
6812
+ /**
6813
+ * `Array.prototype` reference.
6814
+ *
6815
+ * @type object
6816
+ * @ignore
6817
+ */
6818
+ var __arrayProto = Array.prototype;
6819
+
6820
+
6821
+ /**
6822
+ * Abstraction for `context` parameter of the `Api` constructor to allow it to
6823
+ * take several different forms for ease of use.
6824
+ *
6825
+ * Each of the input parameter types will be converted to a DataTables settings
6826
+ * object where possible.
6827
+ *
6828
+ * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
6829
+ * of:
6830
+ *
6831
+ * * `string` - jQuery selector. Any DataTables' matching the given selector
6832
+ * with be found and used.
6833
+ * * `node` - `TABLE` node which has already been formed into a DataTable.
6834
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
6835
+ * * `object` - DataTables settings object
6836
+ * * `DataTables.Api` - API instance
6837
+ * @return {array|null} Matching DataTables settings objects. `null` or
6838
+ * `undefined` is returned if no matching DataTable is found.
6839
+ * @ignore
6840
+ */
6841
+ var _toSettings = function ( mixed )
6842
+ {
6843
+ var idx, jq;
6844
+ var settings = DataTable.settings;
6845
+ var tables = $.map( settings, function (el, i) {
6846
+ return el.nTable;
6847
+ } );
6848
+
6849
+ if ( ! mixed ) {
6850
+ return [];
6851
+ }
6852
+ else if ( mixed.nTable && mixed.oApi ) {
6853
+ // DataTables settings object
6854
+ return [ mixed ];
6855
+ }
6856
+ else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6857
+ // Table node
6858
+ idx = $.inArray( mixed, tables );
6859
+ return idx !== -1 ? [ settings[idx] ] : null;
6860
+ }
6861
+ else if ( mixed && typeof mixed.settings === 'function' ) {
6862
+ return mixed.settings().toArray();
6863
+ }
6864
+ else if ( typeof mixed === 'string' ) {
6865
+ // jQuery selector
6866
+ jq = $(mixed);
6867
+ }
6868
+ else if ( mixed instanceof $ ) {
6869
+ // jQuery object (also DataTables instance)
6870
+ jq = mixed;
6871
+ }
6872
+
6873
+ if ( jq ) {
6874
+ return jq.map( function(i) {
6875
+ idx = $.inArray( this, tables );
6876
+ return idx !== -1 ? settings[idx] : null;
6877
+ } ).toArray();
6878
+ }
6879
+ };
6880
+
6881
+
6882
+ /**
6883
+ * DataTables API class - used to control and interface with one or more
6884
+ * DataTables enhanced tables.
6885
+ *
6886
+ * The API class is heavily based on jQuery, presenting a chainable interface
6887
+ * that you can use to interact with tables. Each instance of the API class has
6888
+ * a "context" - i.e. the tables that it will operate on. This could be a single
6889
+ * table, all tables on a page or a sub-set thereof.
6890
+ *
6891
+ * Additionally the API is designed to allow you to easily work with the data in
6892
+ * the tables, retrieving and manipulating it as required. This is done by
6893
+ * presenting the API class as an array like interface. The contents of the
6894
+ * array depend upon the actions requested by each method (for example
6895
+ * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6896
+ * return an array of objects or arrays depending upon your table's
6897
+ * configuration). The API object has a number of array like methods (`push`,
6898
+ * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
6899
+ * `unique` etc) to assist your working with the data held in a table.
6900
+ *
6901
+ * Most methods (those which return an Api instance) are chainable, which means
6902
+ * the return from a method call also has all of the methods available that the
6903
+ * top level object had. For example, these two calls are equivalent:
6904
+ *
6905
+ * // Not chained
6906
+ * api.row.add( {...} );
6907
+ * api.draw();
6908
+ *
6909
+ * // Chained
6910
+ * api.row.add( {...} ).draw();
6911
+ *
6912
+ * @class DataTable.Api
6913
+ * @param {array|object|string|jQuery} context DataTable identifier. This is
6914
+ * used to define which DataTables enhanced tables this API will operate on.
6915
+ * Can be one of:
6916
+ *
6917
+ * * `string` - jQuery selector. Any DataTables' matching the given selector
6918
+ * with be found and used.
6919
+ * * `node` - `TABLE` node which has already been formed into a DataTable.
6920
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
6921
+ * * `object` - DataTables settings object
6922
+ * @param {array} [data] Data to initialise the Api instance with.
6923
+ *
6924
+ * @example
6925
+ * // Direct initialisation during DataTables construction
6926
+ * var api = $('#example').DataTable();
6927
+ *
6928
+ * @example
6929
+ * // Initialisation using a DataTables jQuery object
6930
+ * var api = $('#example').dataTable().api();
6931
+ *
6932
+ * @example
6933
+ * // Initialisation as a constructor
6934
+ * var api = new $.fn.DataTable.Api( 'table.dataTable' );
6935
+ */
6936
+ _Api = function ( context, data )
6937
+ {
6938
+ if ( ! (this instanceof _Api) ) {
6939
+ return new _Api( context, data );
6940
+ }
6941
+
6942
+ var settings = [];
6943
+ var ctxSettings = function ( o ) {
6944
+ var a = _toSettings( o );
6945
+ if ( a ) {
6946
+ settings.push.apply( settings, a );
6947
+ }
6948
+ };
6949
+
6950
+ if ( Array.isArray( context ) ) {
6951
+ for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6952
+ ctxSettings( context[i] );
6953
+ }
6954
+ }
6955
+ else {
6956
+ ctxSettings( context );
6957
+ }
6958
+
6959
+ // Remove duplicates
6960
+ this.context = _unique( settings );
6961
+
6962
+ // Initial data
6963
+ if ( data ) {
6964
+ $.merge( this, data );
6965
+ }
6966
+
6967
+ // selector
6968
+ this.selector = {
6969
+ rows: null,
6970
+ cols: null,
6971
+ opts: null
6972
+ };
6973
+
6974
+ _Api.extend( this, this, __apiStruct );
6975
+ };
6976
+
6977
+ DataTable.Api = _Api;
6978
+
6979
+ // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6980
+ // isPlainObject.
6981
+ $.extend( _Api.prototype, {
6982
+ any: function ()
6983
+ {
6984
+ return this.count() !== 0;
6985
+ },
6986
+
6987
+
6988
+ concat: __arrayProto.concat,
6989
+
6990
+
6991
+ context: [], // array of table settings objects
6992
+
6993
+
6994
+ count: function ()
6995
+ {
6996
+ return this.flatten().length;
6997
+ },
6998
+
6999
+
7000
+ each: function ( fn )
7001
+ {
7002
+ for ( var i=0, ien=this.length ; i<ien; i++ ) {
7003
+ fn.call( this, this[i], i, this );
7004
+ }
7005
+
7006
+ return this;
7007
+ },
7008
+
7009
+
7010
+ eq: function ( idx )
7011
+ {
7012
+ var ctx = this.context;
7013
+
7014
+ return ctx.length > idx ?
7015
+ new _Api( ctx[idx], this[idx] ) :
7016
+ null;
7017
+ },
7018
+
7019
+
7020
+ filter: function ( fn )
7021
+ {
7022
+ var a = [];
7023
+
7024
+ if ( __arrayProto.filter ) {
7025
+ a = __arrayProto.filter.call( this, fn, this );
7026
+ }
7027
+ else {
7028
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
7029
+ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7030
+ if ( fn.call( this, this[i], i, this ) ) {
7031
+ a.push( this[i] );
7032
+ }
7033
+ }
7034
+ }
7035
+
7036
+ return new _Api( this.context, a );
7037
+ },
7038
+
7039
+
7040
+ flatten: function ()
7041
+ {
7042
+ var a = [];
7043
+ return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
7044
+ },
7045
+
7046
+
7047
+ join: __arrayProto.join,
7048
+
7049
+
7050
+ indexOf: __arrayProto.indexOf || function (obj, start)
7051
+ {
7052
+ for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
7053
+ if ( this[i] === obj ) {
7054
+ return i;
7055
+ }
7056
+ }
7057
+ return -1;
7058
+ },
7059
+
7060
+ iterator: function ( flatten, type, fn, alwaysNew ) {
7061
+ var
7062
+ a = [], ret,
7063
+ i, ien, j, jen,
7064
+ context = this.context,
7065
+ rows, items, item,
7066
+ selector = this.selector;
7067
+
7068
+ // Argument shifting
7069
+ if ( typeof flatten === 'string' ) {
7070
+ alwaysNew = fn;
7071
+ fn = type;
7072
+ type = flatten;
7073
+ flatten = false;
7074
+ }
7075
+
7076
+ for ( i=0, ien=context.length ; i<ien ; i++ ) {
7077
+ var apiInst = new _Api( context[i] );
7078
+
7079
+ if ( type === 'table' ) {
7080
+ ret = fn.call( apiInst, context[i], i );
7081
+
7082
+ if ( ret !== undefined ) {
7083
+ a.push( ret );
7084
+ }
7085
+ }
7086
+ else if ( type === 'columns' || type === 'rows' ) {
7087
+ // this has same length as context - one entry for each table
7088
+ ret = fn.call( apiInst, context[i], this[i], i );
7089
+
7090
+ if ( ret !== undefined ) {
7091
+ a.push( ret );
7092
+ }
7093
+ }
7094
+ else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
7095
+ // columns and rows share the same structure.
7096
+ // 'this' is an array of column indexes for each context
7097
+ items = this[i];
7098
+
7099
+ if ( type === 'column-rows' ) {
7100
+ rows = _selector_row_indexes( context[i], selector.opts );
7101
+ }
7102
+
7103
+ for ( j=0, jen=items.length ; j<jen ; j++ ) {
7104
+ item = items[j];
7105
+
7106
+ if ( type === 'cell' ) {
7107
+ ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
7108
+ }
7109
+ else {
7110
+ ret = fn.call( apiInst, context[i], item, i, j, rows );
7111
+ }
7112
+
7113
+ if ( ret !== undefined ) {
7114
+ a.push( ret );
7115
+ }
7116
+ }
7117
+ }
7118
+ }
7119
+
7120
+ if ( a.length || alwaysNew ) {
7121
+ var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
7122
+ var apiSelector = api.selector;
7123
+ apiSelector.rows = selector.rows;
7124
+ apiSelector.cols = selector.cols;
7125
+ apiSelector.opts = selector.opts;
7126
+ return api;
7127
+ }
7128
+ return this;
7129
+ },
7130
+
7131
+
7132
+ lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
7133
+ {
7134
+ // Bit cheeky...
7135
+ return this.indexOf.apply( this.toArray.reverse(), arguments );
7136
+ },
7137
+
7138
+
7139
+ length: 0,
7140
+
7141
+
7142
+ map: function ( fn )
7143
+ {
7144
+ var a = [];
7145
+
7146
+ if ( __arrayProto.map ) {
7147
+ a = __arrayProto.map.call( this, fn, this );
7148
+ }
7149
+ else {
7150
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
7151
+ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7152
+ a.push( fn.call( this, this[i], i ) );
7153
+ }
7154
+ }
7155
+
7156
+ return new _Api( this.context, a );
7157
+ },
7158
+
7159
+
7160
+ pluck: function ( prop )
7161
+ {
7162
+ return this.map( function ( el ) {
7163
+ return el[ prop ];
7164
+ } );
7165
+ },
7166
+
7167
+ pop: __arrayProto.pop,
7168
+
7169
+
7170
+ push: __arrayProto.push,
7171
+
7172
+
7173
+ // Does not return an API instance
7174
+ reduce: __arrayProto.reduce || function ( fn, init )
7175
+ {
7176
+ return _fnReduce( this, fn, init, 0, this.length, 1 );
7177
+ },
7178
+
7179
+
7180
+ reduceRight: __arrayProto.reduceRight || function ( fn, init )
7181
+ {
7182
+ return _fnReduce( this, fn, init, this.length-1, -1, -1 );
7183
+ },
7184
+
7185
+
7186
+ reverse: __arrayProto.reverse,
7187
+
7188
+
7189
+ // Object with rows, columns and opts
7190
+ selector: null,
7191
+
7192
+
7193
+ shift: __arrayProto.shift,
7194
+
7195
+
7196
+ slice: function () {
7197
+ return new _Api( this.context, this );
7198
+ },
7199
+
7200
+
7201
+ sort: __arrayProto.sort, // ? name - order?
7202
+
7203
+
7204
+ splice: __arrayProto.splice,
7205
+
7206
+
7207
+ toArray: function ()
7208
+ {
7209
+ return __arrayProto.slice.call( this );
7210
+ },
7211
+
7212
+
7213
+ to$: function ()
7214
+ {
7215
+ return $( this );
7216
+ },
7217
+
7218
+
7219
+ toJQuery: function ()
7220
+ {
7221
+ return $( this );
7222
+ },
7223
+
7224
+
7225
+ unique: function ()
7226
+ {
7227
+ return new _Api( this.context, _unique(this) );
7228
+ },
7229
+
7230
+
7231
+ unshift: __arrayProto.unshift
7232
+ } );
7233
+
7234
+
7235
+ _Api.extend = function ( scope, obj, ext )
7236
+ {
7237
+ // Only extend API instances and static properties of the API
7238
+ if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7239
+ return;
7240
+ }
7241
+
7242
+ var
7243
+ i, ien,
7244
+ struct,
7245
+ methodScoping = function ( scope, fn, struc ) {
7246
+ return function () {
7247
+ var ret = fn.apply( scope, arguments );
7248
+
7249
+ // Method extension
7250
+ _Api.extend( ret, ret, struc.methodExt );
7251
+ return ret;
7252
+ };
7253
+ };
7254
+
7255
+ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7256
+ struct = ext[i];
7257
+
7258
+ // Value
7259
+ obj[ struct.name ] = struct.type === 'function' ?
7260
+ methodScoping( scope, struct.val, struct ) :
7261
+ struct.type === 'object' ?
7262
+ {} :
7263
+ struct.val;
7264
+
7265
+ obj[ struct.name ].__dt_wrapper = true;
7266
+
7267
+ // Property extension
7268
+ _Api.extend( scope, obj[ struct.name ], struct.propExt );
7269
+ }
7270
+ };
7271
+
7272
+
7273
+ // @todo - Is there need for an augment function?
7274
+ // _Api.augment = function ( inst, name )
7275
+ // {
7276
+ // // Find src object in the structure from the name
7277
+ // var parts = name.split('.');
7278
+
7279
+ // _Api.extend( inst, obj );
7280
+ // };
7281
+
7282
+
7283
+ // [
7284
+ // {
7285
+ // name: 'data' -- string - Property name
7286
+ // val: function () {}, -- function - Api method (or undefined if just an object
7287
+ // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
7288
+ // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
7289
+ // },
7290
+ // {
7291
+ // name: 'row'
7292
+ // val: {},
7293
+ // methodExt: [ ... ],
7294
+ // propExt: [
7295
+ // {
7296
+ // name: 'data'
7297
+ // val: function () {},
7298
+ // methodExt: [ ... ],
7299
+ // propExt: [ ... ]
7300
+ // },
7301
+ // ...
7302
+ // ]
7303
+ // }
7304
+ // ]
7305
+
7306
+ _Api.register = _api_register = function ( name, val )
7307
+ {
7308
+ if ( Array.isArray( name ) ) {
7309
+ for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7310
+ _Api.register( name[j], val );
7311
+ }
7312
+ return;
7313
+ }
7314
+
7315
+ var
7316
+ i, ien,
7317
+ heir = name.split('.'),
7318
+ struct = __apiStruct,
7319
+ key, method;
7320
+
7321
+ var find = function ( src, name ) {
7322
+ for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7323
+ if ( src[i].name === name ) {
7324
+ return src[i];
7325
+ }
7326
+ }
7327
+ return null;
7328
+ };
7329
+
7330
+ for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7331
+ method = heir[i].indexOf('()') !== -1;
7332
+ key = method ?
7333
+ heir[i].replace('()', '') :
7334
+ heir[i];
7335
+
7336
+ var src = find( struct, key );
7337
+ if ( ! src ) {
7338
+ src = {
7339
+ name: key,
7340
+ val: {},
7341
+ methodExt: [],
7342
+ propExt: [],
7343
+ type: 'object'
7344
+ };
7345
+ struct.push( src );
7346
+ }
7347
+
7348
+ if ( i === ien-1 ) {
7349
+ src.val = val;
7350
+ src.type = typeof val === 'function' ?
7351
+ 'function' :
7352
+ $.isPlainObject( val ) ?
7353
+ 'object' :
7354
+ 'other';
7355
+ }
7356
+ else {
7357
+ struct = method ?
7358
+ src.methodExt :
7359
+ src.propExt;
7360
+ }
7361
+ }
7362
+ };
7363
+
7364
+ _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7365
+ _Api.register( pluralName, val );
7366
+
7367
+ _Api.register( singularName, function () {
7368
+ var ret = val.apply( this, arguments );
7369
+
7370
+ if ( ret === this ) {
7371
+ // Returned item is the API instance that was passed in, return it
7372
+ return this;
7373
+ }
7374
+ else if ( ret instanceof _Api ) {
7375
+ // New API instance returned, want the value from the first item
7376
+ // in the returned array for the singular result.
7377
+ return ret.length ?
7378
+ Array.isArray( ret[0] ) ?
7379
+ new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7380
+ ret[0] :
7381
+ undefined;
7382
+ }
7383
+
7384
+ // Non-API return - just fire it back
7385
+ return ret;
7386
+ } );
7387
+ };
7388
+
7389
+
7390
+ /**
7391
+ * Selector for HTML tables. Apply the given selector to the give array of
7392
+ * DataTables settings objects.
7393
+ *
7394
+ * @param {string|integer} [selector] jQuery selector string or integer
7395
+ * @param {array} Array of DataTables settings objects to be filtered
7396
+ * @return {array}
7397
+ * @ignore
7398
+ */
7399
+ var __table_selector = function ( selector, a )
7400
+ {
7401
+ if ( Array.isArray(selector) ) {
7402
+ return $.map( selector, function (item) {
7403
+ return __table_selector(item, a);
7404
+ } );
7405
+ }
7406
+
7407
+ // Integer is used to pick out a table by index
7408
+ if ( typeof selector === 'number' ) {
7409
+ return [ a[ selector ] ];
7410
+ }
7411
+
7412
+ // Perform a jQuery selector on the table nodes
7413
+ var nodes = $.map( a, function (el, i) {
7414
+ return el.nTable;
7415
+ } );
7416
+
7417
+ return $(nodes)
7418
+ .filter( selector )
7419
+ .map( function (i) {
7420
+ // Need to translate back from the table node to the settings
7421
+ var idx = $.inArray( this, nodes );
7422
+ return a[ idx ];
7423
+ } )
7424
+ .toArray();
7425
+ };
7426
+
7427
+
7428
+
7429
+ /**
7430
+ * Context selector for the API's context (i.e. the tables the API instance
7431
+ * refers to.
7432
+ *
7433
+ * @name DataTable.Api#tables
7434
+ * @param {string|integer} [selector] Selector to pick which tables the iterator
7435
+ * should operate on. If not given, all tables in the current context are
7436
+ * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
7437
+ * select multiple tables or as an integer to select a single table.
7438
+ * @returns {DataTable.Api} Returns a new API instance if a selector is given.
7439
+ */
7440
+ _api_register( 'tables()', function ( selector ) {
7441
+ // A new instance is created if there was a selector specified
7442
+ return selector !== undefined && selector !== null ?
7443
+ new _Api( __table_selector( selector, this.context ) ) :
7444
+ this;
7445
+ } );
7446
+
7447
+
7448
+ _api_register( 'table()', function ( selector ) {
7449
+ var tables = this.tables( selector );
7450
+ var ctx = tables.context;
7451
+
7452
+ // Truncate to the first matched table
7453
+ return ctx.length ?
7454
+ new _Api( ctx[0] ) :
7455
+ tables;
7456
+ } );
7457
+
7458
+
7459
+ _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7460
+ return this.iterator( 'table', function ( ctx ) {
7461
+ return ctx.nTable;
7462
+ }, 1 );
7463
+ } );
7464
+
7465
+
7466
+ _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7467
+ return this.iterator( 'table', function ( ctx ) {
7468
+ return ctx.nTBody;
7469
+ }, 1 );
7470
+ } );
7471
+
7472
+
7473
+ _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7474
+ return this.iterator( 'table', function ( ctx ) {
7475
+ return ctx.nTHead;
7476
+ }, 1 );
7477
+ } );
7478
+
7479
+
7480
+ _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7481
+ return this.iterator( 'table', function ( ctx ) {
7482
+ return ctx.nTFoot;
7483
+ }, 1 );
7484
+ } );
7485
+
7486
+
7487
+ _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7488
+ return this.iterator( 'table', function ( ctx ) {
7489
+ return ctx.nTableWrapper;
7490
+ }, 1 );
7491
+ } );
7492
+
7493
+
7494
+
7495
+ /**
7496
+ * Redraw the tables in the current context.
7497
+ */
7498
+ _api_register( 'draw()', function ( paging ) {
7499
+ return this.iterator( 'table', function ( settings ) {
7500
+ if ( paging === 'page' ) {
7501
+ _fnDraw( settings );
7502
+ }
7503
+ else {
7504
+ if ( typeof paging === 'string' ) {
7505
+ paging = paging === 'full-hold' ?
7506
+ false :
7507
+ true;
7508
+ }
7509
+
7510
+ _fnReDraw( settings, paging===false );
7511
+ }
7512
+ } );
7513
+ } );
7514
+
7515
+
7516
+
7517
+ /**
7518
+ * Get the current page index.
7519
+ *
7520
+ * @return {integer} Current page index (zero based)
7521
+ *//**
7522
+ * Set the current page.
7523
+ *
7524
+ * Note that if you attempt to show a page which does not exist, DataTables will
7525
+ * not throw an error, but rather reset the paging.
7526
+ *
7527
+ * @param {integer|string} action The paging action to take. This can be one of:
7528
+ * * `integer` - The page index to jump to
7529
+ * * `string` - An action to take:
7530
+ * * `first` - Jump to first page.
7531
+ * * `next` - Jump to the next page
7532
+ * * `previous` - Jump to previous page
7533
+ * * `last` - Jump to the last page.
7534
+ * @returns {DataTables.Api} this
7535
+ */
7536
+ _api_register( 'page()', function ( action ) {
7537
+ if ( action === undefined ) {
7538
+ return this.page.info().page; // not an expensive call
7539
+ }
7540
+
7541
+ // else, have an action to take on all tables
7542
+ return this.iterator( 'table', function ( settings ) {
7543
+ _fnPageChange( settings, action );
7544
+ } );
7545
+ } );
7546
+
7547
+
7548
+ /**
7549
+ * Paging information for the first table in the current context.
7550
+ *
7551
+ * If you require paging information for another table, use the `table()` method
7552
+ * with a suitable selector.
7553
+ *
7554
+ * @return {object} Object with the following properties set:
7555
+ * * `page` - Current page index (zero based - i.e. the first page is `0`)
7556
+ * * `pages` - Total number of pages
7557
+ * * `start` - Display index for the first record shown on the current page
7558
+ * * `end` - Display index for the last record shown on the current page
7559
+ * * `length` - Display length (number of records). Note that generally `start
7560
+ * + length = end`, but this is not always true, for example if there are
7561
+ * only 2 records to show on the final page, with a length of 10.
7562
+ * * `recordsTotal` - Full data set length
7563
+ * * `recordsDisplay` - Data set length once the current filtering criterion
7564
+ * are applied.
7565
+ */
7566
+ _api_register( 'page.info()', function ( action ) {
7567
+ if ( this.context.length === 0 ) {
7568
+ return undefined;
7569
+ }
7570
+
7571
+ var
7572
+ settings = this.context[0],
7573
+ start = settings._iDisplayStart,
7574
+ len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7575
+ visRecords = settings.fnRecordsDisplay(),
7576
+ all = len === -1;
7577
+
7578
+ return {
7579
+ "page": all ? 0 : Math.floor( start / len ),
7580
+ "pages": all ? 1 : Math.ceil( visRecords / len ),
7581
+ "start": start,
7582
+ "end": settings.fnDisplayEnd(),
7583
+ "length": len,
7584
+ "recordsTotal": settings.fnRecordsTotal(),
7585
+ "recordsDisplay": visRecords,
7586
+ "serverSide": _fnDataSource( settings ) === 'ssp'
7587
+ };
7588
+ } );
7589
+
7590
+
7591
+ /**
7592
+ * Get the current page length.
7593
+ *
7594
+ * @return {integer} Current page length. Note `-1` indicates that all records
7595
+ * are to be shown.
7596
+ *//**
7597
+ * Set the current page length.
7598
+ *
7599
+ * @param {integer} Page length to set. Use `-1` to show all records.
7600
+ * @returns {DataTables.Api} this
7601
+ */
7602
+ _api_register( 'page.len()', function ( len ) {
7603
+ // Note that we can't call this function 'length()' because `length`
7604
+ // is a Javascript property of functions which defines how many arguments
7605
+ // the function expects.
7606
+ if ( len === undefined ) {
7607
+ return this.context.length !== 0 ?
7608
+ this.context[0]._iDisplayLength :
7609
+ undefined;
7610
+ }
7611
+
7612
+ // else, set the page length
7613
+ return this.iterator( 'table', function ( settings ) {
7614
+ _fnLengthChange( settings, len );
7615
+ } );
7616
+ } );
7617
+
7618
+
7619
+
7620
+ var __reload = function ( settings, holdPosition, callback ) {
7621
+ // Use the draw event to trigger a callback
7622
+ if ( callback ) {
7623
+ var api = new _Api( settings );
7624
+
7625
+ api.one( 'draw', function () {
7626
+ callback( api.ajax.json() );
7627
+ } );
7628
+ }
7629
+
7630
+ if ( _fnDataSource( settings ) == 'ssp' ) {
7631
+ _fnReDraw( settings, holdPosition );
7632
+ }
7633
+ else {
7634
+ _fnProcessingDisplay( settings, true );
7635
+
7636
+ // Cancel an existing request
7637
+ var xhr = settings.jqXHR;
7638
+ if ( xhr && xhr.readyState !== 4 ) {
7639
+ xhr.abort();
7640
+ }
7641
+
7642
+ // Trigger xhr
7643
+ _fnBuildAjax( settings, [], function( json ) {
7644
+ _fnClearTable( settings );
7645
+
7646
+ var data = _fnAjaxDataSrc( settings, json );
7647
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7648
+ _fnAddData( settings, data[i] );
7649
+ }
7650
+
7651
+ _fnReDraw( settings, holdPosition );
7652
+ _fnProcessingDisplay( settings, false );
7653
+ } );
7654
+ }
7655
+ };
7656
+
7657
+
7658
+ /**
7659
+ * Get the JSON response from the last Ajax request that DataTables made to the
7660
+ * server. Note that this returns the JSON from the first table in the current
7661
+ * context.
7662
+ *
7663
+ * @return {object} JSON received from the server.
7664
+ */
7665
+ _api_register( 'ajax.json()', function () {
7666
+ var ctx = this.context;
7667
+
7668
+ if ( ctx.length > 0 ) {
7669
+ return ctx[0].json;
7670
+ }
7671
+
7672
+ // else return undefined;
7673
+ } );
7674
+
7675
+
7676
+ /**
7677
+ * Get the data submitted in the last Ajax request
7678
+ */
7679
+ _api_register( 'ajax.params()', function () {
7680
+ var ctx = this.context;
7681
+
7682
+ if ( ctx.length > 0 ) {
7683
+ return ctx[0].oAjaxData;
7684
+ }
7685
+
7686
+ // else return undefined;
7687
+ } );
7688
+
7689
+
7690
+ /**
7691
+ * Reload tables from the Ajax data source. Note that this function will
7692
+ * automatically re-draw the table when the remote data has been loaded.
7693
+ *
7694
+ * @param {boolean} [reset=true] Reset (default) or hold the current paging
7695
+ * position. A full re-sort and re-filter is performed when this method is
7696
+ * called, which is why the pagination reset is the default action.
7697
+ * @returns {DataTables.Api} this
7698
+ */
7699
+ _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7700
+ return this.iterator( 'table', function (settings) {
7701
+ __reload( settings, resetPaging===false, callback );
7702
+ } );
7703
+ } );
7704
+
7705
+
7706
+ /**
7707
+ * Get the current Ajax URL. Note that this returns the URL from the first
7708
+ * table in the current context.
7709
+ *
7710
+ * @return {string} Current Ajax source URL
7711
+ *//**
7712
+ * Set the Ajax URL. Note that this will set the URL for all tables in the
7713
+ * current context.
7714
+ *
7715
+ * @param {string} url URL to set.
7716
+ * @returns {DataTables.Api} this
7717
+ */
7718
+ _api_register( 'ajax.url()', function ( url ) {
7719
+ var ctx = this.context;
7720
+
7721
+ if ( url === undefined ) {
7722
+ // get
7723
+ if ( ctx.length === 0 ) {
7724
+ return undefined;
7725
+ }
7726
+ ctx = ctx[0];
7727
+
7728
+ return ctx.ajax ?
7729
+ $.isPlainObject( ctx.ajax ) ?
7730
+ ctx.ajax.url :
7731
+ ctx.ajax :
7732
+ ctx.sAjaxSource;
7733
+ }
7734
+
7735
+ // set
7736
+ return this.iterator( 'table', function ( settings ) {
7737
+ if ( $.isPlainObject( settings.ajax ) ) {
7738
+ settings.ajax.url = url;
7739
+ }
7740
+ else {
7741
+ settings.ajax = url;
7742
+ }
7743
+ // No need to consider sAjaxSource here since DataTables gives priority
7744
+ // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
7745
+ // value of `sAjaxSource` redundant.
7746
+ } );
7747
+ } );
7748
+
7749
+
7750
+ /**
7751
+ * Load data from the newly set Ajax URL. Note that this method is only
7752
+ * available when `ajax.url()` is used to set a URL. Additionally, this method
7753
+ * has the same effect as calling `ajax.reload()` but is provided for
7754
+ * convenience when setting a new URL. Like `ajax.reload()` it will
7755
+ * automatically redraw the table once the remote data has been loaded.
7756
+ *
7757
+ * @returns {DataTables.Api} this
7758
+ */
7759
+ _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7760
+ // Same as a reload, but makes sense to present it for easy access after a
7761
+ // url change
7762
+ return this.iterator( 'table', function ( ctx ) {
7763
+ __reload( ctx, resetPaging===false, callback );
7764
+ } );
7765
+ } );
7766
+
7767
+
7768
+
7769
+
7770
+ var _selector_run = function ( type, selector, selectFn, settings, opts )
7771
+ {
7772
+ var
7773
+ out = [], res,
7774
+ a, i, ien, j, jen,
7775
+ selectorType = typeof selector;
7776
+
7777
+ // Can't just check for isArray here, as an API or jQuery instance might be
7778
+ // given with their array like look
7779
+ if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7780
+ selector = [ selector ];
7781
+ }
7782
+
7783
+ for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7784
+ // Only split on simple strings - complex expressions will be jQuery selectors
7785
+ a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
7786
+ selector[i].split(',') :
7787
+ [ selector[i] ];
7788
+
7789
+ for ( j=0, jen=a.length ; j<jen ; j++ ) {
7790
+ res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );
7791
+
7792
+ if ( res && res.length ) {
7793
+ out = out.concat( res );
7794
+ }
7795
+ }
7796
+ }
7797
+
7798
+ // selector extensions
7799
+ var ext = _ext.selector[ type ];
7800
+ if ( ext.length ) {
7801
+ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7802
+ out = ext[i]( settings, opts, out );
7803
+ }
7804
+ }
7805
+
7806
+ return _unique( out );
7807
+ };
7808
+
7809
+
7810
+ var _selector_opts = function ( opts )
7811
+ {
7812
+ if ( ! opts ) {
7813
+ opts = {};
7814
+ }
7815
+
7816
+ // Backwards compatibility for 1.9- which used the terminology filter rather
7817
+ // than search
7818
+ if ( opts.filter && opts.search === undefined ) {
7819
+ opts.search = opts.filter;
7820
+ }
7821
+
7822
+ return $.extend( {
7823
+ search: 'none',
7824
+ order: 'current',
7825
+ page: 'all'
7826
+ }, opts );
7827
+ };
7828
+
7829
+
7830
+ var _selector_first = function ( inst )
7831
+ {
7832
+ // Reduce the API instance to the first item found
7833
+ for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
7834
+ if ( inst[i].length > 0 ) {
7835
+ // Assign the first element to the first item in the instance
7836
+ // and truncate the instance and context
7837
+ inst[0] = inst[i];
7838
+ inst[0].length = 1;
7839
+ inst.length = 1;
7840
+ inst.context = [ inst.context[i] ];
7841
+
7842
+ return inst;
7843
+ }
7844
+ }
7845
+
7846
+ // Not found - return an empty instance
7847
+ inst.length = 0;
7848
+ return inst;
7849
+ };
7850
+
7851
+
7852
+ var _selector_row_indexes = function ( settings, opts )
7853
+ {
7854
+ var
7855
+ i, ien, tmp, a=[],
7856
+ displayFiltered = settings.aiDisplay,
7857
+ displayMaster = settings.aiDisplayMaster;
7858
+
7859
+ var
7860
+ search = opts.search, // none, applied, removed
7861
+ order = opts.order, // applied, current, index (original - compatibility with 1.9)
7862
+ page = opts.page; // all, current
7863
+
7864
+ if ( _fnDataSource( settings ) == 'ssp' ) {
7865
+ // In server-side processing mode, most options are irrelevant since
7866
+ // rows not shown don't exist and the index order is the applied order
7867
+ // Removed is a special case - for consistency just return an empty
7868
+ // array
7869
+ return search === 'removed' ?
7870
+ [] :
7871
+ _range( 0, displayMaster.length );
7872
+ }
7873
+ else if ( page == 'current' ) {
7874
+ // Current page implies that order=current and fitler=applied, since it is
7875
+ // fairly senseless otherwise, regardless of what order and search actually
7876
+ // are
7877
+ for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
7878
+ a.push( displayFiltered[i] );
7879
+ }
7880
+ }
7881
+ else if ( order == 'current' || order == 'applied' ) {
7882
+ if ( search == 'none') {
7883
+ a = displayMaster.slice();
7884
+ }
7885
+ else if ( search == 'applied' ) {
7886
+ a = displayFiltered.slice();
7887
+ }
7888
+ else if ( search == 'removed' ) {
7889
+ // O(n+m) solution by creating a hash map
7890
+ var displayFilteredMap = {};
7891
+
7892
+ for ( var i=0, ien=displayFiltered.length ; i<ien ; i++ ) {
7893
+ displayFilteredMap[displayFiltered[i]] = null;
7894
+ }
7895
+
7896
+ a = $.map( displayMaster, function (el) {
7897
+ return ! displayFilteredMap.hasOwnProperty(el) ?
7898
+ el :
7899
+ null;
7900
+ } );
7901
+ }
7902
+ }
7903
+ else if ( order == 'index' || order == 'original' ) {
7904
+ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7905
+ if ( search == 'none' ) {
7906
+ a.push( i );
7907
+ }
7908
+ else { // applied | removed
7909
+ tmp = $.inArray( i, displayFiltered );
7910
+
7911
+ if ((tmp === -1 && search == 'removed') ||
7912
+ (tmp >= 0 && search == 'applied') )
7913
+ {
7914
+ a.push( i );
7915
+ }
7916
+ }
7917
+ }
7918
+ }
7919
+
7920
+ return a;
7921
+ };
7922
+
7923
+
7924
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7925
+ * Rows
7926
+ *
7927
+ * {} - no selector - use all available rows
7928
+ * {integer} - row aoData index
7929
+ * {node} - TR node
7930
+ * {string} - jQuery selector to apply to the TR elements
7931
+ * {array} - jQuery array of nodes, or simply an array of TR nodes
7932
+ *
7933
+ */
7934
+ var __row_selector = function ( settings, selector, opts )
7935
+ {
7936
+ var rows;
7937
+ var run = function ( sel ) {
7938
+ var selInt = _intVal( sel );
7939
+ var i, ien;
7940
+ var aoData = settings.aoData;
7941
+
7942
+ // Short cut - selector is a number and no options provided (default is
7943
+ // all records, so no need to check if the index is in there, since it
7944
+ // must be - dev error if the index doesn't exist).
7945
+ if ( selInt !== null && ! opts ) {
7946
+ return [ selInt ];
7947
+ }
7948
+
7949
+ if ( ! rows ) {
7950
+ rows = _selector_row_indexes( settings, opts );
7951
+ }
7952
+
7953
+ if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7954
+ // Selector - integer
7955
+ return [ selInt ];
7956
+ }
7957
+ else if ( sel === null || sel === undefined || sel === '' ) {
7958
+ // Selector - none
7959
+ return rows;
7960
+ }
7961
+
7962
+ // Selector - function
7963
+ if ( typeof sel === 'function' ) {
7964
+ return $.map( rows, function (idx) {
7965
+ var row = aoData[ idx ];
7966
+ return sel( idx, row._aData, row.nTr ) ? idx : null;
7967
+ } );
7968
+ }
7969
+
7970
+ // Selector - node
7971
+ if ( sel.nodeName ) {
7972
+ var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup
7973
+ var cellIdx = sel._DT_CellIndex;
7974
+
7975
+ if ( rowIdx !== undefined ) {
7976
+ // Make sure that the row is actually still present in the table
7977
+ return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?
7978
+ [ rowIdx ] :
7979
+ [];
7980
+ }
7981
+ else if ( cellIdx ) {
7982
+ return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ?
7983
+ [ cellIdx.row ] :
7984
+ [];
7985
+ }
7986
+ else {
7987
+ var host = $(sel).closest('*[data-dt-row]');
7988
+ return host.length ?
7989
+ [ host.data('dt-row') ] :
7990
+ [];
7991
+ }
7992
+ }
7993
+
7994
+ // ID selector. Want to always be able to select rows by id, regardless
7995
+ // of if the tr element has been created or not, so can't rely upon
7996
+ // jQuery here - hence a custom implementation. This does not match
7997
+ // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
7998
+ // but to select it using a CSS selector engine (like Sizzle or
7999
+ // querySelect) it would need to need to be escaped for some characters.
8000
+ // DataTables simplifies this for row selectors since you can select
8001
+ // only a row. A # indicates an id any anything that follows is the id -
8002
+ // unescaped.
8003
+ if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
8004
+ // get row index from id
8005
+ var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
8006
+ if ( rowObj !== undefined ) {
8007
+ return [ rowObj.idx ];
8008
+ }
8009
+
8010
+ // need to fall through to jQuery in case there is DOM id that
8011
+ // matches
8012
+ }
8013
+
8014
+ // Get nodes in the order from the `rows` array with null values removed
8015
+ var nodes = _removeEmpty(
8016
+ _pluck_order( settings.aoData, rows, 'nTr' )
8017
+ );
8018
+
8019
+ // Selector - jQuery selector string, array of nodes or jQuery object/
8020
+ // As jQuery's .filter() allows jQuery objects to be passed in filter,
8021
+ // it also allows arrays, so this will cope with all three options
8022
+ return $(nodes)
8023
+ .filter( sel )
8024
+ .map( function () {
8025
+ return this._DT_RowIndex;
8026
+ } )
8027
+ .toArray();
8028
+ };
8029
+
8030
+ return _selector_run( 'row', selector, run, settings, opts );
8031
+ };
8032
+
8033
+
8034
+ _api_register( 'rows()', function ( selector, opts ) {
8035
+ // argument shifting
8036
+ if ( selector === undefined ) {
8037
+ selector = '';
8038
+ }
8039
+ else if ( $.isPlainObject( selector ) ) {
8040
+ opts = selector;
8041
+ selector = '';
8042
+ }
8043
+
8044
+ opts = _selector_opts( opts );
8045
+
8046
+ var inst = this.iterator( 'table', function ( settings ) {
8047
+ return __row_selector( settings, selector, opts );
8048
+ }, 1 );
8049
+
8050
+ // Want argument shifting here and in __row_selector?
8051
+ inst.selector.rows = selector;
8052
+ inst.selector.opts = opts;
8053
+
8054
+ return inst;
8055
+ } );
8056
+
8057
+ _api_register( 'rows().nodes()', function () {
8058
+ return this.iterator( 'row', function ( settings, row ) {
8059
+ return settings.aoData[ row ].nTr || undefined;
8060
+ }, 1 );
8061
+ } );
8062
+
8063
+ _api_register( 'rows().data()', function () {
8064
+ return this.iterator( true, 'rows', function ( settings, rows ) {
8065
+ return _pluck_order( settings.aoData, rows, '_aData' );
8066
+ }, 1 );
8067
+ } );
8068
+
8069
+ _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
8070
+ return this.iterator( 'row', function ( settings, row ) {
8071
+ var r = settings.aoData[ row ];
8072
+ return type === 'search' ? r._aFilterData : r._aSortData;
8073
+ }, 1 );
8074
+ } );
8075
+
8076
+ _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
8077
+ return this.iterator( 'row', function ( settings, row ) {
8078
+ _fnInvalidate( settings, row, src );
8079
+ } );
8080
+ } );
8081
+
8082
+ _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
8083
+ return this.iterator( 'row', function ( settings, row ) {
8084
+ return row;
8085
+ }, 1 );
8086
+ } );
8087
+
8088
+ _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
8089
+ var a = [];
8090
+ var context = this.context;
8091
+
8092
+ // `iterator` will drop undefined values, but in this case we want them
8093
+ for ( var i=0, ien=context.length ; i<ien ; i++ ) {
8094
+ for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
8095
+ var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
8096
+ a.push( (hash === true ? '#' : '' )+ id );
8097
+ }
8098
+ }
8099
+
8100
+ return new _Api( context, a );
8101
+ } );
8102
+
8103
+ _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
8104
+ var that = this;
8105
+
8106
+ this.iterator( 'row', function ( settings, row, thatIdx ) {
8107
+ var data = settings.aoData;
8108
+ var rowData = data[ row ];
8109
+ var i, ien, j, jen;
8110
+ var loopRow, loopCells;
8111
+
8112
+ data.splice( row, 1 );
8113
+
8114
+ // Update the cached indexes
8115
+ for ( i=0, ien=data.length ; i<ien ; i++ ) {
8116
+ loopRow = data[i];
8117
+ loopCells = loopRow.anCells;
8118
+
8119
+ // Rows
8120
+ if ( loopRow.nTr !== null ) {
8121
+ loopRow.nTr._DT_RowIndex = i;
8122
+ }
8123
+
8124
+ // Cells
8125
+ if ( loopCells !== null ) {
8126
+ for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
8127
+ loopCells[j]._DT_CellIndex.row = i;
8128
+ }
8129
+ }
8130
+ }
8131
+
8132
+ // Delete from the display arrays
8133
+ _fnDeleteIndex( settings.aiDisplayMaster, row );
8134
+ _fnDeleteIndex( settings.aiDisplay, row );
8135
+ _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
8136
+
8137
+ // For server-side processing tables - subtract the deleted row from the count
8138
+ if ( settings._iRecordsDisplay > 0 ) {
8139
+ settings._iRecordsDisplay--;
8140
+ }
8141
+
8142
+ // Check for an 'overflow' they case for displaying the table
8143
+ _fnLengthOverflow( settings );
8144
+
8145
+ // Remove the row's ID reference if there is one
8146
+ var id = settings.rowIdFn( rowData._aData );
8147
+ if ( id !== undefined ) {
8148
+ delete settings.aIds[ id ];
8149
+ }
8150
+ } );
8151
+
8152
+ this.iterator( 'table', function ( settings ) {
8153
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8154
+ settings.aoData[i].idx = i;
8155
+ }
8156
+ } );
8157
+
8158
+ return this;
8159
+ } );
8160
+
8161
+
8162
+ _api_register( 'rows.add()', function ( rows ) {
8163
+ var newRows = this.iterator( 'table', function ( settings ) {
8164
+ var row, i, ien;
8165
+ var out = [];
8166
+
8167
+ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8168
+ row = rows[i];
8169
+
8170
+ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8171
+ out.push( _fnAddTr( settings, row )[0] );
8172
+ }
8173
+ else {
8174
+ out.push( _fnAddData( settings, row ) );
8175
+ }
8176
+ }
8177
+
8178
+ return out;
8179
+ }, 1 );
8180
+
8181
+ // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8182
+ var modRows = this.rows( -1 );
8183
+ modRows.pop();
8184
+ $.merge( modRows, newRows );
8185
+
8186
+ return modRows;
8187
+ } );
8188
+
8189
+
8190
+
8191
+
8192
+
8193
+ /**
8194
+ *
8195
+ */
8196
+ _api_register( 'row()', function ( selector, opts ) {
8197
+ return _selector_first( this.rows( selector, opts ) );
8198
+ } );
8199
+
8200
+
8201
+ _api_register( 'row().data()', function ( data ) {
8202
+ var ctx = this.context;
8203
+
8204
+ if ( data === undefined ) {
8205
+ // Get
8206
+ return ctx.length && this.length ?
8207
+ ctx[0].aoData[ this[0] ]._aData :
8208
+ undefined;
8209
+ }
8210
+
8211
+ // Set
8212
+ var row = ctx[0].aoData[ this[0] ];
8213
+ row._aData = data;
8214
+
8215
+ // If the DOM has an id, and the data source is an array
8216
+ if ( Array.isArray( data ) && row.nTr && row.nTr.id ) {
8217
+ _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );
8218
+ }
8219
+
8220
+ // Automatically invalidate
8221
+ _fnInvalidate( ctx[0], this[0], 'data' );
8222
+
8223
+ return this;
8224
+ } );
8225
+
8226
+
8227
+ _api_register( 'row().node()', function () {
8228
+ var ctx = this.context;
8229
+
8230
+ return ctx.length && this.length ?
8231
+ ctx[0].aoData[ this[0] ].nTr || null :
8232
+ null;
8233
+ } );
8234
+
8235
+
8236
+ _api_register( 'row.add()', function ( row ) {
8237
+ // Allow a jQuery object to be passed in - only a single row is added from
8238
+ // it though - the first element in the set
8239
+ if ( row instanceof $ && row.length ) {
8240
+ row = row[0];
8241
+ }
8242
+
8243
+ var rows = this.iterator( 'table', function ( settings ) {
8244
+ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8245
+ return _fnAddTr( settings, row )[0];
8246
+ }
8247
+ return _fnAddData( settings, row );
8248
+ } );
8249
+
8250
+ // Return an Api.rows() extended instance, with the newly added row selected
8251
+ return this.row( rows[0] );
8252
+ } );
8253
+
8254
+
8255
+
8256
+ var __details_add = function ( ctx, row, data, klass )
8257
+ {
8258
+ // Convert to array of TR elements
8259
+ var rows = [];
8260
+ var addRow = function ( r, k ) {
8261
+ // Recursion to allow for arrays of jQuery objects
8262
+ if ( Array.isArray( r ) || r instanceof $ ) {
8263
+ for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8264
+ addRow( r[i], k );
8265
+ }
8266
+ return;
8267
+ }
8268
+
8269
+ // If we get a TR element, then just add it directly - up to the dev
8270
+ // to add the correct number of columns etc
8271
+ if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8272
+ rows.push( r );
8273
+ }
8274
+ else {
8275
+ // Otherwise create a row with a wrapper
8276
+ var created = $('<tr><td></td></tr>').addClass( k );
8277
+ $('td', created)
8278
+ .addClass( k )
8279
+ .html( r )
8280
+ [0].colSpan = _fnVisbleColumns( ctx );
8281
+
8282
+ rows.push( created[0] );
8283
+ }
8284
+ };
8285
+
8286
+ addRow( data, klass );
8287
+
8288
+ if ( row._details ) {
8289
+ row._details.detach();
8290
+ }
8291
+
8292
+ row._details = $(rows);
8293
+
8294
+ // If the children were already shown, that state should be retained
8295
+ if ( row._detailsShow ) {
8296
+ row._details.insertAfter( row.nTr );
8297
+ }
8298
+ };
8299
+
8300
+
8301
+ var __details_remove = function ( api, idx )
8302
+ {
8303
+ var ctx = api.context;
8304
+
8305
+ if ( ctx.length ) {
8306
+ var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8307
+
8308
+ if ( row && row._details ) {
8309
+ row._details.remove();
8310
+
8311
+ row._detailsShow = undefined;
8312
+ row._details = undefined;
8313
+ }
8314
+ }
8315
+ };
8316
+
8317
+
8318
+ var __details_display = function ( api, show ) {
8319
+ var ctx = api.context;
8320
+
8321
+ if ( ctx.length && api.length ) {
8322
+ var row = ctx[0].aoData[ api[0] ];
8323
+
8324
+ if ( row._details ) {
8325
+ row._detailsShow = show;
8326
+
8327
+ if ( show ) {
8328
+ row._details.insertAfter( row.nTr );
8329
+ }
8330
+ else {
8331
+ row._details.detach();
8332
+ }
8333
+
8334
+ __details_events( ctx[0] );
8335
+ }
8336
+ }
8337
+ };
8338
+
8339
+
8340
+ var __details_events = function ( settings )
8341
+ {
8342
+ var api = new _Api( settings );
8343
+ var namespace = '.dt.DT_details';
8344
+ var drawEvent = 'draw'+namespace;
8345
+ var colvisEvent = 'column-visibility'+namespace;
8346
+ var destroyEvent = 'destroy'+namespace;
8347
+ var data = settings.aoData;
8348
+
8349
+ api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8350
+
8351
+ if ( _pluck( data, '_details' ).length > 0 ) {
8352
+ // On each draw, insert the required elements into the document
8353
+ api.on( drawEvent, function ( e, ctx ) {
8354
+ if ( settings !== ctx ) {
8355
+ return;
8356
+ }
8357
+
8358
+ api.rows( {page:'current'} ).eq(0).each( function (idx) {
8359
+ // Internal data grab
8360
+ var row = data[ idx ];
8361
+
8362
+ if ( row._detailsShow ) {
8363
+ row._details.insertAfter( row.nTr );
8364
+ }
8365
+ } );
8366
+ } );
8367
+
8368
+ // Column visibility change - update the colspan
8369
+ api.on( colvisEvent, function ( e, ctx, idx, vis ) {
8370
+ if ( settings !== ctx ) {
8371
+ return;
8372
+ }
8373
+
8374
+ // Update the colspan for the details rows (note, only if it already has
8375
+ // a colspan)
8376
+ var row, visible = _fnVisbleColumns( ctx );
8377
+
8378
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8379
+ row = data[i];
8380
+
8381
+ if ( row._details ) {
8382
+ row._details.children('td[colspan]').attr('colspan', visible );
8383
+ }
8384
+ }
8385
+ } );
8386
+
8387
+ // Table destroyed - nuke any child rows
8388
+ api.on( destroyEvent, function ( e, ctx ) {
8389
+ if ( settings !== ctx ) {
8390
+ return;
8391
+ }
8392
+
8393
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8394
+ if ( data[i]._details ) {
8395
+ __details_remove( api, i );
8396
+ }
8397
+ }
8398
+ } );
8399
+ }
8400
+ };
8401
+
8402
+ // Strings for the method names to help minification
8403
+ var _emp = '';
8404
+ var _child_obj = _emp+'row().child';
8405
+ var _child_mth = _child_obj+'()';
8406
+
8407
+ // data can be:
8408
+ // tr
8409
+ // string
8410
+ // jQuery or array of any of the above
8411
+ _api_register( _child_mth, function ( data, klass ) {
8412
+ var ctx = this.context;
8413
+
8414
+ if ( data === undefined ) {
8415
+ // get
8416
+ return ctx.length && this.length ?
8417
+ ctx[0].aoData[ this[0] ]._details :
8418
+ undefined;
8419
+ }
8420
+ else if ( data === true ) {
8421
+ // show
8422
+ this.child.show();
8423
+ }
8424
+ else if ( data === false ) {
8425
+ // remove
8426
+ __details_remove( this );
8427
+ }
8428
+ else if ( ctx.length && this.length ) {
8429
+ // set
8430
+ __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8431
+ }
8432
+
8433
+ return this;
8434
+ } );
8435
+
8436
+
8437
+ _api_register( [
8438
+ _child_obj+'.show()',
8439
+ _child_mth+'.show()' // only when `child()` was called with parameters (without
8440
+ ], function ( show ) { // it returns an object and this method is not executed)
8441
+ __details_display( this, true );
8442
+ return this;
8443
+ } );
8444
+
8445
+
8446
+ _api_register( [
8447
+ _child_obj+'.hide()',
8448
+ _child_mth+'.hide()' // only when `child()` was called with parameters (without
8449
+ ], function () { // it returns an object and this method is not executed)
8450
+ __details_display( this, false );
8451
+ return this;
8452
+ } );
8453
+
8454
+
8455
+ _api_register( [
8456
+ _child_obj+'.remove()',
8457
+ _child_mth+'.remove()' // only when `child()` was called with parameters (without
8458
+ ], function () { // it returns an object and this method is not executed)
8459
+ __details_remove( this );
8460
+ return this;
8461
+ } );
8462
+
8463
+
8464
+ _api_register( _child_obj+'.isShown()', function () {
8465
+ var ctx = this.context;
8466
+
8467
+ if ( ctx.length && this.length ) {
8468
+ // _detailsShown as false or undefined will fall through to return false
8469
+ return ctx[0].aoData[ this[0] ]._detailsShow || false;
8470
+ }
8471
+ return false;
8472
+ } );
8473
+
8474
+
8475
+
8476
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8477
+ * Columns
8478
+ *
8479
+ * {integer} - column index (>=0 count from left, <0 count from right)
8480
+ * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
8481
+ * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
8482
+ * "{string}:name" - column name
8483
+ * "{string}" - jQuery selector on column header nodes
8484
+ *
8485
+ */
8486
+
8487
+ // can be an array of these items, comma separated list, or an array of comma
8488
+ // separated lists
8489
+
8490
+ var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
8491
+
8492
+
8493
+ // r1 and r2 are redundant - but it means that the parameters match for the
8494
+ // iterator callback in columns().data()
8495
+ var __columnData = function ( settings, column, r1, r2, rows ) {
8496
+ var a = [];
8497
+ for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8498
+ a.push( _fnGetCellData( settings, rows[row], column ) );
8499
+ }
8500
+ return a;
8501
+ };
8502
+
8503
+
8504
+ var __column_selector = function ( settings, selector, opts )
8505
+ {
8506
+ var
8507
+ columns = settings.aoColumns,
8508
+ names = _pluck( columns, 'sName' ),
8509
+ nodes = _pluck( columns, 'nTh' );
8510
+
8511
+ var run = function ( s ) {
8512
+ var selInt = _intVal( s );
8513
+
8514
+ // Selector - all
8515
+ if ( s === '' ) {
8516
+ return _range( columns.length );
8517
+ }
8518
+
8519
+ // Selector - index
8520
+ if ( selInt !== null ) {
8521
+ return [ selInt >= 0 ?
8522
+ selInt : // Count from left
8523
+ columns.length + selInt // Count from right (+ because its a negative value)
8524
+ ];
8525
+ }
8526
+
8527
+ // Selector = function
8528
+ if ( typeof s === 'function' ) {
8529
+ var rows = _selector_row_indexes( settings, opts );
8530
+
8531
+ return $.map( columns, function (col, idx) {
8532
+ return s(
8533
+ idx,
8534
+ __columnData( settings, idx, 0, 0, rows ),
8535
+ nodes[ idx ]
8536
+ ) ? idx : null;
8537
+ } );
8538
+ }
8539
+
8540
+ // jQuery or string selector
8541
+ var match = typeof s === 'string' ?
8542
+ s.match( __re_column_selector ) :
8543
+ '';
8544
+
8545
+ if ( match ) {
8546
+ switch( match[2] ) {
8547
+ case 'visIdx':
8548
+ case 'visible':
8549
+ var idx = parseInt( match[1], 10 );
8550
+ // Visible index given, convert to column index
8551
+ if ( idx < 0 ) {
8552
+ // Counting from the right
8553
+ var visColumns = $.map( columns, function (col,i) {
8554
+ return col.bVisible ? i : null;
8555
+ } );
8556
+ return [ visColumns[ visColumns.length + idx ] ];
8557
+ }
8558
+ // Counting from the left
8559
+ return [ _fnVisibleToColumnIndex( settings, idx ) ];
8560
+
8561
+ case 'name':
8562
+ // match by name. `names` is column index complete and in order
8563
+ return $.map( names, function (name, i) {
8564
+ return name === match[1] ? i : null;
8565
+ } );
8566
+
8567
+ default:
8568
+ return [];
8569
+ }
8570
+ }
8571
+
8572
+ // Cell in the table body
8573
+ if ( s.nodeName && s._DT_CellIndex ) {
8574
+ return [ s._DT_CellIndex.column ];
8575
+ }
8576
+
8577
+ // jQuery selector on the TH elements for the columns
8578
+ var jqResult = $( nodes )
8579
+ .filter( s )
8580
+ .map( function () {
8581
+ return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8582
+ } )
8583
+ .toArray();
8584
+
8585
+ if ( jqResult.length || ! s.nodeName ) {
8586
+ return jqResult;
8587
+ }
8588
+
8589
+ // Otherwise a node which might have a `dt-column` data attribute, or be
8590
+ // a child or such an element
8591
+ var host = $(s).closest('*[data-dt-column]');
8592
+ return host.length ?
8593
+ [ host.data('dt-column') ] :
8594
+ [];
8595
+ };
8596
+
8597
+ return _selector_run( 'column', selector, run, settings, opts );
8598
+ };
8599
+
8600
+
8601
+ var __setColumnVis = function ( settings, column, vis ) {
8602
+ var
8603
+ cols = settings.aoColumns,
8604
+ col = cols[ column ],
8605
+ data = settings.aoData,
8606
+ row, cells, i, ien, tr;
8607
+
8608
+ // Get
8609
+ if ( vis === undefined ) {
8610
+ return col.bVisible;
8611
+ }
8612
+
8613
+ // Set
8614
+ // No change
8615
+ if ( col.bVisible === vis ) {
8616
+ return;
8617
+ }
8618
+
8619
+ if ( vis ) {
8620
+ // Insert column
8621
+ // Need to decide if we should use appendChild or insertBefore
8622
+ var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8623
+
8624
+ for ( i=0, ien=data.length ; i<ien ; i++ ) {
8625
+ tr = data[i].nTr;
8626
+ cells = data[i].anCells;
8627
+
8628
+ if ( tr ) {
8629
+ // insertBefore can act like appendChild if 2nd arg is null
8630
+ tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8631
+ }
8632
+ }
8633
+ }
8634
+ else {
8635
+ // Remove column
8636
+ $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8637
+ }
8638
+
8639
+ // Common actions
8640
+ col.bVisible = vis;
8641
+ };
8642
+
8643
+
8644
+ _api_register( 'columns()', function ( selector, opts ) {
8645
+ // argument shifting
8646
+ if ( selector === undefined ) {
8647
+ selector = '';
8648
+ }
8649
+ else if ( $.isPlainObject( selector ) ) {
8650
+ opts = selector;
8651
+ selector = '';
8652
+ }
8653
+
8654
+ opts = _selector_opts( opts );
8655
+
8656
+ var inst = this.iterator( 'table', function ( settings ) {
8657
+ return __column_selector( settings, selector, opts );
8658
+ }, 1 );
8659
+
8660
+ // Want argument shifting here and in _row_selector?
8661
+ inst.selector.cols = selector;
8662
+ inst.selector.opts = opts;
8663
+
8664
+ return inst;
8665
+ } );
8666
+
8667
+ _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8668
+ return this.iterator( 'column', function ( settings, column ) {
8669
+ return settings.aoColumns[column].nTh;
8670
+ }, 1 );
8671
+ } );
8672
+
8673
+ _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8674
+ return this.iterator( 'column', function ( settings, column ) {
8675
+ return settings.aoColumns[column].nTf;
8676
+ }, 1 );
8677
+ } );
8678
+
8679
+ _api_registerPlural( 'columns().data()', 'column().data()', function () {
8680
+ return this.iterator( 'column-rows', __columnData, 1 );
8681
+ } );
8682
+
8683
+ _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8684
+ return this.iterator( 'column', function ( settings, column ) {
8685
+ return settings.aoColumns[column].mData;
8686
+ }, 1 );
8687
+ } );
8688
+
8689
+ _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8690
+ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8691
+ return _pluck_order( settings.aoData, rows,
8692
+ type === 'search' ? '_aFilterData' : '_aSortData', column
8693
+ );
8694
+ }, 1 );
8695
+ } );
8696
+
8697
+ _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8698
+ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8699
+ return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8700
+ }, 1 );
8701
+ } );
8702
+
8703
+ _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8704
+ var that = this;
8705
+ var ret = this.iterator( 'column', function ( settings, column ) {
8706
+ if ( vis === undefined ) {
8707
+ return settings.aoColumns[ column ].bVisible;
8708
+ } // else
8709
+ __setColumnVis( settings, column, vis );
8710
+ } );
8711
+
8712
+ // Group the column visibility changes
8713
+ if ( vis !== undefined ) {
8714
+ this.iterator( 'table', function ( settings ) {
8715
+ // Redraw the header after changes
8716
+ _fnDrawHead( settings, settings.aoHeader );
8717
+ _fnDrawHead( settings, settings.aoFooter );
8718
+
8719
+ // Update colspan for no records display. Child rows and extensions will use their own
8720
+ // listeners to do this - only need to update the empty table item here
8721
+ if ( ! settings.aiDisplay.length ) {
8722
+ $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));
8723
+ }
8724
+
8725
+ _fnSaveState( settings );
8726
+
8727
+ // Second loop once the first is done for events
8728
+ that.iterator( 'column', function ( settings, column ) {
8729
+ _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
8730
+ } );
8731
+
8732
+ if ( calc === undefined || calc ) {
8733
+ that.columns.adjust();
8734
+ }
8735
+ });
8736
+ }
8737
+
8738
+ return ret;
8739
+ } );
8740
+
8741
+ _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8742
+ return this.iterator( 'column', function ( settings, column ) {
8743
+ return type === 'visible' ?
8744
+ _fnColumnIndexToVisible( settings, column ) :
8745
+ column;
8746
+ }, 1 );
8747
+ } );
8748
+
8749
+ _api_register( 'columns.adjust()', function () {
8750
+ return this.iterator( 'table', function ( settings ) {
8751
+ _fnAdjustColumnSizing( settings );
8752
+ }, 1 );
8753
+ } );
8754
+
8755
+ _api_register( 'column.index()', function ( type, idx ) {
8756
+ if ( this.context.length !== 0 ) {
8757
+ var ctx = this.context[0];
8758
+
8759
+ if ( type === 'fromVisible' || type === 'toData' ) {
8760
+ return _fnVisibleToColumnIndex( ctx, idx );
8761
+ }
8762
+ else if ( type === 'fromData' || type === 'toVisible' ) {
8763
+ return _fnColumnIndexToVisible( ctx, idx );
8764
+ }
8765
+ }
8766
+ } );
8767
+
8768
+ _api_register( 'column()', function ( selector, opts ) {
8769
+ return _selector_first( this.columns( selector, opts ) );
8770
+ } );
8771
+
8772
+ var __cell_selector = function ( settings, selector, opts )
8773
+ {
8774
+ var data = settings.aoData;
8775
+ var rows = _selector_row_indexes( settings, opts );
8776
+ var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8777
+ var allCells = $(_flatten( [], cells ));
8778
+ var row;
8779
+ var columns = settings.aoColumns.length;
8780
+ var a, i, ien, j, o, host;
8781
+
8782
+ var run = function ( s ) {
8783
+ var fnSelector = typeof s === 'function';
8784
+
8785
+ if ( s === null || s === undefined || fnSelector ) {
8786
+ // All cells and function selectors
8787
+ a = [];
8788
+
8789
+ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8790
+ row = rows[i];
8791
+
8792
+ for ( j=0 ; j<columns ; j++ ) {
8793
+ o = {
8794
+ row: row,
8795
+ column: j
8796
+ };
8797
+
8798
+ if ( fnSelector ) {
8799
+ // Selector - function
8800
+ host = data[ row ];
8801
+
8802
+ if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8803
+ a.push( o );
8804
+ }
8805
+ }
8806
+ else {
8807
+ // Selector - all
8808
+ a.push( o );
8809
+ }
8810
+ }
8811
+ }
8812
+
8813
+ return a;
8814
+ }
8815
+
8816
+ // Selector - index
8817
+ if ( $.isPlainObject( s ) ) {
8818
+ // Valid cell index and its in the array of selectable rows
8819
+ return s.column !== undefined && s.row !== undefined && $.inArray( s.row, rows ) !== -1 ?
8820
+ [s] :
8821
+ [];
8822
+ }
8823
+
8824
+ // Selector - jQuery filtered cells
8825
+ var jqResult = allCells
8826
+ .filter( s )
8827
+ .map( function (i, el) {
8828
+ return { // use a new object, in case someone changes the values
8829
+ row: el._DT_CellIndex.row,
8830
+ column: el._DT_CellIndex.column
8831
+ };
8832
+ } )
8833
+ .toArray();
8834
+
8835
+ if ( jqResult.length || ! s.nodeName ) {
8836
+ return jqResult;
8837
+ }
8838
+
8839
+ // Otherwise the selector is a node, and there is one last option - the
8840
+ // element might be a child of an element which has dt-row and dt-column
8841
+ // data attributes
8842
+ host = $(s).closest('*[data-dt-row]');
8843
+ return host.length ?
8844
+ [ {
8845
+ row: host.data('dt-row'),
8846
+ column: host.data('dt-column')
8847
+ } ] :
8848
+ [];
8849
+ };
8850
+
8851
+ return _selector_run( 'cell', selector, run, settings, opts );
8852
+ };
8853
+
8854
+
8855
+
8856
+
8857
+ _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8858
+ // Argument shifting
8859
+ if ( $.isPlainObject( rowSelector ) ) {
8860
+ // Indexes
8861
+ if ( rowSelector.row === undefined ) {
8862
+ // Selector options in first parameter
8863
+ opts = rowSelector;
8864
+ rowSelector = null;
8865
+ }
8866
+ else {
8867
+ // Cell index objects in first parameter
8868
+ opts = columnSelector;
8869
+ columnSelector = null;
8870
+ }
8871
+ }
8872
+ if ( $.isPlainObject( columnSelector ) ) {
8873
+ opts = columnSelector;
8874
+ columnSelector = null;
8875
+ }
8876
+
8877
+ // Cell selector
8878
+ if ( columnSelector === null || columnSelector === undefined ) {
8879
+ return this.iterator( 'table', function ( settings ) {
8880
+ return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8881
+ } );
8882
+ }
8883
+
8884
+ // The default built in options need to apply to row and columns
8885
+ var internalOpts = opts ? {
8886
+ page: opts.page,
8887
+ order: opts.order,
8888
+ search: opts.search
8889
+ } : {};
8890
+
8891
+ // Row + column selector
8892
+ var columns = this.columns( columnSelector, internalOpts );
8893
+ var rows = this.rows( rowSelector, internalOpts );
8894
+ var i, ien, j, jen;
8895
+
8896
+ var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {
8897
+ var a = [];
8898
+
8899
+ for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8900
+ for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8901
+ a.push( {
8902
+ row: rows[idx][i],
8903
+ column: columns[idx][j]
8904
+ } );
8905
+ }
8906
+ }
8907
+
8908
+ return a;
8909
+ }, 1 );
8910
+
8911
+ // There is currently only one extension which uses a cell selector extension
8912
+ // It is a _major_ performance drag to run this if it isn't needed, so this is
8913
+ // an extension specific check at the moment
8914
+ var cells = opts && opts.selected ?
8915
+ this.cells( cellsNoOpts, opts ) :
8916
+ cellsNoOpts;
8917
+
8918
+ $.extend( cells.selector, {
8919
+ cols: columnSelector,
8920
+ rows: rowSelector,
8921
+ opts: opts
8922
+ } );
8923
+
8924
+ return cells;
8925
+ } );
8926
+
8927
+
8928
+ _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8929
+ return this.iterator( 'cell', function ( settings, row, column ) {
8930
+ var data = settings.aoData[ row ];
8931
+
8932
+ return data && data.anCells ?
8933
+ data.anCells[ column ] :
8934
+ undefined;
8935
+ }, 1 );
8936
+ } );
8937
+
8938
+
8939
+ _api_register( 'cells().data()', function () {
8940
+ return this.iterator( 'cell', function ( settings, row, column ) {
8941
+ return _fnGetCellData( settings, row, column );
8942
+ }, 1 );
8943
+ } );
8944
+
8945
+
8946
+ _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8947
+ type = type === 'search' ? '_aFilterData' : '_aSortData';
8948
+
8949
+ return this.iterator( 'cell', function ( settings, row, column ) {
8950
+ return settings.aoData[ row ][ type ][ column ];
8951
+ }, 1 );
8952
+ } );
8953
+
8954
+
8955
+ _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8956
+ return this.iterator( 'cell', function ( settings, row, column ) {
8957
+ return _fnGetCellData( settings, row, column, type );
8958
+ }, 1 );
8959
+ } );
8960
+
8961
+
8962
+ _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8963
+ return this.iterator( 'cell', function ( settings, row, column ) {
8964
+ return {
8965
+ row: row,
8966
+ column: column,
8967
+ columnVisible: _fnColumnIndexToVisible( settings, column )
8968
+ };
8969
+ }, 1 );
8970
+ } );
8971
+
8972
+
8973
+ _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8974
+ return this.iterator( 'cell', function ( settings, row, column ) {
8975
+ _fnInvalidate( settings, row, src, column );
8976
+ } );
8977
+ } );
8978
+
8979
+
8980
+
8981
+ _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8982
+ return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8983
+ } );
8984
+
8985
+
8986
+ _api_register( 'cell().data()', function ( data ) {
8987
+ var ctx = this.context;
8988
+ var cell = this[0];
8989
+
8990
+ if ( data === undefined ) {
8991
+ // Get
8992
+ return ctx.length && cell.length ?
8993
+ _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8994
+ undefined;
8995
+ }
8996
+
8997
+ // Set
8998
+ _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8999
+ _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
9000
+
9001
+ return this;
9002
+ } );
9003
+
9004
+
9005
+
9006
+ /**
9007
+ * Get current ordering (sorting) that has been applied to the table.
9008
+ *
9009
+ * @returns {array} 2D array containing the sorting information for the first
9010
+ * table in the current context. Each element in the parent array represents
9011
+ * a column being sorted upon (i.e. multi-sorting with two columns would have
9012
+ * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
9013
+ * the column index that the sorting condition applies to, the second is the
9014
+ * direction of the sort (`desc` or `asc`) and, optionally, the third is the
9015
+ * index of the sorting order from the `column.sorting` initialisation array.
9016
+ *//**
9017
+ * Set the ordering for the table.
9018
+ *
9019
+ * @param {integer} order Column index to sort upon.
9020
+ * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
9021
+ * @returns {DataTables.Api} this
9022
+ *//**
9023
+ * Set the ordering for the table.
9024
+ *
9025
+ * @param {array} order 1D array of sorting information to be applied.
9026
+ * @param {array} [...] Optional additional sorting conditions
9027
+ * @returns {DataTables.Api} this
9028
+ *//**
9029
+ * Set the ordering for the table.
9030
+ *
9031
+ * @param {array} order 2D array of sorting information to be applied.
9032
+ * @returns {DataTables.Api} this
9033
+ */
9034
+ _api_register( 'order()', function ( order, dir ) {
9035
+ var ctx = this.context;
9036
+
9037
+ if ( order === undefined ) {
9038
+ // get
9039
+ return ctx.length !== 0 ?
9040
+ ctx[0].aaSorting :
9041
+ undefined;
9042
+ }
9043
+
9044
+ // set
9045
+ if ( typeof order === 'number' ) {
9046
+ // Simple column / direction passed in
9047
+ order = [ [ order, dir ] ];
9048
+ }
9049
+ else if ( order.length && ! Array.isArray( order[0] ) ) {
9050
+ // Arguments passed in (list of 1D arrays)
9051
+ order = Array.prototype.slice.call( arguments );
9052
+ }
9053
+ // otherwise a 2D array was passed in
9054
+
9055
+ return this.iterator( 'table', function ( settings ) {
9056
+ settings.aaSorting = order.slice();
9057
+ } );
9058
+ } );
9059
+
9060
+
9061
+ /**
9062
+ * Attach a sort listener to an element for a given column
9063
+ *
9064
+ * @param {node|jQuery|string} node Identifier for the element(s) to attach the
9065
+ * listener to. This can take the form of a single DOM node, a jQuery
9066
+ * collection of nodes or a jQuery selector which will identify the node(s).
9067
+ * @param {integer} column the column that a click on this node will sort on
9068
+ * @param {function} [callback] callback function when sort is run
9069
+ * @returns {DataTables.Api} this
9070
+ */
9071
+ _api_register( 'order.listener()', function ( node, column, callback ) {
9072
+ return this.iterator( 'table', function ( settings ) {
9073
+ _fnSortAttachListener( settings, node, column, callback );
9074
+ } );
9075
+ } );
9076
+
9077
+
9078
+ _api_register( 'order.fixed()', function ( set ) {
9079
+ if ( ! set ) {
9080
+ var ctx = this.context;
9081
+ var fixed = ctx.length ?
9082
+ ctx[0].aaSortingFixed :
9083
+ undefined;
9084
+
9085
+ return Array.isArray( fixed ) ?
9086
+ { pre: fixed } :
9087
+ fixed;
9088
+ }
9089
+
9090
+ return this.iterator( 'table', function ( settings ) {
9091
+ settings.aaSortingFixed = $.extend( true, {}, set );
9092
+ } );
9093
+ } );
9094
+
9095
+
9096
+ // Order by the selected column(s)
9097
+ _api_register( [
9098
+ 'columns().order()',
9099
+ 'column().order()'
9100
+ ], function ( dir ) {
9101
+ var that = this;
9102
+
9103
+ return this.iterator( 'table', function ( settings, i ) {
9104
+ var sort = [];
9105
+
9106
+ $.each( that[i], function (j, col) {
9107
+ sort.push( [ col, dir ] );
9108
+ } );
9109
+
9110
+ settings.aaSorting = sort;
9111
+ } );
9112
+ } );
9113
+
9114
+
9115
+
9116
+ _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
9117
+ var ctx = this.context;
9118
+
9119
+ if ( input === undefined ) {
9120
+ // get
9121
+ return ctx.length !== 0 ?
9122
+ ctx[0].oPreviousSearch.sSearch :
9123
+ undefined;
9124
+ }
9125
+
9126
+ // set
9127
+ return this.iterator( 'table', function ( settings ) {
9128
+ if ( ! settings.oFeatures.bFilter ) {
9129
+ return;
9130
+ }
9131
+
9132
+ _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
9133
+ "sSearch": input+"",
9134
+ "bRegex": regex === null ? false : regex,
9135
+ "bSmart": smart === null ? true : smart,
9136
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
9137
+ } ), 1 );
9138
+ } );
9139
+ } );
9140
+
9141
+
9142
+ _api_registerPlural(
9143
+ 'columns().search()',
9144
+ 'column().search()',
9145
+ function ( input, regex, smart, caseInsen ) {
9146
+ return this.iterator( 'column', function ( settings, column ) {
9147
+ var preSearch = settings.aoPreSearchCols;
9148
+
9149
+ if ( input === undefined ) {
9150
+ // get
9151
+ return preSearch[ column ].sSearch;
9152
+ }
9153
+
9154
+ // set
9155
+ if ( ! settings.oFeatures.bFilter ) {
9156
+ return;
9157
+ }
9158
+
9159
+ $.extend( preSearch[ column ], {
9160
+ "sSearch": input+"",
9161
+ "bRegex": regex === null ? false : regex,
9162
+ "bSmart": smart === null ? true : smart,
9163
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
9164
+ } );
9165
+
9166
+ _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
9167
+ } );
9168
+ }
9169
+ );
9170
+
9171
+ /*
9172
+ * State API methods
9173
+ */
9174
+
9175
+ _api_register( 'state()', function () {
9176
+ return this.context.length ?
9177
+ this.context[0].oSavedState :
9178
+ null;
9179
+ } );
9180
+
9181
+
9182
+ _api_register( 'state.clear()', function () {
9183
+ return this.iterator( 'table', function ( settings ) {
9184
+ // Save an empty object
9185
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
9186
+ } );
9187
+ } );
9188
+
9189
+
9190
+ _api_register( 'state.loaded()', function () {
9191
+ return this.context.length ?
9192
+ this.context[0].oLoadedState :
9193
+ null;
9194
+ } );
9195
+
9196
+
9197
+ _api_register( 'state.save()', function () {
9198
+ return this.iterator( 'table', function ( settings ) {
9199
+ _fnSaveState( settings );
9200
+ } );
9201
+ } );
9202
+
9203
+
9204
+
9205
+ /**
9206
+ * Provide a common method for plug-ins to check the version of DataTables being
9207
+ * used, in order to ensure compatibility.
9208
+ *
9209
+ * @param {string} version Version string to check for, in the format "X.Y.Z".
9210
+ * Note that the formats "X" and "X.Y" are also acceptable.
9211
+ * @returns {boolean} true if this version of DataTables is greater or equal to
9212
+ * the required version, or false if this version of DataTales is not
9213
+ * suitable
9214
+ * @static
9215
+ * @dtopt API-Static
9216
+ *
9217
+ * @example
9218
+ * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
9219
+ */
9220
+ DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
9221
+ {
9222
+ var aThis = DataTable.version.split('.');
9223
+ var aThat = version.split('.');
9224
+ var iThis, iThat;
9225
+
9226
+ for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9227
+ iThis = parseInt( aThis[i], 10 ) || 0;
9228
+ iThat = parseInt( aThat[i], 10 ) || 0;
9229
+
9230
+ // Parts are the same, keep comparing
9231
+ if (iThis === iThat) {
9232
+ continue;
9233
+ }
9234
+
9235
+ // Parts are different, return immediately
9236
+ return iThis > iThat;
9237
+ }
9238
+
9239
+ return true;
9240
+ };
9241
+
9242
+
9243
+ /**
9244
+ * Check if a `<table>` node is a DataTable table already or not.
9245
+ *
9246
+ * @param {node|jquery|string} table Table node, jQuery object or jQuery
9247
+ * selector for the table to test. Note that if more than more than one
9248
+ * table is passed on, only the first will be checked
9249
+ * @returns {boolean} true the table given is a DataTable, or false otherwise
9250
+ * @static
9251
+ * @dtopt API-Static
9252
+ *
9253
+ * @example
9254
+ * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
9255
+ * $('#example').dataTable();
9256
+ * }
9257
+ */
9258
+ DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
9259
+ {
9260
+ var t = $(table).get(0);
9261
+ var is = false;
9262
+
9263
+ if ( table instanceof DataTable.Api ) {
9264
+ return true;
9265
+ }
9266
+
9267
+ $.each( DataTable.settings, function (i, o) {
9268
+ var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9269
+ var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9270
+
9271
+ if ( o.nTable === t || head === t || foot === t ) {
9272
+ is = true;
9273
+ }
9274
+ } );
9275
+
9276
+ return is;
9277
+ };
9278
+
9279
+
9280
+ /**
9281
+ * Get all DataTable tables that have been initialised - optionally you can
9282
+ * select to get only currently visible tables.
9283
+ *
9284
+ * @param {boolean} [visible=false] Flag to indicate if you want all (default)
9285
+ * or visible tables only.
9286
+ * @returns {array} Array of `table` nodes (not DataTable instances) which are
9287
+ * DataTables
9288
+ * @static
9289
+ * @dtopt API-Static
9290
+ *
9291
+ * @example
9292
+ * $.each( $.fn.dataTable.tables(true), function () {
9293
+ * $(table).DataTable().columns.adjust();
9294
+ * } );
9295
+ */
9296
+ DataTable.tables = DataTable.fnTables = function ( visible )
9297
+ {
9298
+ var api = false;
9299
+
9300
+ if ( $.isPlainObject( visible ) ) {
9301
+ api = visible.api;
9302
+ visible = visible.visible;
9303
+ }
9304
+
9305
+ var a = $.map( DataTable.settings, function (o) {
9306
+ if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
9307
+ return o.nTable;
9308
+ }
9309
+ } );
9310
+
9311
+ return api ?
9312
+ new _Api( a ) :
9313
+ a;
9314
+ };
9315
+
9316
+
9317
+ /**
9318
+ * Convert from camel case parameters to Hungarian notation. This is made public
9319
+ * for the extensions to provide the same ability as DataTables core to accept
9320
+ * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
9321
+ * parameters.
9322
+ *
9323
+ * @param {object} src The model object which holds all parameters that can be
9324
+ * mapped.
9325
+ * @param {object} user The object to convert from camel case to Hungarian.
9326
+ * @param {boolean} force When set to `true`, properties which already have a
9327
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
9328
+ * won't be.
9329
+ */
9330
+ DataTable.camelToHungarian = _fnCamelToHungarian;
9331
+
9332
+
9333
+
9334
+ /**
9335
+ *
9336
+ */
9337
+ _api_register( '$()', function ( selector, opts ) {
9338
+ var
9339
+ rows = this.rows( opts ).nodes(), // Get all rows
9340
+ jqRows = $(rows);
9341
+
9342
+ return $( [].concat(
9343
+ jqRows.filter( selector ).toArray(),
9344
+ jqRows.find( selector ).toArray()
9345
+ ) );
9346
+ } );
9347
+
9348
+
9349
+ // jQuery functions to operate on the tables
9350
+ $.each( [ 'on', 'one', 'off' ], function (i, key) {
9351
+ _api_register( key+'()', function ( /* event, handler */ ) {
9352
+ var args = Array.prototype.slice.call(arguments);
9353
+
9354
+ // Add the `dt` namespace automatically if it isn't already present
9355
+ args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
9356
+ return ! e.match(/\.dt\b/) ?
9357
+ e+'.dt' :
9358
+ e;
9359
+ } ).join( ' ' );
9360
+
9361
+ var inst = $( this.tables().nodes() );
9362
+ inst[key].apply( inst, args );
9363
+ return this;
9364
+ } );
9365
+ } );
9366
+
9367
+
9368
+ _api_register( 'clear()', function () {
9369
+ return this.iterator( 'table', function ( settings ) {
9370
+ _fnClearTable( settings );
9371
+ } );
9372
+ } );
9373
+
9374
+
9375
+ _api_register( 'settings()', function () {
9376
+ return new _Api( this.context, this.context );
9377
+ } );
9378
+
9379
+
9380
+ _api_register( 'init()', function () {
9381
+ var ctx = this.context;
9382
+ return ctx.length ? ctx[0].oInit : null;
9383
+ } );
9384
+
9385
+
9386
+ _api_register( 'data()', function () {
9387
+ return this.iterator( 'table', function ( settings ) {
9388
+ return _pluck( settings.aoData, '_aData' );
9389
+ } ).flatten();
9390
+ } );
9391
+
9392
+
9393
+ _api_register( 'destroy()', function ( remove ) {
9394
+ remove = remove || false;
9395
+
9396
+ return this.iterator( 'table', function ( settings ) {
9397
+ var orig = settings.nTableWrapper.parentNode;
9398
+ var classes = settings.oClasses;
9399
+ var table = settings.nTable;
9400
+ var tbody = settings.nTBody;
9401
+ var thead = settings.nTHead;
9402
+ var tfoot = settings.nTFoot;
9403
+ var jqTable = $(table);
9404
+ var jqTbody = $(tbody);
9405
+ var jqWrapper = $(settings.nTableWrapper);
9406
+ var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
9407
+ var i, ien;
9408
+
9409
+ // Flag to note that the table is currently being destroyed - no action
9410
+ // should be taken
9411
+ settings.bDestroying = true;
9412
+
9413
+ // Fire off the destroy callbacks for plug-ins etc
9414
+ _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
9415
+
9416
+ // If not being removed from the document, make all columns visible
9417
+ if ( ! remove ) {
9418
+ new _Api( settings ).columns().visible( true );
9419
+ }
9420
+
9421
+ // Blitz all `DT` namespaced events (these are internal events, the
9422
+ // lowercase, `dt` events are user subscribed and they are responsible
9423
+ // for removing them
9424
+ jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9425
+ $(window).off('.DT-'+settings.sInstance);
9426
+
9427
+ // When scrolling we had to break the table up - restore it
9428
+ if ( table != thead.parentNode ) {
9429
+ jqTable.children('thead').detach();
9430
+ jqTable.append( thead );
9431
+ }
9432
+
9433
+ if ( tfoot && table != tfoot.parentNode ) {
9434
+ jqTable.children('tfoot').detach();
9435
+ jqTable.append( tfoot );
9436
+ }
9437
+
9438
+ settings.aaSorting = [];
9439
+ settings.aaSortingFixed = [];
9440
+ _fnSortingClasses( settings );
9441
+
9442
+ $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9443
+
9444
+ $('th, td', thead).removeClass( classes.sSortable+' '+
9445
+ classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9446
+ );
9447
+
9448
+ // Add the TR elements back into the table in their original order
9449
+ jqTbody.children().detach();
9450
+ jqTbody.append( rows );
9451
+
9452
+ // Remove the DataTables generated nodes, events and classes
9453
+ var removedMethod = remove ? 'remove' : 'detach';
9454
+ jqTable[ removedMethod ]();
9455
+ jqWrapper[ removedMethod ]();
9456
+
9457
+ // If we need to reattach the table to the document
9458
+ if ( ! remove && orig ) {
9459
+ // insertBefore acts like appendChild if !arg[1]
9460
+ orig.insertBefore( table, settings.nTableReinsertBefore );
9461
+
9462
+ // Restore the width of the original table - was read from the style property,
9463
+ // so we can restore directly to that
9464
+ jqTable
9465
+ .css( 'width', settings.sDestroyWidth )
9466
+ .removeClass( classes.sTable );
9467
+
9468
+ // If the were originally stripe classes - then we add them back here.
9469
+ // Note this is not fool proof (for example if not all rows had stripe
9470
+ // classes - but it's a good effort without getting carried away
9471
+ ien = settings.asDestroyStripes.length;
9472
+
9473
+ if ( ien ) {
9474
+ jqTbody.children().each( function (i) {
9475
+ $(this).addClass( settings.asDestroyStripes[i % ien] );
9476
+ } );
9477
+ }
9478
+ }
9479
+
9480
+ /* Remove the settings object from the settings array */
9481
+ var idx = $.inArray( settings, DataTable.settings );
9482
+ if ( idx !== -1 ) {
9483
+ DataTable.settings.splice( idx, 1 );
9484
+ }
9485
+ } );
9486
+ } );
9487
+
9488
+
9489
+ // Add the `every()` method for rows, columns and cells in a compact form
9490
+ $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9491
+ _api_register( type+'s().every()', function ( fn ) {
9492
+ var opts = this.selector.opts;
9493
+ var api = this;
9494
+
9495
+ return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
9496
+ // Rows and columns:
9497
+ // arg1 - index
9498
+ // arg2 - table counter
9499
+ // arg3 - loop counter
9500
+ // arg4 - undefined
9501
+ // Cells:
9502
+ // arg1 - row index
9503
+ // arg2 - column index
9504
+ // arg3 - table counter
9505
+ // arg4 - loop counter
9506
+ fn.call(
9507
+ api[ type ](
9508
+ arg1,
9509
+ type==='cell' ? arg2 : opts,
9510
+ type==='cell' ? opts : undefined
9511
+ ),
9512
+ arg1, arg2, arg3, arg4
9513
+ );
9514
+ } );
9515
+ } );
9516
+ } );
9517
+
9518
+
9519
+ // i18n method for extensions to be able to use the language object from the
9520
+ // DataTable
9521
+ _api_register( 'i18n()', function ( token, def, plural ) {
9522
+ var ctx = this.context[0];
9523
+ var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
9524
+
9525
+ if ( resolved === undefined ) {
9526
+ resolved = def;
9527
+ }
9528
+
9529
+ if ( plural !== undefined && $.isPlainObject( resolved ) ) {
9530
+ resolved = resolved[ plural ] !== undefined ?
9531
+ resolved[ plural ] :
9532
+ resolved._;
9533
+ }
9534
+
9535
+ return resolved.replace( '%d', plural ); // nb: plural might be undefined,
9536
+ } );
9537
+ /**
9538
+ * Version string for plug-ins to check compatibility. Allowed format is
9539
+ * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
9540
+ * only for non-release builds. See http://semver.org/ for more information.
9541
+ * @member
9542
+ * @type string
9543
+ * @default Version number
9544
+ */
9545
+ DataTable.version = "1.10.23";
9546
+
9547
+ /**
9548
+ * Private data store, containing all of the settings objects that are
9549
+ * created for the tables on a given page.
9550
+ *
9551
+ * Note that the `DataTable.settings` object is aliased to
9552
+ * `jQuery.fn.dataTableExt` through which it may be accessed and
9553
+ * manipulated, or `jQuery.fn.dataTable.settings`.
9554
+ * @member
9555
+ * @type array
9556
+ * @default []
9557
+ * @private
9558
+ */
9559
+ DataTable.settings = [];
9560
+
9561
+ /**
9562
+ * Object models container, for the various models that DataTables has
9563
+ * available to it. These models define the objects that are used to hold
9564
+ * the active state and configuration of the table.
9565
+ * @namespace
9566
+ */
9567
+ DataTable.models = {};
9568
+
9569
+
9570
+
9571
+ /**
9572
+ * Template object for the way in which DataTables holds information about
9573
+ * search information for the global filter and individual column filters.
9574
+ * @namespace
9575
+ */
9576
+ DataTable.models.oSearch = {
9577
+ /**
9578
+ * Flag to indicate if the filtering should be case insensitive or not
9579
+ * @type boolean
9580
+ * @default true
9581
+ */
9582
+ "bCaseInsensitive": true,
9583
+
9584
+ /**
9585
+ * Applied search term
9586
+ * @type string
9587
+ * @default <i>Empty string</i>
9588
+ */
9589
+ "sSearch": "",
9590
+
9591
+ /**
9592
+ * Flag to indicate if the search term should be interpreted as a
9593
+ * regular expression (true) or not (false) and therefore and special
9594
+ * regex characters escaped.
9595
+ * @type boolean
9596
+ * @default false
9597
+ */
9598
+ "bRegex": false,
9599
+
9600
+ /**
9601
+ * Flag to indicate if DataTables is to use its smart filtering or not.
9602
+ * @type boolean
9603
+ * @default true
9604
+ */
9605
+ "bSmart": true
9606
+ };
9607
+
9608
+
9609
+
9610
+
9611
+ /**
9612
+ * Template object for the way in which DataTables holds information about
9613
+ * each individual row. This is the object format used for the settings
9614
+ * aoData array.
9615
+ * @namespace
9616
+ */
9617
+ DataTable.models.oRow = {
9618
+ /**
9619
+ * TR element for the row
9620
+ * @type node
9621
+ * @default null
9622
+ */
9623
+ "nTr": null,
9624
+
9625
+ /**
9626
+ * Array of TD elements for each row. This is null until the row has been
9627
+ * created.
9628
+ * @type array nodes
9629
+ * @default []
9630
+ */
9631
+ "anCells": null,
9632
+
9633
+ /**
9634
+ * Data object from the original data source for the row. This is either
9635
+ * an array if using the traditional form of DataTables, or an object if
9636
+ * using mData options. The exact type will depend on the passed in
9637
+ * data from the data source, or will be an array if using DOM a data
9638
+ * source.
9639
+ * @type array|object
9640
+ * @default []
9641
+ */
9642
+ "_aData": [],
9643
+
9644
+ /**
9645
+ * Sorting data cache - this array is ostensibly the same length as the
9646
+ * number of columns (although each index is generated only as it is
9647
+ * needed), and holds the data that is used for sorting each column in the
9648
+ * row. We do this cache generation at the start of the sort in order that
9649
+ * the formatting of the sort data need be done only once for each cell
9650
+ * per sort. This array should not be read from or written to by anything
9651
+ * other than the master sorting methods.
9652
+ * @type array
9653
+ * @default null
9654
+ * @private
9655
+ */
9656
+ "_aSortData": null,
9657
+
9658
+ /**
9659
+ * Per cell filtering data cache. As per the sort data cache, used to
9660
+ * increase the performance of the filtering in DataTables
9661
+ * @type array
9662
+ * @default null
9663
+ * @private
9664
+ */
9665
+ "_aFilterData": null,
9666
+
9667
+ /**
9668
+ * Filtering data cache. This is the same as the cell filtering cache, but
9669
+ * in this case a string rather than an array. This is easily computed with
9670
+ * a join on `_aFilterData`, but is provided as a cache so the join isn't
9671
+ * needed on every search (memory traded for performance)
9672
+ * @type array
9673
+ * @default null
9674
+ * @private
9675
+ */
9676
+ "_sFilterRow": null,
9677
+
9678
+ /**
9679
+ * Cache of the class name that DataTables has applied to the row, so we
9680
+ * can quickly look at this variable rather than needing to do a DOM check
9681
+ * on className for the nTr property.
9682
+ * @type string
9683
+ * @default <i>Empty string</i>
9684
+ * @private
9685
+ */
9686
+ "_sRowStripe": "",
9687
+
9688
+ /**
9689
+ * Denote if the original data source was from the DOM, or the data source
9690
+ * object. This is used for invalidating data, so DataTables can
9691
+ * automatically read data from the original source, unless uninstructed
9692
+ * otherwise.
9693
+ * @type string
9694
+ * @default null
9695
+ * @private
9696
+ */
9697
+ "src": null,
9698
+
9699
+ /**
9700
+ * Index in the aoData array. This saves an indexOf lookup when we have the
9701
+ * object, but want to know the index
9702
+ * @type integer
9703
+ * @default -1
9704
+ * @private
9705
+ */
9706
+ "idx": -1
9707
+ };
9708
+
9709
+
9710
+ /**
9711
+ * Template object for the column information object in DataTables. This object
9712
+ * is held in the settings aoColumns array and contains all the information that
9713
+ * DataTables needs about each individual column.
9714
+ *
9715
+ * Note that this object is related to {@link DataTable.defaults.column}
9716
+ * but this one is the internal data store for DataTables's cache of columns.
9717
+ * It should NOT be manipulated outside of DataTables. Any configuration should
9718
+ * be done through the initialisation options.
9719
+ * @namespace
9720
+ */
9721
+ DataTable.models.oColumn = {
9722
+ /**
9723
+ * Column index. This could be worked out on-the-fly with $.inArray, but it
9724
+ * is faster to just hold it as a variable
9725
+ * @type integer
9726
+ * @default null
9727
+ */
9728
+ "idx": null,
9729
+
9730
+ /**
9731
+ * A list of the columns that sorting should occur on when this column
9732
+ * is sorted. That this property is an array allows multi-column sorting
9733
+ * to be defined for a column (for example first name / last name columns
9734
+ * would benefit from this). The values are integers pointing to the
9735
+ * columns to be sorted on (typically it will be a single integer pointing
9736
+ * at itself, but that doesn't need to be the case).
9737
+ * @type array
9738
+ */
9739
+ "aDataSort": null,
9740
+
9741
+ /**
9742
+ * Define the sorting directions that are applied to the column, in sequence
9743
+ * as the column is repeatedly sorted upon - i.e. the first value is used
9744
+ * as the sorting direction when the column if first sorted (clicked on).
9745
+ * Sort it again (click again) and it will move on to the next index.
9746
+ * Repeat until loop.
9747
+ * @type array
9748
+ */
9749
+ "asSorting": null,
9750
+
9751
+ /**
9752
+ * Flag to indicate if the column is searchable, and thus should be included
9753
+ * in the filtering or not.
9754
+ * @type boolean
9755
+ */
9756
+ "bSearchable": null,
9757
+
9758
+ /**
9759
+ * Flag to indicate if the column is sortable or not.
9760
+ * @type boolean
9761
+ */
9762
+ "bSortable": null,
9763
+
9764
+ /**
9765
+ * Flag to indicate if the column is currently visible in the table or not
9766
+ * @type boolean
9767
+ */
9768
+ "bVisible": null,
9769
+
9770
+ /**
9771
+ * Store for manual type assignment using the `column.type` option. This
9772
+ * is held in store so we can manipulate the column's `sType` property.
9773
+ * @type string
9774
+ * @default null
9775
+ * @private
9776
+ */
9777
+ "_sManualType": null,
9778
+
9779
+ /**
9780
+ * Flag to indicate if HTML5 data attributes should be used as the data
9781
+ * source for filtering or sorting. True is either are.
9782
+ * @type boolean
9783
+ * @default false
9784
+ * @private
9785
+ */
9786
+ "_bAttrSrc": false,
9787
+
9788
+ /**
9789
+ * Developer definable function that is called whenever a cell is created (Ajax source,
9790
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
9791
+ * allowing you to modify the DOM element (add background colour for example) when the
9792
+ * element is available.
9793
+ * @type function
9794
+ * @param {element} nTd The TD node that has been created
9795
+ * @param {*} sData The Data for the cell
9796
+ * @param {array|object} oData The data for the whole row
9797
+ * @param {int} iRow The row index for the aoData data store
9798
+ * @default null
9799
+ */
9800
+ "fnCreatedCell": null,
9801
+
9802
+ /**
9803
+ * Function to get data from a cell in a column. You should <b>never</b>
9804
+ * access data directly through _aData internally in DataTables - always use
9805
+ * the method attached to this property. It allows mData to function as
9806
+ * required. This function is automatically assigned by the column
9807
+ * initialisation method
9808
+ * @type function
9809
+ * @param {array|object} oData The data array/object for the array
9810
+ * (i.e. aoData[]._aData)
9811
+ * @param {string} sSpecific The specific data type you want to get -
9812
+ * 'display', 'type' 'filter' 'sort'
9813
+ * @returns {*} The data for the cell from the given row's data
9814
+ * @default null
9815
+ */
9816
+ "fnGetData": null,
9817
+
9818
+ /**
9819
+ * Function to set data for a cell in the column. You should <b>never</b>
9820
+ * set the data directly to _aData internally in DataTables - always use
9821
+ * this method. It allows mData to function as required. This function
9822
+ * is automatically assigned by the column initialisation method
9823
+ * @type function
9824
+ * @param {array|object} oData The data array/object for the array
9825
+ * (i.e. aoData[]._aData)
9826
+ * @param {*} sValue Value to set
9827
+ * @default null
9828
+ */
9829
+ "fnSetData": null,
9830
+
9831
+ /**
9832
+ * Property to read the value for the cells in the column from the data
9833
+ * source array / object. If null, then the default content is used, if a
9834
+ * function is given then the return from the function is used.
9835
+ * @type function|int|string|null
9836
+ * @default null
9837
+ */
9838
+ "mData": null,
9839
+
9840
+ /**
9841
+ * Partner property to mData which is used (only when defined) to get
9842
+ * the data - i.e. it is basically the same as mData, but without the
9843
+ * 'set' option, and also the data fed to it is the result from mData.
9844
+ * This is the rendering method to match the data method of mData.
9845
+ * @type function|int|string|null
9846
+ * @default null
9847
+ */
9848
+ "mRender": null,
9849
+
9850
+ /**
9851
+ * Unique header TH/TD element for this column - this is what the sorting
9852
+ * listener is attached to (if sorting is enabled.)
9853
+ * @type node
9854
+ * @default null
9855
+ */
9856
+ "nTh": null,
9857
+
9858
+ /**
9859
+ * Unique footer TH/TD element for this column (if there is one). Not used
9860
+ * in DataTables as such, but can be used for plug-ins to reference the
9861
+ * footer for each column.
9862
+ * @type node
9863
+ * @default null
9864
+ */
9865
+ "nTf": null,
9866
+
9867
+ /**
9868
+ * The class to apply to all TD elements in the table's TBODY for the column
9869
+ * @type string
9870
+ * @default null
9871
+ */
9872
+ "sClass": null,
9873
+
9874
+ /**
9875
+ * When DataTables calculates the column widths to assign to each column,
9876
+ * it finds the longest string in each column and then constructs a
9877
+ * temporary table and reads the widths from that. The problem with this
9878
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
9879
+ * string - thus the calculation can go wrong (doing it properly and putting
9880
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
9881
+ * a "work around" we provide this option. It will append its value to the
9882
+ * text that is found to be the longest string for the column - i.e. padding.
9883
+ * @type string
9884
+ */
9885
+ "sContentPadding": null,
9886
+
9887
+ /**
9888
+ * Allows a default value to be given for a column's data, and will be used
9889
+ * whenever a null data source is encountered (this can be because mData
9890
+ * is set to null, or because the data source itself is null).
9891
+ * @type string
9892
+ * @default null
9893
+ */
9894
+ "sDefaultContent": null,
9895
+
9896
+ /**
9897
+ * Name for the column, allowing reference to the column by name as well as
9898
+ * by index (needs a lookup to work by name).
9899
+ * @type string
9900
+ */
9901
+ "sName": null,
9902
+
9903
+ /**
9904
+ * Custom sorting data type - defines which of the available plug-ins in
9905
+ * afnSortData the custom sorting will use - if any is defined.
9906
+ * @type string
9907
+ * @default std
9908
+ */
9909
+ "sSortDataType": 'std',
9910
+
9911
+ /**
9912
+ * Class to be applied to the header element when sorting on this column
9913
+ * @type string
9914
+ * @default null
9915
+ */
9916
+ "sSortingClass": null,
9917
+
9918
+ /**
9919
+ * Class to be applied to the header element when sorting on this column -
9920
+ * when jQuery UI theming is used.
9921
+ * @type string
9922
+ * @default null
9923
+ */
9924
+ "sSortingClassJUI": null,
9925
+
9926
+ /**
9927
+ * Title of the column - what is seen in the TH element (nTh).
9928
+ * @type string
9929
+ */
9930
+ "sTitle": null,
9931
+
9932
+ /**
9933
+ * Column sorting and filtering type
9934
+ * @type string
9935
+ * @default null
9936
+ */
9937
+ "sType": null,
9938
+
9939
+ /**
9940
+ * Width of the column
9941
+ * @type string
9942
+ * @default null
9943
+ */
9944
+ "sWidth": null,
9945
+
9946
+ /**
9947
+ * Width of the column when it was first "encountered"
9948
+ * @type string
9949
+ * @default null
9950
+ */
9951
+ "sWidthOrig": null
9952
+ };
9953
+
9954
+
9955
+ /*
9956
+ * Developer note: The properties of the object below are given in Hungarian
9957
+ * notation, that was used as the interface for DataTables prior to v1.10, however
9958
+ * from v1.10 onwards the primary interface is camel case. In order to avoid
9959
+ * breaking backwards compatibility utterly with this change, the Hungarian
9960
+ * version is still, internally the primary interface, but is is not documented
9961
+ * - hence the @name tags in each doc comment. This allows a Javascript function
9962
+ * to create a map from Hungarian notation to camel case (going the other direction
9963
+ * would require each property to be listed, which would add around 3K to the size
9964
+ * of DataTables, while this method is about a 0.5K hit).
9965
+ *
9966
+ * Ultimately this does pave the way for Hungarian notation to be dropped
9967
+ * completely, but that is a massive amount of work and will break current
9968
+ * installs (therefore is on-hold until v2).
9969
+ */
9970
+
9971
+ /**
9972
+ * Initialisation options that can be given to DataTables at initialisation
9973
+ * time.
9974
+ * @namespace
9975
+ */
9976
+ DataTable.defaults = {
9977
+ /**
9978
+ * An array of data to use for the table, passed in at initialisation which
9979
+ * will be used in preference to any data which is already in the DOM. This is
9980
+ * particularly useful for constructing tables purely in Javascript, for
9981
+ * example with a custom Ajax call.
9982
+ * @type array
9983
+ * @default null
9984
+ *
9985
+ * @dtopt Option
9986
+ * @name DataTable.defaults.data
9987
+ *
9988
+ * @example
9989
+ * // Using a 2D array data source
9990
+ * $(document).ready( function () {
9991
+ * $('#example').dataTable( {
9992
+ * "data": [
9993
+ * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
9994
+ * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
9995
+ * ],
9996
+ * "columns": [
9997
+ * { "title": "Engine" },
9998
+ * { "title": "Browser" },
9999
+ * { "title": "Platform" },
10000
+ * { "title": "Version" },
10001
+ * { "title": "Grade" }
10002
+ * ]
10003
+ * } );
10004
+ * } );
10005
+ *
10006
+ * @example
10007
+ * // Using an array of objects as a data source (`data`)
10008
+ * $(document).ready( function () {
10009
+ * $('#example').dataTable( {
10010
+ * "data": [
10011
+ * {
10012
+ * "engine": "Trident",
10013
+ * "browser": "Internet Explorer 4.0",
10014
+ * "platform": "Win 95+",
10015
+ * "version": 4,
10016
+ * "grade": "X"
10017
+ * },
10018
+ * {
10019
+ * "engine": "Trident",
10020
+ * "browser": "Internet Explorer 5.0",
10021
+ * "platform": "Win 95+",
10022
+ * "version": 5,
10023
+ * "grade": "C"
10024
+ * }
10025
+ * ],
10026
+ * "columns": [
10027
+ * { "title": "Engine", "data": "engine" },
10028
+ * { "title": "Browser", "data": "browser" },
10029
+ * { "title": "Platform", "data": "platform" },
10030
+ * { "title": "Version", "data": "version" },
10031
+ * { "title": "Grade", "data": "grade" }
10032
+ * ]
10033
+ * } );
10034
+ * } );
10035
+ */
10036
+ "aaData": null,
10037
+
10038
+
10039
+ /**
10040
+ * If ordering is enabled, then DataTables will perform a first pass sort on
10041
+ * initialisation. You can define which column(s) the sort is performed
10042
+ * upon, and the sorting direction, with this variable. The `sorting` array
10043
+ * should contain an array for each column to be sorted initially containing
10044
+ * the column's index and a direction string ('asc' or 'desc').
10045
+ * @type array
10046
+ * @default [[0,'asc']]
10047
+ *
10048
+ * @dtopt Option
10049
+ * @name DataTable.defaults.order
10050
+ *
10051
+ * @example
10052
+ * // Sort by 3rd column first, and then 4th column
10053
+ * $(document).ready( function() {
10054
+ * $('#example').dataTable( {
10055
+ * "order": [[2,'asc'], [3,'desc']]
10056
+ * } );
10057
+ * } );
10058
+ *
10059
+ * // No initial sorting
10060
+ * $(document).ready( function() {
10061
+ * $('#example').dataTable( {
10062
+ * "order": []
10063
+ * } );
10064
+ * } );
10065
+ */
10066
+ "aaSorting": [[0,'asc']],
10067
+
10068
+
10069
+ /**
10070
+ * This parameter is basically identical to the `sorting` parameter, but
10071
+ * cannot be overridden by user interaction with the table. What this means
10072
+ * is that you could have a column (visible or hidden) which the sorting
10073
+ * will always be forced on first - any sorting after that (from the user)
10074
+ * will then be performed as required. This can be useful for grouping rows
10075
+ * together.
10076
+ * @type array
10077
+ * @default null
10078
+ *
10079
+ * @dtopt Option
10080
+ * @name DataTable.defaults.orderFixed
10081
+ *
10082
+ * @example
10083
+ * $(document).ready( function() {
10084
+ * $('#example').dataTable( {
10085
+ * "orderFixed": [[0,'asc']]
10086
+ * } );
10087
+ * } )
10088
+ */
10089
+ "aaSortingFixed": [],
10090
+
10091
+
10092
+ /**
10093
+ * DataTables can be instructed to load data to display in the table from a
10094
+ * Ajax source. This option defines how that Ajax call is made and where to.
10095
+ *
10096
+ * The `ajax` property has three different modes of operation, depending on
10097
+ * how it is defined. These are:
10098
+ *
10099
+ * * `string` - Set the URL from where the data should be loaded from.
10100
+ * * `object` - Define properties for `jQuery.ajax`.
10101
+ * * `function` - Custom data get function
10102
+ *
10103
+ * `string`
10104
+ * --------
10105
+ *
10106
+ * As a string, the `ajax` property simply defines the URL from which
10107
+ * DataTables will load data.
10108
+ *
10109
+ * `object`
10110
+ * --------
10111
+ *
10112
+ * As an object, the parameters in the object are passed to
10113
+ * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
10114
+ * of the Ajax request. DataTables has a number of default parameters which
10115
+ * you can override using this option. Please refer to the jQuery
10116
+ * documentation for a full description of the options available, although
10117
+ * the following parameters provide additional options in DataTables or
10118
+ * require special consideration:
10119
+ *
10120
+ * * `data` - As with jQuery, `data` can be provided as an object, but it
10121
+ * can also be used as a function to manipulate the data DataTables sends
10122
+ * to the server. The function takes a single parameter, an object of
10123
+ * parameters with the values that DataTables has readied for sending. An
10124
+ * object may be returned which will be merged into the DataTables
10125
+ * defaults, or you can add the items to the object that was passed in and
10126
+ * not return anything from the function. This supersedes `fnServerParams`
10127
+ * from DataTables 1.9-.
10128
+ *
10129
+ * * `dataSrc` - By default DataTables will look for the property `data` (or
10130
+ * `aaData` for compatibility with DataTables 1.9-) when obtaining data
10131
+ * from an Ajax source or for server-side processing - this parameter
10132
+ * allows that property to be changed. You can use Javascript dotted
10133
+ * object notation to get a data source for multiple levels of nesting, or
10134
+ * it my be used as a function. As a function it takes a single parameter,
10135
+ * the JSON returned from the server, which can be manipulated as
10136
+ * required, with the returned value being that used by DataTables as the
10137
+ * data source for the table. This supersedes `sAjaxDataProp` from
10138
+ * DataTables 1.9-.
10139
+ *
10140
+ * * `success` - Should not be overridden it is used internally in
10141
+ * DataTables. To manipulate / transform the data returned by the server
10142
+ * use `ajax.dataSrc`, or use `ajax` as a function (see below).
10143
+ *
10144
+ * `function`
10145
+ * ----------
10146
+ *
10147
+ * As a function, making the Ajax call is left up to yourself allowing
10148
+ * complete control of the Ajax request. Indeed, if desired, a method other
10149
+ * than Ajax could be used to obtain the required data, such as Web storage
10150
+ * or an AIR database.
10151
+ *
10152
+ * The function is given four parameters and no return is required. The
10153
+ * parameters are:
10154
+ *
10155
+ * 1. _object_ - Data to send to the server
10156
+ * 2. _function_ - Callback function that must be executed when the required
10157
+ * data has been obtained. That data should be passed into the callback
10158
+ * as the only parameter
10159
+ * 3. _object_ - DataTables settings object for the table
10160
+ *
10161
+ * Note that this supersedes `fnServerData` from DataTables 1.9-.
10162
+ *
10163
+ * @type string|object|function
10164
+ * @default null
10165
+ *
10166
+ * @dtopt Option
10167
+ * @name DataTable.defaults.ajax
10168
+ * @since 1.10.0
10169
+ *
10170
+ * @example
10171
+ * // Get JSON data from a file via Ajax.
10172
+ * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
10173
+ * $('#example').dataTable( {
10174
+ * "ajax": "data.json"
10175
+ * } );
10176
+ *
10177
+ * @example
10178
+ * // Get JSON data from a file via Ajax, using `dataSrc` to change
10179
+ * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
10180
+ * $('#example').dataTable( {
10181
+ * "ajax": {
10182
+ * "url": "data.json",
10183
+ * "dataSrc": "tableData"
10184
+ * }
10185
+ * } );
10186
+ *
10187
+ * @example
10188
+ * // Get JSON data from a file via Ajax, using `dataSrc` to read data
10189
+ * // from a plain array rather than an array in an object
10190
+ * $('#example').dataTable( {
10191
+ * "ajax": {
10192
+ * "url": "data.json",
10193
+ * "dataSrc": ""
10194
+ * }
10195
+ * } );
10196
+ *
10197
+ * @example
10198
+ * // Manipulate the data returned from the server - add a link to data
10199
+ * // (note this can, should, be done using `render` for the column - this
10200
+ * // is just a simple example of how the data can be manipulated).
10201
+ * $('#example').dataTable( {
10202
+ * "ajax": {
10203
+ * "url": "data.json",
10204
+ * "dataSrc": function ( json ) {
10205
+ * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
10206
+ * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
10207
+ * }
10208
+ * return json;
10209
+ * }
10210
+ * }
10211
+ * } );
10212
+ *
10213
+ * @example
10214
+ * // Add data to the request
10215
+ * $('#example').dataTable( {
10216
+ * "ajax": {
10217
+ * "url": "data.json",
10218
+ * "data": function ( d ) {
10219
+ * return {
10220
+ * "extra_search": $('#extra').val()
10221
+ * };
10222
+ * }
10223
+ * }
10224
+ * } );
10225
+ *
10226
+ * @example
10227
+ * // Send request as POST
10228
+ * $('#example').dataTable( {
10229
+ * "ajax": {
10230
+ * "url": "data.json",
10231
+ * "type": "POST"
10232
+ * }
10233
+ * } );
10234
+ *
10235
+ * @example
10236
+ * // Get the data from localStorage (could interface with a form for
10237
+ * // adding, editing and removing rows).
10238
+ * $('#example').dataTable( {
10239
+ * "ajax": function (data, callback, settings) {
10240
+ * callback(
10241
+ * JSON.parse( localStorage.getItem('dataTablesData') )
10242
+ * );
10243
+ * }
10244
+ * } );
10245
+ */
10246
+ "ajax": null,
10247
+
10248
+
10249
+ /**
10250
+ * This parameter allows you to readily specify the entries in the length drop
10251
+ * down menu that DataTables shows when pagination is enabled. It can be
10252
+ * either a 1D array of options which will be used for both the displayed
10253
+ * option and the value, or a 2D array which will use the array in the first
10254
+ * position as the value, and the array in the second position as the
10255
+ * displayed options (useful for language strings such as 'All').
10256
+ *
10257
+ * Note that the `pageLength` property will be automatically set to the
10258
+ * first value given in this array, unless `pageLength` is also provided.
10259
+ * @type array
10260
+ * @default [ 10, 25, 50, 100 ]
10261
+ *
10262
+ * @dtopt Option
10263
+ * @name DataTable.defaults.lengthMenu
10264
+ *
10265
+ * @example
10266
+ * $(document).ready( function() {
10267
+ * $('#example').dataTable( {
10268
+ * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
10269
+ * } );
10270
+ * } );
10271
+ */
10272
+ "aLengthMenu": [ 10, 25, 50, 100 ],
10273
+
10274
+
10275
+ /**
10276
+ * The `columns` option in the initialisation parameter allows you to define
10277
+ * details about the way individual columns behave. For a full list of
10278
+ * column options that can be set, please see
10279
+ * {@link DataTable.defaults.column}. Note that if you use `columns` to
10280
+ * define your columns, you must have an entry in the array for every single
10281
+ * column that you have in your table (these can be null if you don't which
10282
+ * to specify any options).
10283
+ * @member
10284
+ *
10285
+ * @name DataTable.defaults.column
10286
+ */
10287
+ "aoColumns": null,
10288
+
10289
+ /**
10290
+ * Very similar to `columns`, `columnDefs` allows you to target a specific
10291
+ * column, multiple columns, or all columns, using the `targets` property of
10292
+ * each object in the array. This allows great flexibility when creating
10293
+ * tables, as the `columnDefs` arrays can be of any length, targeting the
10294
+ * columns you specifically want. `columnDefs` may use any of the column
10295
+ * options available: {@link DataTable.defaults.column}, but it _must_
10296
+ * have `targets` defined in each object in the array. Values in the `targets`
10297
+ * array may be:
10298
+ * <ul>
10299
+ * <li>a string - class name will be matched on the TH for the column</li>
10300
+ * <li>0 or a positive integer - column index counting from the left</li>
10301
+ * <li>a negative integer - column index counting from the right</li>
10302
+ * <li>the string "_all" - all columns (i.e. assign a default)</li>
10303
+ * </ul>
10304
+ * @member
10305
+ *
10306
+ * @name DataTable.defaults.columnDefs
10307
+ */
10308
+ "aoColumnDefs": null,
10309
+
10310
+
10311
+ /**
10312
+ * Basically the same as `search`, this parameter defines the individual column
10313
+ * filtering state at initialisation time. The array must be of the same size
10314
+ * as the number of columns, and each element be an object with the parameters
10315
+ * `search` and `escapeRegex` (the latter is optional). 'null' is also
10316
+ * accepted and the default will be used.
10317
+ * @type array
10318
+ * @default []
10319
+ *
10320
+ * @dtopt Option
10321
+ * @name DataTable.defaults.searchCols
10322
+ *
10323
+ * @example
10324
+ * $(document).ready( function() {
10325
+ * $('#example').dataTable( {
10326
+ * "searchCols": [
10327
+ * null,
10328
+ * { "search": "My filter" },
10329
+ * null,
10330
+ * { "search": "^[0-9]", "escapeRegex": false }
10331
+ * ]
10332
+ * } );
10333
+ * } )
10334
+ */
10335
+ "aoSearchCols": [],
10336
+
10337
+
10338
+ /**
10339
+ * An array of CSS classes that should be applied to displayed rows. This
10340
+ * array may be of any length, and DataTables will apply each class
10341
+ * sequentially, looping when required.
10342
+ * @type array
10343
+ * @default null <i>Will take the values determined by the `oClasses.stripe*`
10344
+ * options</i>
10345
+ *
10346
+ * @dtopt Option
10347
+ * @name DataTable.defaults.stripeClasses
10348
+ *
10349
+ * @example
10350
+ * $(document).ready( function() {
10351
+ * $('#example').dataTable( {
10352
+ * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
10353
+ * } );
10354
+ * } )
10355
+ */
10356
+ "asStripeClasses": null,
10357
+
10358
+
10359
+ /**
10360
+ * Enable or disable automatic column width calculation. This can be disabled
10361
+ * as an optimisation (it takes some time to calculate the widths) if the
10362
+ * tables widths are passed in using `columns`.
10363
+ * @type boolean
10364
+ * @default true
10365
+ *
10366
+ * @dtopt Features
10367
+ * @name DataTable.defaults.autoWidth
10368
+ *
10369
+ * @example
10370
+ * $(document).ready( function () {
10371
+ * $('#example').dataTable( {
10372
+ * "autoWidth": false
10373
+ * } );
10374
+ * } );
10375
+ */
10376
+ "bAutoWidth": true,
10377
+
10378
+
10379
+ /**
10380
+ * Deferred rendering can provide DataTables with a huge speed boost when you
10381
+ * are using an Ajax or JS data source for the table. This option, when set to
10382
+ * true, will cause DataTables to defer the creation of the table elements for
10383
+ * each row until they are needed for a draw - saving a significant amount of
10384
+ * time.
10385
+ * @type boolean
10386
+ * @default false
10387
+ *
10388
+ * @dtopt Features
10389
+ * @name DataTable.defaults.deferRender
10390
+ *
10391
+ * @example
10392
+ * $(document).ready( function() {
10393
+ * $('#example').dataTable( {
10394
+ * "ajax": "sources/arrays.txt",
10395
+ * "deferRender": true
10396
+ * } );
10397
+ * } );
10398
+ */
10399
+ "bDeferRender": false,
10400
+
10401
+
10402
+ /**
10403
+ * Replace a DataTable which matches the given selector and replace it with
10404
+ * one which has the properties of the new initialisation object passed. If no
10405
+ * table matches the selector, then the new DataTable will be constructed as
10406
+ * per normal.
10407
+ * @type boolean
10408
+ * @default false
10409
+ *
10410
+ * @dtopt Options
10411
+ * @name DataTable.defaults.destroy
10412
+ *
10413
+ * @example
10414
+ * $(document).ready( function() {
10415
+ * $('#example').dataTable( {
10416
+ * "srollY": "200px",
10417
+ * "paginate": false
10418
+ * } );
10419
+ *
10420
+ * // Some time later....
10421
+ * $('#example').dataTable( {
10422
+ * "filter": false,
10423
+ * "destroy": true
10424
+ * } );
10425
+ * } );
10426
+ */
10427
+ "bDestroy": false,
10428
+
10429
+
10430
+ /**
10431
+ * Enable or disable filtering of data. Filtering in DataTables is "smart" in
10432
+ * that it allows the end user to input multiple words (space separated) and
10433
+ * will match a row containing those words, even if not in the order that was
10434
+ * specified (this allow matching across multiple columns). Note that if you
10435
+ * wish to use filtering in DataTables this must remain 'true' - to remove the
10436
+ * default filtering input box and retain filtering abilities, please use
10437
+ * {@link DataTable.defaults.dom}.
10438
+ * @type boolean
10439
+ * @default true
10440
+ *
10441
+ * @dtopt Features
10442
+ * @name DataTable.defaults.searching
10443
+ *
10444
+ * @example
10445
+ * $(document).ready( function () {
10446
+ * $('#example').dataTable( {
10447
+ * "searching": false
10448
+ * } );
10449
+ * } );
10450
+ */
10451
+ "bFilter": true,
10452
+
10453
+
10454
+ /**
10455
+ * Enable or disable the table information display. This shows information
10456
+ * about the data that is currently visible on the page, including information
10457
+ * about filtered data if that action is being performed.
10458
+ * @type boolean
10459
+ * @default true
10460
+ *
10461
+ * @dtopt Features
10462
+ * @name DataTable.defaults.info
10463
+ *
10464
+ * @example
10465
+ * $(document).ready( function () {
10466
+ * $('#example').dataTable( {
10467
+ * "info": false
10468
+ * } );
10469
+ * } );
10470
+ */
10471
+ "bInfo": true,
10472
+
10473
+
10474
+ /**
10475
+ * Allows the end user to select the size of a formatted page from a select
10476
+ * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
10477
+ * @type boolean
10478
+ * @default true
10479
+ *
10480
+ * @dtopt Features
10481
+ * @name DataTable.defaults.lengthChange
10482
+ *
10483
+ * @example
10484
+ * $(document).ready( function () {
10485
+ * $('#example').dataTable( {
10486
+ * "lengthChange": false
10487
+ * } );
10488
+ * } );
10489
+ */
10490
+ "bLengthChange": true,
10491
+
10492
+
10493
+ /**
10494
+ * Enable or disable pagination.
10495
+ * @type boolean
10496
+ * @default true
10497
+ *
10498
+ * @dtopt Features
10499
+ * @name DataTable.defaults.paging
10500
+ *
10501
+ * @example
10502
+ * $(document).ready( function () {
10503
+ * $('#example').dataTable( {
10504
+ * "paging": false
10505
+ * } );
10506
+ * } );
10507
+ */
10508
+ "bPaginate": true,
10509
+
10510
+
10511
+ /**
10512
+ * Enable or disable the display of a 'processing' indicator when the table is
10513
+ * being processed (e.g. a sort). This is particularly useful for tables with
10514
+ * large amounts of data where it can take a noticeable amount of time to sort
10515
+ * the entries.
10516
+ * @type boolean
10517
+ * @default false
10518
+ *
10519
+ * @dtopt Features
10520
+ * @name DataTable.defaults.processing
10521
+ *
10522
+ * @example
10523
+ * $(document).ready( function () {
10524
+ * $('#example').dataTable( {
10525
+ * "processing": true
10526
+ * } );
10527
+ * } );
10528
+ */
10529
+ "bProcessing": false,
10530
+
10531
+
10532
+ /**
10533
+ * Retrieve the DataTables object for the given selector. Note that if the
10534
+ * table has already been initialised, this parameter will cause DataTables
10535
+ * to simply return the object that has already been set up - it will not take
10536
+ * account of any changes you might have made to the initialisation object
10537
+ * passed to DataTables (setting this parameter to true is an acknowledgement
10538
+ * that you understand this). `destroy` can be used to reinitialise a table if
10539
+ * you need.
10540
+ * @type boolean
10541
+ * @default false
10542
+ *
10543
+ * @dtopt Options
10544
+ * @name DataTable.defaults.retrieve
10545
+ *
10546
+ * @example
10547
+ * $(document).ready( function() {
10548
+ * initTable();
10549
+ * tableActions();
10550
+ * } );
10551
+ *
10552
+ * function initTable ()
10553
+ * {
10554
+ * return $('#example').dataTable( {
10555
+ * "scrollY": "200px",
10556
+ * "paginate": false,
10557
+ * "retrieve": true
10558
+ * } );
10559
+ * }
10560
+ *
10561
+ * function tableActions ()
10562
+ * {
10563
+ * var table = initTable();
10564
+ * // perform API operations with oTable
10565
+ * }
10566
+ */
10567
+ "bRetrieve": false,
10568
+
10569
+
10570
+ /**
10571
+ * When vertical (y) scrolling is enabled, DataTables will force the height of
10572
+ * the table's viewport to the given height at all times (useful for layout).
10573
+ * However, this can look odd when filtering data down to a small data set,
10574
+ * and the footer is left "floating" further down. This parameter (when
10575
+ * enabled) will cause DataTables to collapse the table's viewport down when
10576
+ * the result set will fit within the given Y height.
10577
+ * @type boolean
10578
+ * @default false
10579
+ *
10580
+ * @dtopt Options
10581
+ * @name DataTable.defaults.scrollCollapse
10582
+ *
10583
+ * @example
10584
+ * $(document).ready( function() {
10585
+ * $('#example').dataTable( {
10586
+ * "scrollY": "200",
10587
+ * "scrollCollapse": true
10588
+ * } );
10589
+ * } );
10590
+ */
10591
+ "bScrollCollapse": false,
10592
+
10593
+
10594
+ /**
10595
+ * Configure DataTables to use server-side processing. Note that the
10596
+ * `ajax` parameter must also be given in order to give DataTables a
10597
+ * source to obtain the required data for each draw.
10598
+ * @type boolean
10599
+ * @default false
10600
+ *
10601
+ * @dtopt Features
10602
+ * @dtopt Server-side
10603
+ * @name DataTable.defaults.serverSide
10604
+ *
10605
+ * @example
10606
+ * $(document).ready( function () {
10607
+ * $('#example').dataTable( {
10608
+ * "serverSide": true,
10609
+ * "ajax": "xhr.php"
10610
+ * } );
10611
+ * } );
10612
+ */
10613
+ "bServerSide": false,
10614
+
10615
+
10616
+ /**
10617
+ * Enable or disable sorting of columns. Sorting of individual columns can be
10618
+ * disabled by the `sortable` option for each column.
10619
+ * @type boolean
10620
+ * @default true
10621
+ *
10622
+ * @dtopt Features
10623
+ * @name DataTable.defaults.ordering
10624
+ *
10625
+ * @example
10626
+ * $(document).ready( function () {
10627
+ * $('#example').dataTable( {
10628
+ * "ordering": false
10629
+ * } );
10630
+ * } );
10631
+ */
10632
+ "bSort": true,
10633
+
10634
+
10635
+ /**
10636
+ * Enable or display DataTables' ability to sort multiple columns at the
10637
+ * same time (activated by shift-click by the user).
10638
+ * @type boolean
10639
+ * @default true
10640
+ *
10641
+ * @dtopt Options
10642
+ * @name DataTable.defaults.orderMulti
10643
+ *
10644
+ * @example
10645
+ * // Disable multiple column sorting ability
10646
+ * $(document).ready( function () {
10647
+ * $('#example').dataTable( {
10648
+ * "orderMulti": false
10649
+ * } );
10650
+ * } );
10651
+ */
10652
+ "bSortMulti": true,
10653
+
10654
+
10655
+ /**
10656
+ * Allows control over whether DataTables should use the top (true) unique
10657
+ * cell that is found for a single column, or the bottom (false - default).
10658
+ * This is useful when using complex headers.
10659
+ * @type boolean
10660
+ * @default false
10661
+ *
10662
+ * @dtopt Options
10663
+ * @name DataTable.defaults.orderCellsTop
10664
+ *
10665
+ * @example
10666
+ * $(document).ready( function() {
10667
+ * $('#example').dataTable( {
10668
+ * "orderCellsTop": true
10669
+ * } );
10670
+ * } );
10671
+ */
10672
+ "bSortCellsTop": false,
10673
+
10674
+
10675
+ /**
10676
+ * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10677
+ * `sorting\_3` to the columns which are currently being sorted on. This is
10678
+ * presented as a feature switch as it can increase processing time (while
10679
+ * classes are removed and added) so for large data sets you might want to
10680
+ * turn this off.
10681
+ * @type boolean
10682
+ * @default true
10683
+ *
10684
+ * @dtopt Features
10685
+ * @name DataTable.defaults.orderClasses
10686
+ *
10687
+ * @example
10688
+ * $(document).ready( function () {
10689
+ * $('#example').dataTable( {
10690
+ * "orderClasses": false
10691
+ * } );
10692
+ * } );
10693
+ */
10694
+ "bSortClasses": true,
10695
+
10696
+
10697
+ /**
10698
+ * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10699
+ * used to save table display information such as pagination information,
10700
+ * display length, filtering and sorting. As such when the end user reloads
10701
+ * the page the display display will match what thy had previously set up.
10702
+ *
10703
+ * Due to the use of `localStorage` the default state saving is not supported
10704
+ * in IE6 or 7. If state saving is required in those browsers, use
10705
+ * `stateSaveCallback` to provide a storage solution such as cookies.
10706
+ * @type boolean
10707
+ * @default false
10708
+ *
10709
+ * @dtopt Features
10710
+ * @name DataTable.defaults.stateSave
10711
+ *
10712
+ * @example
10713
+ * $(document).ready( function () {
10714
+ * $('#example').dataTable( {
10715
+ * "stateSave": true
10716
+ * } );
10717
+ * } );
10718
+ */
10719
+ "bStateSave": false,
10720
+
10721
+
10722
+ /**
10723
+ * This function is called when a TR element is created (and all TD child
10724
+ * elements have been inserted), or registered if using a DOM source, allowing
10725
+ * manipulation of the TR element (adding classes etc).
10726
+ * @type function
10727
+ * @param {node} row "TR" element for the current row
10728
+ * @param {array} data Raw data array for this row
10729
+ * @param {int} dataIndex The index of this row in the internal aoData array
10730
+ *
10731
+ * @dtopt Callbacks
10732
+ * @name DataTable.defaults.createdRow
10733
+ *
10734
+ * @example
10735
+ * $(document).ready( function() {
10736
+ * $('#example').dataTable( {
10737
+ * "createdRow": function( row, data, dataIndex ) {
10738
+ * // Bold the grade for all 'A' grade browsers
10739
+ * if ( data[4] == "A" )
10740
+ * {
10741
+ * $('td:eq(4)', row).html( '<b>A</b>' );
10742
+ * }
10743
+ * }
10744
+ * } );
10745
+ * } );
10746
+ */
10747
+ "fnCreatedRow": null,
10748
+
10749
+
10750
+ /**
10751
+ * This function is called on every 'draw' event, and allows you to
10752
+ * dynamically modify any aspect you want about the created DOM.
10753
+ * @type function
10754
+ * @param {object} settings DataTables settings object
10755
+ *
10756
+ * @dtopt Callbacks
10757
+ * @name DataTable.defaults.drawCallback
10758
+ *
10759
+ * @example
10760
+ * $(document).ready( function() {
10761
+ * $('#example').dataTable( {
10762
+ * "drawCallback": function( settings ) {
10763
+ * alert( 'DataTables has redrawn the table' );
10764
+ * }
10765
+ * } );
10766
+ * } );
10767
+ */
10768
+ "fnDrawCallback": null,
10769
+
10770
+
10771
+ /**
10772
+ * Identical to fnHeaderCallback() but for the table footer this function
10773
+ * allows you to modify the table footer on every 'draw' event.
10774
+ * @type function
10775
+ * @param {node} foot "TR" element for the footer
10776
+ * @param {array} data Full table data (as derived from the original HTML)
10777
+ * @param {int} start Index for the current display starting point in the
10778
+ * display array
10779
+ * @param {int} end Index for the current display ending point in the
10780
+ * display array
10781
+ * @param {array int} display Index array to translate the visual position
10782
+ * to the full data array
10783
+ *
10784
+ * @dtopt Callbacks
10785
+ * @name DataTable.defaults.footerCallback
10786
+ *
10787
+ * @example
10788
+ * $(document).ready( function() {
10789
+ * $('#example').dataTable( {
10790
+ * "footerCallback": function( tfoot, data, start, end, display ) {
10791
+ * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
10792
+ * }
10793
+ * } );
10794
+ * } )
10795
+ */
10796
+ "fnFooterCallback": null,
10797
+
10798
+
10799
+ /**
10800
+ * When rendering large numbers in the information element for the table
10801
+ * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
10802
+ * to have a comma separator for the 'thousands' units (e.g. 1 million is
10803
+ * rendered as "1,000,000") to help readability for the end user. This
10804
+ * function will override the default method DataTables uses.
10805
+ * @type function
10806
+ * @member
10807
+ * @param {int} toFormat number to be formatted
10808
+ * @returns {string} formatted string for DataTables to show the number
10809
+ *
10810
+ * @dtopt Callbacks
10811
+ * @name DataTable.defaults.formatNumber
10812
+ *
10813
+ * @example
10814
+ * // Format a number using a single quote for the separator (note that
10815
+ * // this can also be done with the language.thousands option)
10816
+ * $(document).ready( function() {
10817
+ * $('#example').dataTable( {
10818
+ * "formatNumber": function ( toFormat ) {
10819
+ * return toFormat.toString().replace(
10820
+ * /\B(?=(\d{3})+(?!\d))/g, "'"
10821
+ * );
10822
+ * };
10823
+ * } );
10824
+ * } );
10825
+ */
10826
+ "fnFormatNumber": function ( toFormat ) {
10827
+ return toFormat.toString().replace(
10828
+ /\B(?=(\d{3})+(?!\d))/g,
10829
+ this.oLanguage.sThousands
10830
+ );
10831
+ },
10832
+
10833
+
10834
+ /**
10835
+ * This function is called on every 'draw' event, and allows you to
10836
+ * dynamically modify the header row. This can be used to calculate and
10837
+ * display useful information about the table.
10838
+ * @type function
10839
+ * @param {node} head "TR" element for the header
10840
+ * @param {array} data Full table data (as derived from the original HTML)
10841
+ * @param {int} start Index for the current display starting point in the
10842
+ * display array
10843
+ * @param {int} end Index for the current display ending point in the
10844
+ * display array
10845
+ * @param {array int} display Index array to translate the visual position
10846
+ * to the full data array
10847
+ *
10848
+ * @dtopt Callbacks
10849
+ * @name DataTable.defaults.headerCallback
10850
+ *
10851
+ * @example
10852
+ * $(document).ready( function() {
10853
+ * $('#example').dataTable( {
10854
+ * "fheaderCallback": function( head, data, start, end, display ) {
10855
+ * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
10856
+ * }
10857
+ * } );
10858
+ * } )
10859
+ */
10860
+ "fnHeaderCallback": null,
10861
+
10862
+
10863
+ /**
10864
+ * The information element can be used to convey information about the current
10865
+ * state of the table. Although the internationalisation options presented by
10866
+ * DataTables are quite capable of dealing with most customisations, there may
10867
+ * be times where you wish to customise the string further. This callback
10868
+ * allows you to do exactly that.
10869
+ * @type function
10870
+ * @param {object} oSettings DataTables settings object
10871
+ * @param {int} start Starting position in data for the draw
10872
+ * @param {int} end End position in data for the draw
10873
+ * @param {int} max Total number of rows in the table (regardless of
10874
+ * filtering)
10875
+ * @param {int} total Total number of rows in the data set, after filtering
10876
+ * @param {string} pre The string that DataTables has formatted using it's
10877
+ * own rules
10878
+ * @returns {string} The string to be displayed in the information element.
10879
+ *
10880
+ * @dtopt Callbacks
10881
+ * @name DataTable.defaults.infoCallback
10882
+ *
10883
+ * @example
10884
+ * $('#example').dataTable( {
10885
+ * "infoCallback": function( settings, start, end, max, total, pre ) {
10886
+ * return start +" to "+ end;
10887
+ * }
10888
+ * } );
10889
+ */
10890
+ "fnInfoCallback": null,
10891
+
10892
+
10893
+ /**
10894
+ * Called when the table has been initialised. Normally DataTables will
10895
+ * initialise sequentially and there will be no need for this function,
10896
+ * however, this does not hold true when using external language information
10897
+ * since that is obtained using an async XHR call.
10898
+ * @type function
10899
+ * @param {object} settings DataTables settings object
10900
+ * @param {object} json The JSON object request from the server - only
10901
+ * present if client-side Ajax sourced data is used
10902
+ *
10903
+ * @dtopt Callbacks
10904
+ * @name DataTable.defaults.initComplete
10905
+ *
10906
+ * @example
10907
+ * $(document).ready( function() {
10908
+ * $('#example').dataTable( {
10909
+ * "initComplete": function(settings, json) {
10910
+ * alert( 'DataTables has finished its initialisation.' );
10911
+ * }
10912
+ * } );
10913
+ * } )
10914
+ */
10915
+ "fnInitComplete": null,
10916
+
10917
+
10918
+ /**
10919
+ * Called at the very start of each table draw and can be used to cancel the
10920
+ * draw by returning false, any other return (including undefined) results in
10921
+ * the full draw occurring).
10922
+ * @type function
10923
+ * @param {object} settings DataTables settings object
10924
+ * @returns {boolean} False will cancel the draw, anything else (including no
10925
+ * return) will allow it to complete.
10926
+ *
10927
+ * @dtopt Callbacks
10928
+ * @name DataTable.defaults.preDrawCallback
10929
+ *
10930
+ * @example
10931
+ * $(document).ready( function() {
10932
+ * $('#example').dataTable( {
10933
+ * "preDrawCallback": function( settings ) {
10934
+ * if ( $('#test').val() == 1 ) {
10935
+ * return false;
10936
+ * }
10937
+ * }
10938
+ * } );
10939
+ * } );
10940
+ */
10941
+ "fnPreDrawCallback": null,
10942
+
10943
+
10944
+ /**
10945
+ * This function allows you to 'post process' each row after it have been
10946
+ * generated for each table draw, but before it is rendered on screen. This
10947
+ * function might be used for setting the row class name etc.
10948
+ * @type function
10949
+ * @param {node} row "TR" element for the current row
10950
+ * @param {array} data Raw data array for this row
10951
+ * @param {int} displayIndex The display index for the current table draw
10952
+ * @param {int} displayIndexFull The index of the data in the full list of
10953
+ * rows (after filtering)
10954
+ *
10955
+ * @dtopt Callbacks
10956
+ * @name DataTable.defaults.rowCallback
10957
+ *
10958
+ * @example
10959
+ * $(document).ready( function() {
10960
+ * $('#example').dataTable( {
10961
+ * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
10962
+ * // Bold the grade for all 'A' grade browsers
10963
+ * if ( data[4] == "A" ) {
10964
+ * $('td:eq(4)', row).html( '<b>A</b>' );
10965
+ * }
10966
+ * }
10967
+ * } );
10968
+ * } );
10969
+ */
10970
+ "fnRowCallback": null,
10971
+
10972
+
10973
+ /**
10974
+ * __Deprecated__ The functionality provided by this parameter has now been
10975
+ * superseded by that provided through `ajax`, which should be used instead.
10976
+ *
10977
+ * This parameter allows you to override the default function which obtains
10978
+ * the data from the server so something more suitable for your application.
10979
+ * For example you could use POST data, or pull information from a Gears or
10980
+ * AIR database.
10981
+ * @type function
10982
+ * @member
10983
+ * @param {string} source HTTP source to obtain the data from (`ajax`)
10984
+ * @param {array} data A key/value pair object containing the data to send
10985
+ * to the server
10986
+ * @param {function} callback to be called on completion of the data get
10987
+ * process that will draw the data on the page.
10988
+ * @param {object} settings DataTables settings object
10989
+ *
10990
+ * @dtopt Callbacks
10991
+ * @dtopt Server-side
10992
+ * @name DataTable.defaults.serverData
10993
+ *
10994
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
10995
+ */
10996
+ "fnServerData": null,
10997
+
10998
+
10999
+ /**
11000
+ * __Deprecated__ The functionality provided by this parameter has now been
11001
+ * superseded by that provided through `ajax`, which should be used instead.
11002
+ *
11003
+ * It is often useful to send extra data to the server when making an Ajax
11004
+ * request - for example custom filtering information, and this callback
11005
+ * function makes it trivial to send extra information to the server. The
11006
+ * passed in parameter is the data set that has been constructed by
11007
+ * DataTables, and you can add to this or modify it as you require.
11008
+ * @type function
11009
+ * @param {array} data Data array (array of objects which are name/value
11010
+ * pairs) that has been constructed by DataTables and will be sent to the
11011
+ * server. In the case of Ajax sourced data with server-side processing
11012
+ * this will be an empty array, for server-side processing there will be a
11013
+ * significant number of parameters!
11014
+ * @returns {undefined} Ensure that you modify the data array passed in,
11015
+ * as this is passed by reference.
11016
+ *
11017
+ * @dtopt Callbacks
11018
+ * @dtopt Server-side
11019
+ * @name DataTable.defaults.serverParams
11020
+ *
11021
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
11022
+ */
11023
+ "fnServerParams": null,
11024
+
11025
+
11026
+ /**
11027
+ * Load the table state. With this function you can define from where, and how, the
11028
+ * state of a table is loaded. By default DataTables will load from `localStorage`
11029
+ * but you might wish to use a server-side database or cookies.
11030
+ * @type function
11031
+ * @member
11032
+ * @param {object} settings DataTables settings object
11033
+ * @param {object} callback Callback that can be executed when done. It
11034
+ * should be passed the loaded state object.
11035
+ * @return {object} The DataTables state object to be loaded
11036
+ *
11037
+ * @dtopt Callbacks
11038
+ * @name DataTable.defaults.stateLoadCallback
11039
+ *
11040
+ * @example
11041
+ * $(document).ready( function() {
11042
+ * $('#example').dataTable( {
11043
+ * "stateSave": true,
11044
+ * "stateLoadCallback": function (settings, callback) {
11045
+ * $.ajax( {
11046
+ * "url": "/state_load",
11047
+ * "dataType": "json",
11048
+ * "success": function (json) {
11049
+ * callback( json );
11050
+ * }
11051
+ * } );
11052
+ * }
11053
+ * } );
11054
+ * } );
11055
+ */
11056
+ "fnStateLoadCallback": function ( settings ) {
11057
+ try {
11058
+ return JSON.parse(
11059
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
11060
+ 'DataTables_'+settings.sInstance+'_'+location.pathname
11061
+ )
11062
+ );
11063
+ } catch (e) {
11064
+ return {};
11065
+ }
11066
+ },
11067
+
11068
+
11069
+ /**
11070
+ * Callback which allows modification of the saved state prior to loading that state.
11071
+ * This callback is called when the table is loading state from the stored data, but
11072
+ * prior to the settings object being modified by the saved state. Note that for
11073
+ * plug-in authors, you should use the `stateLoadParams` event to load parameters for
11074
+ * a plug-in.
11075
+ * @type function
11076
+ * @param {object} settings DataTables settings object
11077
+ * @param {object} data The state object that is to be loaded
11078
+ *
11079
+ * @dtopt Callbacks
11080
+ * @name DataTable.defaults.stateLoadParams
11081
+ *
11082
+ * @example
11083
+ * // Remove a saved filter, so filtering is never loaded
11084
+ * $(document).ready( function() {
11085
+ * $('#example').dataTable( {
11086
+ * "stateSave": true,
11087
+ * "stateLoadParams": function (settings, data) {
11088
+ * data.oSearch.sSearch = "";
11089
+ * }
11090
+ * } );
11091
+ * } );
11092
+ *
11093
+ * @example
11094
+ * // Disallow state loading by returning false
11095
+ * $(document).ready( function() {
11096
+ * $('#example').dataTable( {
11097
+ * "stateSave": true,
11098
+ * "stateLoadParams": function (settings, data) {
11099
+ * return false;
11100
+ * }
11101
+ * } );
11102
+ * } );
11103
+ */
11104
+ "fnStateLoadParams": null,
11105
+
11106
+
11107
+ /**
11108
+ * Callback that is called when the state has been loaded from the state saving method
11109
+ * and the DataTables settings object has been modified as a result of the loaded state.
11110
+ * @type function
11111
+ * @param {object} settings DataTables settings object
11112
+ * @param {object} data The state object that was loaded
11113
+ *
11114
+ * @dtopt Callbacks
11115
+ * @name DataTable.defaults.stateLoaded
11116
+ *
11117
+ * @example
11118
+ * // Show an alert with the filtering value that was saved
11119
+ * $(document).ready( function() {
11120
+ * $('#example').dataTable( {
11121
+ * "stateSave": true,
11122
+ * "stateLoaded": function (settings, data) {
11123
+ * alert( 'Saved filter was: '+data.oSearch.sSearch );
11124
+ * }
11125
+ * } );
11126
+ * } );
11127
+ */
11128
+ "fnStateLoaded": null,
11129
+
11130
+
11131
+ /**
11132
+ * Save the table state. This function allows you to define where and how the state
11133
+ * information for the table is stored By default DataTables will use `localStorage`
11134
+ * but you might wish to use a server-side database or cookies.
11135
+ * @type function
11136
+ * @member
11137
+ * @param {object} settings DataTables settings object
11138
+ * @param {object} data The state object to be saved
11139
+ *
11140
+ * @dtopt Callbacks
11141
+ * @name DataTable.defaults.stateSaveCallback
11142
+ *
11143
+ * @example
11144
+ * $(document).ready( function() {
11145
+ * $('#example').dataTable( {
11146
+ * "stateSave": true,
11147
+ * "stateSaveCallback": function (settings, data) {
11148
+ * // Send an Ajax request to the server with the state object
11149
+ * $.ajax( {
11150
+ * "url": "/state_save",
11151
+ * "data": data,
11152
+ * "dataType": "json",
11153
+ * "method": "POST"
11154
+ * "success": function () {}
11155
+ * } );
11156
+ * }
11157
+ * } );
11158
+ * } );
11159
+ */
11160
+ "fnStateSaveCallback": function ( settings, data ) {
11161
+ try {
11162
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
11163
+ 'DataTables_'+settings.sInstance+'_'+location.pathname,
11164
+ JSON.stringify( data )
11165
+ );
11166
+ } catch (e) {}
11167
+ },
11168
+
11169
+
11170
+ /**
11171
+ * Callback which allows modification of the state to be saved. Called when the table
11172
+ * has changed state a new state save is required. This method allows modification of
11173
+ * the state saving object prior to actually doing the save, including addition or
11174
+ * other state properties or modification. Note that for plug-in authors, you should
11175
+ * use the `stateSaveParams` event to save parameters for a plug-in.
11176
+ * @type function
11177
+ * @param {object} settings DataTables settings object
11178
+ * @param {object} data The state object to be saved
11179
+ *
11180
+ * @dtopt Callbacks
11181
+ * @name DataTable.defaults.stateSaveParams
11182
+ *
11183
+ * @example
11184
+ * // Remove a saved filter, so filtering is never saved
11185
+ * $(document).ready( function() {
11186
+ * $('#example').dataTable( {
11187
+ * "stateSave": true,
11188
+ * "stateSaveParams": function (settings, data) {
11189
+ * data.oSearch.sSearch = "";
11190
+ * }
11191
+ * } );
11192
+ * } );
11193
+ */
11194
+ "fnStateSaveParams": null,
11195
+
11196
+
11197
+ /**
11198
+ * Duration for which the saved state information is considered valid. After this period
11199
+ * has elapsed the state will be returned to the default.
11200
+ * Value is given in seconds.
11201
+ * @type int
11202
+ * @default 7200 <i>(2 hours)</i>
11203
+ *
11204
+ * @dtopt Options
11205
+ * @name DataTable.defaults.stateDuration
11206
+ *
11207
+ * @example
11208
+ * $(document).ready( function() {
11209
+ * $('#example').dataTable( {
11210
+ * "stateDuration": 60*60*24; // 1 day
11211
+ * } );
11212
+ * } )
11213
+ */
11214
+ "iStateDuration": 7200,
11215
+
11216
+
11217
+ /**
11218
+ * When enabled DataTables will not make a request to the server for the first
11219
+ * page draw - rather it will use the data already on the page (no sorting etc
11220
+ * will be applied to it), thus saving on an XHR at load time. `deferLoading`
11221
+ * is used to indicate that deferred loading is required, but it is also used
11222
+ * to tell DataTables how many records there are in the full table (allowing
11223
+ * the information element and pagination to be displayed correctly). In the case
11224
+ * where a filtering is applied to the table on initial load, this can be
11225
+ * indicated by giving the parameter as an array, where the first element is
11226
+ * the number of records available after filtering and the second element is the
11227
+ * number of records without filtering (allowing the table information element
11228
+ * to be shown correctly).
11229
+ * @type int | array
11230
+ * @default null
11231
+ *
11232
+ * @dtopt Options
11233
+ * @name DataTable.defaults.deferLoading
11234
+ *
11235
+ * @example
11236
+ * // 57 records available in the table, no filtering applied
11237
+ * $(document).ready( function() {
11238
+ * $('#example').dataTable( {
11239
+ * "serverSide": true,
11240
+ * "ajax": "scripts/server_processing.php",
11241
+ * "deferLoading": 57
11242
+ * } );
11243
+ * } );
11244
+ *
11245
+ * @example
11246
+ * // 57 records after filtering, 100 without filtering (an initial filter applied)
11247
+ * $(document).ready( function() {
11248
+ * $('#example').dataTable( {
11249
+ * "serverSide": true,
11250
+ * "ajax": "scripts/server_processing.php",
11251
+ * "deferLoading": [ 57, 100 ],
11252
+ * "search": {
11253
+ * "search": "my_filter"
11254
+ * }
11255
+ * } );
11256
+ * } );
11257
+ */
11258
+ "iDeferLoading": null,
11259
+
11260
+
11261
+ /**
11262
+ * Number of rows to display on a single page when using pagination. If
11263
+ * feature enabled (`lengthChange`) then the end user will be able to override
11264
+ * this to a custom setting using a pop-up menu.
11265
+ * @type int
11266
+ * @default 10
11267
+ *
11268
+ * @dtopt Options
11269
+ * @name DataTable.defaults.pageLength
11270
+ *
11271
+ * @example
11272
+ * $(document).ready( function() {
11273
+ * $('#example').dataTable( {
11274
+ * "pageLength": 50
11275
+ * } );
11276
+ * } )
11277
+ */
11278
+ "iDisplayLength": 10,
11279
+
11280
+
11281
+ /**
11282
+ * Define the starting point for data display when using DataTables with
11283
+ * pagination. Note that this parameter is the number of records, rather than
11284
+ * the page number, so if you have 10 records per page and want to start on
11285
+ * the third page, it should be "20".
11286
+ * @type int
11287
+ * @default 0
11288
+ *
11289
+ * @dtopt Options
11290
+ * @name DataTable.defaults.displayStart
11291
+ *
11292
+ * @example
11293
+ * $(document).ready( function() {
11294
+ * $('#example').dataTable( {
11295
+ * "displayStart": 20
11296
+ * } );
11297
+ * } )
11298
+ */
11299
+ "iDisplayStart": 0,
11300
+
11301
+
11302
+ /**
11303
+ * By default DataTables allows keyboard navigation of the table (sorting, paging,
11304
+ * and filtering) by adding a `tabindex` attribute to the required elements. This
11305
+ * allows you to tab through the controls and press the enter key to activate them.
11306
+ * The tabindex is default 0, meaning that the tab follows the flow of the document.
11307
+ * You can overrule this using this parameter if you wish. Use a value of -1 to
11308
+ * disable built-in keyboard navigation.
11309
+ * @type int
11310
+ * @default 0
11311
+ *
11312
+ * @dtopt Options
11313
+ * @name DataTable.defaults.tabIndex
11314
+ *
11315
+ * @example
11316
+ * $(document).ready( function() {
11317
+ * $('#example').dataTable( {
11318
+ * "tabIndex": 1
11319
+ * } );
11320
+ * } );
11321
+ */
11322
+ "iTabIndex": 0,
11323
+
11324
+
11325
+ /**
11326
+ * Classes that DataTables assigns to the various components and features
11327
+ * that it adds to the HTML table. This allows classes to be configured
11328
+ * during initialisation in addition to through the static
11329
+ * {@link DataTable.ext.oStdClasses} object).
11330
+ * @namespace
11331
+ * @name DataTable.defaults.classes
11332
+ */
11333
+ "oClasses": {},
11334
+
11335
+
11336
+ /**
11337
+ * All strings that DataTables uses in the user interface that it creates
11338
+ * are defined in this object, allowing you to modified them individually or
11339
+ * completely replace them all as required.
11340
+ * @namespace
11341
+ * @name DataTable.defaults.language
11342
+ */
11343
+ "oLanguage": {
11344
+ /**
11345
+ * Strings that are used for WAI-ARIA labels and controls only (these are not
11346
+ * actually visible on the page, but will be read by screenreaders, and thus
11347
+ * must be internationalised as well).
11348
+ * @namespace
11349
+ * @name DataTable.defaults.language.aria
11350
+ */
11351
+ "oAria": {
11352
+ /**
11353
+ * ARIA label that is added to the table headers when the column may be
11354
+ * sorted ascending by activing the column (click or return when focused).
11355
+ * Note that the column header is prefixed to this string.
11356
+ * @type string
11357
+ * @default : activate to sort column ascending
11358
+ *
11359
+ * @dtopt Language
11360
+ * @name DataTable.defaults.language.aria.sortAscending
11361
+ *
11362
+ * @example
11363
+ * $(document).ready( function() {
11364
+ * $('#example').dataTable( {
11365
+ * "language": {
11366
+ * "aria": {
11367
+ * "sortAscending": " - click/return to sort ascending"
11368
+ * }
11369
+ * }
11370
+ * } );
11371
+ * } );
11372
+ */
11373
+ "sSortAscending": ": activate to sort column ascending",
11374
+
11375
+ /**
11376
+ * ARIA label that is added to the table headers when the column may be
11377
+ * sorted descending by activing the column (click or return when focused).
11378
+ * Note that the column header is prefixed to this string.
11379
+ * @type string
11380
+ * @default : activate to sort column ascending
11381
+ *
11382
+ * @dtopt Language
11383
+ * @name DataTable.defaults.language.aria.sortDescending
11384
+ *
11385
+ * @example
11386
+ * $(document).ready( function() {
11387
+ * $('#example').dataTable( {
11388
+ * "language": {
11389
+ * "aria": {
11390
+ * "sortDescending": " - click/return to sort descending"
11391
+ * }
11392
+ * }
11393
+ * } );
11394
+ * } );
11395
+ */
11396
+ "sSortDescending": ": activate to sort column descending"
11397
+ },
11398
+
11399
+ /**
11400
+ * Pagination string used by DataTables for the built-in pagination
11401
+ * control types.
11402
+ * @namespace
11403
+ * @name DataTable.defaults.language.paginate
11404
+ */
11405
+ "oPaginate": {
11406
+ /**
11407
+ * Text to use when using the 'full_numbers' type of pagination for the
11408
+ * button to take the user to the first page.
11409
+ * @type string
11410
+ * @default First
11411
+ *
11412
+ * @dtopt Language
11413
+ * @name DataTable.defaults.language.paginate.first
11414
+ *
11415
+ * @example
11416
+ * $(document).ready( function() {
11417
+ * $('#example').dataTable( {
11418
+ * "language": {
11419
+ * "paginate": {
11420
+ * "first": "First page"
11421
+ * }
11422
+ * }
11423
+ * } );
11424
+ * } );
11425
+ */
11426
+ "sFirst": "First",
11427
+
11428
+
11429
+ /**
11430
+ * Text to use when using the 'full_numbers' type of pagination for the
11431
+ * button to take the user to the last page.
11432
+ * @type string
11433
+ * @default Last
11434
+ *
11435
+ * @dtopt Language
11436
+ * @name DataTable.defaults.language.paginate.last
11437
+ *
11438
+ * @example
11439
+ * $(document).ready( function() {
11440
+ * $('#example').dataTable( {
11441
+ * "language": {
11442
+ * "paginate": {
11443
+ * "last": "Last page"
11444
+ * }
11445
+ * }
11446
+ * } );
11447
+ * } );
11448
+ */
11449
+ "sLast": "Last",
11450
+
11451
+
11452
+ /**
11453
+ * Text to use for the 'next' pagination button (to take the user to the
11454
+ * next page).
11455
+ * @type string
11456
+ * @default Next
11457
+ *
11458
+ * @dtopt Language
11459
+ * @name DataTable.defaults.language.paginate.next
11460
+ *
11461
+ * @example
11462
+ * $(document).ready( function() {
11463
+ * $('#example').dataTable( {
11464
+ * "language": {
11465
+ * "paginate": {
11466
+ * "next": "Next page"
11467
+ * }
11468
+ * }
11469
+ * } );
11470
+ * } );
11471
+ */
11472
+ "sNext": "Next",
11473
+
11474
+
11475
+ /**
11476
+ * Text to use for the 'previous' pagination button (to take the user to
11477
+ * the previous page).
11478
+ * @type string
11479
+ * @default Previous
11480
+ *
11481
+ * @dtopt Language
11482
+ * @name DataTable.defaults.language.paginate.previous
11483
+ *
11484
+ * @example
11485
+ * $(document).ready( function() {
11486
+ * $('#example').dataTable( {
11487
+ * "language": {
11488
+ * "paginate": {
11489
+ * "previous": "Previous page"
11490
+ * }
11491
+ * }
11492
+ * } );
11493
+ * } );
11494
+ */
11495
+ "sPrevious": "Previous"
11496
+ },
11497
+
11498
+ /**
11499
+ * This string is shown in preference to `zeroRecords` when the table is
11500
+ * empty of data (regardless of filtering). Note that this is an optional
11501
+ * parameter - if it is not given, the value of `zeroRecords` will be used
11502
+ * instead (either the default or given value).
11503
+ * @type string
11504
+ * @default No data available in table
11505
+ *
11506
+ * @dtopt Language
11507
+ * @name DataTable.defaults.language.emptyTable
11508
+ *
11509
+ * @example
11510
+ * $(document).ready( function() {
11511
+ * $('#example').dataTable( {
11512
+ * "language": {
11513
+ * "emptyTable": "No data available in table"
11514
+ * }
11515
+ * } );
11516
+ * } );
11517
+ */
11518
+ "sEmptyTable": "No data available in table",
11519
+
11520
+
11521
+ /**
11522
+ * This string gives information to the end user about the information
11523
+ * that is current on display on the page. The following tokens can be
11524
+ * used in the string and will be dynamically replaced as the table
11525
+ * display updates. This tokens can be placed anywhere in the string, or
11526
+ * removed as needed by the language requires:
11527
+ *
11528
+ * * `\_START\_` - Display index of the first record on the current page
11529
+ * * `\_END\_` - Display index of the last record on the current page
11530
+ * * `\_TOTAL\_` - Number of records in the table after filtering
11531
+ * * `\_MAX\_` - Number of records in the table without filtering
11532
+ * * `\_PAGE\_` - Current page number
11533
+ * * `\_PAGES\_` - Total number of pages of data in the table
11534
+ *
11535
+ * @type string
11536
+ * @default Showing _START_ to _END_ of _TOTAL_ entries
11537
+ *
11538
+ * @dtopt Language
11539
+ * @name DataTable.defaults.language.info
11540
+ *
11541
+ * @example
11542
+ * $(document).ready( function() {
11543
+ * $('#example').dataTable( {
11544
+ * "language": {
11545
+ * "info": "Showing page _PAGE_ of _PAGES_"
11546
+ * }
11547
+ * } );
11548
+ * } );
11549
+ */
11550
+ "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11551
+
11552
+
11553
+ /**
11554
+ * Display information string for when the table is empty. Typically the
11555
+ * format of this string should match `info`.
11556
+ * @type string
11557
+ * @default Showing 0 to 0 of 0 entries
11558
+ *
11559
+ * @dtopt Language
11560
+ * @name DataTable.defaults.language.infoEmpty
11561
+ *
11562
+ * @example
11563
+ * $(document).ready( function() {
11564
+ * $('#example').dataTable( {
11565
+ * "language": {
11566
+ * "infoEmpty": "No entries to show"
11567
+ * }
11568
+ * } );
11569
+ * } );
11570
+ */
11571
+ "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11572
+
11573
+
11574
+ /**
11575
+ * When a user filters the information in a table, this string is appended
11576
+ * to the information (`info`) to give an idea of how strong the filtering
11577
+ * is. The variable _MAX_ is dynamically updated.
11578
+ * @type string
11579
+ * @default (filtered from _MAX_ total entries)
11580
+ *
11581
+ * @dtopt Language
11582
+ * @name DataTable.defaults.language.infoFiltered
11583
+ *
11584
+ * @example
11585
+ * $(document).ready( function() {
11586
+ * $('#example').dataTable( {
11587
+ * "language": {
11588
+ * "infoFiltered": " - filtering from _MAX_ records"
11589
+ * }
11590
+ * } );
11591
+ * } );
11592
+ */
11593
+ "sInfoFiltered": "(filtered from _MAX_ total entries)",
11594
+
11595
+
11596
+ /**
11597
+ * If can be useful to append extra information to the info string at times,
11598
+ * and this variable does exactly that. This information will be appended to
11599
+ * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
11600
+ * being used) at all times.
11601
+ * @type string
11602
+ * @default <i>Empty string</i>
11603
+ *
11604
+ * @dtopt Language
11605
+ * @name DataTable.defaults.language.infoPostFix
11606
+ *
11607
+ * @example
11608
+ * $(document).ready( function() {
11609
+ * $('#example').dataTable( {
11610
+ * "language": {
11611
+ * "infoPostFix": "All records shown are derived from real information."
11612
+ * }
11613
+ * } );
11614
+ * } );
11615
+ */
11616
+ "sInfoPostFix": "",
11617
+
11618
+
11619
+ /**
11620
+ * This decimal place operator is a little different from the other
11621
+ * language options since DataTables doesn't output floating point
11622
+ * numbers, so it won't ever use this for display of a number. Rather,
11623
+ * what this parameter does is modify the sort methods of the table so
11624
+ * that numbers which are in a format which has a character other than
11625
+ * a period (`.`) as a decimal place will be sorted numerically.
11626
+ *
11627
+ * Note that numbers with different decimal places cannot be shown in
11628
+ * the same table and still be sortable, the table must be consistent.
11629
+ * However, multiple different tables on the page can use different
11630
+ * decimal place characters.
11631
+ * @type string
11632
+ * @default
11633
+ *
11634
+ * @dtopt Language
11635
+ * @name DataTable.defaults.language.decimal
11636
+ *
11637
+ * @example
11638
+ * $(document).ready( function() {
11639
+ * $('#example').dataTable( {
11640
+ * "language": {
11641
+ * "decimal": ","
11642
+ * "thousands": "."
11643
+ * }
11644
+ * } );
11645
+ * } );
11646
+ */
11647
+ "sDecimal": "",
11648
+
11649
+
11650
+ /**
11651
+ * DataTables has a build in number formatter (`formatNumber`) which is
11652
+ * used to format large numbers that are used in the table information.
11653
+ * By default a comma is used, but this can be trivially changed to any
11654
+ * character you wish with this parameter.
11655
+ * @type string
11656
+ * @default ,
11657
+ *
11658
+ * @dtopt Language
11659
+ * @name DataTable.defaults.language.thousands
11660
+ *
11661
+ * @example
11662
+ * $(document).ready( function() {
11663
+ * $('#example').dataTable( {
11664
+ * "language": {
11665
+ * "thousands": "'"
11666
+ * }
11667
+ * } );
11668
+ * } );
11669
+ */
11670
+ "sThousands": ",",
11671
+
11672
+
11673
+ /**
11674
+ * Detail the action that will be taken when the drop down menu for the
11675
+ * pagination length option is changed. The '_MENU_' variable is replaced
11676
+ * with a default select list of 10, 25, 50 and 100, and can be replaced
11677
+ * with a custom select box if required.
11678
+ * @type string
11679
+ * @default Show _MENU_ entries
11680
+ *
11681
+ * @dtopt Language
11682
+ * @name DataTable.defaults.language.lengthMenu
11683
+ *
11684
+ * @example
11685
+ * // Language change only
11686
+ * $(document).ready( function() {
11687
+ * $('#example').dataTable( {
11688
+ * "language": {
11689
+ * "lengthMenu": "Display _MENU_ records"
11690
+ * }
11691
+ * } );
11692
+ * } );
11693
+ *
11694
+ * @example
11695
+ * // Language and options change
11696
+ * $(document).ready( function() {
11697
+ * $('#example').dataTable( {
11698
+ * "language": {
11699
+ * "lengthMenu": 'Display <select>'+
11700
+ * '<option value="10">10</option>'+
11701
+ * '<option value="20">20</option>'+
11702
+ * '<option value="30">30</option>'+
11703
+ * '<option value="40">40</option>'+
11704
+ * '<option value="50">50</option>'+
11705
+ * '<option value="-1">All</option>'+
11706
+ * '</select> records'
11707
+ * }
11708
+ * } );
11709
+ * } );
11710
+ */
11711
+ "sLengthMenu": "Show _MENU_ entries",
11712
+
11713
+
11714
+ /**
11715
+ * When using Ajax sourced data and during the first draw when DataTables is
11716
+ * gathering the data, this message is shown in an empty row in the table to
11717
+ * indicate to the end user the the data is being loaded. Note that this
11718
+ * parameter is not used when loading data by server-side processing, just
11719
+ * Ajax sourced data with client-side processing.
11720
+ * @type string
11721
+ * @default Loading...
11722
+ *
11723
+ * @dtopt Language
11724
+ * @name DataTable.defaults.language.loadingRecords
11725
+ *
11726
+ * @example
11727
+ * $(document).ready( function() {
11728
+ * $('#example').dataTable( {
11729
+ * "language": {
11730
+ * "loadingRecords": "Please wait - loading..."
11731
+ * }
11732
+ * } );
11733
+ * } );
11734
+ */
11735
+ "sLoadingRecords": "Loading...",
11736
+
11737
+
11738
+ /**
11739
+ * Text which is displayed when the table is processing a user action
11740
+ * (usually a sort command or similar).
11741
+ * @type string
11742
+ * @default Processing...
11743
+ *
11744
+ * @dtopt Language
11745
+ * @name DataTable.defaults.language.processing
11746
+ *
11747
+ * @example
11748
+ * $(document).ready( function() {
11749
+ * $('#example').dataTable( {
11750
+ * "language": {
11751
+ * "processing": "DataTables is currently busy"
11752
+ * }
11753
+ * } );
11754
+ * } );
11755
+ */
11756
+ "sProcessing": "Processing...",
11757
+
11758
+
11759
+ /**
11760
+ * Details the actions that will be taken when the user types into the
11761
+ * filtering input text box. The variable "_INPUT_", if used in the string,
11762
+ * is replaced with the HTML text box for the filtering input allowing
11763
+ * control over where it appears in the string. If "_INPUT_" is not given
11764
+ * then the input box is appended to the string automatically.
11765
+ * @type string
11766
+ * @default Search:
11767
+ *
11768
+ * @dtopt Language
11769
+ * @name DataTable.defaults.language.search
11770
+ *
11771
+ * @example
11772
+ * // Input text box will be appended at the end automatically
11773
+ * $(document).ready( function() {
11774
+ * $('#example').dataTable( {
11775
+ * "language": {
11776
+ * "search": "Filter records:"
11777
+ * }
11778
+ * } );
11779
+ * } );
11780
+ *
11781
+ * @example
11782
+ * // Specify where the filter should appear
11783
+ * $(document).ready( function() {
11784
+ * $('#example').dataTable( {
11785
+ * "language": {
11786
+ * "search": "Apply filter _INPUT_ to table"
11787
+ * }
11788
+ * } );
11789
+ * } );
11790
+ */
11791
+ "sSearch": "Search:",
11792
+
11793
+
11794
+ /**
11795
+ * Assign a `placeholder` attribute to the search `input` element
11796
+ * @type string
11797
+ * @default
11798
+ *
11799
+ * @dtopt Language
11800
+ * @name DataTable.defaults.language.searchPlaceholder
11801
+ */
11802
+ "sSearchPlaceholder": "",
11803
+
11804
+
11805
+ /**
11806
+ * All of the language information can be stored in a file on the
11807
+ * server-side, which DataTables will look up if this parameter is passed.
11808
+ * It must store the URL of the language file, which is in a JSON format,
11809
+ * and the object has the same properties as the oLanguage object in the
11810
+ * initialiser object (i.e. the above parameters). Please refer to one of
11811
+ * the example language files to see how this works in action.
11812
+ * @type string
11813
+ * @default <i>Empty string - i.e. disabled</i>
11814
+ *
11815
+ * @dtopt Language
11816
+ * @name DataTable.defaults.language.url
11817
+ *
11818
+ * @example
11819
+ * $(document).ready( function() {
11820
+ * $('#example').dataTable( {
11821
+ * "language": {
11822
+ * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
11823
+ * }
11824
+ * } );
11825
+ * } );
11826
+ */
11827
+ "sUrl": "",
11828
+
11829
+
11830
+ /**
11831
+ * Text shown inside the table records when the is no information to be
11832
+ * displayed after filtering. `emptyTable` is shown when there is simply no
11833
+ * information in the table at all (regardless of filtering).
11834
+ * @type string
11835
+ * @default No matching records found
11836
+ *
11837
+ * @dtopt Language
11838
+ * @name DataTable.defaults.language.zeroRecords
11839
+ *
11840
+ * @example
11841
+ * $(document).ready( function() {
11842
+ * $('#example').dataTable( {
11843
+ * "language": {
11844
+ * "zeroRecords": "No records to display"
11845
+ * }
11846
+ * } );
11847
+ * } );
11848
+ */
11849
+ "sZeroRecords": "No matching records found"
11850
+ },
11851
+
11852
+
11853
+ /**
11854
+ * This parameter allows you to have define the global filtering state at
11855
+ * initialisation time. As an object the `search` parameter must be
11856
+ * defined, but all other parameters are optional. When `regex` is true,
11857
+ * the search string will be treated as a regular expression, when false
11858
+ * (default) it will be treated as a straight string. When `smart`
11859
+ * DataTables will use it's smart filtering methods (to word match at
11860
+ * any point in the data), when false this will not be done.
11861
+ * @namespace
11862
+ * @extends DataTable.models.oSearch
11863
+ *
11864
+ * @dtopt Options
11865
+ * @name DataTable.defaults.search
11866
+ *
11867
+ * @example
11868
+ * $(document).ready( function() {
11869
+ * $('#example').dataTable( {
11870
+ * "search": {"search": "Initial search"}
11871
+ * } );
11872
+ * } )
11873
+ */
11874
+ "oSearch": $.extend( {}, DataTable.models.oSearch ),
11875
+
11876
+
11877
+ /**
11878
+ * __Deprecated__ The functionality provided by this parameter has now been
11879
+ * superseded by that provided through `ajax`, which should be used instead.
11880
+ *
11881
+ * By default DataTables will look for the property `data` (or `aaData` for
11882
+ * compatibility with DataTables 1.9-) when obtaining data from an Ajax
11883
+ * source or for server-side processing - this parameter allows that
11884
+ * property to be changed. You can use Javascript dotted object notation to
11885
+ * get a data source for multiple levels of nesting.
11886
+ * @type string
11887
+ * @default data
11888
+ *
11889
+ * @dtopt Options
11890
+ * @dtopt Server-side
11891
+ * @name DataTable.defaults.ajaxDataProp
11892
+ *
11893
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
11894
+ */
11895
+ "sAjaxDataProp": "data",
11896
+
11897
+
11898
+ /**
11899
+ * __Deprecated__ The functionality provided by this parameter has now been
11900
+ * superseded by that provided through `ajax`, which should be used instead.
11901
+ *
11902
+ * You can instruct DataTables to load data from an external
11903
+ * source using this parameter (use aData if you want to pass data in you
11904
+ * already have). Simply provide a url a JSON object can be obtained from.
11905
+ * @type string
11906
+ * @default null
11907
+ *
11908
+ * @dtopt Options
11909
+ * @dtopt Server-side
11910
+ * @name DataTable.defaults.ajaxSource
11911
+ *
11912
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
11913
+ */
11914
+ "sAjaxSource": null,
11915
+
11916
+
11917
+ /**
11918
+ * This initialisation variable allows you to specify exactly where in the
11919
+ * DOM you want DataTables to inject the various controls it adds to the page
11920
+ * (for example you might want the pagination controls at the top of the
11921
+ * table). DIV elements (with or without a custom class) can also be added to
11922
+ * aid styling. The follow syntax is used:
11923
+ * <ul>
11924
+ * <li>The following options are allowed:
11925
+ * <ul>
11926
+ * <li>'l' - Length changing</li>
11927
+ * <li>'f' - Filtering input</li>
11928
+ * <li>'t' - The table!</li>
11929
+ * <li>'i' - Information</li>
11930
+ * <li>'p' - Pagination</li>
11931
+ * <li>'r' - pRocessing</li>
11932
+ * </ul>
11933
+ * </li>
11934
+ * <li>The following constants are allowed:
11935
+ * <ul>
11936
+ * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
11937
+ * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
11938
+ * </ul>
11939
+ * </li>
11940
+ * <li>The following syntax is expected:
11941
+ * <ul>
11942
+ * <li>'&lt;' and '&gt;' - div elements</li>
11943
+ * <li>'&lt;"class" and '&gt;' - div with a class</li>
11944
+ * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
11945
+ * </ul>
11946
+ * </li>
11947
+ * <li>Examples:
11948
+ * <ul>
11949
+ * <li>'&lt;"wrapper"flipt&gt;'</li>
11950
+ * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
11951
+ * </ul>
11952
+ * </li>
11953
+ * </ul>
11954
+ * @type string
11955
+ * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
11956
+ * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
11957
+ *
11958
+ * @dtopt Options
11959
+ * @name DataTable.defaults.dom
11960
+ *
11961
+ * @example
11962
+ * $(document).ready( function() {
11963
+ * $('#example').dataTable( {
11964
+ * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
11965
+ * } );
11966
+ * } );
11967
+ */
11968
+ "sDom": "lfrtip",
11969
+
11970
+
11971
+ /**
11972
+ * Search delay option. This will throttle full table searches that use the
11973
+ * DataTables provided search input element (it does not effect calls to
11974
+ * `dt-api search()`, providing a delay before the search is made.
11975
+ * @type integer
11976
+ * @default 0
11977
+ *
11978
+ * @dtopt Options
11979
+ * @name DataTable.defaults.searchDelay
11980
+ *
11981
+ * @example
11982
+ * $(document).ready( function() {
11983
+ * $('#example').dataTable( {
11984
+ * "searchDelay": 200
11985
+ * } );
11986
+ * } )
11987
+ */
11988
+ "searchDelay": null,
11989
+
11990
+
11991
+ /**
11992
+ * DataTables features six different built-in options for the buttons to
11993
+ * display for pagination control:
11994
+ *
11995
+ * * `numbers` - Page number buttons only
11996
+ * * `simple` - 'Previous' and 'Next' buttons only
11997
+ * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
11998
+ * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
11999
+ * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
12000
+ * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
12001
+ *
12002
+ * Further methods can be added using {@link DataTable.ext.oPagination}.
12003
+ * @type string
12004
+ * @default simple_numbers
12005
+ *
12006
+ * @dtopt Options
12007
+ * @name DataTable.defaults.pagingType
12008
+ *
12009
+ * @example
12010
+ * $(document).ready( function() {
12011
+ * $('#example').dataTable( {
12012
+ * "pagingType": "full_numbers"
12013
+ * } );
12014
+ * } )
12015
+ */
12016
+ "sPaginationType": "simple_numbers",
12017
+
12018
+
12019
+ /**
12020
+ * Enable horizontal scrolling. When a table is too wide to fit into a
12021
+ * certain layout, or you have a large number of columns in the table, you
12022
+ * can enable x-scrolling to show the table in a viewport, which can be
12023
+ * scrolled. This property can be `true` which will allow the table to
12024
+ * scroll horizontally when needed, or any CSS unit, or a number (in which
12025
+ * case it will be treated as a pixel measurement). Setting as simply `true`
12026
+ * is recommended.
12027
+ * @type boolean|string
12028
+ * @default <i>blank string - i.e. disabled</i>
12029
+ *
12030
+ * @dtopt Features
12031
+ * @name DataTable.defaults.scrollX
12032
+ *
12033
+ * @example
12034
+ * $(document).ready( function() {
12035
+ * $('#example').dataTable( {
12036
+ * "scrollX": true,
12037
+ * "scrollCollapse": true
12038
+ * } );
12039
+ * } );
12040
+ */
12041
+ "sScrollX": "",
12042
+
12043
+
12044
+ /**
12045
+ * This property can be used to force a DataTable to use more width than it
12046
+ * might otherwise do when x-scrolling is enabled. For example if you have a
12047
+ * table which requires to be well spaced, this parameter is useful for
12048
+ * "over-sizing" the table, and thus forcing scrolling. This property can by
12049
+ * any CSS unit, or a number (in which case it will be treated as a pixel
12050
+ * measurement).
12051
+ * @type string
12052
+ * @default <i>blank string - i.e. disabled</i>
12053
+ *
12054
+ * @dtopt Options
12055
+ * @name DataTable.defaults.scrollXInner
12056
+ *
12057
+ * @example
12058
+ * $(document).ready( function() {
12059
+ * $('#example').dataTable( {
12060
+ * "scrollX": "100%",
12061
+ * "scrollXInner": "110%"
12062
+ * } );
12063
+ * } );
12064
+ */
12065
+ "sScrollXInner": "",
12066
+
12067
+
12068
+ /**
12069
+ * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
12070
+ * to the given height, and enable scrolling for any data which overflows the
12071
+ * current viewport. This can be used as an alternative to paging to display
12072
+ * a lot of data in a small area (although paging and scrolling can both be
12073
+ * enabled at the same time). This property can be any CSS unit, or a number
12074
+ * (in which case it will be treated as a pixel measurement).
12075
+ * @type string
12076
+ * @default <i>blank string - i.e. disabled</i>
12077
+ *
12078
+ * @dtopt Features
12079
+ * @name DataTable.defaults.scrollY
12080
+ *
12081
+ * @example
12082
+ * $(document).ready( function() {
12083
+ * $('#example').dataTable( {
12084
+ * "scrollY": "200px",
12085
+ * "paginate": false
12086
+ * } );
12087
+ * } );
12088
+ */
12089
+ "sScrollY": "",
12090
+
12091
+
12092
+ /**
12093
+ * __Deprecated__ The functionality provided by this parameter has now been
12094
+ * superseded by that provided through `ajax`, which should be used instead.
12095
+ *
12096
+ * Set the HTTP method that is used to make the Ajax call for server-side
12097
+ * processing or Ajax sourced data.
12098
+ * @type string
12099
+ * @default GET
12100
+ *
12101
+ * @dtopt Options
12102
+ * @dtopt Server-side
12103
+ * @name DataTable.defaults.serverMethod
12104
+ *
12105
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
12106
+ */
12107
+ "sServerMethod": "GET",
12108
+
12109
+
12110
+ /**
12111
+ * DataTables makes use of renderers when displaying HTML elements for
12112
+ * a table. These renderers can be added or modified by plug-ins to
12113
+ * generate suitable mark-up for a site. For example the Bootstrap
12114
+ * integration plug-in for DataTables uses a paging button renderer to
12115
+ * display pagination buttons in the mark-up required by Bootstrap.
12116
+ *
12117
+ * For further information about the renderers available see
12118
+ * DataTable.ext.renderer
12119
+ * @type string|object
12120
+ * @default null
12121
+ *
12122
+ * @name DataTable.defaults.renderer
12123
+ *
12124
+ */
12125
+ "renderer": null,
12126
+
12127
+
12128
+ /**
12129
+ * Set the data property name that DataTables should use to get a row's id
12130
+ * to set as the `id` property in the node.
12131
+ * @type string
12132
+ * @default DT_RowId
12133
+ *
12134
+ * @name DataTable.defaults.rowId
12135
+ */
12136
+ "rowId": "DT_RowId"
12137
+ };
12138
+
12139
+ _fnHungarianMap( DataTable.defaults );
12140
+
12141
+
12142
+
12143
+ /*
12144
+ * Developer note - See note in model.defaults.js about the use of Hungarian
12145
+ * notation and camel case.
12146
+ */
12147
+
12148
+ /**
12149
+ * Column options that can be given to DataTables at initialisation time.
12150
+ * @namespace
12151
+ */
12152
+ DataTable.defaults.column = {
12153
+ /**
12154
+ * Define which column(s) an order will occur on for this column. This
12155
+ * allows a column's ordering to take multiple columns into account when
12156
+ * doing a sort or use the data from a different column. For example first
12157
+ * name / last name columns make sense to do a multi-column sort over the
12158
+ * two columns.
12159
+ * @type array|int
12160
+ * @default null <i>Takes the value of the column index automatically</i>
12161
+ *
12162
+ * @name DataTable.defaults.column.orderData
12163
+ * @dtopt Columns
12164
+ *
12165
+ * @example
12166
+ * // Using `columnDefs`
12167
+ * $(document).ready( function() {
12168
+ * $('#example').dataTable( {
12169
+ * "columnDefs": [
12170
+ * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
12171
+ * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
12172
+ * { "orderData": 2, "targets": [ 2 ] }
12173
+ * ]
12174
+ * } );
12175
+ * } );
12176
+ *
12177
+ * @example
12178
+ * // Using `columns`
12179
+ * $(document).ready( function() {
12180
+ * $('#example').dataTable( {
12181
+ * "columns": [
12182
+ * { "orderData": [ 0, 1 ] },
12183
+ * { "orderData": [ 1, 0 ] },
12184
+ * { "orderData": 2 },
12185
+ * null,
12186
+ * null
12187
+ * ]
12188
+ * } );
12189
+ * } );
12190
+ */
12191
+ "aDataSort": null,
12192
+ "iDataSort": -1,
12193
+
12194
+
12195
+ /**
12196
+ * You can control the default ordering direction, and even alter the
12197
+ * behaviour of the sort handler (i.e. only allow ascending ordering etc)
12198
+ * using this parameter.
12199
+ * @type array
12200
+ * @default [ 'asc', 'desc' ]
12201
+ *
12202
+ * @name DataTable.defaults.column.orderSequence
12203
+ * @dtopt Columns
12204
+ *
12205
+ * @example
12206
+ * // Using `columnDefs`
12207
+ * $(document).ready( function() {
12208
+ * $('#example').dataTable( {
12209
+ * "columnDefs": [
12210
+ * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
12211
+ * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
12212
+ * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
12213
+ * ]
12214
+ * } );
12215
+ * } );
12216
+ *
12217
+ * @example
12218
+ * // Using `columns`
12219
+ * $(document).ready( function() {
12220
+ * $('#example').dataTable( {
12221
+ * "columns": [
12222
+ * null,
12223
+ * { "orderSequence": [ "asc" ] },
12224
+ * { "orderSequence": [ "desc", "asc", "asc" ] },
12225
+ * { "orderSequence": [ "desc" ] },
12226
+ * null
12227
+ * ]
12228
+ * } );
12229
+ * } );
12230
+ */
12231
+ "asSorting": [ 'asc', 'desc' ],
12232
+
12233
+
12234
+ /**
12235
+ * Enable or disable filtering on the data in this column.
12236
+ * @type boolean
12237
+ * @default true
12238
+ *
12239
+ * @name DataTable.defaults.column.searchable
12240
+ * @dtopt Columns
12241
+ *
12242
+ * @example
12243
+ * // Using `columnDefs`
12244
+ * $(document).ready( function() {
12245
+ * $('#example').dataTable( {
12246
+ * "columnDefs": [
12247
+ * { "searchable": false, "targets": [ 0 ] }
12248
+ * ] } );
12249
+ * } );
12250
+ *
12251
+ * @example
12252
+ * // Using `columns`
12253
+ * $(document).ready( function() {
12254
+ * $('#example').dataTable( {
12255
+ * "columns": [
12256
+ * { "searchable": false },
12257
+ * null,
12258
+ * null,
12259
+ * null,
12260
+ * null
12261
+ * ] } );
12262
+ * } );
12263
+ */
12264
+ "bSearchable": true,
12265
+
12266
+
12267
+ /**
12268
+ * Enable or disable ordering on this column.
12269
+ * @type boolean
12270
+ * @default true
12271
+ *
12272
+ * @name DataTable.defaults.column.orderable
12273
+ * @dtopt Columns
12274
+ *
12275
+ * @example
12276
+ * // Using `columnDefs`
12277
+ * $(document).ready( function() {
12278
+ * $('#example').dataTable( {
12279
+ * "columnDefs": [
12280
+ * { "orderable": false, "targets": [ 0 ] }
12281
+ * ] } );
12282
+ * } );
12283
+ *
12284
+ * @example
12285
+ * // Using `columns`
12286
+ * $(document).ready( function() {
12287
+ * $('#example').dataTable( {
12288
+ * "columns": [
12289
+ * { "orderable": false },
12290
+ * null,
12291
+ * null,
12292
+ * null,
12293
+ * null
12294
+ * ] } );
12295
+ * } );
12296
+ */
12297
+ "bSortable": true,
12298
+
12299
+
12300
+ /**
12301
+ * Enable or disable the display of this column.
12302
+ * @type boolean
12303
+ * @default true
12304
+ *
12305
+ * @name DataTable.defaults.column.visible
12306
+ * @dtopt Columns
12307
+ *
12308
+ * @example
12309
+ * // Using `columnDefs`
12310
+ * $(document).ready( function() {
12311
+ * $('#example').dataTable( {
12312
+ * "columnDefs": [
12313
+ * { "visible": false, "targets": [ 0 ] }
12314
+ * ] } );
12315
+ * } );
12316
+ *
12317
+ * @example
12318
+ * // Using `columns`
12319
+ * $(document).ready( function() {
12320
+ * $('#example').dataTable( {
12321
+ * "columns": [
12322
+ * { "visible": false },
12323
+ * null,
12324
+ * null,
12325
+ * null,
12326
+ * null
12327
+ * ] } );
12328
+ * } );
12329
+ */
12330
+ "bVisible": true,
12331
+
12332
+
12333
+ /**
12334
+ * Developer definable function that is called whenever a cell is created (Ajax source,
12335
+ * etc) or processed for input (DOM source). This can be used as a compliment to mRender
12336
+ * allowing you to modify the DOM element (add background colour for example) when the
12337
+ * element is available.
12338
+ * @type function
12339
+ * @param {element} td The TD node that has been created
12340
+ * @param {*} cellData The Data for the cell
12341
+ * @param {array|object} rowData The data for the whole row
12342
+ * @param {int} row The row index for the aoData data store
12343
+ * @param {int} col The column index for aoColumns
12344
+ *
12345
+ * @name DataTable.defaults.column.createdCell
12346
+ * @dtopt Columns
12347
+ *
12348
+ * @example
12349
+ * $(document).ready( function() {
12350
+ * $('#example').dataTable( {
12351
+ * "columnDefs": [ {
12352
+ * "targets": [3],
12353
+ * "createdCell": function (td, cellData, rowData, row, col) {
12354
+ * if ( cellData == "1.7" ) {
12355
+ * $(td).css('color', 'blue')
12356
+ * }
12357
+ * }
12358
+ * } ]
12359
+ * });
12360
+ * } );
12361
+ */
12362
+ "fnCreatedCell": null,
12363
+
12364
+
12365
+ /**
12366
+ * This parameter has been replaced by `data` in DataTables to ensure naming
12367
+ * consistency. `dataProp` can still be used, as there is backwards
12368
+ * compatibility in DataTables for this option, but it is strongly
12369
+ * recommended that you use `data` in preference to `dataProp`.
12370
+ * @name DataTable.defaults.column.dataProp
12371
+ */
12372
+
12373
+
12374
+ /**
12375
+ * This property can be used to read data from any data source property,
12376
+ * including deeply nested objects / properties. `data` can be given in a
12377
+ * number of different ways which effect its behaviour:
12378
+ *
12379
+ * * `integer` - treated as an array index for the data source. This is the
12380
+ * default that DataTables uses (incrementally increased for each column).
12381
+ * * `string` - read an object property from the data source. There are
12382
+ * three 'special' options that can be used in the string to alter how
12383
+ * DataTables reads the data from the source object:
12384
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
12385
+ * Javascript to read from nested objects, so to can the options
12386
+ * specified in `data`. For example: `browser.version` or
12387
+ * `browser.name`. If your object parameter name contains a period, use
12388
+ * `\\` to escape it - i.e. `first\\.name`.
12389
+ * * `[]` - Array notation. DataTables can automatically combine data
12390
+ * from and array source, joining the data with the characters provided
12391
+ * between the two brackets. For example: `name[, ]` would provide a
12392
+ * comma-space separated list from the source array. If no characters
12393
+ * are provided between the brackets, the original array source is
12394
+ * returned.
12395
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
12396
+ * execute a function of the name given. For example: `browser()` for a
12397
+ * simple function on the data source, `browser.version()` for a
12398
+ * function in a nested property or even `browser().version` to get an
12399
+ * object property if the function called returns an object. Note that
12400
+ * function notation is recommended for use in `render` rather than
12401
+ * `data` as it is much simpler to use as a renderer.
12402
+ * * `null` - use the original data source for the row rather than plucking
12403
+ * data directly from it. This action has effects on two other
12404
+ * initialisation options:
12405
+ * * `defaultContent` - When null is given as the `data` option and
12406
+ * `defaultContent` is specified for the column, the value defined by
12407
+ * `defaultContent` will be used for the cell.
12408
+ * * `render` - When null is used for the `data` option and the `render`
12409
+ * option is specified for the column, the whole data source for the
12410
+ * row is used for the renderer.
12411
+ * * `function` - the function given will be executed whenever DataTables
12412
+ * needs to set or get the data for a cell in the column. The function
12413
+ * takes three parameters:
12414
+ * * Parameters:
12415
+ * * `{array|object}` The data source for the row
12416
+ * * `{string}` The type call data requested - this will be 'set' when
12417
+ * setting data or 'filter', 'display', 'type', 'sort' or undefined
12418
+ * when gathering data. Note that when `undefined` is given for the
12419
+ * type DataTables expects to get the raw data for the object back<
12420
+ * * `{*}` Data to set when the second parameter is 'set'.
12421
+ * * Return:
12422
+ * * The return value from the function is not required when 'set' is
12423
+ * the type of call, but otherwise the return is what will be used
12424
+ * for the data requested.
12425
+ *
12426
+ * Note that `data` is a getter and setter option. If you just require
12427
+ * formatting of data for output, you will likely want to use `render` which
12428
+ * is simply a getter and thus simpler to use.
12429
+ *
12430
+ * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
12431
+ * name change reflects the flexibility of this property and is consistent
12432
+ * with the naming of mRender. If 'mDataProp' is given, then it will still
12433
+ * be used by DataTables, as it automatically maps the old name to the new
12434
+ * if required.
12435
+ *
12436
+ * @type string|int|function|null
12437
+ * @default null <i>Use automatically calculated column index</i>
12438
+ *
12439
+ * @name DataTable.defaults.column.data
12440
+ * @dtopt Columns
12441
+ *
12442
+ * @example
12443
+ * // Read table data from objects
12444
+ * // JSON structure for each row:
12445
+ * // {
12446
+ * // "engine": {value},
12447
+ * // "browser": {value},
12448
+ * // "platform": {value},
12449
+ * // "version": {value},
12450
+ * // "grade": {value}
12451
+ * // }
12452
+ * $(document).ready( function() {
12453
+ * $('#example').dataTable( {
12454
+ * "ajaxSource": "sources/objects.txt",
12455
+ * "columns": [
12456
+ * { "data": "engine" },
12457
+ * { "data": "browser" },
12458
+ * { "data": "platform" },
12459
+ * { "data": "version" },
12460
+ * { "data": "grade" }
12461
+ * ]
12462
+ * } );
12463
+ * } );
12464
+ *
12465
+ * @example
12466
+ * // Read information from deeply nested objects
12467
+ * // JSON structure for each row:
12468
+ * // {
12469
+ * // "engine": {value},
12470
+ * // "browser": {value},
12471
+ * // "platform": {
12472
+ * // "inner": {value}
12473
+ * // },
12474
+ * // "details": [
12475
+ * // {value}, {value}
12476
+ * // ]
12477
+ * // }
12478
+ * $(document).ready( function() {
12479
+ * $('#example').dataTable( {
12480
+ * "ajaxSource": "sources/deep.txt",
12481
+ * "columns": [
12482
+ * { "data": "engine" },
12483
+ * { "data": "browser" },
12484
+ * { "data": "platform.inner" },
12485
+ * { "data": "details.0" },
12486
+ * { "data": "details.1" }
12487
+ * ]
12488
+ * } );
12489
+ * } );
12490
+ *
12491
+ * @example
12492
+ * // Using `data` as a function to provide different information for
12493
+ * // sorting, filtering and display. In this case, currency (price)
12494
+ * $(document).ready( function() {
12495
+ * $('#example').dataTable( {
12496
+ * "columnDefs": [ {
12497
+ * "targets": [ 0 ],
12498
+ * "data": function ( source, type, val ) {
12499
+ * if (type === 'set') {
12500
+ * source.price = val;
12501
+ * // Store the computed dislay and filter values for efficiency
12502
+ * source.price_display = val=="" ? "" : "$"+numberFormat(val);
12503
+ * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
12504
+ * return;
12505
+ * }
12506
+ * else if (type === 'display') {
12507
+ * return source.price_display;
12508
+ * }
12509
+ * else if (type === 'filter') {
12510
+ * return source.price_filter;
12511
+ * }
12512
+ * // 'sort', 'type' and undefined all just use the integer
12513
+ * return source.price;
12514
+ * }
12515
+ * } ]
12516
+ * } );
12517
+ * } );
12518
+ *
12519
+ * @example
12520
+ * // Using default content
12521
+ * $(document).ready( function() {
12522
+ * $('#example').dataTable( {
12523
+ * "columnDefs": [ {
12524
+ * "targets": [ 0 ],
12525
+ * "data": null,
12526
+ * "defaultContent": "Click to edit"
12527
+ * } ]
12528
+ * } );
12529
+ * } );
12530
+ *
12531
+ * @example
12532
+ * // Using array notation - outputting a list from an array
12533
+ * $(document).ready( function() {
12534
+ * $('#example').dataTable( {
12535
+ * "columnDefs": [ {
12536
+ * "targets": [ 0 ],
12537
+ * "data": "name[, ]"
12538
+ * } ]
12539
+ * } );
12540
+ * } );
12541
+ *
12542
+ */
12543
+ "mData": null,
12544
+
12545
+
12546
+ /**
12547
+ * This property is the rendering partner to `data` and it is suggested that
12548
+ * when you want to manipulate data for display (including filtering,
12549
+ * sorting etc) without altering the underlying data for the table, use this
12550
+ * property. `render` can be considered to be the the read only companion to
12551
+ * `data` which is read / write (then as such more complex). Like `data`
12552
+ * this option can be given in a number of different ways to effect its
12553
+ * behaviour:
12554
+ *
12555
+ * * `integer` - treated as an array index for the data source. This is the
12556
+ * default that DataTables uses (incrementally increased for each column).
12557
+ * * `string` - read an object property from the data source. There are
12558
+ * three 'special' options that can be used in the string to alter how
12559
+ * DataTables reads the data from the source object:
12560
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
12561
+ * Javascript to read from nested objects, so to can the options
12562
+ * specified in `data`. For example: `browser.version` or
12563
+ * `browser.name`. If your object parameter name contains a period, use
12564
+ * `\\` to escape it - i.e. `first\\.name`.
12565
+ * * `[]` - Array notation. DataTables can automatically combine data
12566
+ * from and array source, joining the data with the characters provided
12567
+ * between the two brackets. For example: `name[, ]` would provide a
12568
+ * comma-space separated list from the source array. If no characters
12569
+ * are provided between the brackets, the original array source is
12570
+ * returned.
12571
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
12572
+ * execute a function of the name given. For example: `browser()` for a
12573
+ * simple function on the data source, `browser.version()` for a
12574
+ * function in a nested property or even `browser().version` to get an
12575
+ * object property if the function called returns an object.
12576
+ * * `object` - use different data for the different data types requested by
12577
+ * DataTables ('filter', 'display', 'type' or 'sort'). The property names
12578
+ * of the object is the data type the property refers to and the value can
12579
+ * defined using an integer, string or function using the same rules as
12580
+ * `render` normally does. Note that an `_` option _must_ be specified.
12581
+ * This is the default value to use if you haven't specified a value for
12582
+ * the data type requested by DataTables.
12583
+ * * `function` - the function given will be executed whenever DataTables
12584
+ * needs to set or get the data for a cell in the column. The function
12585
+ * takes three parameters:
12586
+ * * Parameters:
12587
+ * * {array|object} The data source for the row (based on `data`)
12588
+ * * {string} The type call data requested - this will be 'filter',
12589
+ * 'display', 'type' or 'sort'.
12590
+ * * {array|object} The full data source for the row (not based on
12591
+ * `data`)
12592
+ * * Return:
12593
+ * * The return value from the function is what will be used for the
12594
+ * data requested.
12595
+ *
12596
+ * @type string|int|function|object|null
12597
+ * @default null Use the data source value.
12598
+ *
12599
+ * @name DataTable.defaults.column.render
12600
+ * @dtopt Columns
12601
+ *
12602
+ * @example
12603
+ * // Create a comma separated list from an array of objects
12604
+ * $(document).ready( function() {
12605
+ * $('#example').dataTable( {
12606
+ * "ajaxSource": "sources/deep.txt",
12607
+ * "columns": [
12608
+ * { "data": "engine" },
12609
+ * { "data": "browser" },
12610
+ * {
12611
+ * "data": "platform",
12612
+ * "render": "[, ].name"
12613
+ * }
12614
+ * ]
12615
+ * } );
12616
+ * } );
12617
+ *
12618
+ * @example
12619
+ * // Execute a function to obtain data
12620
+ * $(document).ready( function() {
12621
+ * $('#example').dataTable( {
12622
+ * "columnDefs": [ {
12623
+ * "targets": [ 0 ],
12624
+ * "data": null, // Use the full data source object for the renderer's source
12625
+ * "render": "browserName()"
12626
+ * } ]
12627
+ * } );
12628
+ * } );
12629
+ *
12630
+ * @example
12631
+ * // As an object, extracting different data for the different types
12632
+ * // This would be used with a data source such as:
12633
+ * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
12634
+ * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
12635
+ * // (which has both forms) is used for filtering for if a user inputs either format, while
12636
+ * // the formatted phone number is the one that is shown in the table.
12637
+ * $(document).ready( function() {
12638
+ * $('#example').dataTable( {
12639
+ * "columnDefs": [ {
12640
+ * "targets": [ 0 ],
12641
+ * "data": null, // Use the full data source object for the renderer's source
12642
+ * "render": {
12643
+ * "_": "phone",
12644
+ * "filter": "phone_filter",
12645
+ * "display": "phone_display"
12646
+ * }
12647
+ * } ]
12648
+ * } );
12649
+ * } );
12650
+ *
12651
+ * @example
12652
+ * // Use as a function to create a link from the data source
12653
+ * $(document).ready( function() {
12654
+ * $('#example').dataTable( {
12655
+ * "columnDefs": [ {
12656
+ * "targets": [ 0 ],
12657
+ * "data": "download_link",
12658
+ * "render": function ( data, type, full ) {
12659
+ * return '<a href="'+data+'">Download</a>';
12660
+ * }
12661
+ * } ]
12662
+ * } );
12663
+ * } );
12664
+ */
12665
+ "mRender": null,
12666
+
12667
+
12668
+ /**
12669
+ * Change the cell type created for the column - either TD cells or TH cells. This
12670
+ * can be useful as TH cells have semantic meaning in the table body, allowing them
12671
+ * to act as a header for a row (you may wish to add scope='row' to the TH elements).
12672
+ * @type string
12673
+ * @default td
12674
+ *
12675
+ * @name DataTable.defaults.column.cellType
12676
+ * @dtopt Columns
12677
+ *
12678
+ * @example
12679
+ * // Make the first column use TH cells
12680
+ * $(document).ready( function() {
12681
+ * $('#example').dataTable( {
12682
+ * "columnDefs": [ {
12683
+ * "targets": [ 0 ],
12684
+ * "cellType": "th"
12685
+ * } ]
12686
+ * } );
12687
+ * } );
12688
+ */
12689
+ "sCellType": "td",
12690
+
12691
+
12692
+ /**
12693
+ * Class to give to each cell in this column.
12694
+ * @type string
12695
+ * @default <i>Empty string</i>
12696
+ *
12697
+ * @name DataTable.defaults.column.class
12698
+ * @dtopt Columns
12699
+ *
12700
+ * @example
12701
+ * // Using `columnDefs`
12702
+ * $(document).ready( function() {
12703
+ * $('#example').dataTable( {
12704
+ * "columnDefs": [
12705
+ * { "class": "my_class", "targets": [ 0 ] }
12706
+ * ]
12707
+ * } );
12708
+ * } );
12709
+ *
12710
+ * @example
12711
+ * // Using `columns`
12712
+ * $(document).ready( function() {
12713
+ * $('#example').dataTable( {
12714
+ * "columns": [
12715
+ * { "class": "my_class" },
12716
+ * null,
12717
+ * null,
12718
+ * null,
12719
+ * null
12720
+ * ]
12721
+ * } );
12722
+ * } );
12723
+ */
12724
+ "sClass": "",
12725
+
12726
+ /**
12727
+ * When DataTables calculates the column widths to assign to each column,
12728
+ * it finds the longest string in each column and then constructs a
12729
+ * temporary table and reads the widths from that. The problem with this
12730
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
12731
+ * string - thus the calculation can go wrong (doing it properly and putting
12732
+ * it into an DOM object and measuring that is horribly(!) slow). Thus as
12733
+ * a "work around" we provide this option. It will append its value to the
12734
+ * text that is found to be the longest string for the column - i.e. padding.
12735
+ * Generally you shouldn't need this!
12736
+ * @type string
12737
+ * @default <i>Empty string<i>
12738
+ *
12739
+ * @name DataTable.defaults.column.contentPadding
12740
+ * @dtopt Columns
12741
+ *
12742
+ * @example
12743
+ * // Using `columns`
12744
+ * $(document).ready( function() {
12745
+ * $('#example').dataTable( {
12746
+ * "columns": [
12747
+ * null,
12748
+ * null,
12749
+ * null,
12750
+ * {
12751
+ * "contentPadding": "mmm"
12752
+ * }
12753
+ * ]
12754
+ * } );
12755
+ * } );
12756
+ */
12757
+ "sContentPadding": "",
12758
+
12759
+
12760
+ /**
12761
+ * Allows a default value to be given for a column's data, and will be used
12762
+ * whenever a null data source is encountered (this can be because `data`
12763
+ * is set to null, or because the data source itself is null).
12764
+ * @type string
12765
+ * @default null
12766
+ *
12767
+ * @name DataTable.defaults.column.defaultContent
12768
+ * @dtopt Columns
12769
+ *
12770
+ * @example
12771
+ * // Using `columnDefs`
12772
+ * $(document).ready( function() {
12773
+ * $('#example').dataTable( {
12774
+ * "columnDefs": [
12775
+ * {
12776
+ * "data": null,
12777
+ * "defaultContent": "Edit",
12778
+ * "targets": [ -1 ]
12779
+ * }
12780
+ * ]
12781
+ * } );
12782
+ * } );
12783
+ *
12784
+ * @example
12785
+ * // Using `columns`
12786
+ * $(document).ready( function() {
12787
+ * $('#example').dataTable( {
12788
+ * "columns": [
12789
+ * null,
12790
+ * null,
12791
+ * null,
12792
+ * {
12793
+ * "data": null,
12794
+ * "defaultContent": "Edit"
12795
+ * }
12796
+ * ]
12797
+ * } );
12798
+ * } );
12799
+ */
12800
+ "sDefaultContent": null,
12801
+
12802
+
12803
+ /**
12804
+ * This parameter is only used in DataTables' server-side processing. It can
12805
+ * be exceptionally useful to know what columns are being displayed on the
12806
+ * client side, and to map these to database fields. When defined, the names
12807
+ * also allow DataTables to reorder information from the server if it comes
12808
+ * back in an unexpected order (i.e. if you switch your columns around on the
12809
+ * client-side, your server-side code does not also need updating).
12810
+ * @type string
12811
+ * @default <i>Empty string</i>
12812
+ *
12813
+ * @name DataTable.defaults.column.name
12814
+ * @dtopt Columns
12815
+ *
12816
+ * @example
12817
+ * // Using `columnDefs`
12818
+ * $(document).ready( function() {
12819
+ * $('#example').dataTable( {
12820
+ * "columnDefs": [
12821
+ * { "name": "engine", "targets": [ 0 ] },
12822
+ * { "name": "browser", "targets": [ 1 ] },
12823
+ * { "name": "platform", "targets": [ 2 ] },
12824
+ * { "name": "version", "targets": [ 3 ] },
12825
+ * { "name": "grade", "targets": [ 4 ] }
12826
+ * ]
12827
+ * } );
12828
+ * } );
12829
+ *
12830
+ * @example
12831
+ * // Using `columns`
12832
+ * $(document).ready( function() {
12833
+ * $('#example').dataTable( {
12834
+ * "columns": [
12835
+ * { "name": "engine" },
12836
+ * { "name": "browser" },
12837
+ * { "name": "platform" },
12838
+ * { "name": "version" },
12839
+ * { "name": "grade" }
12840
+ * ]
12841
+ * } );
12842
+ * } );
12843
+ */
12844
+ "sName": "",
12845
+
12846
+
12847
+ /**
12848
+ * Defines a data source type for the ordering which can be used to read
12849
+ * real-time information from the table (updating the internally cached
12850
+ * version) prior to ordering. This allows ordering to occur on user
12851
+ * editable elements such as form inputs.
12852
+ * @type string
12853
+ * @default std
12854
+ *
12855
+ * @name DataTable.defaults.column.orderDataType
12856
+ * @dtopt Columns
12857
+ *
12858
+ * @example
12859
+ * // Using `columnDefs`
12860
+ * $(document).ready( function() {
12861
+ * $('#example').dataTable( {
12862
+ * "columnDefs": [
12863
+ * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
12864
+ * { "type": "numeric", "targets": [ 3 ] },
12865
+ * { "orderDataType": "dom-select", "targets": [ 4 ] },
12866
+ * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
12867
+ * ]
12868
+ * } );
12869
+ * } );
12870
+ *
12871
+ * @example
12872
+ * // Using `columns`
12873
+ * $(document).ready( function() {
12874
+ * $('#example').dataTable( {
12875
+ * "columns": [
12876
+ * null,
12877
+ * null,
12878
+ * { "orderDataType": "dom-text" },
12879
+ * { "orderDataType": "dom-text", "type": "numeric" },
12880
+ * { "orderDataType": "dom-select" },
12881
+ * { "orderDataType": "dom-checkbox" }
12882
+ * ]
12883
+ * } );
12884
+ * } );
12885
+ */
12886
+ "sSortDataType": "std",
12887
+
12888
+
12889
+ /**
12890
+ * The title of this column.
12891
+ * @type string
12892
+ * @default null <i>Derived from the 'TH' value for this column in the
12893
+ * original HTML table.</i>
12894
+ *
12895
+ * @name DataTable.defaults.column.title
12896
+ * @dtopt Columns
12897
+ *
12898
+ * @example
12899
+ * // Using `columnDefs`
12900
+ * $(document).ready( function() {
12901
+ * $('#example').dataTable( {
12902
+ * "columnDefs": [
12903
+ * { "title": "My column title", "targets": [ 0 ] }
12904
+ * ]
12905
+ * } );
12906
+ * } );
12907
+ *
12908
+ * @example
12909
+ * // Using `columns`
12910
+ * $(document).ready( function() {
12911
+ * $('#example').dataTable( {
12912
+ * "columns": [
12913
+ * { "title": "My column title" },
12914
+ * null,
12915
+ * null,
12916
+ * null,
12917
+ * null
12918
+ * ]
12919
+ * } );
12920
+ * } );
12921
+ */
12922
+ "sTitle": null,
12923
+
12924
+
12925
+ /**
12926
+ * The type allows you to specify how the data for this column will be
12927
+ * ordered. Four types (string, numeric, date and html (which will strip
12928
+ * HTML tags before ordering)) are currently available. Note that only date
12929
+ * formats understood by Javascript's Date() object will be accepted as type
12930
+ * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
12931
+ * 'numeric', 'date' or 'html' (by default). Further types can be adding
12932
+ * through plug-ins.
12933
+ * @type string
12934
+ * @default null <i>Auto-detected from raw data</i>
12935
+ *
12936
+ * @name DataTable.defaults.column.type
12937
+ * @dtopt Columns
12938
+ *
12939
+ * @example
12940
+ * // Using `columnDefs`
12941
+ * $(document).ready( function() {
12942
+ * $('#example').dataTable( {
12943
+ * "columnDefs": [
12944
+ * { "type": "html", "targets": [ 0 ] }
12945
+ * ]
12946
+ * } );
12947
+ * } );
12948
+ *
12949
+ * @example
12950
+ * // Using `columns`
12951
+ * $(document).ready( function() {
12952
+ * $('#example').dataTable( {
12953
+ * "columns": [
12954
+ * { "type": "html" },
12955
+ * null,
12956
+ * null,
12957
+ * null,
12958
+ * null
12959
+ * ]
12960
+ * } );
12961
+ * } );
12962
+ */
12963
+ "sType": null,
12964
+
12965
+
12966
+ /**
12967
+ * Defining the width of the column, this parameter may take any CSS value
12968
+ * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
12969
+ * been given a specific width through this interface ensuring that the table
12970
+ * remains readable.
12971
+ * @type string
12972
+ * @default null <i>Automatic</i>
12973
+ *
12974
+ * @name DataTable.defaults.column.width
12975
+ * @dtopt Columns
12976
+ *
12977
+ * @example
12978
+ * // Using `columnDefs`
12979
+ * $(document).ready( function() {
12980
+ * $('#example').dataTable( {
12981
+ * "columnDefs": [
12982
+ * { "width": "20%", "targets": [ 0 ] }
12983
+ * ]
12984
+ * } );
12985
+ * } );
12986
+ *
12987
+ * @example
12988
+ * // Using `columns`
12989
+ * $(document).ready( function() {
12990
+ * $('#example').dataTable( {
12991
+ * "columns": [
12992
+ * { "width": "20%" },
12993
+ * null,
12994
+ * null,
12995
+ * null,
12996
+ * null
12997
+ * ]
12998
+ * } );
12999
+ * } );
13000
+ */
13001
+ "sWidth": null
13002
+ };
13003
+
13004
+ _fnHungarianMap( DataTable.defaults.column );
13005
+
13006
+
13007
+
13008
+ /**
13009
+ * DataTables settings object - this holds all the information needed for a
13010
+ * given table, including configuration, data and current application of the
13011
+ * table options. DataTables does not have a single instance for each DataTable
13012
+ * with the settings attached to that instance, but rather instances of the
13013
+ * DataTable "class" are created on-the-fly as needed (typically by a
13014
+ * $().dataTable() call) and the settings object is then applied to that
13015
+ * instance.
13016
+ *
13017
+ * Note that this object is related to {@link DataTable.defaults} but this
13018
+ * one is the internal data store for DataTables's cache of columns. It should
13019
+ * NOT be manipulated outside of DataTables. Any configuration should be done
13020
+ * through the initialisation options.
13021
+ * @namespace
13022
+ * @todo Really should attach the settings object to individual instances so we
13023
+ * don't need to create new instances on each $().dataTable() call (if the
13024
+ * table already exists). It would also save passing oSettings around and
13025
+ * into every single function. However, this is a very significant
13026
+ * architecture change for DataTables and will almost certainly break
13027
+ * backwards compatibility with older installations. This is something that
13028
+ * will be done in 2.0.
13029
+ */
13030
+ DataTable.models.oSettings = {
13031
+ /**
13032
+ * Primary features of DataTables and their enablement state.
13033
+ * @namespace
13034
+ */
13035
+ "oFeatures": {
13036
+
13037
+ /**
13038
+ * Flag to say if DataTables should automatically try to calculate the
13039
+ * optimum table and columns widths (true) or not (false).
13040
+ * Note that this parameter will be set by the initialisation routine. To
13041
+ * set a default use {@link DataTable.defaults}.
13042
+ * @type boolean
13043
+ */
13044
+ "bAutoWidth": null,
13045
+
13046
+ /**
13047
+ * Delay the creation of TR and TD elements until they are actually
13048
+ * needed by a driven page draw. This can give a significant speed
13049
+ * increase for Ajax source and Javascript source data, but makes no
13050
+ * difference at all fro DOM and server-side processing tables.
13051
+ * Note that this parameter will be set by the initialisation routine. To
13052
+ * set a default use {@link DataTable.defaults}.
13053
+ * @type boolean
13054
+ */
13055
+ "bDeferRender": null,
13056
+
13057
+ /**
13058
+ * Enable filtering on the table or not. Note that if this is disabled
13059
+ * then there is no filtering at all on the table, including fnFilter.
13060
+ * To just remove the filtering input use sDom and remove the 'f' option.
13061
+ * Note that this parameter will be set by the initialisation routine. To
13062
+ * set a default use {@link DataTable.defaults}.
13063
+ * @type boolean
13064
+ */
13065
+ "bFilter": null,
13066
+
13067
+ /**
13068
+ * Table information element (the 'Showing x of y records' div) enable
13069
+ * flag.
13070
+ * Note that this parameter will be set by the initialisation routine. To
13071
+ * set a default use {@link DataTable.defaults}.
13072
+ * @type boolean
13073
+ */
13074
+ "bInfo": null,
13075
+
13076
+ /**
13077
+ * Present a user control allowing the end user to change the page size
13078
+ * when pagination is enabled.
13079
+ * Note that this parameter will be set by the initialisation routine. To
13080
+ * set a default use {@link DataTable.defaults}.
13081
+ * @type boolean
13082
+ */
13083
+ "bLengthChange": null,
13084
+
13085
+ /**
13086
+ * Pagination enabled or not. Note that if this is disabled then length
13087
+ * changing must also be disabled.
13088
+ * Note that this parameter will be set by the initialisation routine. To
13089
+ * set a default use {@link DataTable.defaults}.
13090
+ * @type boolean
13091
+ */
13092
+ "bPaginate": null,
13093
+
13094
+ /**
13095
+ * Processing indicator enable flag whenever DataTables is enacting a
13096
+ * user request - typically an Ajax request for server-side processing.
13097
+ * Note that this parameter will be set by the initialisation routine. To
13098
+ * set a default use {@link DataTable.defaults}.
13099
+ * @type boolean
13100
+ */
13101
+ "bProcessing": null,
13102
+
13103
+ /**
13104
+ * Server-side processing enabled flag - when enabled DataTables will
13105
+ * get all data from the server for every draw - there is no filtering,
13106
+ * sorting or paging done on the client-side.
13107
+ * Note that this parameter will be set by the initialisation routine. To
13108
+ * set a default use {@link DataTable.defaults}.
13109
+ * @type boolean
13110
+ */
13111
+ "bServerSide": null,
13112
+
13113
+ /**
13114
+ * Sorting enablement flag.
13115
+ * Note that this parameter will be set by the initialisation routine. To
13116
+ * set a default use {@link DataTable.defaults}.
13117
+ * @type boolean
13118
+ */
13119
+ "bSort": null,
13120
+
13121
+ /**
13122
+ * Multi-column sorting
13123
+ * Note that this parameter will be set by the initialisation routine. To
13124
+ * set a default use {@link DataTable.defaults}.
13125
+ * @type boolean
13126
+ */
13127
+ "bSortMulti": null,
13128
+
13129
+ /**
13130
+ * Apply a class to the columns which are being sorted to provide a
13131
+ * visual highlight or not. This can slow things down when enabled since
13132
+ * there is a lot of DOM interaction.
13133
+ * Note that this parameter will be set by the initialisation routine. To
13134
+ * set a default use {@link DataTable.defaults}.
13135
+ * @type boolean
13136
+ */
13137
+ "bSortClasses": null,
13138
+
13139
+ /**
13140
+ * State saving enablement flag.
13141
+ * Note that this parameter will be set by the initialisation routine. To
13142
+ * set a default use {@link DataTable.defaults}.
13143
+ * @type boolean
13144
+ */
13145
+ "bStateSave": null
13146
+ },
13147
+
13148
+
13149
+ /**
13150
+ * Scrolling settings for a table.
13151
+ * @namespace
13152
+ */
13153
+ "oScroll": {
13154
+ /**
13155
+ * When the table is shorter in height than sScrollY, collapse the
13156
+ * table container down to the height of the table (when true).
13157
+ * Note that this parameter will be set by the initialisation routine. To
13158
+ * set a default use {@link DataTable.defaults}.
13159
+ * @type boolean
13160
+ */
13161
+ "bCollapse": null,
13162
+
13163
+ /**
13164
+ * Width of the scrollbar for the web-browser's platform. Calculated
13165
+ * during table initialisation.
13166
+ * @type int
13167
+ * @default 0
13168
+ */
13169
+ "iBarWidth": 0,
13170
+
13171
+ /**
13172
+ * Viewport width for horizontal scrolling. Horizontal scrolling is
13173
+ * disabled if an empty string.
13174
+ * Note that this parameter will be set by the initialisation routine. To
13175
+ * set a default use {@link DataTable.defaults}.
13176
+ * @type string
13177
+ */
13178
+ "sX": null,
13179
+
13180
+ /**
13181
+ * Width to expand the table to when using x-scrolling. Typically you
13182
+ * should not need to use this.
13183
+ * Note that this parameter will be set by the initialisation routine. To
13184
+ * set a default use {@link DataTable.defaults}.
13185
+ * @type string
13186
+ * @deprecated
13187
+ */
13188
+ "sXInner": null,
13189
+
13190
+ /**
13191
+ * Viewport height for vertical scrolling. Vertical scrolling is disabled
13192
+ * if an empty string.
13193
+ * Note that this parameter will be set by the initialisation routine. To
13194
+ * set a default use {@link DataTable.defaults}.
13195
+ * @type string
13196
+ */
13197
+ "sY": null
13198
+ },
13199
+
13200
+ /**
13201
+ * Language information for the table.
13202
+ * @namespace
13203
+ * @extends DataTable.defaults.oLanguage
13204
+ */
13205
+ "oLanguage": {
13206
+ /**
13207
+ * Information callback function. See
13208
+ * {@link DataTable.defaults.fnInfoCallback}
13209
+ * @type function
13210
+ * @default null
13211
+ */
13212
+ "fnInfoCallback": null
13213
+ },
13214
+
13215
+ /**
13216
+ * Browser support parameters
13217
+ * @namespace
13218
+ */
13219
+ "oBrowser": {
13220
+ /**
13221
+ * Indicate if the browser incorrectly calculates width:100% inside a
13222
+ * scrolling element (IE6/7)
13223
+ * @type boolean
13224
+ * @default false
13225
+ */
13226
+ "bScrollOversize": false,
13227
+
13228
+ /**
13229
+ * Determine if the vertical scrollbar is on the right or left of the
13230
+ * scrolling container - needed for rtl language layout, although not
13231
+ * all browsers move the scrollbar (Safari).
13232
+ * @type boolean
13233
+ * @default false
13234
+ */
13235
+ "bScrollbarLeft": false,
13236
+
13237
+ /**
13238
+ * Flag for if `getBoundingClientRect` is fully supported or not
13239
+ * @type boolean
13240
+ * @default false
13241
+ */
13242
+ "bBounding": false,
13243
+
13244
+ /**
13245
+ * Browser scrollbar width
13246
+ * @type integer
13247
+ * @default 0
13248
+ */
13249
+ "barWidth": 0
13250
+ },
13251
+
13252
+
13253
+ "ajax": null,
13254
+
13255
+
13256
+ /**
13257
+ * Array referencing the nodes which are used for the features. The
13258
+ * parameters of this object match what is allowed by sDom - i.e.
13259
+ * <ul>
13260
+ * <li>'l' - Length changing</li>
13261
+ * <li>'f' - Filtering input</li>
13262
+ * <li>'t' - The table!</li>
13263
+ * <li>'i' - Information</li>
13264
+ * <li>'p' - Pagination</li>
13265
+ * <li>'r' - pRocessing</li>
13266
+ * </ul>
13267
+ * @type array
13268
+ * @default []
13269
+ */
13270
+ "aanFeatures": [],
13271
+
13272
+ /**
13273
+ * Store data information - see {@link DataTable.models.oRow} for detailed
13274
+ * information.
13275
+ * @type array
13276
+ * @default []
13277
+ */
13278
+ "aoData": [],
13279
+
13280
+ /**
13281
+ * Array of indexes which are in the current display (after filtering etc)
13282
+ * @type array
13283
+ * @default []
13284
+ */
13285
+ "aiDisplay": [],
13286
+
13287
+ /**
13288
+ * Array of indexes for display - no filtering
13289
+ * @type array
13290
+ * @default []
13291
+ */
13292
+ "aiDisplayMaster": [],
13293
+
13294
+ /**
13295
+ * Map of row ids to data indexes
13296
+ * @type object
13297
+ * @default {}
13298
+ */
13299
+ "aIds": {},
13300
+
13301
+ /**
13302
+ * Store information about each column that is in use
13303
+ * @type array
13304
+ * @default []
13305
+ */
13306
+ "aoColumns": [],
13307
+
13308
+ /**
13309
+ * Store information about the table's header
13310
+ * @type array
13311
+ * @default []
13312
+ */
13313
+ "aoHeader": [],
13314
+
13315
+ /**
13316
+ * Store information about the table's footer
13317
+ * @type array
13318
+ * @default []
13319
+ */
13320
+ "aoFooter": [],
13321
+
13322
+ /**
13323
+ * Store the applied global search information in case we want to force a
13324
+ * research or compare the old search to a new one.
13325
+ * Note that this parameter will be set by the initialisation routine. To
13326
+ * set a default use {@link DataTable.defaults}.
13327
+ * @namespace
13328
+ * @extends DataTable.models.oSearch
13329
+ */
13330
+ "oPreviousSearch": {},
13331
+
13332
+ /**
13333
+ * Store the applied search for each column - see
13334
+ * {@link DataTable.models.oSearch} for the format that is used for the
13335
+ * filtering information for each column.
13336
+ * @type array
13337
+ * @default []
13338
+ */
13339
+ "aoPreSearchCols": [],
13340
+
13341
+ /**
13342
+ * Sorting that is applied to the table. Note that the inner arrays are
13343
+ * used in the following manner:
13344
+ * <ul>
13345
+ * <li>Index 0 - column number</li>
13346
+ * <li>Index 1 - current sorting direction</li>
13347
+ * </ul>
13348
+ * Note that this parameter will be set by the initialisation routine. To
13349
+ * set a default use {@link DataTable.defaults}.
13350
+ * @type array
13351
+ * @todo These inner arrays should really be objects
13352
+ */
13353
+ "aaSorting": null,
13354
+
13355
+ /**
13356
+ * Sorting that is always applied to the table (i.e. prefixed in front of
13357
+ * aaSorting).
13358
+ * Note that this parameter will be set by the initialisation routine. To
13359
+ * set a default use {@link DataTable.defaults}.
13360
+ * @type array
13361
+ * @default []
13362
+ */
13363
+ "aaSortingFixed": [],
13364
+
13365
+ /**
13366
+ * Classes to use for the striping of a table.
13367
+ * Note that this parameter will be set by the initialisation routine. To
13368
+ * set a default use {@link DataTable.defaults}.
13369
+ * @type array
13370
+ * @default []
13371
+ */
13372
+ "asStripeClasses": null,
13373
+
13374
+ /**
13375
+ * If restoring a table - we should restore its striping classes as well
13376
+ * @type array
13377
+ * @default []
13378
+ */
13379
+ "asDestroyStripes": [],
13380
+
13381
+ /**
13382
+ * If restoring a table - we should restore its width
13383
+ * @type int
13384
+ * @default 0
13385
+ */
13386
+ "sDestroyWidth": 0,
13387
+
13388
+ /**
13389
+ * Callback functions array for every time a row is inserted (i.e. on a draw).
13390
+ * @type array
13391
+ * @default []
13392
+ */
13393
+ "aoRowCallback": [],
13394
+
13395
+ /**
13396
+ * Callback functions for the header on each draw.
13397
+ * @type array
13398
+ * @default []
13399
+ */
13400
+ "aoHeaderCallback": [],
13401
+
13402
+ /**
13403
+ * Callback function for the footer on each draw.
13404
+ * @type array
13405
+ * @default []
13406
+ */
13407
+ "aoFooterCallback": [],
13408
+
13409
+ /**
13410
+ * Array of callback functions for draw callback functions
13411
+ * @type array
13412
+ * @default []
13413
+ */
13414
+ "aoDrawCallback": [],
13415
+
13416
+ /**
13417
+ * Array of callback functions for row created function
13418
+ * @type array
13419
+ * @default []
13420
+ */
13421
+ "aoRowCreatedCallback": [],
13422
+
13423
+ /**
13424
+ * Callback functions for just before the table is redrawn. A return of
13425
+ * false will be used to cancel the draw.
13426
+ * @type array
13427
+ * @default []
13428
+ */
13429
+ "aoPreDrawCallback": [],
13430
+
13431
+ /**
13432
+ * Callback functions for when the table has been initialised.
13433
+ * @type array
13434
+ * @default []
13435
+ */
13436
+ "aoInitComplete": [],
13437
+
13438
+
13439
+ /**
13440
+ * Callbacks for modifying the settings to be stored for state saving, prior to
13441
+ * saving state.
13442
+ * @type array
13443
+ * @default []
13444
+ */
13445
+ "aoStateSaveParams": [],
13446
+
13447
+ /**
13448
+ * Callbacks for modifying the settings that have been stored for state saving
13449
+ * prior to using the stored values to restore the state.
13450
+ * @type array
13451
+ * @default []
13452
+ */
13453
+ "aoStateLoadParams": [],
13454
+
13455
+ /**
13456
+ * Callbacks for operating on the settings object once the saved state has been
13457
+ * loaded
13458
+ * @type array
13459
+ * @default []
13460
+ */
13461
+ "aoStateLoaded": [],
13462
+
13463
+ /**
13464
+ * Cache the table ID for quick access
13465
+ * @type string
13466
+ * @default <i>Empty string</i>
13467
+ */
13468
+ "sTableId": "",
13469
+
13470
+ /**
13471
+ * The TABLE node for the main table
13472
+ * @type node
13473
+ * @default null
13474
+ */
13475
+ "nTable": null,
13476
+
13477
+ /**
13478
+ * Permanent ref to the thead element
13479
+ * @type node
13480
+ * @default null
13481
+ */
13482
+ "nTHead": null,
13483
+
13484
+ /**
13485
+ * Permanent ref to the tfoot element - if it exists
13486
+ * @type node
13487
+ * @default null
13488
+ */
13489
+ "nTFoot": null,
13490
+
13491
+ /**
13492
+ * Permanent ref to the tbody element
13493
+ * @type node
13494
+ * @default null
13495
+ */
13496
+ "nTBody": null,
13497
+
13498
+ /**
13499
+ * Cache the wrapper node (contains all DataTables controlled elements)
13500
+ * @type node
13501
+ * @default null
13502
+ */
13503
+ "nTableWrapper": null,
13504
+
13505
+ /**
13506
+ * Indicate if when using server-side processing the loading of data
13507
+ * should be deferred until the second draw.
13508
+ * Note that this parameter will be set by the initialisation routine. To
13509
+ * set a default use {@link DataTable.defaults}.
13510
+ * @type boolean
13511
+ * @default false
13512
+ */
13513
+ "bDeferLoading": false,
13514
+
13515
+ /**
13516
+ * Indicate if all required information has been read in
13517
+ * @type boolean
13518
+ * @default false
13519
+ */
13520
+ "bInitialised": false,
13521
+
13522
+ /**
13523
+ * Information about open rows. Each object in the array has the parameters
13524
+ * 'nTr' and 'nParent'
13525
+ * @type array
13526
+ * @default []
13527
+ */
13528
+ "aoOpenRows": [],
13529
+
13530
+ /**
13531
+ * Dictate the positioning of DataTables' control elements - see
13532
+ * {@link DataTable.model.oInit.sDom}.
13533
+ * Note that this parameter will be set by the initialisation routine. To
13534
+ * set a default use {@link DataTable.defaults}.
13535
+ * @type string
13536
+ * @default null
13537
+ */
13538
+ "sDom": null,
13539
+
13540
+ /**
13541
+ * Search delay (in mS)
13542
+ * @type integer
13543
+ * @default null
13544
+ */
13545
+ "searchDelay": null,
13546
+
13547
+ /**
13548
+ * Which type of pagination should be used.
13549
+ * Note that this parameter will be set by the initialisation routine. To
13550
+ * set a default use {@link DataTable.defaults}.
13551
+ * @type string
13552
+ * @default two_button
13553
+ */
13554
+ "sPaginationType": "two_button",
13555
+
13556
+ /**
13557
+ * The state duration (for `stateSave`) in seconds.
13558
+ * Note that this parameter will be set by the initialisation routine. To
13559
+ * set a default use {@link DataTable.defaults}.
13560
+ * @type int
13561
+ * @default 0
13562
+ */
13563
+ "iStateDuration": 0,
13564
+
13565
+ /**
13566
+ * Array of callback functions for state saving. Each array element is an
13567
+ * object with the following parameters:
13568
+ * <ul>
13569
+ * <li>function:fn - function to call. Takes two parameters, oSettings
13570
+ * and the JSON string to save that has been thus far created. Returns
13571
+ * a JSON string to be inserted into a json object
13572
+ * (i.e. '"param": [ 0, 1, 2]')</li>
13573
+ * <li>string:sName - name of callback</li>
13574
+ * </ul>
13575
+ * @type array
13576
+ * @default []
13577
+ */
13578
+ "aoStateSave": [],
13579
+
13580
+ /**
13581
+ * Array of callback functions for state loading. Each array element is an
13582
+ * object with the following parameters:
13583
+ * <ul>
13584
+ * <li>function:fn - function to call. Takes two parameters, oSettings
13585
+ * and the object stored. May return false to cancel state loading</li>
13586
+ * <li>string:sName - name of callback</li>
13587
+ * </ul>
13588
+ * @type array
13589
+ * @default []
13590
+ */
13591
+ "aoStateLoad": [],
13592
+
13593
+ /**
13594
+ * State that was saved. Useful for back reference
13595
+ * @type object
13596
+ * @default null
13597
+ */
13598
+ "oSavedState": null,
13599
+
13600
+ /**
13601
+ * State that was loaded. Useful for back reference
13602
+ * @type object
13603
+ * @default null
13604
+ */
13605
+ "oLoadedState": null,
13606
+
13607
+ /**
13608
+ * Source url for AJAX data for the table.
13609
+ * Note that this parameter will be set by the initialisation routine. To
13610
+ * set a default use {@link DataTable.defaults}.
13611
+ * @type string
13612
+ * @default null
13613
+ */
13614
+ "sAjaxSource": null,
13615
+
13616
+ /**
13617
+ * Property from a given object from which to read the table data from. This
13618
+ * can be an empty string (when not server-side processing), in which case
13619
+ * it is assumed an an array is given directly.
13620
+ * Note that this parameter will be set by the initialisation routine. To
13621
+ * set a default use {@link DataTable.defaults}.
13622
+ * @type string
13623
+ */
13624
+ "sAjaxDataProp": null,
13625
+
13626
+ /**
13627
+ * Note if draw should be blocked while getting data
13628
+ * @type boolean
13629
+ * @default true
13630
+ */
13631
+ "bAjaxDataGet": true,
13632
+
13633
+ /**
13634
+ * The last jQuery XHR object that was used for server-side data gathering.
13635
+ * This can be used for working with the XHR information in one of the
13636
+ * callbacks
13637
+ * @type object
13638
+ * @default null
13639
+ */
13640
+ "jqXHR": null,
13641
+
13642
+ /**
13643
+ * JSON returned from the server in the last Ajax request
13644
+ * @type object
13645
+ * @default undefined
13646
+ */
13647
+ "json": undefined,
13648
+
13649
+ /**
13650
+ * Data submitted as part of the last Ajax request
13651
+ * @type object
13652
+ * @default undefined
13653
+ */
13654
+ "oAjaxData": undefined,
13655
+
13656
+ /**
13657
+ * Function to get the server-side data.
13658
+ * Note that this parameter will be set by the initialisation routine. To
13659
+ * set a default use {@link DataTable.defaults}.
13660
+ * @type function
13661
+ */
13662
+ "fnServerData": null,
13663
+
13664
+ /**
13665
+ * Functions which are called prior to sending an Ajax request so extra
13666
+ * parameters can easily be sent to the server
13667
+ * @type array
13668
+ * @default []
13669
+ */
13670
+ "aoServerParams": [],
13671
+
13672
+ /**
13673
+ * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13674
+ * required).
13675
+ * Note that this parameter will be set by the initialisation routine. To
13676
+ * set a default use {@link DataTable.defaults}.
13677
+ * @type string
13678
+ */
13679
+ "sServerMethod": null,
13680
+
13681
+ /**
13682
+ * Format numbers for display.
13683
+ * Note that this parameter will be set by the initialisation routine. To
13684
+ * set a default use {@link DataTable.defaults}.
13685
+ * @type function
13686
+ */
13687
+ "fnFormatNumber": null,
13688
+
13689
+ /**
13690
+ * List of options that can be used for the user selectable length menu.
13691
+ * Note that this parameter will be set by the initialisation routine. To
13692
+ * set a default use {@link DataTable.defaults}.
13693
+ * @type array
13694
+ * @default []
13695
+ */
13696
+ "aLengthMenu": null,
13697
+
13698
+ /**
13699
+ * Counter for the draws that the table does. Also used as a tracker for
13700
+ * server-side processing
13701
+ * @type int
13702
+ * @default 0
13703
+ */
13704
+ "iDraw": 0,
13705
+
13706
+ /**
13707
+ * Indicate if a redraw is being done - useful for Ajax
13708
+ * @type boolean
13709
+ * @default false
13710
+ */
13711
+ "bDrawing": false,
13712
+
13713
+ /**
13714
+ * Draw index (iDraw) of the last error when parsing the returned data
13715
+ * @type int
13716
+ * @default -1
13717
+ */
13718
+ "iDrawError": -1,
13719
+
13720
+ /**
13721
+ * Paging display length
13722
+ * @type int
13723
+ * @default 10
13724
+ */
13725
+ "_iDisplayLength": 10,
13726
+
13727
+ /**
13728
+ * Paging start point - aiDisplay index
13729
+ * @type int
13730
+ * @default 0
13731
+ */
13732
+ "_iDisplayStart": 0,
13733
+
13734
+ /**
13735
+ * Server-side processing - number of records in the result set
13736
+ * (i.e. before filtering), Use fnRecordsTotal rather than
13737
+ * this property to get the value of the number of records, regardless of
13738
+ * the server-side processing setting.
13739
+ * @type int
13740
+ * @default 0
13741
+ * @private
13742
+ */
13743
+ "_iRecordsTotal": 0,
13744
+
13745
+ /**
13746
+ * Server-side processing - number of records in the current display set
13747
+ * (i.e. after filtering). Use fnRecordsDisplay rather than
13748
+ * this property to get the value of the number of records, regardless of
13749
+ * the server-side processing setting.
13750
+ * @type boolean
13751
+ * @default 0
13752
+ * @private
13753
+ */
13754
+ "_iRecordsDisplay": 0,
13755
+
13756
+ /**
13757
+ * The classes to use for the table
13758
+ * @type object
13759
+ * @default {}
13760
+ */
13761
+ "oClasses": {},
13762
+
13763
+ /**
13764
+ * Flag attached to the settings object so you can check in the draw
13765
+ * callback if filtering has been done in the draw. Deprecated in favour of
13766
+ * events.
13767
+ * @type boolean
13768
+ * @default false
13769
+ * @deprecated
13770
+ */
13771
+ "bFiltered": false,
13772
+
13773
+ /**
13774
+ * Flag attached to the settings object so you can check in the draw
13775
+ * callback if sorting has been done in the draw. Deprecated in favour of
13776
+ * events.
13777
+ * @type boolean
13778
+ * @default false
13779
+ * @deprecated
13780
+ */
13781
+ "bSorted": false,
13782
+
13783
+ /**
13784
+ * Indicate that if multiple rows are in the header and there is more than
13785
+ * one unique cell per column, if the top one (true) or bottom one (false)
13786
+ * should be used for sorting / title by DataTables.
13787
+ * Note that this parameter will be set by the initialisation routine. To
13788
+ * set a default use {@link DataTable.defaults}.
13789
+ * @type boolean
13790
+ */
13791
+ "bSortCellsTop": null,
13792
+
13793
+ /**
13794
+ * Initialisation object that is used for the table
13795
+ * @type object
13796
+ * @default null
13797
+ */
13798
+ "oInit": null,
13799
+
13800
+ /**
13801
+ * Destroy callback functions - for plug-ins to attach themselves to the
13802
+ * destroy so they can clean up markup and events.
13803
+ * @type array
13804
+ * @default []
13805
+ */
13806
+ "aoDestroyCallback": [],
13807
+
13808
+
13809
+ /**
13810
+ * Get the number of records in the current record set, before filtering
13811
+ * @type function
13812
+ */
13813
+ "fnRecordsTotal": function ()
13814
+ {
13815
+ return _fnDataSource( this ) == 'ssp' ?
13816
+ this._iRecordsTotal * 1 :
13817
+ this.aiDisplayMaster.length;
13818
+ },
13819
+
13820
+ /**
13821
+ * Get the number of records in the current record set, after filtering
13822
+ * @type function
13823
+ */
13824
+ "fnRecordsDisplay": function ()
13825
+ {
13826
+ return _fnDataSource( this ) == 'ssp' ?
13827
+ this._iRecordsDisplay * 1 :
13828
+ this.aiDisplay.length;
13829
+ },
13830
+
13831
+ /**
13832
+ * Get the display end point - aiDisplay index
13833
+ * @type function
13834
+ */
13835
+ "fnDisplayEnd": function ()
13836
+ {
13837
+ var
13838
+ len = this._iDisplayLength,
13839
+ start = this._iDisplayStart,
13840
+ calc = start + len,
13841
+ records = this.aiDisplay.length,
13842
+ features = this.oFeatures,
13843
+ paginate = features.bPaginate;
13844
+
13845
+ if ( features.bServerSide ) {
13846
+ return paginate === false || len === -1 ?
13847
+ start + records :
13848
+ Math.min( start+len, this._iRecordsDisplay );
13849
+ }
13850
+ else {
13851
+ return ! paginate || calc>records || len===-1 ?
13852
+ records :
13853
+ calc;
13854
+ }
13855
+ },
13856
+
13857
+ /**
13858
+ * The DataTables object for this table
13859
+ * @type object
13860
+ * @default null
13861
+ */
13862
+ "oInstance": null,
13863
+
13864
+ /**
13865
+ * Unique identifier for each instance of the DataTables object. If there
13866
+ * is an ID on the table node, then it takes that value, otherwise an
13867
+ * incrementing internal counter is used.
13868
+ * @type string
13869
+ * @default null
13870
+ */
13871
+ "sInstance": null,
13872
+
13873
+ /**
13874
+ * tabindex attribute value that is added to DataTables control elements, allowing
13875
+ * keyboard navigation of the table and its controls.
13876
+ */
13877
+ "iTabIndex": 0,
13878
+
13879
+ /**
13880
+ * DIV container for the footer scrolling table if scrolling
13881
+ */
13882
+ "nScrollHead": null,
13883
+
13884
+ /**
13885
+ * DIV container for the footer scrolling table if scrolling
13886
+ */
13887
+ "nScrollFoot": null,
13888
+
13889
+ /**
13890
+ * Last applied sort
13891
+ * @type array
13892
+ * @default []
13893
+ */
13894
+ "aLastSort": [],
13895
+
13896
+ /**
13897
+ * Stored plug-in instances
13898
+ * @type object
13899
+ * @default {}
13900
+ */
13901
+ "oPlugins": {},
13902
+
13903
+ /**
13904
+ * Function used to get a row's id from the row's data
13905
+ * @type function
13906
+ * @default null
13907
+ */
13908
+ "rowIdFn": null,
13909
+
13910
+ /**
13911
+ * Data location where to store a row's id
13912
+ * @type string
13913
+ * @default null
13914
+ */
13915
+ "rowId": null
13916
+ };
13917
+
13918
+ /**
13919
+ * Extension object for DataTables that is used to provide all extension
13920
+ * options.
13921
+ *
13922
+ * Note that the `DataTable.ext` object is available through
13923
+ * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
13924
+ * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
13925
+ * @namespace
13926
+ * @extends DataTable.models.ext
13927
+ */
13928
+
13929
+
13930
+ /**
13931
+ * DataTables extensions
13932
+ *
13933
+ * This namespace acts as a collection area for plug-ins that can be used to
13934
+ * extend DataTables capabilities. Indeed many of the build in methods
13935
+ * use this method to provide their own capabilities (sorting methods for
13936
+ * example).
13937
+ *
13938
+ * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
13939
+ * reasons
13940
+ *
13941
+ * @namespace
13942
+ */
13943
+ DataTable.ext = _ext = {
13944
+ /**
13945
+ * Buttons. For use with the Buttons extension for DataTables. This is
13946
+ * defined here so other extensions can define buttons regardless of load
13947
+ * order. It is _not_ used by DataTables core.
13948
+ *
13949
+ * @type object
13950
+ * @default {}
13951
+ */
13952
+ buttons: {},
13953
+
13954
+
13955
+ /**
13956
+ * Element class names
13957
+ *
13958
+ * @type object
13959
+ * @default {}
13960
+ */
13961
+ classes: {},
13962
+
13963
+
13964
+ /**
13965
+ * DataTables build type (expanded by the download builder)
13966
+ *
13967
+ * @type string
13968
+ */
13969
+ build:"dt/dt-1.10.23/af-2.3.5/b-1.6.5/cr-1.5.3/fc-3.3.2/fh-3.1.8/kt-2.6.1/r-2.2.7/rr-1.2.7/sc-2.0.3/sl-1.3.1",
13970
+
13971
+
13972
+ /**
13973
+ * Error reporting.
13974
+ *
13975
+ * How should DataTables report an error. Can take the value 'alert',
13976
+ * 'throw', 'none' or a function.
13977
+ *
13978
+ * @type string|function
13979
+ * @default alert
13980
+ */
13981
+ errMode: "alert",
13982
+
13983
+
13984
+ /**
13985
+ * Feature plug-ins.
13986
+ *
13987
+ * This is an array of objects which describe the feature plug-ins that are
13988
+ * available to DataTables. These feature plug-ins are then available for
13989
+ * use through the `dom` initialisation option.
13990
+ *
13991
+ * Each feature plug-in is described by an object which must have the
13992
+ * following properties:
13993
+ *
13994
+ * * `fnInit` - function that is used to initialise the plug-in,
13995
+ * * `cFeature` - a character so the feature can be enabled by the `dom`
13996
+ * instillation option. This is case sensitive.
13997
+ *
13998
+ * The `fnInit` function has the following input parameters:
13999
+ *
14000
+ * 1. `{object}` DataTables settings object: see
14001
+ * {@link DataTable.models.oSettings}
14002
+ *
14003
+ * And the following return is expected:
14004
+ *
14005
+ * * {node|null} The element which contains your feature. Note that the
14006
+ * return may also be void if your plug-in does not require to inject any
14007
+ * DOM elements into DataTables control (`dom`) - for example this might
14008
+ * be useful when developing a plug-in which allows table control via
14009
+ * keyboard entry
14010
+ *
14011
+ * @type array
14012
+ *
14013
+ * @example
14014
+ * $.fn.dataTable.ext.features.push( {
14015
+ * "fnInit": function( oSettings ) {
14016
+ * return new TableTools( { "oDTSettings": oSettings } );
14017
+ * },
14018
+ * "cFeature": "T"
14019
+ * } );
14020
+ */
14021
+ feature: [],
14022
+
14023
+
14024
+ /**
14025
+ * Row searching.
14026
+ *
14027
+ * This method of searching is complimentary to the default type based
14028
+ * searching, and a lot more comprehensive as it allows you complete control
14029
+ * over the searching logic. Each element in this array is a function
14030
+ * (parameters described below) that is called for every row in the table,
14031
+ * and your logic decides if it should be included in the searching data set
14032
+ * or not.
14033
+ *
14034
+ * Searching functions have the following input parameters:
14035
+ *
14036
+ * 1. `{object}` DataTables settings object: see
14037
+ * {@link DataTable.models.oSettings}
14038
+ * 2. `{array|object}` Data for the row to be processed (same as the
14039
+ * original format that was passed in as the data source, or an array
14040
+ * from a DOM data source
14041
+ * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
14042
+ * can be useful to retrieve the `TR` element if you need DOM interaction.
14043
+ *
14044
+ * And the following return is expected:
14045
+ *
14046
+ * * {boolean} Include the row in the searched result set (true) or not
14047
+ * (false)
14048
+ *
14049
+ * Note that as with the main search ability in DataTables, technically this
14050
+ * is "filtering", since it is subtractive. However, for consistency in
14051
+ * naming we call it searching here.
14052
+ *
14053
+ * @type array
14054
+ * @default []
14055
+ *
14056
+ * @example
14057
+ * // The following example shows custom search being applied to the
14058
+ * // fourth column (i.e. the data[3] index) based on two input values
14059
+ * // from the end-user, matching the data in a certain range.
14060
+ * $.fn.dataTable.ext.search.push(
14061
+ * function( settings, data, dataIndex ) {
14062
+ * var min = document.getElementById('min').value * 1;
14063
+ * var max = document.getElementById('max').value * 1;
14064
+ * var version = data[3] == "-" ? 0 : data[3]*1;
14065
+ *
14066
+ * if ( min == "" && max == "" ) {
14067
+ * return true;
14068
+ * }
14069
+ * else if ( min == "" && version < max ) {
14070
+ * return true;
14071
+ * }
14072
+ * else if ( min < version && "" == max ) {
14073
+ * return true;
14074
+ * }
14075
+ * else if ( min < version && version < max ) {
14076
+ * return true;
14077
+ * }
14078
+ * return false;
14079
+ * }
14080
+ * );
14081
+ */
14082
+ search: [],
14083
+
14084
+
14085
+ /**
14086
+ * Selector extensions
14087
+ *
14088
+ * The `selector` option can be used to extend the options available for the
14089
+ * selector modifier options (`selector-modifier` object data type) that
14090
+ * each of the three built in selector types offer (row, column and cell +
14091
+ * their plural counterparts). For example the Select extension uses this
14092
+ * mechanism to provide an option to select only rows, columns and cells
14093
+ * that have been marked as selected by the end user (`{selected: true}`),
14094
+ * which can be used in conjunction with the existing built in selector
14095
+ * options.
14096
+ *
14097
+ * Each property is an array to which functions can be pushed. The functions
14098
+ * take three attributes:
14099
+ *
14100
+ * * Settings object for the host table
14101
+ * * Options object (`selector-modifier` object type)
14102
+ * * Array of selected item indexes
14103
+ *
14104
+ * The return is an array of the resulting item indexes after the custom
14105
+ * selector has been applied.
14106
+ *
14107
+ * @type object
14108
+ */
14109
+ selector: {
14110
+ cell: [],
14111
+ column: [],
14112
+ row: []
14113
+ },
14114
+
14115
+
14116
+ /**
14117
+ * Internal functions, exposed for used in plug-ins.
14118
+ *
14119
+ * Please note that you should not need to use the internal methods for
14120
+ * anything other than a plug-in (and even then, try to avoid if possible).
14121
+ * The internal function may change between releases.
14122
+ *
14123
+ * @type object
14124
+ * @default {}
14125
+ */
14126
+ internal: {},
14127
+
14128
+
14129
+ /**
14130
+ * Legacy configuration options. Enable and disable legacy options that
14131
+ * are available in DataTables.
14132
+ *
14133
+ * @type object
14134
+ */
14135
+ legacy: {
14136
+ /**
14137
+ * Enable / disable DataTables 1.9 compatible server-side processing
14138
+ * requests
14139
+ *
14140
+ * @type boolean
14141
+ * @default null
14142
+ */
14143
+ ajax: null
14144
+ },
14145
+
14146
+
14147
+ /**
14148
+ * Pagination plug-in methods.
14149
+ *
14150
+ * Each entry in this object is a function and defines which buttons should
14151
+ * be shown by the pagination rendering method that is used for the table:
14152
+ * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
14153
+ * buttons are displayed in the document, while the functions here tell it
14154
+ * what buttons to display. This is done by returning an array of button
14155
+ * descriptions (what each button will do).
14156
+ *
14157
+ * Pagination types (the four built in options and any additional plug-in
14158
+ * options defined here) can be used through the `paginationType`
14159
+ * initialisation parameter.
14160
+ *
14161
+ * The functions defined take two parameters:
14162
+ *
14163
+ * 1. `{int} page` The current page index
14164
+ * 2. `{int} pages` The number of pages in the table
14165
+ *
14166
+ * Each function is expected to return an array where each element of the
14167
+ * array can be one of:
14168
+ *
14169
+ * * `first` - Jump to first page when activated
14170
+ * * `last` - Jump to last page when activated
14171
+ * * `previous` - Show previous page when activated
14172
+ * * `next` - Show next page when activated
14173
+ * * `{int}` - Show page of the index given
14174
+ * * `{array}` - A nested array containing the above elements to add a
14175
+ * containing 'DIV' element (might be useful for styling).
14176
+ *
14177
+ * Note that DataTables v1.9- used this object slightly differently whereby
14178
+ * an object with two functions would be defined for each plug-in. That
14179
+ * ability is still supported by DataTables 1.10+ to provide backwards
14180
+ * compatibility, but this option of use is now decremented and no longer
14181
+ * documented in DataTables 1.10+.
14182
+ *
14183
+ * @type object
14184
+ * @default {}
14185
+ *
14186
+ * @example
14187
+ * // Show previous, next and current page buttons only
14188
+ * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
14189
+ * return [ 'previous', page, 'next' ];
14190
+ * };
14191
+ */
14192
+ pager: {},
14193
+
14194
+
14195
+ renderer: {
14196
+ pageButton: {},
14197
+ header: {}
14198
+ },
14199
+
14200
+
14201
+ /**
14202
+ * Ordering plug-ins - custom data source
14203
+ *
14204
+ * The extension options for ordering of data available here is complimentary
14205
+ * to the default type based ordering that DataTables typically uses. It
14206
+ * allows much greater control over the the data that is being used to
14207
+ * order a column, but is necessarily therefore more complex.
14208
+ *
14209
+ * This type of ordering is useful if you want to do ordering based on data
14210
+ * live from the DOM (for example the contents of an 'input' element) rather
14211
+ * than just the static string that DataTables knows of.
14212
+ *
14213
+ * The way these plug-ins work is that you create an array of the values you
14214
+ * wish to be ordering for the column in question and then return that
14215
+ * array. The data in the array much be in the index order of the rows in
14216
+ * the table (not the currently ordering order!). Which order data gathering
14217
+ * function is run here depends on the `dt-init columns.orderDataType`
14218
+ * parameter that is used for the column (if any).
14219
+ *
14220
+ * The functions defined take two parameters:
14221
+ *
14222
+ * 1. `{object}` DataTables settings object: see
14223
+ * {@link DataTable.models.oSettings}
14224
+ * 2. `{int}` Target column index
14225
+ *
14226
+ * Each function is expected to return an array:
14227
+ *
14228
+ * * `{array}` Data for the column to be ordering upon
14229
+ *
14230
+ * @type array
14231
+ *
14232
+ * @example
14233
+ * // Ordering using `input` node values
14234
+ * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
14235
+ * {
14236
+ * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
14237
+ * return $('input', td).val();
14238
+ * } );
14239
+ * }
14240
+ */
14241
+ order: {},
14242
+
14243
+
14244
+ /**
14245
+ * Type based plug-ins.
14246
+ *
14247
+ * Each column in DataTables has a type assigned to it, either by automatic
14248
+ * detection or by direct assignment using the `type` option for the column.
14249
+ * The type of a column will effect how it is ordering and search (plug-ins
14250
+ * can also make use of the column type if required).
14251
+ *
14252
+ * @namespace
14253
+ */
14254
+ type: {
14255
+ /**
14256
+ * Type detection functions.
14257
+ *
14258
+ * The functions defined in this object are used to automatically detect
14259
+ * a column's type, making initialisation of DataTables super easy, even
14260
+ * when complex data is in the table.
14261
+ *
14262
+ * The functions defined take two parameters:
14263
+ *
14264
+ * 1. `{*}` Data from the column cell to be analysed
14265
+ * 2. `{settings}` DataTables settings object. This can be used to
14266
+ * perform context specific type detection - for example detection
14267
+ * based on language settings such as using a comma for a decimal
14268
+ * place. Generally speaking the options from the settings will not
14269
+ * be required
14270
+ *
14271
+ * Each function is expected to return:
14272
+ *
14273
+ * * `{string|null}` Data type detected, or null if unknown (and thus
14274
+ * pass it on to the other type detection functions.
14275
+ *
14276
+ * @type array
14277
+ *
14278
+ * @example
14279
+ * // Currency type detection plug-in:
14280
+ * $.fn.dataTable.ext.type.detect.push(
14281
+ * function ( data, settings ) {
14282
+ * // Check the numeric part
14283
+ * if ( ! data.substring(1).match(/[0-9]/) ) {
14284
+ * return null;
14285
+ * }
14286
+ *
14287
+ * // Check prefixed by currency
14288
+ * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
14289
+ * return 'currency';
14290
+ * }
14291
+ * return null;
14292
+ * }
14293
+ * );
14294
+ */
14295
+ detect: [],
14296
+
14297
+
14298
+ /**
14299
+ * Type based search formatting.
14300
+ *
14301
+ * The type based searching functions can be used to pre-format the
14302
+ * data to be search on. For example, it can be used to strip HTML
14303
+ * tags or to de-format telephone numbers for numeric only searching.
14304
+ *
14305
+ * Note that is a search is not defined for a column of a given type,
14306
+ * no search formatting will be performed.
14307
+ *
14308
+ * Pre-processing of searching data plug-ins - When you assign the sType
14309
+ * for a column (or have it automatically detected for you by DataTables
14310
+ * or a type detection plug-in), you will typically be using this for
14311
+ * custom sorting, but it can also be used to provide custom searching
14312
+ * by allowing you to pre-processing the data and returning the data in
14313
+ * the format that should be searched upon. This is done by adding
14314
+ * functions this object with a parameter name which matches the sType
14315
+ * for that target column. This is the corollary of <i>afnSortData</i>
14316
+ * for searching data.
14317
+ *
14318
+ * The functions defined take a single parameter:
14319
+ *
14320
+ * 1. `{*}` Data from the column cell to be prepared for searching
14321
+ *
14322
+ * Each function is expected to return:
14323
+ *
14324
+ * * `{string|null}` Formatted string that will be used for the searching.
14325
+ *
14326
+ * @type object
14327
+ * @default {}
14328
+ *
14329
+ * @example
14330
+ * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
14331
+ * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
14332
+ * }
14333
+ */
14334
+ search: {},
14335
+
14336
+
14337
+ /**
14338
+ * Type based ordering.
14339
+ *
14340
+ * The column type tells DataTables what ordering to apply to the table
14341
+ * when a column is sorted upon. The order for each type that is defined,
14342
+ * is defined by the functions available in this object.
14343
+ *
14344
+ * Each ordering option can be described by three properties added to
14345
+ * this object:
14346
+ *
14347
+ * * `{type}-pre` - Pre-formatting function
14348
+ * * `{type}-asc` - Ascending order function
14349
+ * * `{type}-desc` - Descending order function
14350
+ *
14351
+ * All three can be used together, only `{type}-pre` or only
14352
+ * `{type}-asc` and `{type}-desc` together. It is generally recommended
14353
+ * that only `{type}-pre` is used, as this provides the optimal
14354
+ * implementation in terms of speed, although the others are provided
14355
+ * for compatibility with existing Javascript sort functions.
14356
+ *
14357
+ * `{type}-pre`: Functions defined take a single parameter:
14358
+ *
14359
+ * 1. `{*}` Data from the column cell to be prepared for ordering
14360
+ *
14361
+ * And return:
14362
+ *
14363
+ * * `{*}` Data to be sorted upon
14364
+ *
14365
+ * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
14366
+ * functions, taking two parameters:
14367
+ *
14368
+ * 1. `{*}` Data to compare to the second parameter
14369
+ * 2. `{*}` Data to compare to the first parameter
14370
+ *
14371
+ * And returning:
14372
+ *
14373
+ * * `{*}` Ordering match: <0 if first parameter should be sorted lower
14374
+ * than the second parameter, ===0 if the two parameters are equal and
14375
+ * >0 if the first parameter should be sorted height than the second
14376
+ * parameter.
14377
+ *
14378
+ * @type object
14379
+ * @default {}
14380
+ *
14381
+ * @example
14382
+ * // Numeric ordering of formatted numbers with a pre-formatter
14383
+ * $.extend( $.fn.dataTable.ext.type.order, {
14384
+ * "string-pre": function(x) {
14385
+ * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
14386
+ * return parseFloat( a );
14387
+ * }
14388
+ * } );
14389
+ *
14390
+ * @example
14391
+ * // Case-sensitive string ordering, with no pre-formatting method
14392
+ * $.extend( $.fn.dataTable.ext.order, {
14393
+ * "string-case-asc": function(x,y) {
14394
+ * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14395
+ * },
14396
+ * "string-case-desc": function(x,y) {
14397
+ * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14398
+ * }
14399
+ * } );
14400
+ */
14401
+ order: {}
14402
+ },
14403
+
14404
+ /**
14405
+ * Unique DataTables instance counter
14406
+ *
14407
+ * @type int
14408
+ * @private
14409
+ */
14410
+ _unique: 0,
14411
+
14412
+
14413
+ //
14414
+ // Depreciated
14415
+ // The following properties are retained for backwards compatiblity only.
14416
+ // The should not be used in new projects and will be removed in a future
14417
+ // version
14418
+ //
14419
+
14420
+ /**
14421
+ * Version check function.
14422
+ * @type function
14423
+ * @depreciated Since 1.10
14424
+ */
14425
+ fnVersionCheck: DataTable.fnVersionCheck,
14426
+
14427
+
14428
+ /**
14429
+ * Index for what 'this' index API functions should use
14430
+ * @type int
14431
+ * @deprecated Since v1.10
14432
+ */
14433
+ iApiIndex: 0,
14434
+
14435
+
14436
+ /**
14437
+ * jQuery UI class container
14438
+ * @type object
14439
+ * @deprecated Since v1.10
14440
+ */
14441
+ oJUIClasses: {},
14442
+
14443
+
14444
+ /**
14445
+ * Software version
14446
+ * @type string
14447
+ * @deprecated Since v1.10
14448
+ */
14449
+ sVersion: DataTable.version
14450
+ };
14451
+
14452
+
14453
+ //
14454
+ // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
14455
+ //
14456
+ $.extend( _ext, {
14457
+ afnFiltering: _ext.search,
14458
+ aTypes: _ext.type.detect,
14459
+ ofnSearch: _ext.type.search,
14460
+ oSort: _ext.type.order,
14461
+ afnSortData: _ext.order,
14462
+ aoFeatures: _ext.feature,
14463
+ oApi: _ext.internal,
14464
+ oStdClasses: _ext.classes,
14465
+ oPagination: _ext.pager
14466
+ } );
14467
+
14468
+
14469
+ $.extend( DataTable.ext.classes, {
14470
+ "sTable": "dataTable",
14471
+ "sNoFooter": "no-footer",
14472
+
14473
+ /* Paging buttons */
14474
+ "sPageButton": "paginate_button",
14475
+ "sPageButtonActive": "current",
14476
+ "sPageButtonDisabled": "disabled",
14477
+
14478
+ /* Striping classes */
14479
+ "sStripeOdd": "odd",
14480
+ "sStripeEven": "even",
14481
+
14482
+ /* Empty row */
14483
+ "sRowEmpty": "dataTables_empty",
14484
+
14485
+ /* Features */
14486
+ "sWrapper": "dataTables_wrapper",
14487
+ "sFilter": "dataTables_filter",
14488
+ "sInfo": "dataTables_info",
14489
+ "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
14490
+ "sLength": "dataTables_length",
14491
+ "sProcessing": "dataTables_processing",
14492
+
14493
+ /* Sorting */
14494
+ "sSortAsc": "sorting_asc",
14495
+ "sSortDesc": "sorting_desc",
14496
+ "sSortable": "sorting", /* Sortable in both directions */
14497
+ "sSortableAsc": "sorting_asc_disabled",
14498
+ "sSortableDesc": "sorting_desc_disabled",
14499
+ "sSortableNone": "sorting_disabled",
14500
+ "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
14501
+
14502
+ /* Filtering */
14503
+ "sFilterInput": "",
14504
+
14505
+ /* Page length */
14506
+ "sLengthSelect": "",
14507
+
14508
+ /* Scrolling */
14509
+ "sScrollWrapper": "dataTables_scroll",
14510
+ "sScrollHead": "dataTables_scrollHead",
14511
+ "sScrollHeadInner": "dataTables_scrollHeadInner",
14512
+ "sScrollBody": "dataTables_scrollBody",
14513
+ "sScrollFoot": "dataTables_scrollFoot",
14514
+ "sScrollFootInner": "dataTables_scrollFootInner",
14515
+
14516
+ /* Misc */
14517
+ "sHeaderTH": "",
14518
+ "sFooterTH": "",
14519
+
14520
+ // Deprecated
14521
+ "sSortJUIAsc": "",
14522
+ "sSortJUIDesc": "",
14523
+ "sSortJUI": "",
14524
+ "sSortJUIAscAllowed": "",
14525
+ "sSortJUIDescAllowed": "",
14526
+ "sSortJUIWrapper": "",
14527
+ "sSortIcon": "",
14528
+ "sJUIHeader": "",
14529
+ "sJUIFooter": ""
14530
+ } );
14531
+
14532
+
14533
+ var extPagination = DataTable.ext.pager;
14534
+
14535
+ function _numbers ( page, pages ) {
14536
+ var
14537
+ numbers = [],
14538
+ buttons = extPagination.numbers_length,
14539
+ half = Math.floor( buttons / 2 ),
14540
+ i = 1;
14541
+
14542
+ if ( pages <= buttons ) {
14543
+ numbers = _range( 0, pages );
14544
+ }
14545
+ else if ( page <= half ) {
14546
+ numbers = _range( 0, buttons-2 );
14547
+ numbers.push( 'ellipsis' );
14548
+ numbers.push( pages-1 );
14549
+ }
14550
+ else if ( page >= pages - 1 - half ) {
14551
+ numbers = _range( pages-(buttons-2), pages );
14552
+ numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
14553
+ numbers.splice( 0, 0, 0 );
14554
+ }
14555
+ else {
14556
+ numbers = _range( page-half+2, page+half-1 );
14557
+ numbers.push( 'ellipsis' );
14558
+ numbers.push( pages-1 );
14559
+ numbers.splice( 0, 0, 'ellipsis' );
14560
+ numbers.splice( 0, 0, 0 );
14561
+ }
14562
+
14563
+ numbers.DT_el = 'span';
14564
+ return numbers;
14565
+ }
14566
+
14567
+
14568
+ $.extend( extPagination, {
14569
+ simple: function ( page, pages ) {
14570
+ return [ 'previous', 'next' ];
14571
+ },
14572
+
14573
+ full: function ( page, pages ) {
14574
+ return [ 'first', 'previous', 'next', 'last' ];
14575
+ },
14576
+
14577
+ numbers: function ( page, pages ) {
14578
+ return [ _numbers(page, pages) ];
14579
+ },
14580
+
14581
+ simple_numbers: function ( page, pages ) {
14582
+ return [ 'previous', _numbers(page, pages), 'next' ];
14583
+ },
14584
+
14585
+ full_numbers: function ( page, pages ) {
14586
+ return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14587
+ },
14588
+
14589
+ first_last_numbers: function (page, pages) {
14590
+ return ['first', _numbers(page, pages), 'last'];
14591
+ },
14592
+
14593
+ // For testing and plug-ins to use
14594
+ _numbers: _numbers,
14595
+
14596
+ // Number of number buttons (including ellipsis) to show. _Must be odd!_
14597
+ numbers_length: 7
14598
+ } );
14599
+
14600
+
14601
+ $.extend( true, DataTable.ext.renderer, {
14602
+ pageButton: {
14603
+ _: function ( settings, host, idx, buttons, page, pages ) {
14604
+ var classes = settings.oClasses;
14605
+ var lang = settings.oLanguage.oPaginate;
14606
+ var aria = settings.oLanguage.oAria.paginate || {};
14607
+ var btnDisplay, btnClass, counter=0;
14608
+
14609
+ var attach = function( container, buttons ) {
14610
+ var i, ien, node, button, tabIndex;
14611
+ var disabledClass = classes.sPageButtonDisabled;
14612
+ var clickHandler = function ( e ) {
14613
+ _fnPageChange( settings, e.data.action, true );
14614
+ };
14615
+
14616
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14617
+ button = buttons[i];
14618
+
14619
+ if ( Array.isArray( button ) ) {
14620
+ var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14621
+ .appendTo( container );
14622
+ attach( inner, button );
14623
+ }
14624
+ else {
14625
+ btnDisplay = null;
14626
+ btnClass = button;
14627
+ tabIndex = settings.iTabIndex;
14628
+
14629
+ switch ( button ) {
14630
+ case 'ellipsis':
14631
+ container.append('<span class="ellipsis">&#x2026;</span>');
14632
+ break;
14633
+
14634
+ case 'first':
14635
+ btnDisplay = lang.sFirst;
14636
+
14637
+ if ( page === 0 ) {
14638
+ tabIndex = -1;
14639
+ btnClass += ' ' + disabledClass;
14640
+ }
14641
+ break;
14642
+
14643
+ case 'previous':
14644
+ btnDisplay = lang.sPrevious;
14645
+
14646
+ if ( page === 0 ) {
14647
+ tabIndex = -1;
14648
+ btnClass += ' ' + disabledClass;
14649
+ }
14650
+ break;
14651
+
14652
+ case 'next':
14653
+ btnDisplay = lang.sNext;
14654
+
14655
+ if ( pages === 0 || page === pages-1 ) {
14656
+ tabIndex = -1;
14657
+ btnClass += ' ' + disabledClass;
14658
+ }
14659
+ break;
14660
+
14661
+ case 'last':
14662
+ btnDisplay = lang.sLast;
14663
+
14664
+ if ( pages === 0 || page === pages-1 ) {
14665
+ tabIndex = -1;
14666
+ btnClass += ' ' + disabledClass;
14667
+ }
14668
+ break;
14669
+
14670
+ default:
14671
+ btnDisplay = settings.fnFormatNumber( button + 1 );
14672
+ btnClass = page === button ?
14673
+ classes.sPageButtonActive : '';
14674
+ break;
14675
+ }
14676
+
14677
+ if ( btnDisplay !== null ) {
14678
+ node = $('<a>', {
14679
+ 'class': classes.sPageButton+' '+btnClass,
14680
+ 'aria-controls': settings.sTableId,
14681
+ 'aria-label': aria[ button ],
14682
+ 'data-dt-idx': counter,
14683
+ 'tabindex': tabIndex,
14684
+ 'id': idx === 0 && typeof button === 'string' ?
14685
+ settings.sTableId +'_'+ button :
14686
+ null
14687
+ } )
14688
+ .html( btnDisplay )
14689
+ .appendTo( container );
14690
+
14691
+ _fnBindAction(
14692
+ node, {action: button}, clickHandler
14693
+ );
14694
+
14695
+ counter++;
14696
+ }
14697
+ }
14698
+ }
14699
+ };
14700
+
14701
+ // IE9 throws an 'unknown error' if document.activeElement is used
14702
+ // inside an iframe or frame. Try / catch the error. Not good for
14703
+ // accessibility, but neither are frames.
14704
+ var activeEl;
14705
+
14706
+ try {
14707
+ // Because this approach is destroying and recreating the paging
14708
+ // elements, focus is lost on the select button which is bad for
14709
+ // accessibility. So we want to restore focus once the draw has
14710
+ // completed
14711
+ activeEl = $(host).find(document.activeElement).data('dt-idx');
14712
+ }
14713
+ catch (e) {}
14714
+
14715
+ attach( $(host).empty(), buttons );
14716
+
14717
+ if ( activeEl !== undefined ) {
14718
+ $(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
14719
+ }
14720
+ }
14721
+ }
14722
+ } );
14723
+
14724
+
14725
+
14726
+ // Built in type detection. See model.ext.aTypes for information about
14727
+ // what is required from this methods.
14728
+ $.extend( DataTable.ext.type.detect, [
14729
+ // Plain numbers - first since V8 detects some plain numbers as dates
14730
+ // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
14731
+ function ( d, settings )
14732
+ {
14733
+ var decimal = settings.oLanguage.sDecimal;
14734
+ return _isNumber( d, decimal ) ? 'num'+decimal : null;
14735
+ },
14736
+
14737
+ // Dates (only those recognised by the browser's Date.parse)
14738
+ function ( d, settings )
14739
+ {
14740
+ // V8 tries _very_ hard to make a string passed into `Date.parse()`
14741
+ // valid, so we need to use a regex to restrict date formats. Use a
14742
+ // plug-in for anything other than ISO8601 style strings
14743
+ if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
14744
+ return null;
14745
+ }
14746
+ var parsed = Date.parse(d);
14747
+ return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14748
+ },
14749
+
14750
+ // Formatted numbers
14751
+ function ( d, settings )
14752
+ {
14753
+ var decimal = settings.oLanguage.sDecimal;
14754
+ return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14755
+ },
14756
+
14757
+ // HTML numeric
14758
+ function ( d, settings )
14759
+ {
14760
+ var decimal = settings.oLanguage.sDecimal;
14761
+ return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14762
+ },
14763
+
14764
+ // HTML numeric, formatted
14765
+ function ( d, settings )
14766
+ {
14767
+ var decimal = settings.oLanguage.sDecimal;
14768
+ return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14769
+ },
14770
+
14771
+ // HTML (this is strict checking - there must be html)
14772
+ function ( d, settings )
14773
+ {
14774
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
14775
+ 'html' : null;
14776
+ }
14777
+ ] );
14778
+
14779
+
14780
+
14781
+ // Filter formatting functions. See model.ext.ofnSearch for information about
14782
+ // what is required from these methods.
14783
+ //
14784
+ // Note that additional search methods are added for the html numbers and
14785
+ // html formatted numbers by `_addNumericSort()` when we know what the decimal
14786
+ // place is
14787
+
14788
+
14789
+ $.extend( DataTable.ext.type.search, {
14790
+ html: function ( data ) {
14791
+ return _empty(data) ?
14792
+ data :
14793
+ typeof data === 'string' ?
14794
+ data
14795
+ .replace( _re_new_lines, " " )
14796
+ .replace( _re_html, "" ) :
14797
+ '';
14798
+ },
14799
+
14800
+ string: function ( data ) {
14801
+ return _empty(data) ?
14802
+ data :
14803
+ typeof data === 'string' ?
14804
+ data.replace( _re_new_lines, " " ) :
14805
+ data;
14806
+ }
14807
+ } );
14808
+
14809
+
14810
+
14811
+ var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14812
+ if ( d !== 0 && (!d || d === '-') ) {
14813
+ return -Infinity;
14814
+ }
14815
+
14816
+ // If a decimal place other than `.` is used, it needs to be given to the
14817
+ // function so we can detect it and replace with a `.` which is the only
14818
+ // decimal place Javascript recognises - it is not locale aware.
14819
+ if ( decimalPlace ) {
14820
+ d = _numToDecimal( d, decimalPlace );
14821
+ }
14822
+
14823
+ if ( d.replace ) {
14824
+ if ( re1 ) {
14825
+ d = d.replace( re1, '' );
14826
+ }
14827
+
14828
+ if ( re2 ) {
14829
+ d = d.replace( re2, '' );
14830
+ }
14831
+ }
14832
+
14833
+ return d * 1;
14834
+ };
14835
+
14836
+
14837
+ // Add the numeric 'deformatting' functions for sorting and search. This is done
14838
+ // in a function to provide an easy ability for the language options to add
14839
+ // additional methods if a non-period decimal place is used.
14840
+ function _addNumericSort ( decimalPlace ) {
14841
+ $.each(
14842
+ {
14843
+ // Plain numbers
14844
+ "num": function ( d ) {
14845
+ return __numericReplace( d, decimalPlace );
14846
+ },
14847
+
14848
+ // Formatted numbers
14849
+ "num-fmt": function ( d ) {
14850
+ return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14851
+ },
14852
+
14853
+ // HTML numeric
14854
+ "html-num": function ( d ) {
14855
+ return __numericReplace( d, decimalPlace, _re_html );
14856
+ },
14857
+
14858
+ // HTML numeric, formatted
14859
+ "html-num-fmt": function ( d ) {
14860
+ return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
14861
+ }
14862
+ },
14863
+ function ( key, fn ) {
14864
+ // Add the ordering method
14865
+ _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14866
+
14867
+ // For HTML types add a search formatter that will strip the HTML
14868
+ if ( key.match(/^html\-/) ) {
14869
+ _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
14870
+ }
14871
+ }
14872
+ );
14873
+ }
14874
+
14875
+
14876
+ // Default sort methods
14877
+ $.extend( _ext.type.order, {
14878
+ // Dates
14879
+ "date-pre": function ( d ) {
14880
+ var ts = Date.parse( d );
14881
+ return isNaN(ts) ? -Infinity : ts;
14882
+ },
14883
+
14884
+ // html
14885
+ "html-pre": function ( a ) {
14886
+ return _empty(a) ?
14887
+ '' :
14888
+ a.replace ?
14889
+ a.replace( /<.*?>/g, "" ).toLowerCase() :
14890
+ a+'';
14891
+ },
14892
+
14893
+ // string
14894
+ "string-pre": function ( a ) {
14895
+ // This is a little complex, but faster than always calling toString,
14896
+ // http://jsperf.com/tostring-v-check
14897
+ return _empty(a) ?
14898
+ '' :
14899
+ typeof a === 'string' ?
14900
+ a.toLowerCase() :
14901
+ ! a.toString ?
14902
+ '' :
14903
+ a.toString();
14904
+ },
14905
+
14906
+ // string-asc and -desc are retained only for compatibility with the old
14907
+ // sort methods
14908
+ "string-asc": function ( x, y ) {
14909
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14910
+ },
14911
+
14912
+ "string-desc": function ( x, y ) {
14913
+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14914
+ }
14915
+ } );
14916
+
14917
+
14918
+ // Numeric sorting types - order doesn't matter here
14919
+ _addNumericSort( '' );
14920
+
14921
+
14922
+ $.extend( true, DataTable.ext.renderer, {
14923
+ header: {
14924
+ _: function ( settings, cell, column, classes ) {
14925
+ // No additional mark-up required
14926
+ // Attach a sort listener to update on sort - note that using the
14927
+ // `DT` namespace will allow the event to be removed automatically
14928
+ // on destroy, while the `dt` namespaced event is the one we are
14929
+ // listening for
14930
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14931
+ if ( settings !== ctx ) { // need to check this this is the host
14932
+ return; // table, not a nested one
14933
+ }
14934
+
14935
+ var colIdx = column.idx;
14936
+
14937
+ cell
14938
+ .removeClass(
14939
+ column.sSortingClass +' '+
14940
+ classes.sSortAsc +' '+
14941
+ classes.sSortDesc
14942
+ )
14943
+ .addClass( columns[ colIdx ] == 'asc' ?
14944
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14945
+ classes.sSortDesc :
14946
+ column.sSortingClass
14947
+ );
14948
+ } );
14949
+ },
14950
+
14951
+ jqueryui: function ( settings, cell, column, classes ) {
14952
+ $('<div/>')
14953
+ .addClass( classes.sSortJUIWrapper )
14954
+ .append( cell.contents() )
14955
+ .append( $('<span/>')
14956
+ .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14957
+ )
14958
+ .appendTo( cell );
14959
+
14960
+ // Attach a sort listener to update on sort
14961
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14962
+ if ( settings !== ctx ) {
14963
+ return;
14964
+ }
14965
+
14966
+ var colIdx = column.idx;
14967
+
14968
+ cell
14969
+ .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14970
+ .addClass( columns[ colIdx ] == 'asc' ?
14971
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14972
+ classes.sSortDesc :
14973
+ column.sSortingClass
14974
+ );
14975
+
14976
+ cell
14977
+ .find( 'span.'+classes.sSortIcon )
14978
+ .removeClass(
14979
+ classes.sSortJUIAsc +" "+
14980
+ classes.sSortJUIDesc +" "+
14981
+ classes.sSortJUI +" "+
14982
+ classes.sSortJUIAscAllowed +" "+
14983
+ classes.sSortJUIDescAllowed
14984
+ )
14985
+ .addClass( columns[ colIdx ] == 'asc' ?
14986
+ classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
14987
+ classes.sSortJUIDesc :
14988
+ column.sSortingClassJUI
14989
+ );
14990
+ } );
14991
+ }
14992
+ }
14993
+ } );
14994
+
14995
+ /*
14996
+ * Public helper functions. These aren't used internally by DataTables, or
14997
+ * called by any of the options passed into DataTables, but they can be used
14998
+ * externally by developers working with DataTables. They are helper functions
14999
+ * to make working with DataTables a little bit easier.
15000
+ */
15001
+
15002
+ var __htmlEscapeEntities = function ( d ) {
15003
+ return typeof d === 'string' ?
15004
+ d
15005
+ .replace(/&/g, '&amp;')
15006
+ .replace(/</g, '&lt;')
15007
+ .replace(/>/g, '&gt;')
15008
+ .replace(/"/g, '&quot;') :
15009
+ d;
15010
+ };
15011
+
15012
+ /**
15013
+ * Helpers for `columns.render`.
15014
+ *
15015
+ * The options defined here can be used with the `columns.render` initialisation
15016
+ * option to provide a display renderer. The following functions are defined:
15017
+ *
15018
+ * * `number` - Will format numeric data (defined by `columns.data`) for
15019
+ * display, retaining the original unformatted data for sorting and filtering.
15020
+ * It takes 5 parameters:
15021
+ * * `string` - Thousands grouping separator
15022
+ * * `string` - Decimal point indicator
15023
+ * * `integer` - Number of decimal points to show
15024
+ * * `string` (optional) - Prefix.
15025
+ * * `string` (optional) - Postfix (/suffix).
15026
+ * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
15027
+ * parameters.
15028
+ *
15029
+ * @example
15030
+ * // Column definition using the number renderer
15031
+ * {
15032
+ * data: "salary",
15033
+ * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
15034
+ * }
15035
+ *
15036
+ * @namespace
15037
+ */
15038
+ DataTable.render = {
15039
+ number: function ( thousands, decimal, precision, prefix, postfix ) {
15040
+ return {
15041
+ display: function ( d ) {
15042
+ if ( typeof d !== 'number' && typeof d !== 'string' ) {
15043
+ return d;
15044
+ }
15045
+
15046
+ var negative = d < 0 ? '-' : '';
15047
+ var flo = parseFloat( d );
15048
+
15049
+ // If NaN then there isn't much formatting that we can do - just
15050
+ // return immediately, escaping any HTML (this was supposed to
15051
+ // be a number after all)
15052
+ if ( isNaN( flo ) ) {
15053
+ return __htmlEscapeEntities( d );
15054
+ }
15055
+
15056
+ flo = flo.toFixed( precision );
15057
+ d = Math.abs( flo );
15058
+
15059
+ var intPart = parseInt( d, 10 );
15060
+ var floatPart = precision ?
15061
+ decimal+(d - intPart).toFixed( precision ).substring( 2 ):
15062
+ '';
15063
+
15064
+ return negative + (prefix||'') +
15065
+ intPart.toString().replace(
15066
+ /\B(?=(\d{3})+(?!\d))/g, thousands
15067
+ ) +
15068
+ floatPart +
15069
+ (postfix||'');
15070
+ }
15071
+ };
15072
+ },
15073
+
15074
+ text: function () {
15075
+ return {
15076
+ display: __htmlEscapeEntities,
15077
+ filter: __htmlEscapeEntities
15078
+ };
15079
+ }
15080
+ };
15081
+
15082
+
15083
+ /*
15084
+ * This is really a good bit rubbish this method of exposing the internal methods
15085
+ * publicly... - To be fixed in 2.0 using methods on the prototype
15086
+ */
15087
+
15088
+
15089
+ /**
15090
+ * Create a wrapper function for exporting an internal functions to an external API.
15091
+ * @param {string} fn API function name
15092
+ * @returns {function} wrapped function
15093
+ * @memberof DataTable#internal
15094
+ */
15095
+ function _fnExternApiFunc (fn)
15096
+ {
15097
+ return function() {
15098
+ var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
15099
+ Array.prototype.slice.call(arguments)
15100
+ );
15101
+ return DataTable.ext.internal[fn].apply( this, args );
15102
+ };
15103
+ }
15104
+
15105
+
15106
+ /**
15107
+ * Reference to internal functions for use by plug-in developers. Note that
15108
+ * these methods are references to internal functions and are considered to be
15109
+ * private. If you use these methods, be aware that they are liable to change
15110
+ * between versions.
15111
+ * @namespace
15112
+ */
15113
+ $.extend( DataTable.ext.internal, {
15114
+ _fnExternApiFunc: _fnExternApiFunc,
15115
+ _fnBuildAjax: _fnBuildAjax,
15116
+ _fnAjaxUpdate: _fnAjaxUpdate,
15117
+ _fnAjaxParameters: _fnAjaxParameters,
15118
+ _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
15119
+ _fnAjaxDataSrc: _fnAjaxDataSrc,
15120
+ _fnAddColumn: _fnAddColumn,
15121
+ _fnColumnOptions: _fnColumnOptions,
15122
+ _fnAdjustColumnSizing: _fnAdjustColumnSizing,
15123
+ _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
15124
+ _fnColumnIndexToVisible: _fnColumnIndexToVisible,
15125
+ _fnVisbleColumns: _fnVisbleColumns,
15126
+ _fnGetColumns: _fnGetColumns,
15127
+ _fnColumnTypes: _fnColumnTypes,
15128
+ _fnApplyColumnDefs: _fnApplyColumnDefs,
15129
+ _fnHungarianMap: _fnHungarianMap,
15130
+ _fnCamelToHungarian: _fnCamelToHungarian,
15131
+ _fnLanguageCompat: _fnLanguageCompat,
15132
+ _fnBrowserDetect: _fnBrowserDetect,
15133
+ _fnAddData: _fnAddData,
15134
+ _fnAddTr: _fnAddTr,
15135
+ _fnNodeToDataIndex: _fnNodeToDataIndex,
15136
+ _fnNodeToColumnIndex: _fnNodeToColumnIndex,
15137
+ _fnGetCellData: _fnGetCellData,
15138
+ _fnSetCellData: _fnSetCellData,
15139
+ _fnSplitObjNotation: _fnSplitObjNotation,
15140
+ _fnGetObjectDataFn: _fnGetObjectDataFn,
15141
+ _fnSetObjectDataFn: _fnSetObjectDataFn,
15142
+ _fnGetDataMaster: _fnGetDataMaster,
15143
+ _fnClearTable: _fnClearTable,
15144
+ _fnDeleteIndex: _fnDeleteIndex,
15145
+ _fnInvalidate: _fnInvalidate,
15146
+ _fnGetRowElements: _fnGetRowElements,
15147
+ _fnCreateTr: _fnCreateTr,
15148
+ _fnBuildHead: _fnBuildHead,
15149
+ _fnDrawHead: _fnDrawHead,
15150
+ _fnDraw: _fnDraw,
15151
+ _fnReDraw: _fnReDraw,
15152
+ _fnAddOptionsHtml: _fnAddOptionsHtml,
15153
+ _fnDetectHeader: _fnDetectHeader,
15154
+ _fnGetUniqueThs: _fnGetUniqueThs,
15155
+ _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
15156
+ _fnFilterComplete: _fnFilterComplete,
15157
+ _fnFilterCustom: _fnFilterCustom,
15158
+ _fnFilterColumn: _fnFilterColumn,
15159
+ _fnFilter: _fnFilter,
15160
+ _fnFilterCreateSearch: _fnFilterCreateSearch,
15161
+ _fnEscapeRegex: _fnEscapeRegex,
15162
+ _fnFilterData: _fnFilterData,
15163
+ _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
15164
+ _fnUpdateInfo: _fnUpdateInfo,
15165
+ _fnInfoMacros: _fnInfoMacros,
15166
+ _fnInitialise: _fnInitialise,
15167
+ _fnInitComplete: _fnInitComplete,
15168
+ _fnLengthChange: _fnLengthChange,
15169
+ _fnFeatureHtmlLength: _fnFeatureHtmlLength,
15170
+ _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
15171
+ _fnPageChange: _fnPageChange,
15172
+ _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
15173
+ _fnProcessingDisplay: _fnProcessingDisplay,
15174
+ _fnFeatureHtmlTable: _fnFeatureHtmlTable,
15175
+ _fnScrollDraw: _fnScrollDraw,
15176
+ _fnApplyToChildren: _fnApplyToChildren,
15177
+ _fnCalculateColumnWidths: _fnCalculateColumnWidths,
15178
+ _fnThrottle: _fnThrottle,
15179
+ _fnConvertToWidth: _fnConvertToWidth,
15180
+ _fnGetWidestNode: _fnGetWidestNode,
15181
+ _fnGetMaxLenString: _fnGetMaxLenString,
15182
+ _fnStringToCss: _fnStringToCss,
15183
+ _fnSortFlatten: _fnSortFlatten,
15184
+ _fnSort: _fnSort,
15185
+ _fnSortAria: _fnSortAria,
15186
+ _fnSortListener: _fnSortListener,
15187
+ _fnSortAttachListener: _fnSortAttachListener,
15188
+ _fnSortingClasses: _fnSortingClasses,
15189
+ _fnSortData: _fnSortData,
15190
+ _fnSaveState: _fnSaveState,
15191
+ _fnLoadState: _fnLoadState,
15192
+ _fnSettingsFromNode: _fnSettingsFromNode,
15193
+ _fnLog: _fnLog,
15194
+ _fnMap: _fnMap,
15195
+ _fnBindAction: _fnBindAction,
15196
+ _fnCallbackReg: _fnCallbackReg,
15197
+ _fnCallbackFire: _fnCallbackFire,
15198
+ _fnLengthOverflow: _fnLengthOverflow,
15199
+ _fnRenderer: _fnRenderer,
15200
+ _fnDataSource: _fnDataSource,
15201
+ _fnRowAttributes: _fnRowAttributes,
15202
+ _fnExtend: _fnExtend,
15203
+ _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
15204
+ // in 1.10, so this dead-end function is
15205
+ // added to prevent errors
15206
+ } );
15207
+
15208
+
15209
+ // jQuery access
15210
+ $.fn.dataTable = DataTable;
15211
+
15212
+ // Provide access to the host jQuery object (circular reference)
15213
+ DataTable.$ = $;
15214
+
15215
+ // Legacy aliases
15216
+ $.fn.dataTableSettings = DataTable.settings;
15217
+ $.fn.dataTableExt = DataTable.ext;
15218
+
15219
+ // With a capital `D` we return a DataTables API instance rather than a
15220
+ // jQuery object
15221
+ $.fn.DataTable = function ( opts ) {
15222
+ return $(this).dataTable( opts ).api();
15223
+ };
15224
+
15225
+ // All properties that are available to $.fn.dataTable should also be
15226
+ // available on $.fn.DataTable
15227
+ $.each( DataTable, function ( prop, val ) {
15228
+ $.fn.DataTable[ prop ] = val;
15229
+ } );
15230
+
15231
+
15232
+ // Information about events fired by DataTables - for documentation.
15233
+ /**
15234
+ * Draw event, fired whenever the table is redrawn on the page, at the same
15235
+ * point as fnDrawCallback. This may be useful for binding events or
15236
+ * performing calculations when the table is altered at all.
15237
+ * @name DataTable#draw.dt
15238
+ * @event
15239
+ * @param {event} e jQuery event object
15240
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15241
+ */
15242
+
15243
+ /**
15244
+ * Search event, fired when the searching applied to the table (using the
15245
+ * built-in global search, or column filters) is altered.
15246
+ * @name DataTable#search.dt
15247
+ * @event
15248
+ * @param {event} e jQuery event object
15249
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15250
+ */
15251
+
15252
+ /**
15253
+ * Page change event, fired when the paging of the table is altered.
15254
+ * @name DataTable#page.dt
15255
+ * @event
15256
+ * @param {event} e jQuery event object
15257
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15258
+ */
15259
+
15260
+ /**
15261
+ * Order event, fired when the ordering applied to the table is altered.
15262
+ * @name DataTable#order.dt
15263
+ * @event
15264
+ * @param {event} e jQuery event object
15265
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15266
+ */
15267
+
15268
+ /**
15269
+ * DataTables initialisation complete event, fired when the table is fully
15270
+ * drawn, including Ajax data loaded, if Ajax data is required.
15271
+ * @name DataTable#init.dt
15272
+ * @event
15273
+ * @param {event} e jQuery event object
15274
+ * @param {object} oSettings DataTables settings object
15275
+ * @param {object} json The JSON object request from the server - only
15276
+ * present if client-side Ajax sourced data is used</li></ol>
15277
+ */
15278
+
15279
+ /**
15280
+ * State save event, fired when the table has changed state a new state save
15281
+ * is required. This event allows modification of the state saving object
15282
+ * prior to actually doing the save, including addition or other state
15283
+ * properties (for plug-ins) or modification of a DataTables core property.
15284
+ * @name DataTable#stateSaveParams.dt
15285
+ * @event
15286
+ * @param {event} e jQuery event object
15287
+ * @param {object} oSettings DataTables settings object
15288
+ * @param {object} json The state information to be saved
15289
+ */
15290
+
15291
+ /**
15292
+ * State load event, fired when the table is loading state from the stored
15293
+ * data, but prior to the settings object being modified by the saved state
15294
+ * - allowing modification of the saved state is required or loading of
15295
+ * state for a plug-in.
15296
+ * @name DataTable#stateLoadParams.dt
15297
+ * @event
15298
+ * @param {event} e jQuery event object
15299
+ * @param {object} oSettings DataTables settings object
15300
+ * @param {object} json The saved state information
15301
+ */
15302
+
15303
+ /**
15304
+ * State loaded event, fired when state has been loaded from stored data and
15305
+ * the settings object has been modified by the loaded data.
15306
+ * @name DataTable#stateLoaded.dt
15307
+ * @event
15308
+ * @param {event} e jQuery event object
15309
+ * @param {object} oSettings DataTables settings object
15310
+ * @param {object} json The saved state information
15311
+ */
15312
+
15313
+ /**
15314
+ * Processing event, fired when DataTables is doing some kind of processing
15315
+ * (be it, order, search or anything else). It can be used to indicate to
15316
+ * the end user that there is something happening, or that something has
15317
+ * finished.
15318
+ * @name DataTable#processing.dt
15319
+ * @event
15320
+ * @param {event} e jQuery event object
15321
+ * @param {object} oSettings DataTables settings object
15322
+ * @param {boolean} bShow Flag for if DataTables is doing processing or not
15323
+ */
15324
+
15325
+ /**
15326
+ * Ajax (XHR) event, fired whenever an Ajax request is completed from a
15327
+ * request to made to the server for new data. This event is called before
15328
+ * DataTables processed the returned data, so it can also be used to pre-
15329
+ * process the data returned from the server, if needed.
15330
+ *
15331
+ * Note that this trigger is called in `fnServerData`, if you override
15332
+ * `fnServerData` and which to use this event, you need to trigger it in you
15333
+ * success function.
15334
+ * @name DataTable#xhr.dt
15335
+ * @event
15336
+ * @param {event} e jQuery event object
15337
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15338
+ * @param {object} json JSON returned from the server
15339
+ *
15340
+ * @example
15341
+ * // Use a custom property returned from the server in another DOM element
15342
+ * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
15343
+ * $('#status').html( json.status );
15344
+ * } );
15345
+ *
15346
+ * @example
15347
+ * // Pre-process the data returned from the server
15348
+ * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
15349
+ * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
15350
+ * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
15351
+ * }
15352
+ * // Note no return - manipulate the data directly in the JSON object.
15353
+ * } );
15354
+ */
15355
+
15356
+ /**
15357
+ * Destroy event, fired when the DataTable is destroyed by calling fnDestroy
15358
+ * or passing the bDestroy:true parameter in the initialisation object. This
15359
+ * can be used to remove bound events, added DOM nodes, etc.
15360
+ * @name DataTable#destroy.dt
15361
+ * @event
15362
+ * @param {event} e jQuery event object
15363
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15364
+ */
15365
+
15366
+ /**
15367
+ * Page length change event, fired when number of records to show on each
15368
+ * page (the length) is changed.
15369
+ * @name DataTable#length.dt
15370
+ * @event
15371
+ * @param {event} e jQuery event object
15372
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15373
+ * @param {integer} len New length
15374
+ */
15375
+
15376
+ /**
15377
+ * Column sizing has changed.
15378
+ * @name DataTable#column-sizing.dt
15379
+ * @event
15380
+ * @param {event} e jQuery event object
15381
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15382
+ */
15383
+
15384
+ /**
15385
+ * Column visibility has changed.
15386
+ * @name DataTable#column-visibility.dt
15387
+ * @event
15388
+ * @param {event} e jQuery event object
15389
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15390
+ * @param {int} column Column index
15391
+ * @param {bool} vis `false` if column now hidden, or `true` if visible
15392
+ */
15393
+
15394
+ return $.fn.dataTable;
15395
+ }));
15396
+
15397
+
15398
+ /*! AutoFill 2.3.5
15399
+ * ©2008-2020 SpryMedia Ltd - datatables.net/license
15400
+ */
15401
+
15402
+ /**
15403
+ * @summary AutoFill
15404
+ * @description Add Excel like click and drag auto-fill options to DataTables
15405
+ * @version 2.3.5
15406
+ * @file dataTables.autoFill.js
15407
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
15408
+ * @contact www.sprymedia.co.uk/contact
15409
+ * @copyright Copyright 2010-2020 SpryMedia Ltd.
15410
+ *
15411
+ * This source file is free software, available under the following license:
15412
+ * MIT license - http://datatables.net/license/mit
15413
+ *
15414
+ * This source file is distributed in the hope that it will be useful, but
15415
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15416
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
15417
+ *
15418
+ * For details please refer to: http://www.datatables.net
15419
+ */
15420
+ (function( factory ){
15421
+ if ( typeof define === 'function' && define.amd ) {
15422
+ // AMD
15423
+ define( ['jquery', 'datatables.net'], function ( $ ) {
15424
+ return factory( $, window, document );
15425
+ } );
15426
+ }
15427
+ else if ( typeof exports === 'object' ) {
15428
+ // CommonJS
15429
+ module.exports = function (root, $) {
15430
+ if ( ! root ) {
15431
+ root = window;
15432
+ }
15433
+
15434
+ if ( ! $ || ! $.fn.dataTable ) {
15435
+ $ = require('datatables.net')(root, $).$;
15436
+ }
15437
+
15438
+ return factory( $, root, root.document );
15439
+ };
15440
+ }
15441
+ else {
15442
+ // Browser
15443
+ factory( jQuery, window, document );
15444
+ }
15445
+ }(function( $, window, document, undefined ) {
15446
+ 'use strict';
15447
+ var DataTable = $.fn.dataTable;
15448
+
15449
+
15450
+ var _instance = 0;
15451
+
15452
+ /**
15453
+ * AutoFill provides Excel like auto-fill features for a DataTable
15454
+ *
15455
+ * @class AutoFill
15456
+ * @constructor
15457
+ * @param {object} oTD DataTables settings object
15458
+ * @param {object} oConfig Configuration object for AutoFill
15459
+ */
15460
+ var AutoFill = function( dt, opts )
15461
+ {
15462
+ if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
15463
+ throw( "Warning: AutoFill requires DataTables 1.10.8 or greater");
15464
+ }
15465
+
15466
+ // User and defaults configuration object
15467
+ this.c = $.extend( true, {},
15468
+ DataTable.defaults.autoFill,
15469
+ AutoFill.defaults,
15470
+ opts
15471
+ );
15472
+
15473
+ /**
15474
+ * @namespace Settings object which contains customisable information for AutoFill instance
15475
+ */
15476
+ this.s = {
15477
+ /** @type {DataTable.Api} DataTables' API instance */
15478
+ dt: new DataTable.Api( dt ),
15479
+
15480
+ /** @type {String} Unique namespace for events attached to the document */
15481
+ namespace: '.autoFill'+(_instance++),
15482
+
15483
+ /** @type {Object} Cached dimension information for use in the mouse move event handler */
15484
+ scroll: {},
15485
+
15486
+ /** @type {integer} Interval object used for smooth scrolling */
15487
+ scrollInterval: null,
15488
+
15489
+ handle: {
15490
+ height: 0,
15491
+ width: 0
15492
+ },
15493
+
15494
+ /**
15495
+ * Enabled setting
15496
+ * @type {Boolean}
15497
+ */
15498
+ enabled: false
15499
+ };
15500
+
15501
+
15502
+ /**
15503
+ * @namespace Common and useful DOM elements for the class instance
15504
+ */
15505
+ this.dom = {
15506
+ /** @type {jQuery} AutoFill handle */
15507
+ handle: $('<div class="dt-autofill-handle"/>'),
15508
+
15509
+ /**
15510
+ * @type {Object} Selected cells outline - Need to use 4 elements,
15511
+ * otherwise the mouse over if you back into the selected rectangle
15512
+ * will be over that element, rather than the cells!
15513
+ */
15514
+ select: {
15515
+ top: $('<div class="dt-autofill-select top"/>'),
15516
+ right: $('<div class="dt-autofill-select right"/>'),
15517
+ bottom: $('<div class="dt-autofill-select bottom"/>'),
15518
+ left: $('<div class="dt-autofill-select left"/>')
15519
+ },
15520
+
15521
+ /** @type {jQuery} Fill type chooser background */
15522
+ background: $('<div class="dt-autofill-background"/>'),
15523
+
15524
+ /** @type {jQuery} Fill type chooser */
15525
+ list: $('<div class="dt-autofill-list">'+this.s.dt.i18n('autoFill.info', '')+'<ul/></div>'),
15526
+
15527
+ /** @type {jQuery} DataTables scrolling container */
15528
+ dtScroll: null,
15529
+
15530
+ /** @type {jQuery} Offset parent element */
15531
+ offsetParent: null
15532
+ };
15533
+
15534
+
15535
+ /* Constructor logic */
15536
+ this._constructor();
15537
+ };
15538
+
15539
+
15540
+
15541
+ $.extend( AutoFill.prototype, {
15542
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15543
+ * Public methods (exposed via the DataTables API below)
15544
+ */
15545
+ enabled: function ()
15546
+ {
15547
+ return this.s.enabled;
15548
+ },
15549
+
15550
+
15551
+ enable: function ( flag )
15552
+ {
15553
+ var that = this;
15554
+
15555
+ if ( flag === false ) {
15556
+ return this.disable();
15557
+ }
15558
+
15559
+ this.s.enabled = true;
15560
+
15561
+ this._focusListener();
15562
+
15563
+ this.dom.handle.on( 'mousedown', function (e) {
15564
+ that._mousedown( e );
15565
+ return false;
15566
+ } );
15567
+
15568
+ return this;
15569
+ },
15570
+
15571
+ disable: function ()
15572
+ {
15573
+ this.s.enabled = false;
15574
+
15575
+ this._focusListenerRemove();
15576
+
15577
+ return this;
15578
+ },
15579
+
15580
+
15581
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15582
+ * Constructor
15583
+ */
15584
+
15585
+ /**
15586
+ * Initialise the RowReorder instance
15587
+ *
15588
+ * @private
15589
+ */
15590
+ _constructor: function ()
15591
+ {
15592
+ var that = this;
15593
+ var dt = this.s.dt;
15594
+ var dtScroll = $('div.dataTables_scrollBody', this.s.dt.table().container());
15595
+
15596
+ // Make the instance accessible to the API
15597
+ dt.settings()[0].autoFill = this;
15598
+
15599
+ if ( dtScroll.length ) {
15600
+ this.dom.dtScroll = dtScroll;
15601
+
15602
+ // Need to scroll container to be the offset parent
15603
+ if ( dtScroll.css('position') === 'static' ) {
15604
+ dtScroll.css( 'position', 'relative' );
15605
+ }
15606
+ }
15607
+
15608
+ if ( this.c.enable !== false ) {
15609
+ this.enable();
15610
+ }
15611
+
15612
+ dt.on( 'destroy.autoFill', function () {
15613
+ that._focusListenerRemove();
15614
+ } );
15615
+ },
15616
+
15617
+
15618
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15619
+ * Private methods
15620
+ */
15621
+
15622
+ /**
15623
+ * Display the AutoFill drag handle by appending it to a table cell. This
15624
+ * is the opposite of the _detach method.
15625
+ *
15626
+ * @param {node} node TD/TH cell to insert the handle into
15627
+ * @private
15628
+ */
15629
+ _attach: function ( node )
15630
+ {
15631
+ var dt = this.s.dt;
15632
+ var idx = dt.cell( node ).index();
15633
+ var handle = this.dom.handle;
15634
+ var handleDim = this.s.handle;
15635
+
15636
+ if ( ! idx || dt.columns( this.c.columns ).indexes().indexOf( idx.column ) === -1 ) {
15637
+ this._detach();
15638
+ return;
15639
+ }
15640
+
15641
+ if ( ! this.dom.offsetParent ) {
15642
+ // We attach to the table's offset parent
15643
+ this.dom.offsetParent = $( dt.table().node() ).offsetParent();
15644
+ }
15645
+
15646
+ if ( ! handleDim.height || ! handleDim.width ) {
15647
+ // Append to document so we can get its size. Not expecting it to
15648
+ // change during the life time of the page
15649
+ handle.appendTo( 'body' );
15650
+ handleDim.height = handle.outerHeight();
15651
+ handleDim.width = handle.outerWidth();
15652
+ }
15653
+
15654
+ // Might need to go through multiple offset parents
15655
+ var offset = this._getPosition( node, this.dom.offsetParent );
15656
+
15657
+ this.dom.attachedTo = node;
15658
+ handle
15659
+ .css( {
15660
+ top: offset.top + node.offsetHeight - handleDim.height,
15661
+ left: offset.left + node.offsetWidth - handleDim.width
15662
+ } )
15663
+ .appendTo( this.dom.offsetParent );
15664
+ },
15665
+
15666
+
15667
+ /**
15668
+ * Determine can the fill type should be. This can be automatic, or ask the
15669
+ * end user.
15670
+ *
15671
+ * @param {array} cells Information about the selected cells from the key
15672
+ * up function
15673
+ * @private
15674
+ */
15675
+ _actionSelector: function ( cells )
15676
+ {
15677
+ var that = this;
15678
+ var dt = this.s.dt;
15679
+ var actions = AutoFill.actions;
15680
+ var available = [];
15681
+
15682
+ // "Ask" each plug-in if it wants to handle this data
15683
+ $.each( actions, function ( key, action ) {
15684
+ if ( action.available( dt, cells ) ) {
15685
+ available.push( key );
15686
+ }
15687
+ } );
15688
+
15689
+ if ( available.length === 1 && this.c.alwaysAsk === false ) {
15690
+ // Only one action available - enact it immediately
15691
+ var result = actions[ available[0] ].execute( dt, cells );
15692
+ this._update( result, cells );
15693
+ }
15694
+ else if ( available.length > 1 ) {
15695
+ // Multiple actions available - ask the end user what they want to do
15696
+ var list = this.dom.list.children('ul').empty();
15697
+
15698
+ // Add a cancel option
15699
+ available.push( 'cancel' );
15700
+
15701
+ $.each( available, function ( i, name ) {
15702
+ list.append( $('<li/>')
15703
+ .append(
15704
+ '<div class="dt-autofill-question">'+
15705
+ actions[ name ].option( dt, cells )+
15706
+ '<div>'
15707
+ )
15708
+ .append( $('<div class="dt-autofill-button">' )
15709
+ .append( $('<button class="'+AutoFill.classes.btn+'">'+dt.i18n('autoFill.button', '&gt;')+'</button>')
15710
+ .on( 'click', function () {
15711
+ var result = actions[ name ].execute(
15712
+ dt, cells, $(this).closest('li')
15713
+ );
15714
+ that._update( result, cells );
15715
+
15716
+ that.dom.background.remove();
15717
+ that.dom.list.remove();
15718
+ } )
15719
+ )
15720
+ )
15721
+ );
15722
+ } );
15723
+
15724
+ this.dom.background.appendTo( 'body' );
15725
+ this.dom.list.appendTo( 'body' );
15726
+
15727
+ this.dom.list.css( 'margin-top', this.dom.list.outerHeight()/2 * -1 );
15728
+ }
15729
+ },
15730
+
15731
+
15732
+ /**
15733
+ * Remove the AutoFill handle from the document
15734
+ *
15735
+ * @private
15736
+ */
15737
+ _detach: function ()
15738
+ {
15739
+ this.dom.attachedTo = null;
15740
+ this.dom.handle.detach();
15741
+ },
15742
+
15743
+
15744
+ /**
15745
+ * Draw the selection outline by calculating the range between the start
15746
+ * and end cells, then placing the highlighting elements to draw a rectangle
15747
+ *
15748
+ * @param {node} target End cell
15749
+ * @param {object} e Originating event
15750
+ * @private
15751
+ */
15752
+ _drawSelection: function ( target, e )
15753
+ {
15754
+ // Calculate boundary for start cell to this one
15755
+ var dt = this.s.dt;
15756
+ var start = this.s.start;
15757
+ var startCell = $(this.dom.start);
15758
+ var end = {
15759
+ row: this.c.vertical ?
15760
+ dt.rows( { page: 'current' } ).nodes().indexOf( target.parentNode ) :
15761
+ start.row,
15762
+ column: this.c.horizontal ?
15763
+ $(target).index() :
15764
+ start.column
15765
+ };
15766
+ var colIndx = dt.column.index( 'toData', end.column );
15767
+ var endRow = dt.row( ':eq('+end.row+')', { page: 'current' } ); // Workaround for M581
15768
+ var endCell = $( dt.cell( endRow.index(), colIndx ).node() );
15769
+
15770
+ // Be sure that is a DataTables controlled cell
15771
+ if ( ! dt.cell( endCell ).any() ) {
15772
+ return;
15773
+ }
15774
+
15775
+ // if target is not in the columns available - do nothing
15776
+ if ( dt.columns( this.c.columns ).indexes().indexOf( colIndx ) === -1 ) {
15777
+ return;
15778
+ }
15779
+
15780
+ this.s.end = end;
15781
+
15782
+ var top, bottom, left, right, height, width;
15783
+
15784
+ top = start.row < end.row ? startCell : endCell;
15785
+ bottom = start.row < end.row ? endCell : startCell;
15786
+ left = start.column < end.column ? startCell : endCell;
15787
+ right = start.column < end.column ? endCell : startCell;
15788
+
15789
+ top = this._getPosition( top.get(0) ).top;
15790
+ left = this._getPosition( left.get(0) ).left;
15791
+ height = this._getPosition( bottom.get(0) ).top + bottom.outerHeight() - top;
15792
+ width = this._getPosition( right.get(0) ).left + right.outerWidth() - left;
15793
+
15794
+ var select = this.dom.select;
15795
+ select.top.css( {
15796
+ top: top,
15797
+ left: left,
15798
+ width: width
15799
+ } );
15800
+
15801
+ select.left.css( {
15802
+ top: top,
15803
+ left: left,
15804
+ height: height
15805
+ } );
15806
+
15807
+ select.bottom.css( {
15808
+ top: top + height,
15809
+ left: left,
15810
+ width: width
15811
+ } );
15812
+
15813
+ select.right.css( {
15814
+ top: top,
15815
+ left: left + width,
15816
+ height: height
15817
+ } );
15818
+ },
15819
+
15820
+
15821
+ /**
15822
+ * Use the Editor API to perform an update based on the new data for the
15823
+ * cells
15824
+ *
15825
+ * @param {array} cells Information about the selected cells from the key
15826
+ * up function
15827
+ * @private
15828
+ */
15829
+ _editor: function ( cells )
15830
+ {
15831
+ var dt = this.s.dt;
15832
+ var editor = this.c.editor;
15833
+
15834
+ if ( ! editor ) {
15835
+ return;
15836
+ }
15837
+
15838
+ // Build the object structure for Editor's multi-row editing
15839
+ var idValues = {};
15840
+ var nodes = [];
15841
+ var fields = editor.fields();
15842
+
15843
+ for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
15844
+ for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
15845
+ var cell = cells[i][j];
15846
+
15847
+ // Determine the field name for the cell being edited
15848
+ var col = dt.settings()[0].aoColumns[ cell.index.column ];
15849
+ var fieldName = col.editField;
15850
+
15851
+ if ( fieldName === undefined ) {
15852
+ var dataSrc = col.mData;
15853
+
15854
+ // dataSrc is the `field.data` property, but we need to set
15855
+ // using the field name, so we need to translate from the
15856
+ // data to the name
15857
+ for ( var k=0, ken=fields.length ; k<ken ; k++ ) {
15858
+ var field = editor.field( fields[k] );
15859
+
15860
+ if ( field.dataSrc() === dataSrc ) {
15861
+ fieldName = field.name();
15862
+ break;
15863
+ }
15864
+ }
15865
+ }
15866
+
15867
+ if ( ! fieldName ) {
15868
+ throw 'Could not automatically determine field data. '+
15869
+ 'Please see https://datatables.net/tn/11';
15870
+ }
15871
+
15872
+ if ( ! idValues[ fieldName ] ) {
15873
+ idValues[ fieldName ] = {};
15874
+ }
15875
+
15876
+ var id = dt.row( cell.index.row ).id();
15877
+ idValues[ fieldName ][ id ] = cell.set;
15878
+
15879
+ // Keep a list of cells so we can activate the bubble editing
15880
+ // with them
15881
+ nodes.push( cell.index );
15882
+ }
15883
+ }
15884
+
15885
+ // Perform the edit using bubble editing as it allows us to specify
15886
+ // the cells to be edited, rather than using full rows
15887
+ editor
15888
+ .bubble( nodes, false )
15889
+ .multiSet( idValues )
15890
+ .submit();
15891
+ },
15892
+
15893
+
15894
+ /**
15895
+ * Emit an event on the DataTable for listeners
15896
+ *
15897
+ * @param {string} name Event name
15898
+ * @param {array} args Event arguments
15899
+ * @private
15900
+ */
15901
+ _emitEvent: function ( name, args )
15902
+ {
15903
+ this.s.dt.iterator( 'table', function ( ctx, i ) {
15904
+ $(ctx.nTable).triggerHandler( name+'.dt', args );
15905
+ } );
15906
+ },
15907
+
15908
+
15909
+ /**
15910
+ * Attach suitable listeners (based on the configuration) that will attach
15911
+ * and detach the AutoFill handle in the document.
15912
+ *
15913
+ * @private
15914
+ */
15915
+ _focusListener: function ()
15916
+ {
15917
+ var that = this;
15918
+ var dt = this.s.dt;
15919
+ var namespace = this.s.namespace;
15920
+ var focus = this.c.focus !== null ?
15921
+ this.c.focus :
15922
+ dt.init().keys || dt.settings()[0].keytable ?
15923
+ 'focus' :
15924
+ 'hover';
15925
+
15926
+ // All event listeners attached here are removed in the `destroy`
15927
+ // callback in the constructor
15928
+ if ( focus === 'focus' ) {
15929
+ dt
15930
+ .on( 'key-focus.autoFill', function ( e, dt, cell ) {
15931
+ that._attach( cell.node() );
15932
+ } )
15933
+ .on( 'key-blur.autoFill', function ( e, dt, cell ) {
15934
+ that._detach();
15935
+ } );
15936
+ }
15937
+ else if ( focus === 'click' ) {
15938
+ $(dt.table().body()).on( 'click'+namespace, 'td, th', function (e) {
15939
+ that._attach( this );
15940
+ } );
15941
+
15942
+ $(document.body).on( 'click'+namespace, function (e) {
15943
+ if ( ! $(e.target).parents().filter( dt.table().body() ).length ) {
15944
+ that._detach();
15945
+ }
15946
+ } );
15947
+ }
15948
+ else {
15949
+ $(dt.table().body())
15950
+ .on( 'mouseenter'+namespace, 'td, th', function (e) {
15951
+ that._attach( this );
15952
+ } )
15953
+ .on( 'mouseleave'+namespace, function (e) {
15954
+ if ( $(e.relatedTarget).hasClass('dt-autofill-handle') ) {
15955
+ return;
15956
+ }
15957
+
15958
+ that._detach();
15959
+ } );
15960
+ }
15961
+ },
15962
+
15963
+
15964
+ _focusListenerRemove: function ()
15965
+ {
15966
+ var dt = this.s.dt;
15967
+
15968
+ dt.off( '.autoFill' );
15969
+ $(dt.table().body()).off( this.s.namespace );
15970
+ $(document.body).off( this.s.namespace );
15971
+ },
15972
+
15973
+
15974
+ /**
15975
+ * Get the position of a node, relative to another, including any scrolling
15976
+ * offsets.
15977
+ * @param {Node} node Node to get the position of
15978
+ * @param {jQuery} targetParent Node to use as the parent
15979
+ * @return {object} Offset calculation
15980
+ * @private
15981
+ */
15982
+ _getPosition: function ( node, targetParent )
15983
+ {
15984
+ var
15985
+ currNode = node,
15986
+ currOffsetParent,
15987
+ top = 0,
15988
+ left = 0;
15989
+
15990
+ if ( ! targetParent ) {
15991
+ targetParent = $( $( this.s.dt.table().node() )[0].offsetParent );
15992
+ }
15993
+
15994
+ do {
15995
+ // Don't use jQuery().position() the behaviour changes between 1.x and 3.x for
15996
+ // tables
15997
+ var positionTop = currNode.offsetTop;
15998
+ var positionLeft = currNode.offsetLeft;
15999
+
16000
+ // jQuery doesn't give a `table` as the offset parent oddly, so use DOM directly
16001
+ currOffsetParent = $( currNode.offsetParent );
16002
+
16003
+ top += positionTop + parseInt( currOffsetParent.css('border-top-width') ) * 1;
16004
+ left += positionLeft + parseInt( currOffsetParent.css('border-left-width') ) * 1;
16005
+
16006
+ // Emergency fall back. Shouldn't happen, but just in case!
16007
+ if ( currNode.nodeName.toLowerCase() === 'body' ) {
16008
+ break;
16009
+ }
16010
+
16011
+ currNode = currOffsetParent.get(0); // for next loop
16012
+ }
16013
+ while ( currOffsetParent.get(0) !== targetParent.get(0) )
16014
+
16015
+ return {
16016
+ top: top,
16017
+ left: left
16018
+ };
16019
+ },
16020
+
16021
+
16022
+ /**
16023
+ * Start mouse drag - selects the start cell
16024
+ *
16025
+ * @param {object} e Mouse down event
16026
+ * @private
16027
+ */
16028
+ _mousedown: function ( e )
16029
+ {
16030
+ var that = this;
16031
+ var dt = this.s.dt;
16032
+
16033
+ this.dom.start = this.dom.attachedTo;
16034
+ this.s.start = {
16035
+ row: dt.rows( { page: 'current' } ).nodes().indexOf( $(this.dom.start).parent()[0] ),
16036
+ column: $(this.dom.start).index()
16037
+ };
16038
+
16039
+ $(document.body)
16040
+ .on( 'mousemove.autoFill', function (e) {
16041
+ that._mousemove( e );
16042
+ } )
16043
+ .on( 'mouseup.autoFill', function (e) {
16044
+ that._mouseup( e );
16045
+ } );
16046
+
16047
+ var select = this.dom.select;
16048
+ var offsetParent = $( dt.table().node() ).offsetParent();
16049
+ select.top.appendTo( offsetParent );
16050
+ select.left.appendTo( offsetParent );
16051
+ select.right.appendTo( offsetParent );
16052
+ select.bottom.appendTo( offsetParent );
16053
+
16054
+ this._drawSelection( this.dom.start, e );
16055
+
16056
+ this.dom.handle.css( 'display', 'none' );
16057
+
16058
+ // Cache scrolling information so mouse move doesn't need to read.
16059
+ // This assumes that the window and DT scroller will not change size
16060
+ // during an AutoFill drag, which I think is a fair assumption
16061
+ var scrollWrapper = this.dom.dtScroll;
16062
+ this.s.scroll = {
16063
+ windowHeight: $(window).height(),
16064
+ windowWidth: $(window).width(),
16065
+ dtTop: scrollWrapper ? scrollWrapper.offset().top : null,
16066
+ dtLeft: scrollWrapper ? scrollWrapper.offset().left : null,
16067
+ dtHeight: scrollWrapper ? scrollWrapper.outerHeight() : null,
16068
+ dtWidth: scrollWrapper ? scrollWrapper.outerWidth() : null
16069
+ };
16070
+ },
16071
+
16072
+
16073
+ /**
16074
+ * Mouse drag - selects the end cell and update the selection display for
16075
+ * the end user
16076
+ *
16077
+ * @param {object} e Mouse move event
16078
+ * @private
16079
+ */
16080
+ _mousemove: function ( e )
16081
+ {
16082
+ var that = this;
16083
+ var dt = this.s.dt;
16084
+ var name = e.target.nodeName.toLowerCase();
16085
+ if ( name !== 'td' && name !== 'th' ) {
16086
+ return;
16087
+ }
16088
+
16089
+ this._drawSelection( e.target, e );
16090
+ this._shiftScroll( e );
16091
+ },
16092
+
16093
+
16094
+ /**
16095
+ * End mouse drag - perform the update actions
16096
+ *
16097
+ * @param {object} e Mouse up event
16098
+ * @private
16099
+ */
16100
+ _mouseup: function ( e )
16101
+ {
16102
+ $(document.body).off( '.autoFill' );
16103
+
16104
+ var that = this;
16105
+ var dt = this.s.dt;
16106
+ var select = this.dom.select;
16107
+ select.top.remove();
16108
+ select.left.remove();
16109
+ select.right.remove();
16110
+ select.bottom.remove();
16111
+
16112
+ this.dom.handle.css( 'display', 'block' );
16113
+
16114
+ // Display complete - now do something useful with the selection!
16115
+ var start = this.s.start;
16116
+ var end = this.s.end;
16117
+
16118
+ // Haven't selected multiple cells, so nothing to do
16119
+ if ( start.row === end.row && start.column === end.column ) {
16120
+ return;
16121
+ }
16122
+
16123
+ var startDt = dt.cell( ':eq('+start.row+')', start.column+':visible', {page:'current'} );
16124
+
16125
+ // If Editor is active inside this cell (inline editing) we need to wait for Editor to
16126
+ // submit and then we can loop back and trigger the fill.
16127
+ if ( $('div.DTE', startDt.node()).length ) {
16128
+ var editor = dt.editor();
16129
+
16130
+ editor
16131
+ .on( 'submitSuccess.dtaf close.dtaf', function () {
16132
+ editor.off( '.dtaf');
16133
+
16134
+ setTimeout( function () {
16135
+ that._mouseup( e );
16136
+ }, 100 );
16137
+ } )
16138
+ .on( 'submitComplete.dtaf preSubmitCancelled.dtaf close.dtaf', function () {
16139
+ editor.off( '.dtaf');
16140
+ } );
16141
+
16142
+ // Make the current input submit
16143
+ editor.submit();
16144
+
16145
+ return;
16146
+ }
16147
+
16148
+ // Build a matrix representation of the selected rows
16149
+ var rows = this._range( start.row, end.row );
16150
+ var columns = this._range( start.column, end.column );
16151
+ var selected = [];
16152
+ var dtSettings = dt.settings()[0];
16153
+ var dtColumns = dtSettings.aoColumns;
16154
+ var enabledColumns = dt.columns( this.c.columns ).indexes();
16155
+
16156
+ // Can't use Array.prototype.map as IE8 doesn't support it
16157
+ // Can't use $.map as jQuery flattens 2D arrays
16158
+ // Need to use a good old fashioned for loop
16159
+ for ( var rowIdx=0 ; rowIdx<rows.length ; rowIdx++ ) {
16160
+ selected.push(
16161
+ $.map( columns, function (column) {
16162
+ var row = dt.row( ':eq('+rows[rowIdx]+')', {page:'current'} ); // Workaround for M581
16163
+ var cell = dt.cell( row.index(), column+':visible' );
16164
+ var data = cell.data();
16165
+ var cellIndex = cell.index();
16166
+ var editField = dtColumns[ cellIndex.column ].editField;
16167
+
16168
+ if ( editField !== undefined ) {
16169
+ data = dtSettings.oApi._fnGetObjectDataFn( editField )( dt.row( cellIndex.row ).data() );
16170
+ }
16171
+
16172
+ if ( enabledColumns.indexOf(cellIndex.column) === -1 ) {
16173
+ return;
16174
+ }
16175
+
16176
+ return {
16177
+ cell: cell,
16178
+ data: data,
16179
+ label: cell.data(),
16180
+ index: cellIndex
16181
+ };
16182
+ } )
16183
+ );
16184
+ }
16185
+
16186
+ this._actionSelector( selected );
16187
+
16188
+ // Stop shiftScroll
16189
+ clearInterval( this.s.scrollInterval );
16190
+ this.s.scrollInterval = null;
16191
+ },
16192
+
16193
+
16194
+ /**
16195
+ * Create an array with a range of numbers defined by the start and end
16196
+ * parameters passed in (inclusive!).
16197
+ *
16198
+ * @param {integer} start Start
16199
+ * @param {integer} end End
16200
+ * @private
16201
+ */
16202
+ _range: function ( start, end )
16203
+ {
16204
+ var out = [];
16205
+ var i;
16206
+
16207
+ if ( start <= end ) {
16208
+ for ( i=start ; i<=end ; i++ ) {
16209
+ out.push( i );
16210
+ }
16211
+ }
16212
+ else {
16213
+ for ( i=start ; i>=end ; i-- ) {
16214
+ out.push( i );
16215
+ }
16216
+ }
16217
+
16218
+ return out;
16219
+ },
16220
+
16221
+
16222
+ /**
16223
+ * Move the window and DataTables scrolling during a drag to scroll new
16224
+ * content into view. This is done by proximity to the edge of the scrolling
16225
+ * container of the mouse - for example near the top edge of the window
16226
+ * should scroll up. This is a little complicated as there are two elements
16227
+ * that can be scrolled - the window and the DataTables scrolling view port
16228
+ * (if scrollX and / or scrollY is enabled).
16229
+ *
16230
+ * @param {object} e Mouse move event object
16231
+ * @private
16232
+ */
16233
+ _shiftScroll: function ( e )
16234
+ {
16235
+ var that = this;
16236
+ var dt = this.s.dt;
16237
+ var scroll = this.s.scroll;
16238
+ var runInterval = false;
16239
+ var scrollSpeed = 5;
16240
+ var buffer = 65;
16241
+ var
16242
+ windowY = e.pageY - document.body.scrollTop,
16243
+ windowX = e.pageX - document.body.scrollLeft,
16244
+ windowVert, windowHoriz,
16245
+ dtVert, dtHoriz;
16246
+
16247
+ // Window calculations - based on the mouse position in the window,
16248
+ // regardless of scrolling
16249
+ if ( windowY < buffer ) {
16250
+ windowVert = scrollSpeed * -1;
16251
+ }
16252
+ else if ( windowY > scroll.windowHeight - buffer ) {
16253
+ windowVert = scrollSpeed;
16254
+ }
16255
+
16256
+ if ( windowX < buffer ) {
16257
+ windowHoriz = scrollSpeed * -1;
16258
+ }
16259
+ else if ( windowX > scroll.windowWidth - buffer ) {
16260
+ windowHoriz = scrollSpeed;
16261
+ }
16262
+
16263
+ // DataTables scrolling calculations - based on the table's position in
16264
+ // the document and the mouse position on the page
16265
+ if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
16266
+ dtVert = scrollSpeed * -1;
16267
+ }
16268
+ else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
16269
+ dtVert = scrollSpeed;
16270
+ }
16271
+
16272
+ if ( scroll.dtLeft !== null && e.pageX < scroll.dtLeft + buffer ) {
16273
+ dtHoriz = scrollSpeed * -1;
16274
+ }
16275
+ else if ( scroll.dtLeft !== null && e.pageX > scroll.dtLeft + scroll.dtWidth - buffer ) {
16276
+ dtHoriz = scrollSpeed;
16277
+ }
16278
+
16279
+ // This is where it gets interesting. We want to continue scrolling
16280
+ // without requiring a mouse move, so we need an interval to be
16281
+ // triggered. The interval should continue until it is no longer needed,
16282
+ // but it must also use the latest scroll commands (for example consider
16283
+ // that the mouse might move from scrolling up to scrolling left, all
16284
+ // with the same interval running. We use the `scroll` object to "pass"
16285
+ // this information to the interval. Can't use local variables as they
16286
+ // wouldn't be the ones that are used by an already existing interval!
16287
+ if ( windowVert || windowHoriz || dtVert || dtHoriz ) {
16288
+ scroll.windowVert = windowVert;
16289
+ scroll.windowHoriz = windowHoriz;
16290
+ scroll.dtVert = dtVert;
16291
+ scroll.dtHoriz = dtHoriz;
16292
+ runInterval = true;
16293
+ }
16294
+ else if ( this.s.scrollInterval ) {
16295
+ // Don't need to scroll - remove any existing timer
16296
+ clearInterval( this.s.scrollInterval );
16297
+ this.s.scrollInterval = null;
16298
+ }
16299
+
16300
+ // If we need to run the interval to scroll and there is no existing
16301
+ // interval (if there is an existing one, it will continue to run)
16302
+ if ( ! this.s.scrollInterval && runInterval ) {
16303
+ this.s.scrollInterval = setInterval( function () {
16304
+ // Don't need to worry about setting scroll <0 or beyond the
16305
+ // scroll bound as the browser will just reject that.
16306
+ if ( scroll.windowVert ) {
16307
+ document.body.scrollTop += scroll.windowVert;
16308
+ }
16309
+ if ( scroll.windowHoriz ) {
16310
+ document.body.scrollLeft += scroll.windowHoriz;
16311
+ }
16312
+
16313
+ // DataTables scrolling
16314
+ if ( scroll.dtVert || scroll.dtHoriz ) {
16315
+ var scroller = that.dom.dtScroll[0];
16316
+
16317
+ if ( scroll.dtVert ) {
16318
+ scroller.scrollTop += scroll.dtVert;
16319
+ }
16320
+ if ( scroll.dtHoriz ) {
16321
+ scroller.scrollLeft += scroll.dtHoriz;
16322
+ }
16323
+ }
16324
+ }, 20 );
16325
+ }
16326
+ },
16327
+
16328
+
16329
+ /**
16330
+ * Update the DataTable after the user has selected what they want to do
16331
+ *
16332
+ * @param {false|undefined} result Return from the `execute` method - can
16333
+ * be false internally to do nothing. This is not documented for plug-ins
16334
+ * and is used only by the cancel option.
16335
+ * @param {array} cells Information about the selected cells from the key
16336
+ * up function, argumented with the set values
16337
+ * @private
16338
+ */
16339
+ _update: function ( result, cells )
16340
+ {
16341
+ // Do nothing on `false` return from an execute function
16342
+ if ( result === false ) {
16343
+ return;
16344
+ }
16345
+
16346
+ var dt = this.s.dt;
16347
+ var cell;
16348
+ var columns = dt.columns( this.c.columns ).indexes();
16349
+
16350
+ // Potentially allow modifications to the cells matrix
16351
+ this._emitEvent( 'preAutoFill', [ dt, cells ] );
16352
+
16353
+ this._editor( cells );
16354
+
16355
+ // Automatic updates are not performed if `update` is null and the
16356
+ // `editor` parameter is passed in - the reason being that Editor will
16357
+ // update the data once submitted
16358
+ var update = this.c.update !== null ?
16359
+ this.c.update :
16360
+ this.c.editor ?
16361
+ false :
16362
+ true;
16363
+
16364
+ if ( update ) {
16365
+ for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16366
+ for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16367
+ cell = cells[i][j];
16368
+
16369
+ if ( columns.indexOf(cell.index.column) !== -1 ) {
16370
+ cell.cell.data( cell.set );
16371
+ }
16372
+ }
16373
+ }
16374
+
16375
+ dt.draw(false);
16376
+ }
16377
+
16378
+ this._emitEvent( 'autoFill', [ dt, cells ] );
16379
+ }
16380
+ } );
16381
+
16382
+
16383
+ /**
16384
+ * AutoFill actions. The options here determine how AutoFill will fill the data
16385
+ * in the table when the user has selected a range of cells. Please see the
16386
+ * documentation on the DataTables site for full details on how to create plug-
16387
+ * ins.
16388
+ *
16389
+ * @type {Object}
16390
+ */
16391
+ AutoFill.actions = {
16392
+ increment: {
16393
+ available: function ( dt, cells ) {
16394
+ var d = cells[0][0].label;
16395
+
16396
+ // is numeric test based on jQuery's old `isNumeric` function
16397
+ return !isNaN( d - parseFloat( d ) );
16398
+ },
16399
+
16400
+ option: function ( dt, cells ) {
16401
+ return dt.i18n(
16402
+ 'autoFill.increment',
16403
+ 'Increment / decrement each cell by: <input type="number" value="1">'
16404
+ );
16405
+ },
16406
+
16407
+ execute: function ( dt, cells, node ) {
16408
+ var value = cells[0][0].data * 1;
16409
+ var increment = $('input', node).val() * 1;
16410
+
16411
+ for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16412
+ for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16413
+ cells[i][j].set = value;
16414
+
16415
+ value += increment;
16416
+ }
16417
+ }
16418
+ }
16419
+ },
16420
+
16421
+ fill: {
16422
+ available: function ( dt, cells ) {
16423
+ return true;
16424
+ },
16425
+
16426
+ option: function ( dt, cells ) {
16427
+ return dt.i18n('autoFill.fill', 'Fill all cells with <i>'+cells[0][0].label+'</i>' );
16428
+ },
16429
+
16430
+ execute: function ( dt, cells, node ) {
16431
+ var value = cells[0][0].data;
16432
+
16433
+ for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16434
+ for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16435
+ cells[i][j].set = value;
16436
+ }
16437
+ }
16438
+ }
16439
+ },
16440
+
16441
+ fillHorizontal: {
16442
+ available: function ( dt, cells ) {
16443
+ return cells.length > 1 && cells[0].length > 1;
16444
+ },
16445
+
16446
+ option: function ( dt, cells ) {
16447
+ return dt.i18n('autoFill.fillHorizontal', 'Fill cells horizontally' );
16448
+ },
16449
+
16450
+ execute: function ( dt, cells, node ) {
16451
+ for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16452
+ for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16453
+ cells[i][j].set = cells[i][0].data;
16454
+ }
16455
+ }
16456
+ }
16457
+ },
16458
+
16459
+ fillVertical: {
16460
+ available: function ( dt, cells ) {
16461
+ return cells.length > 1;
16462
+ },
16463
+
16464
+ option: function ( dt, cells ) {
16465
+ return dt.i18n('autoFill.fillVertical', 'Fill cells vertically' );
16466
+ },
16467
+
16468
+ execute: function ( dt, cells, node ) {
16469
+ for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16470
+ for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16471
+ cells[i][j].set = cells[0][j].data;
16472
+ }
16473
+ }
16474
+ }
16475
+ },
16476
+
16477
+ // Special type that does not make itself available, but is added
16478
+ // automatically by AutoFill if a multi-choice list is shown. This allows
16479
+ // sensible code reuse
16480
+ cancel: {
16481
+ available: function () {
16482
+ return false;
16483
+ },
16484
+
16485
+ option: function ( dt ) {
16486
+ return dt.i18n('autoFill.cancel', 'Cancel' );
16487
+ },
16488
+
16489
+ execute: function () {
16490
+ return false;
16491
+ }
16492
+ }
16493
+ };
16494
+
16495
+
16496
+ /**
16497
+ * AutoFill version
16498
+ *
16499
+ * @static
16500
+ * @type String
16501
+ */
16502
+ AutoFill.version = '2.3.5';
16503
+
16504
+
16505
+ /**
16506
+ * AutoFill defaults
16507
+ *
16508
+ * @namespace
16509
+ */
16510
+ AutoFill.defaults = {
16511
+ /** @type {Boolean} Ask user what they want to do, even for a single option */
16512
+ alwaysAsk: false,
16513
+
16514
+ /** @type {string|null} What will trigger a focus */
16515
+ focus: null, // focus, click, hover
16516
+
16517
+ /** @type {column-selector} Columns to provide auto fill for */
16518
+ columns: '', // all
16519
+
16520
+ /** @type {Boolean} Enable AutoFill on load */
16521
+ enable: true,
16522
+
16523
+ /** @type {boolean|null} Update the cells after a drag */
16524
+ update: null, // false is editor given, true otherwise
16525
+
16526
+ /** @type {DataTable.Editor} Editor instance for automatic submission */
16527
+ editor: null,
16528
+
16529
+ /** @type {boolean} Enable vertical fill */
16530
+ vertical: true,
16531
+
16532
+ /** @type {boolean} Enable horizontal fill */
16533
+ horizontal: true
16534
+ };
16535
+
16536
+
16537
+ /**
16538
+ * Classes used by AutoFill that are configurable
16539
+ *
16540
+ * @namespace
16541
+ */
16542
+ AutoFill.classes = {
16543
+ /** @type {String} Class used by the selection button */
16544
+ btn: 'btn'
16545
+ };
16546
+
16547
+
16548
+ /*
16549
+ * API
16550
+ */
16551
+ var Api = $.fn.dataTable.Api;
16552
+
16553
+ // Doesn't do anything - Not documented
16554
+ Api.register( 'autoFill()', function () {
16555
+ return this;
16556
+ } );
16557
+
16558
+ Api.register( 'autoFill().enabled()', function () {
16559
+ var ctx = this.context[0];
16560
+
16561
+ return ctx.autoFill ?
16562
+ ctx.autoFill.enabled() :
16563
+ false;
16564
+ } );
16565
+
16566
+ Api.register( 'autoFill().enable()', function ( flag ) {
16567
+ return this.iterator( 'table', function ( ctx ) {
16568
+ if ( ctx.autoFill ) {
16569
+ ctx.autoFill.enable( flag );
16570
+ }
16571
+ } );
16572
+ } );
16573
+
16574
+ Api.register( 'autoFill().disable()', function () {
16575
+ return this.iterator( 'table', function ( ctx ) {
16576
+ if ( ctx.autoFill ) {
16577
+ ctx.autoFill.disable();
16578
+ }
16579
+ } );
16580
+ } );
16581
+
16582
+
16583
+ // Attach a listener to the document which listens for DataTables initialisation
16584
+ // events so we can automatically initialise
16585
+ $(document).on( 'preInit.dt.autofill', function (e, settings, json) {
16586
+ if ( e.namespace !== 'dt' ) {
16587
+ return;
16588
+ }
16589
+
16590
+ var init = settings.oInit.autoFill;
16591
+ var defaults = DataTable.defaults.autoFill;
16592
+
16593
+ if ( init || defaults ) {
16594
+ var opts = $.extend( {}, init, defaults );
16595
+
16596
+ if ( init !== false ) {
16597
+ new AutoFill( settings, opts );
16598
+ }
16599
+ }
16600
+ } );
16601
+
16602
+
16603
+ // Alias for access
16604
+ DataTable.AutoFill = AutoFill;
16605
+ DataTable.AutoFill = AutoFill;
16606
+
16607
+
16608
+ return AutoFill;
16609
+ }));
16610
+
16611
+
16612
+ /*! Buttons for DataTables 1.6.5
16613
+ * ©2016-2020 SpryMedia Ltd - datatables.net/license
16614
+ */
16615
+
16616
+ (function( factory ){
16617
+ if ( typeof define === 'function' && define.amd ) {
16618
+ // AMD
16619
+ define( ['jquery', 'datatables.net'], function ( $ ) {
16620
+ return factory( $, window, document );
16621
+ } );
16622
+ }
16623
+ else if ( typeof exports === 'object' ) {
16624
+ // CommonJS
16625
+ module.exports = function (root, $) {
16626
+ if ( ! root ) {
16627
+ root = window;
16628
+ }
16629
+
16630
+ if ( ! $ || ! $.fn.dataTable ) {
16631
+ $ = require('datatables.net')(root, $).$;
16632
+ }
16633
+
16634
+ return factory( $, root, root.document );
16635
+ };
16636
+ }
16637
+ else {
16638
+ // Browser
16639
+ factory( jQuery, window, document );
16640
+ }
16641
+ }(function( $, window, document, undefined ) {
16642
+ 'use strict';
16643
+ var DataTable = $.fn.dataTable;
16644
+
16645
+
16646
+ // Used for namespacing events added to the document by each instance, so they
16647
+ // can be removed on destroy
16648
+ var _instCounter = 0;
16649
+
16650
+ // Button namespacing counter for namespacing events on individual buttons
16651
+ var _buttonCounter = 0;
16652
+
16653
+ var _dtButtons = DataTable.ext.buttons;
16654
+
16655
+ // Allow for jQuery slim
16656
+ function _fadeIn(el, duration, fn) {
16657
+ if ($.fn.animate) {
16658
+ el
16659
+ .stop()
16660
+ .fadeIn( duration, fn );
16661
+ }
16662
+ else {
16663
+ el.css('display', 'block');
16664
+
16665
+ if (fn) {
16666
+ fn.call(el);
16667
+ }
16668
+ }
16669
+ }
16670
+
16671
+ function _fadeOut(el, duration, fn) {
16672
+ if ($.fn.animate) {
16673
+ el
16674
+ .stop()
16675
+ .fadeOut( duration, fn );
16676
+ }
16677
+ else {
16678
+ el.css('display', 'none');
16679
+
16680
+ if (fn) {
16681
+ fn.call(el);
16682
+ }
16683
+ }
16684
+ }
16685
+
16686
+ /**
16687
+ * [Buttons description]
16688
+ * @param {[type]}
16689
+ * @param {[type]}
16690
+ */
16691
+ var Buttons = function( dt, config )
16692
+ {
16693
+ // If not created with a `new` keyword then we return a wrapper function that
16694
+ // will take the settings object for a DT. This allows easy use of new instances
16695
+ // with the `layout` option - e.g. `topLeft: $.fn.dataTable.Buttons( ... )`.
16696
+ if ( !(this instanceof Buttons) ) {
16697
+ return function (settings) {
16698
+ return new Buttons( settings, dt ).container();
16699
+ };
16700
+ }
16701
+
16702
+ // If there is no config set it to an empty object
16703
+ if ( typeof( config ) === 'undefined' ) {
16704
+ config = {};
16705
+ }
16706
+
16707
+ // Allow a boolean true for defaults
16708
+ if ( config === true ) {
16709
+ config = {};
16710
+ }
16711
+
16712
+ // For easy configuration of buttons an array can be given
16713
+ if ( Array.isArray( config ) ) {
16714
+ config = { buttons: config };
16715
+ }
16716
+
16717
+ this.c = $.extend( true, {}, Buttons.defaults, config );
16718
+
16719
+ // Don't want a deep copy for the buttons
16720
+ if ( config.buttons ) {
16721
+ this.c.buttons = config.buttons;
16722
+ }
16723
+
16724
+ this.s = {
16725
+ dt: new DataTable.Api( dt ),
16726
+ buttons: [],
16727
+ listenKeys: '',
16728
+ namespace: 'dtb'+(_instCounter++)
16729
+ };
16730
+
16731
+ this.dom = {
16732
+ container: $('<'+this.c.dom.container.tag+'/>')
16733
+ .addClass( this.c.dom.container.className )
16734
+ };
16735
+
16736
+ this._constructor();
16737
+ };
16738
+
16739
+
16740
+ $.extend( Buttons.prototype, {
16741
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16742
+ * Public methods
16743
+ */
16744
+
16745
+ /**
16746
+ * Get the action of a button
16747
+ * @param {int|string} Button index
16748
+ * @return {function}
16749
+ *//**
16750
+ * Set the action of a button
16751
+ * @param {node} node Button element
16752
+ * @param {function} action Function to set
16753
+ * @return {Buttons} Self for chaining
16754
+ */
16755
+ action: function ( node, action )
16756
+ {
16757
+ var button = this._nodeToButton( node );
16758
+
16759
+ if ( action === undefined ) {
16760
+ return button.conf.action;
16761
+ }
16762
+
16763
+ button.conf.action = action;
16764
+
16765
+ return this;
16766
+ },
16767
+
16768
+ /**
16769
+ * Add an active class to the button to make to look active or get current
16770
+ * active state.
16771
+ * @param {node} node Button element
16772
+ * @param {boolean} [flag] Enable / disable flag
16773
+ * @return {Buttons} Self for chaining or boolean for getter
16774
+ */
16775
+ active: function ( node, flag ) {
16776
+ var button = this._nodeToButton( node );
16777
+ var klass = this.c.dom.button.active;
16778
+ var jqNode = $(button.node);
16779
+
16780
+ if ( flag === undefined ) {
16781
+ return jqNode.hasClass( klass );
16782
+ }
16783
+
16784
+ jqNode.toggleClass( klass, flag === undefined ? true : flag );
16785
+
16786
+ return this;
16787
+ },
16788
+
16789
+ /**
16790
+ * Add a new button
16791
+ * @param {object} config Button configuration object, base string name or function
16792
+ * @param {int|string} [idx] Button index for where to insert the button
16793
+ * @return {Buttons} Self for chaining
16794
+ */
16795
+ add: function ( config, idx )
16796
+ {
16797
+ var buttons = this.s.buttons;
16798
+
16799
+ if ( typeof idx === 'string' ) {
16800
+ var split = idx.split('-');
16801
+ var base = this.s;
16802
+
16803
+ for ( var i=0, ien=split.length-1 ; i<ien ; i++ ) {
16804
+ base = base.buttons[ split[i]*1 ];
16805
+ }
16806
+
16807
+ buttons = base.buttons;
16808
+ idx = split[ split.length-1 ]*1;
16809
+ }
16810
+
16811
+ this._expandButton( buttons, config, base !== undefined, idx );
16812
+ this._draw();
16813
+
16814
+ return this;
16815
+ },
16816
+
16817
+ /**
16818
+ * Get the container node for the buttons
16819
+ * @return {jQuery} Buttons node
16820
+ */
16821
+ container: function ()
16822
+ {
16823
+ return this.dom.container;
16824
+ },
16825
+
16826
+ /**
16827
+ * Disable a button
16828
+ * @param {node} node Button node
16829
+ * @return {Buttons} Self for chaining
16830
+ */
16831
+ disable: function ( node ) {
16832
+ var button = this._nodeToButton( node );
16833
+
16834
+ $(button.node)
16835
+ .addClass( this.c.dom.button.disabled )
16836
+ .attr('disabled', true);
16837
+
16838
+ return this;
16839
+ },
16840
+
16841
+ /**
16842
+ * Destroy the instance, cleaning up event handlers and removing DOM
16843
+ * elements
16844
+ * @return {Buttons} Self for chaining
16845
+ */
16846
+ destroy: function ()
16847
+ {
16848
+ // Key event listener
16849
+ $('body').off( 'keyup.'+this.s.namespace );
16850
+
16851
+ // Individual button destroy (so they can remove their own events if
16852
+ // needed). Take a copy as the array is modified by `remove`
16853
+ var buttons = this.s.buttons.slice();
16854
+ var i, ien;
16855
+
16856
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
16857
+ this.remove( buttons[i].node );
16858
+ }
16859
+
16860
+ // Container
16861
+ this.dom.container.remove();
16862
+
16863
+ // Remove from the settings object collection
16864
+ var buttonInsts = this.s.dt.settings()[0];
16865
+
16866
+ for ( i=0, ien=buttonInsts.length ; i<ien ; i++ ) {
16867
+ if ( buttonInsts.inst === this ) {
16868
+ buttonInsts.splice( i, 1 );
16869
+ break;
16870
+ }
16871
+ }
16872
+
16873
+ return this;
16874
+ },
16875
+
16876
+ /**
16877
+ * Enable / disable a button
16878
+ * @param {node} node Button node
16879
+ * @param {boolean} [flag=true] Enable / disable flag
16880
+ * @return {Buttons} Self for chaining
16881
+ */
16882
+ enable: function ( node, flag )
16883
+ {
16884
+ if ( flag === false ) {
16885
+ return this.disable( node );
16886
+ }
16887
+
16888
+ var button = this._nodeToButton( node );
16889
+ $(button.node)
16890
+ .removeClass( this.c.dom.button.disabled )
16891
+ .removeAttr('disabled');
16892
+
16893
+ return this;
16894
+ },
16895
+
16896
+ /**
16897
+ * Get the instance name for the button set selector
16898
+ * @return {string} Instance name
16899
+ */
16900
+ name: function ()
16901
+ {
16902
+ return this.c.name;
16903
+ },
16904
+
16905
+ /**
16906
+ * Get a button's node of the buttons container if no button is given
16907
+ * @param {node} [node] Button node
16908
+ * @return {jQuery} Button element, or container
16909
+ */
16910
+ node: function ( node )
16911
+ {
16912
+ if ( ! node ) {
16913
+ return this.dom.container;
16914
+ }
16915
+
16916
+ var button = this._nodeToButton( node );
16917
+ return $(button.node);
16918
+ },
16919
+
16920
+ /**
16921
+ * Set / get a processing class on the selected button
16922
+ * @param {element} node Triggering button node
16923
+ * @param {boolean} flag true to add, false to remove, undefined to get
16924
+ * @return {boolean|Buttons} Getter value or this if a setter.
16925
+ */
16926
+ processing: function ( node, flag )
16927
+ {
16928
+ var dt = this.s.dt;
16929
+ var button = this._nodeToButton( node );
16930
+
16931
+ if ( flag === undefined ) {
16932
+ return $(button.node).hasClass( 'processing' );
16933
+ }
16934
+
16935
+ $(button.node).toggleClass( 'processing', flag );
16936
+
16937
+ $(dt.table().node()).triggerHandler( 'buttons-processing.dt', [
16938
+ flag, dt.button( node ), dt, $(node), button.conf
16939
+ ] );
16940
+
16941
+ return this;
16942
+ },
16943
+
16944
+ /**
16945
+ * Remove a button.
16946
+ * @param {node} node Button node
16947
+ * @return {Buttons} Self for chaining
16948
+ */
16949
+ remove: function ( node )
16950
+ {
16951
+ var button = this._nodeToButton( node );
16952
+ var host = this._nodeToHost( node );
16953
+ var dt = this.s.dt;
16954
+
16955
+ // Remove any child buttons first
16956
+ if ( button.buttons.length ) {
16957
+ for ( var i=button.buttons.length-1 ; i>=0 ; i-- ) {
16958
+ this.remove( button.buttons[i].node );
16959
+ }
16960
+ }
16961
+
16962
+ // Allow the button to remove event handlers, etc
16963
+ if ( button.conf.destroy ) {
16964
+ button.conf.destroy.call( dt.button(node), dt, $(node), button.conf );
16965
+ }
16966
+
16967
+ this._removeKey( button.conf );
16968
+
16969
+ $(button.node).remove();
16970
+
16971
+ var idx = $.inArray( button, host );
16972
+ host.splice( idx, 1 );
16973
+
16974
+ return this;
16975
+ },
16976
+
16977
+ /**
16978
+ * Get the text for a button
16979
+ * @param {int|string} node Button index
16980
+ * @return {string} Button text
16981
+ *//**
16982
+ * Set the text for a button
16983
+ * @param {int|string|function} node Button index
16984
+ * @param {string} label Text
16985
+ * @return {Buttons} Self for chaining
16986
+ */
16987
+ text: function ( node, label )
16988
+ {
16989
+ var button = this._nodeToButton( node );
16990
+ var buttonLiner = this.c.dom.collection.buttonLiner;
16991
+ var linerTag = button.inCollection && buttonLiner && buttonLiner.tag ?
16992
+ buttonLiner.tag :
16993
+ this.c.dom.buttonLiner.tag;
16994
+ var dt = this.s.dt;
16995
+ var jqNode = $(button.node);
16996
+ var text = function ( opt ) {
16997
+ return typeof opt === 'function' ?
16998
+ opt( dt, jqNode, button.conf ) :
16999
+ opt;
17000
+ };
17001
+
17002
+ if ( label === undefined ) {
17003
+ return text( button.conf.text );
17004
+ }
17005
+
17006
+ button.conf.text = label;
17007
+
17008
+ if ( linerTag ) {
17009
+ jqNode.children( linerTag ).html( text(label) );
17010
+ }
17011
+ else {
17012
+ jqNode.html( text(label) );
17013
+ }
17014
+
17015
+ return this;
17016
+ },
17017
+
17018
+
17019
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17020
+ * Constructor
17021
+ */
17022
+
17023
+ /**
17024
+ * Buttons constructor
17025
+ * @private
17026
+ */
17027
+ _constructor: function ()
17028
+ {
17029
+ var that = this;
17030
+ var dt = this.s.dt;
17031
+ var dtSettings = dt.settings()[0];
17032
+ var buttons = this.c.buttons;
17033
+
17034
+ if ( ! dtSettings._buttons ) {
17035
+ dtSettings._buttons = [];
17036
+ }
17037
+
17038
+ dtSettings._buttons.push( {
17039
+ inst: this,
17040
+ name: this.c.name
17041
+ } );
17042
+
17043
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17044
+ this.add( buttons[i] );
17045
+ }
17046
+
17047
+ dt.on( 'destroy', function ( e, settings ) {
17048
+ if ( settings === dtSettings ) {
17049
+ that.destroy();
17050
+ }
17051
+ } );
17052
+
17053
+ // Global key event binding to listen for button keys
17054
+ $('body').on( 'keyup.'+this.s.namespace, function ( e ) {
17055
+ if ( ! document.activeElement || document.activeElement === document.body ) {
17056
+ // SUse a string of characters for fast lookup of if we need to
17057
+ // handle this
17058
+ var character = String.fromCharCode(e.keyCode).toLowerCase();
17059
+
17060
+ if ( that.s.listenKeys.toLowerCase().indexOf( character ) !== -1 ) {
17061
+ that._keypress( character, e );
17062
+ }
17063
+ }
17064
+ } );
17065
+ },
17066
+
17067
+
17068
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17069
+ * Private methods
17070
+ */
17071
+
17072
+ /**
17073
+ * Add a new button to the key press listener
17074
+ * @param {object} conf Resolved button configuration object
17075
+ * @private
17076
+ */
17077
+ _addKey: function ( conf )
17078
+ {
17079
+ if ( conf.key ) {
17080
+ this.s.listenKeys += $.isPlainObject( conf.key ) ?
17081
+ conf.key.key :
17082
+ conf.key;
17083
+ }
17084
+ },
17085
+
17086
+ /**
17087
+ * Insert the buttons into the container. Call without parameters!
17088
+ * @param {node} [container] Recursive only - Insert point
17089
+ * @param {array} [buttons] Recursive only - Buttons array
17090
+ * @private
17091
+ */
17092
+ _draw: function ( container, buttons )
17093
+ {
17094
+ if ( ! container ) {
17095
+ container = this.dom.container;
17096
+ buttons = this.s.buttons;
17097
+ }
17098
+
17099
+ container.children().detach();
17100
+
17101
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17102
+ container.append( buttons[i].inserter );
17103
+ container.append( ' ' );
17104
+
17105
+ if ( buttons[i].buttons && buttons[i].buttons.length ) {
17106
+ this._draw( buttons[i].collection, buttons[i].buttons );
17107
+ }
17108
+ }
17109
+ },
17110
+
17111
+ /**
17112
+ * Create buttons from an array of buttons
17113
+ * @param {array} attachTo Buttons array to attach to
17114
+ * @param {object} button Button definition
17115
+ * @param {boolean} inCollection true if the button is in a collection
17116
+ * @private
17117
+ */
17118
+ _expandButton: function ( attachTo, button, inCollection, attachPoint )
17119
+ {
17120
+ var dt = this.s.dt;
17121
+ var buttonCounter = 0;
17122
+ var buttons = ! Array.isArray( button ) ?
17123
+ [ button ] :
17124
+ button;
17125
+
17126
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17127
+ var conf = this._resolveExtends( buttons[i] );
17128
+
17129
+ if ( ! conf ) {
17130
+ continue;
17131
+ }
17132
+
17133
+ // If the configuration is an array, then expand the buttons at this
17134
+ // point
17135
+ if ( Array.isArray( conf ) ) {
17136
+ this._expandButton( attachTo, conf, inCollection, attachPoint );
17137
+ continue;
17138
+ }
17139
+
17140
+ var built = this._buildButton( conf, inCollection );
17141
+ if ( ! built ) {
17142
+ continue;
17143
+ }
17144
+
17145
+ if ( attachPoint !== undefined && attachPoint !== null ) {
17146
+ attachTo.splice( attachPoint, 0, built );
17147
+ attachPoint++;
17148
+ }
17149
+ else {
17150
+ attachTo.push( built );
17151
+ }
17152
+
17153
+ if ( built.conf.buttons ) {
17154
+ built.collection = $('<'+this.c.dom.collection.tag+'/>');
17155
+
17156
+ built.conf._collection = built.collection;
17157
+
17158
+ this._expandButton( built.buttons, built.conf.buttons, true, attachPoint );
17159
+ }
17160
+
17161
+ // init call is made here, rather than buildButton as it needs to
17162
+ // be selectable, and for that it needs to be in the buttons array
17163
+ if ( conf.init ) {
17164
+ conf.init.call( dt.button( built.node ), dt, $(built.node), conf );
17165
+ }
17166
+
17167
+ buttonCounter++;
17168
+ }
17169
+ },
17170
+
17171
+ /**
17172
+ * Create an individual button
17173
+ * @param {object} config Resolved button configuration
17174
+ * @param {boolean} inCollection `true` if a collection button
17175
+ * @return {jQuery} Created button node (jQuery)
17176
+ * @private
17177
+ */
17178
+ _buildButton: function ( config, inCollection )
17179
+ {
17180
+ var buttonDom = this.c.dom.button;
17181
+ var linerDom = this.c.dom.buttonLiner;
17182
+ var collectionDom = this.c.dom.collection;
17183
+ var dt = this.s.dt;
17184
+ var text = function ( opt ) {
17185
+ return typeof opt === 'function' ?
17186
+ opt( dt, button, config ) :
17187
+ opt;
17188
+ };
17189
+
17190
+ if ( inCollection && collectionDom.button ) {
17191
+ buttonDom = collectionDom.button;
17192
+ }
17193
+
17194
+ if ( inCollection && collectionDom.buttonLiner ) {
17195
+ linerDom = collectionDom.buttonLiner;
17196
+ }
17197
+
17198
+ // Make sure that the button is available based on whatever requirements
17199
+ // it has. For example, Flash buttons require Flash
17200
+ if ( config.available && ! config.available( dt, config ) ) {
17201
+ return false;
17202
+ }
17203
+
17204
+ var action = function ( e, dt, button, config ) {
17205
+ config.action.call( dt.button( button ), e, dt, button, config );
17206
+
17207
+ $(dt.table().node()).triggerHandler( 'buttons-action.dt', [
17208
+ dt.button( button ), dt, button, config
17209
+ ] );
17210
+ };
17211
+
17212
+ var tag = config.tag || buttonDom.tag;
17213
+ var clickBlurs = config.clickBlurs === undefined ? true : config.clickBlurs
17214
+ var button = $('<'+tag+'/>')
17215
+ .addClass( buttonDom.className )
17216
+ .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex )
17217
+ .attr( 'aria-controls', this.s.dt.table().node().id )
17218
+ .on( 'click.dtb', function (e) {
17219
+ e.preventDefault();
17220
+
17221
+ if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
17222
+ action( e, dt, button, config );
17223
+ }
17224
+ if( clickBlurs ) {
17225
+ button.trigger('blur');
17226
+ }
17227
+ } )
17228
+ .on( 'keyup.dtb', function (e) {
17229
+ if ( e.keyCode === 13 ) {
17230
+ if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
17231
+ action( e, dt, button, config );
17232
+ }
17233
+ }
17234
+ } );
17235
+
17236
+ // Make `a` tags act like a link
17237
+ if ( tag.toLowerCase() === 'a' ) {
17238
+ button.attr( 'href', '#' );
17239
+ }
17240
+
17241
+ // Button tags should have `type=button` so they don't have any default behaviour
17242
+ if ( tag.toLowerCase() === 'button' ) {
17243
+ button.attr( 'type', 'button' );
17244
+ }
17245
+
17246
+ if ( linerDom.tag ) {
17247
+ var liner = $('<'+linerDom.tag+'/>')
17248
+ .html( text( config.text ) )
17249
+ .addClass( linerDom.className );
17250
+
17251
+ if ( linerDom.tag.toLowerCase() === 'a' ) {
17252
+ liner.attr( 'href', '#' );
17253
+ }
17254
+
17255
+ button.append( liner );
17256
+ }
17257
+ else {
17258
+ button.html( text( config.text ) );
17259
+ }
17260
+
17261
+ if ( config.enabled === false ) {
17262
+ button.addClass( buttonDom.disabled );
17263
+ }
17264
+
17265
+ if ( config.className ) {
17266
+ button.addClass( config.className );
17267
+ }
17268
+
17269
+ if ( config.titleAttr ) {
17270
+ button.attr( 'title', text( config.titleAttr ) );
17271
+ }
17272
+
17273
+ if ( config.attr ) {
17274
+ button.attr( config.attr );
17275
+ }
17276
+
17277
+ if ( ! config.namespace ) {
17278
+ config.namespace = '.dt-button-'+(_buttonCounter++);
17279
+ }
17280
+
17281
+ var buttonContainer = this.c.dom.buttonContainer;
17282
+ var inserter;
17283
+ if ( buttonContainer && buttonContainer.tag ) {
17284
+ inserter = $('<'+buttonContainer.tag+'/>')
17285
+ .addClass( buttonContainer.className )
17286
+ .append( button );
17287
+ }
17288
+ else {
17289
+ inserter = button;
17290
+ }
17291
+
17292
+ this._addKey( config );
17293
+
17294
+ // Style integration callback for DOM manipulation
17295
+ // Note that this is _not_ documented. It is currently
17296
+ // for style integration only
17297
+ if( this.c.buttonCreated ) {
17298
+ inserter = this.c.buttonCreated( config, inserter );
17299
+ }
17300
+
17301
+ return {
17302
+ conf: config,
17303
+ node: button.get(0),
17304
+ inserter: inserter,
17305
+ buttons: [],
17306
+ inCollection: inCollection,
17307
+ collection: null
17308
+ };
17309
+ },
17310
+
17311
+ /**
17312
+ * Get the button object from a node (recursive)
17313
+ * @param {node} node Button node
17314
+ * @param {array} [buttons] Button array, uses base if not defined
17315
+ * @return {object} Button object
17316
+ * @private
17317
+ */
17318
+ _nodeToButton: function ( node, buttons )
17319
+ {
17320
+ if ( ! buttons ) {
17321
+ buttons = this.s.buttons;
17322
+ }
17323
+
17324
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17325
+ if ( buttons[i].node === node ) {
17326
+ return buttons[i];
17327
+ }
17328
+
17329
+ if ( buttons[i].buttons.length ) {
17330
+ var ret = this._nodeToButton( node, buttons[i].buttons );
17331
+
17332
+ if ( ret ) {
17333
+ return ret;
17334
+ }
17335
+ }
17336
+ }
17337
+ },
17338
+
17339
+ /**
17340
+ * Get container array for a button from a button node (recursive)
17341
+ * @param {node} node Button node
17342
+ * @param {array} [buttons] Button array, uses base if not defined
17343
+ * @return {array} Button's host array
17344
+ * @private
17345
+ */
17346
+ _nodeToHost: function ( node, buttons )
17347
+ {
17348
+ if ( ! buttons ) {
17349
+ buttons = this.s.buttons;
17350
+ }
17351
+
17352
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17353
+ if ( buttons[i].node === node ) {
17354
+ return buttons;
17355
+ }
17356
+
17357
+ if ( buttons[i].buttons.length ) {
17358
+ var ret = this._nodeToHost( node, buttons[i].buttons );
17359
+
17360
+ if ( ret ) {
17361
+ return ret;
17362
+ }
17363
+ }
17364
+ }
17365
+ },
17366
+
17367
+ /**
17368
+ * Handle a key press - determine if any button's key configured matches
17369
+ * what was typed and trigger the action if so.
17370
+ * @param {string} character The character pressed
17371
+ * @param {object} e Key event that triggered this call
17372
+ * @private
17373
+ */
17374
+ _keypress: function ( character, e )
17375
+ {
17376
+ // Check if this button press already activated on another instance of Buttons
17377
+ if ( e._buttonsHandled ) {
17378
+ return;
17379
+ }
17380
+
17381
+ var run = function ( conf, node ) {
17382
+ if ( ! conf.key ) {
17383
+ return;
17384
+ }
17385
+
17386
+ if ( conf.key === character ) {
17387
+ e._buttonsHandled = true;
17388
+ $(node).click();
17389
+ }
17390
+ else if ( $.isPlainObject( conf.key ) ) {
17391
+ if ( conf.key.key !== character ) {
17392
+ return;
17393
+ }
17394
+
17395
+ if ( conf.key.shiftKey && ! e.shiftKey ) {
17396
+ return;
17397
+ }
17398
+
17399
+ if ( conf.key.altKey && ! e.altKey ) {
17400
+ return;
17401
+ }
17402
+
17403
+ if ( conf.key.ctrlKey && ! e.ctrlKey ) {
17404
+ return;
17405
+ }
17406
+
17407
+ if ( conf.key.metaKey && ! e.metaKey ) {
17408
+ return;
17409
+ }
17410
+
17411
+ // Made it this far - it is good
17412
+ e._buttonsHandled = true;
17413
+ $(node).click();
17414
+ }
17415
+ };
17416
+
17417
+ var recurse = function ( a ) {
17418
+ for ( var i=0, ien=a.length ; i<ien ; i++ ) {
17419
+ run( a[i].conf, a[i].node );
17420
+
17421
+ if ( a[i].buttons.length ) {
17422
+ recurse( a[i].buttons );
17423
+ }
17424
+ }
17425
+ };
17426
+
17427
+ recurse( this.s.buttons );
17428
+ },
17429
+
17430
+ /**
17431
+ * Remove a key from the key listener for this instance (to be used when a
17432
+ * button is removed)
17433
+ * @param {object} conf Button configuration
17434
+ * @private
17435
+ */
17436
+ _removeKey: function ( conf )
17437
+ {
17438
+ if ( conf.key ) {
17439
+ var character = $.isPlainObject( conf.key ) ?
17440
+ conf.key.key :
17441
+ conf.key;
17442
+
17443
+ // Remove only one character, as multiple buttons could have the
17444
+ // same listening key
17445
+ var a = this.s.listenKeys.split('');
17446
+ var idx = $.inArray( character, a );
17447
+ a.splice( idx, 1 );
17448
+ this.s.listenKeys = a.join('');
17449
+ }
17450
+ },
17451
+
17452
+ /**
17453
+ * Resolve a button configuration
17454
+ * @param {string|function|object} conf Button config to resolve
17455
+ * @return {object} Button configuration
17456
+ * @private
17457
+ */
17458
+ _resolveExtends: function ( conf )
17459
+ {
17460
+ var dt = this.s.dt;
17461
+ var i, ien;
17462
+ var toConfObject = function ( base ) {
17463
+ var loop = 0;
17464
+
17465
+ // Loop until we have resolved to a button configuration, or an
17466
+ // array of button configurations (which will be iterated
17467
+ // separately)
17468
+ while ( ! $.isPlainObject(base) && ! Array.isArray(base) ) {
17469
+ if ( base === undefined ) {
17470
+ return;
17471
+ }
17472
+
17473
+ if ( typeof base === 'function' ) {
17474
+ base = base( dt, conf );
17475
+
17476
+ if ( ! base ) {
17477
+ return false;
17478
+ }
17479
+ }
17480
+ else if ( typeof base === 'string' ) {
17481
+ if ( ! _dtButtons[ base ] ) {
17482
+ throw 'Unknown button type: '+base;
17483
+ }
17484
+
17485
+ base = _dtButtons[ base ];
17486
+ }
17487
+
17488
+ loop++;
17489
+ if ( loop > 30 ) {
17490
+ // Protect against misconfiguration killing the browser
17491
+ throw 'Buttons: Too many iterations';
17492
+ }
17493
+ }
17494
+
17495
+ return Array.isArray( base ) ?
17496
+ base :
17497
+ $.extend( {}, base );
17498
+ };
17499
+
17500
+ conf = toConfObject( conf );
17501
+
17502
+ while ( conf && conf.extend ) {
17503
+ // Use `toConfObject` in case the button definition being extended
17504
+ // is itself a string or a function
17505
+ if ( ! _dtButtons[ conf.extend ] ) {
17506
+ throw 'Cannot extend unknown button type: '+conf.extend;
17507
+ }
17508
+
17509
+ var objArray = toConfObject( _dtButtons[ conf.extend ] );
17510
+ if ( Array.isArray( objArray ) ) {
17511
+ return objArray;
17512
+ }
17513
+ else if ( ! objArray ) {
17514
+ // This is a little brutal as it might be possible to have a
17515
+ // valid button without the extend, but if there is no extend
17516
+ // then the host button would be acting in an undefined state
17517
+ return false;
17518
+ }
17519
+
17520
+ // Stash the current class name
17521
+ var originalClassName = objArray.className;
17522
+
17523
+ conf = $.extend( {}, objArray, conf );
17524
+
17525
+ // The extend will have overwritten the original class name if the
17526
+ // `conf` object also assigned a class, but we want to concatenate
17527
+ // them so they are list that is combined from all extended buttons
17528
+ if ( originalClassName && conf.className !== originalClassName ) {
17529
+ conf.className = originalClassName+' '+conf.className;
17530
+ }
17531
+
17532
+ // Buttons to be added to a collection -gives the ability to define
17533
+ // if buttons should be added to the start or end of a collection
17534
+ var postfixButtons = conf.postfixButtons;
17535
+ if ( postfixButtons ) {
17536
+ if ( ! conf.buttons ) {
17537
+ conf.buttons = [];
17538
+ }
17539
+
17540
+ for ( i=0, ien=postfixButtons.length ; i<ien ; i++ ) {
17541
+ conf.buttons.push( postfixButtons[i] );
17542
+ }
17543
+
17544
+ conf.postfixButtons = null;
17545
+ }
17546
+
17547
+ var prefixButtons = conf.prefixButtons;
17548
+ if ( prefixButtons ) {
17549
+ if ( ! conf.buttons ) {
17550
+ conf.buttons = [];
17551
+ }
17552
+
17553
+ for ( i=0, ien=prefixButtons.length ; i<ien ; i++ ) {
17554
+ conf.buttons.splice( i, 0, prefixButtons[i] );
17555
+ }
17556
+
17557
+ conf.prefixButtons = null;
17558
+ }
17559
+
17560
+ // Although we want the `conf` object to overwrite almost all of
17561
+ // the properties of the object being extended, the `extend`
17562
+ // property should come from the object being extended
17563
+ conf.extend = objArray.extend;
17564
+ }
17565
+
17566
+ return conf;
17567
+ },
17568
+
17569
+ /**
17570
+ * Display (and replace if there is an existing one) a popover attached to a button
17571
+ * @param {string|node} content Content to show
17572
+ * @param {DataTable.Api} hostButton DT API instance of the button
17573
+ * @param {object} inOpts Options (see object below for all options)
17574
+ */
17575
+ _popover: function ( content, hostButton, inOpts ) {
17576
+ var dt = hostButton;
17577
+ var buttonsSettings = this.c;
17578
+ var options = $.extend( {
17579
+ align: 'button-left', // button-right, dt-container
17580
+ autoClose: false,
17581
+ background: true,
17582
+ backgroundClassName: 'dt-button-background',
17583
+ contentClassName: buttonsSettings.dom.collection.className,
17584
+ collectionLayout: '',
17585
+ collectionTitle: '',
17586
+ dropup: false,
17587
+ fade: 400,
17588
+ rightAlignClassName: 'dt-button-right',
17589
+ tag: buttonsSettings.dom.collection.tag
17590
+ }, inOpts );
17591
+ var hostNode = hostButton.node();
17592
+
17593
+ var close = function () {
17594
+ _fadeOut(
17595
+ $('.dt-button-collection'),
17596
+ options.fade,
17597
+ function () {
17598
+ $(this).detach();
17599
+ }
17600
+ );
17601
+
17602
+ $(dt.buttons( '[aria-haspopup="true"][aria-expanded="true"]' ).nodes())
17603
+ .attr('aria-expanded', 'false');
17604
+
17605
+ $('div.dt-button-background').off( 'click.dtb-collection' );
17606
+ Buttons.background( false, options.backgroundClassName, options.fade, hostNode );
17607
+
17608
+ $('body').off( '.dtb-collection' );
17609
+ dt.off( 'buttons-action.b-internal' );
17610
+ };
17611
+
17612
+ if (content === false) {
17613
+ close();
17614
+ }
17615
+
17616
+ var existingExpanded = $(dt.buttons( '[aria-haspopup="true"][aria-expanded="true"]' ).nodes());
17617
+ if ( existingExpanded.length ) {
17618
+ hostNode = existingExpanded.eq(0);
17619
+
17620
+ close();
17621
+ }
17622
+
17623
+ var display = $('<div/>')
17624
+ .addClass('dt-button-collection')
17625
+ .addClass(options.collectionLayout)
17626
+ .css('display', 'none');
17627
+
17628
+ content = $(content)
17629
+ .addClass(options.contentClassName)
17630
+ .attr('role', 'menu')
17631
+ .appendTo(display);
17632
+
17633
+ hostNode.attr( 'aria-expanded', 'true' );
17634
+
17635
+ if ( hostNode.parents('body')[0] !== document.body ) {
17636
+ hostNode = document.body.lastChild;
17637
+ }
17638
+
17639
+ if ( options.collectionTitle ) {
17640
+ display.prepend('<div class="dt-button-collection-title">'+options.collectionTitle+'</div>');
17641
+ }
17642
+
17643
+ _fadeIn( display.insertAfter( hostNode ), options.fade );
17644
+
17645
+ var tableContainer = $( hostButton.table().container() );
17646
+ var position = display.css( 'position' );
17647
+
17648
+ if ( options.align === 'dt-container' ) {
17649
+ hostNode = hostNode.parent();
17650
+ display.css('width', tableContainer.width());
17651
+ }
17652
+
17653
+ // Align the popover relative to the DataTables container
17654
+ // Useful for wide popovers such as SearchPanes
17655
+ if (
17656
+ position === 'absolute' &&
17657
+ (
17658
+ display.hasClass( options.rightAlignClassName ) ||
17659
+ display.hasClass( options.leftAlignClassName ) ||
17660
+ options.align === 'dt-container'
17661
+ )
17662
+ ) {
17663
+
17664
+ var hostPosition = hostNode.position();
17665
+
17666
+ display.css( {
17667
+ top: hostPosition.top + hostNode.outerHeight(),
17668
+ left: hostPosition.left
17669
+ } );
17670
+
17671
+ // calculate overflow when positioned beneath
17672
+ var collectionHeight = display.outerHeight();
17673
+ var tableBottom = tableContainer.offset().top + tableContainer.height();
17674
+ var listBottom = hostPosition.top + hostNode.outerHeight() + collectionHeight;
17675
+ var bottomOverflow = listBottom - tableBottom;
17676
+
17677
+ // calculate overflow when positioned above
17678
+ var listTop = hostPosition.top - collectionHeight;
17679
+ var tableTop = tableContainer.offset().top;
17680
+ var topOverflow = tableTop - listTop;
17681
+
17682
+ // if bottom overflow is larger, move to the top because it fits better, or if dropup is requested
17683
+ var moveTop = hostPosition.top - collectionHeight - 5;
17684
+ if ( (bottomOverflow > topOverflow || options.dropup) && -moveTop < tableTop ) {
17685
+ display.css( 'top', moveTop);
17686
+ }
17687
+
17688
+ // Get the size of the container (left and width - and thus also right)
17689
+ var tableLeft = tableContainer.offset().left;
17690
+ var tableWidth = tableContainer.width();
17691
+ var tableRight = tableLeft + tableWidth;
17692
+
17693
+ // Get the size of the popover (left and width - and ...)
17694
+ var popoverLeft = display.offset().left;
17695
+ var popoverWidth = display.width();
17696
+ var popoverRight = popoverLeft + popoverWidth;
17697
+
17698
+ // Get the size of the host buttons (left and width - and ...)
17699
+ var buttonsLeft = hostNode.offset().left;
17700
+ var buttonsWidth = hostNode.outerWidth()
17701
+ var buttonsRight = buttonsLeft + buttonsWidth;
17702
+
17703
+ // You've then got all the numbers you need to do some calculations and if statements,
17704
+ // so we can do some quick JS maths and apply it only once
17705
+ // If it has the right align class OR the buttons are right aligned OR the button container is floated right,
17706
+ // then calculate left position for the popover to align the popover to the right hand
17707
+ // side of the button - check to see if the left of the popover is inside the table container.
17708
+ // If not, move the popover so it is, but not more than it means that the popover is to the right of the table container
17709
+ var popoverShuffle = 0;
17710
+ if ( display.hasClass( options.rightAlignClassName )) {
17711
+ popoverShuffle = buttonsRight - popoverRight;
17712
+ if(tableLeft > (popoverLeft + popoverShuffle)){
17713
+ var leftGap = tableLeft - (popoverLeft + popoverShuffle);
17714
+ var rightGap = tableRight - (popoverRight + popoverShuffle);
17715
+
17716
+ if(leftGap > rightGap){
17717
+ popoverShuffle += rightGap;
17718
+ }
17719
+ else {
17720
+ popoverShuffle += leftGap;
17721
+ }
17722
+ }
17723
+ }
17724
+ // else attempt to left align the popover to the button. Similar to above, if the popover's right goes past the table container's right,
17725
+ // then move it back, but not so much that it goes past the left of the table container
17726
+ else {
17727
+ popoverShuffle = tableLeft - popoverLeft;
17728
+
17729
+ if(tableRight < (popoverRight + popoverShuffle)){
17730
+ var leftGap = tableLeft - (popoverLeft + popoverShuffle);
17731
+ var rightGap = tableRight - (popoverRight + popoverShuffle);
17732
+
17733
+ if(leftGap > rightGap ){
17734
+ popoverShuffle += rightGap;
17735
+ }
17736
+ else {
17737
+ popoverShuffle += leftGap;
17738
+ }
17739
+
17740
+ }
17741
+ }
17742
+
17743
+ display.css('left', display.position().left + popoverShuffle);
17744
+
17745
+ }
17746
+ else if (position === 'absolute') {
17747
+ // Align relative to the host button
17748
+ var hostPosition = hostNode.position();
17749
+
17750
+ display.css( {
17751
+ top: hostPosition.top + hostNode.outerHeight(),
17752
+ left: hostPosition.left
17753
+ } );
17754
+
17755
+ // calculate overflow when positioned beneath
17756
+ var collectionHeight = display.outerHeight();
17757
+ var top = hostNode.offset().top
17758
+ var popoverShuffle = 0;
17759
+
17760
+ // Get the size of the host buttons (left and width - and ...)
17761
+ var buttonsLeft = hostNode.offset().left;
17762
+ var buttonsWidth = hostNode.outerWidth()
17763
+ var buttonsRight = buttonsLeft + buttonsWidth;
17764
+
17765
+ // Get the size of the popover (left and width - and ...)
17766
+ var popoverLeft = display.offset().left;
17767
+ var popoverWidth = content.width();
17768
+ var popoverRight = popoverLeft + popoverWidth;
17769
+
17770
+ var moveTop = hostPosition.top - collectionHeight - 5;
17771
+ var tableBottom = tableContainer.offset().top + tableContainer.height();
17772
+ var listBottom = hostPosition.top + hostNode.outerHeight() + collectionHeight;
17773
+ var bottomOverflow = listBottom - tableBottom;
17774
+
17775
+ // calculate overflow when positioned above
17776
+ var listTop = hostPosition.top - collectionHeight;
17777
+ var tableTop = tableContainer.offset().top;
17778
+ var topOverflow = tableTop - listTop;
17779
+
17780
+ if ( (bottomOverflow > topOverflow || options.dropup) && -moveTop < tableTop ) {
17781
+ display.css( 'top', moveTop);
17782
+ }
17783
+
17784
+ popoverShuffle = options.align === 'button-right'
17785
+ ? buttonsRight - popoverRight
17786
+ : buttonsLeft - popoverLeft;
17787
+
17788
+ display.css('left', display.position().left + popoverShuffle);
17789
+ }
17790
+ else {
17791
+ // Fix position - centre on screen
17792
+ var top = display.height() / 2;
17793
+ if ( top > $(window).height() / 2 ) {
17794
+ top = $(window).height() / 2;
17795
+ }
17796
+
17797
+ display.css( 'marginTop', top*-1 );
17798
+ }
17799
+
17800
+ if ( options.background ) {
17801
+ Buttons.background( true, options.backgroundClassName, options.fade, hostNode );
17802
+ }
17803
+
17804
+ // This is bonkers, but if we don't have a click listener on the
17805
+ // background element, iOS Safari will ignore the body click
17806
+ // listener below. An empty function here is all that is
17807
+ // required to make it work...
17808
+ $('div.dt-button-background').on( 'click.dtb-collection', function () {} );
17809
+
17810
+ $('body')
17811
+ .on( 'click.dtb-collection', function (e) {
17812
+ // andSelf is deprecated in jQ1.8, but we want 1.7 compat
17813
+ var back = $.fn.addBack ? 'addBack' : 'andSelf';
17814
+ var parent = $(e.target).parent()[0];
17815
+
17816
+ if (( ! $(e.target).parents()[back]().filter( content ).length && !$(parent).hasClass('dt-buttons')) || $(e.target).hasClass('dt-button-background')) {
17817
+ close();
17818
+ }
17819
+ } )
17820
+ .on( 'keyup.dtb-collection', function (e) {
17821
+ if ( e.keyCode === 27 ) {
17822
+ close();
17823
+ }
17824
+ } );
17825
+
17826
+ if ( options.autoClose ) {
17827
+ setTimeout( function () {
17828
+ dt.on( 'buttons-action.b-internal', function (e, btn, dt, node) {
17829
+ if ( node[0] === hostNode[0] ) {
17830
+ return;
17831
+ }
17832
+ close();
17833
+ } );
17834
+ }, 0);
17835
+ }
17836
+
17837
+ $(display).trigger('buttons-popover.dt');
17838
+ }
17839
+ } );
17840
+
17841
+
17842
+
17843
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17844
+ * Statics
17845
+ */
17846
+
17847
+ /**
17848
+ * Show / hide a background layer behind a collection
17849
+ * @param {boolean} Flag to indicate if the background should be shown or
17850
+ * hidden
17851
+ * @param {string} Class to assign to the background
17852
+ * @static
17853
+ */
17854
+ Buttons.background = function ( show, className, fade, insertPoint ) {
17855
+ if ( fade === undefined ) {
17856
+ fade = 400;
17857
+ }
17858
+ if ( ! insertPoint ) {
17859
+ insertPoint = document.body;
17860
+ }
17861
+
17862
+ if ( show ) {
17863
+ _fadeIn(
17864
+ $('<div/>')
17865
+ .addClass( className )
17866
+ .css( 'display', 'none' )
17867
+ .insertAfter( insertPoint ),
17868
+ fade
17869
+ );
17870
+ }
17871
+ else {
17872
+ _fadeOut(
17873
+ $('div.'+className),
17874
+ fade,
17875
+ function () {
17876
+ $(this)
17877
+ .removeClass( className )
17878
+ .remove();
17879
+ }
17880
+ );
17881
+ }
17882
+ };
17883
+
17884
+ /**
17885
+ * Instance selector - select Buttons instances based on an instance selector
17886
+ * value from the buttons assigned to a DataTable. This is only useful if
17887
+ * multiple instances are attached to a DataTable.
17888
+ * @param {string|int|array} Instance selector - see `instance-selector`
17889
+ * documentation on the DataTables site
17890
+ * @param {array} Button instance array that was attached to the DataTables
17891
+ * settings object
17892
+ * @return {array} Buttons instances
17893
+ * @static
17894
+ */
17895
+ Buttons.instanceSelector = function ( group, buttons )
17896
+ {
17897
+ if ( group === undefined || group === null ) {
17898
+ return $.map( buttons, function ( v ) {
17899
+ return v.inst;
17900
+ } );
17901
+ }
17902
+
17903
+ var ret = [];
17904
+ var names = $.map( buttons, function ( v ) {
17905
+ return v.name;
17906
+ } );
17907
+
17908
+ // Flatten the group selector into an array of single options
17909
+ var process = function ( input ) {
17910
+ if ( Array.isArray( input ) ) {
17911
+ for ( var i=0, ien=input.length ; i<ien ; i++ ) {
17912
+ process( input[i] );
17913
+ }
17914
+ return;
17915
+ }
17916
+
17917
+ if ( typeof input === 'string' ) {
17918
+ if ( input.indexOf( ',' ) !== -1 ) {
17919
+ // String selector, list of names
17920
+ process( input.split(',') );
17921
+ }
17922
+ else {
17923
+ // String selector individual name
17924
+ var idx = $.inArray( input.trim(), names );
17925
+
17926
+ if ( idx !== -1 ) {
17927
+ ret.push( buttons[ idx ].inst );
17928
+ }
17929
+ }
17930
+ }
17931
+ else if ( typeof input === 'number' ) {
17932
+ // Index selector
17933
+ ret.push( buttons[ input ].inst );
17934
+ }
17935
+ };
17936
+
17937
+ process( group );
17938
+
17939
+ return ret;
17940
+ };
17941
+
17942
+ /**
17943
+ * Button selector - select one or more buttons from a selector input so some
17944
+ * operation can be performed on them.
17945
+ * @param {array} Button instances array that the selector should operate on
17946
+ * @param {string|int|node|jQuery|array} Button selector - see
17947
+ * `button-selector` documentation on the DataTables site
17948
+ * @return {array} Array of objects containing `inst` and `idx` properties of
17949
+ * the selected buttons so you know which instance each button belongs to.
17950
+ * @static
17951
+ */
17952
+ Buttons.buttonSelector = function ( insts, selector )
17953
+ {
17954
+ var ret = [];
17955
+ var nodeBuilder = function ( a, buttons, baseIdx ) {
17956
+ var button;
17957
+ var idx;
17958
+
17959
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17960
+ button = buttons[i];
17961
+
17962
+ if ( button ) {
17963
+ idx = baseIdx !== undefined ?
17964
+ baseIdx+i :
17965
+ i+'';
17966
+
17967
+ a.push( {
17968
+ node: button.node,
17969
+ name: button.conf.name,
17970
+ idx: idx
17971
+ } );
17972
+
17973
+ if ( button.buttons ) {
17974
+ nodeBuilder( a, button.buttons, idx+'-' );
17975
+ }
17976
+ }
17977
+ }
17978
+ };
17979
+
17980
+ var run = function ( selector, inst ) {
17981
+ var i, ien;
17982
+ var buttons = [];
17983
+ nodeBuilder( buttons, inst.s.buttons );
17984
+
17985
+ var nodes = $.map( buttons, function (v) {
17986
+ return v.node;
17987
+ } );
17988
+
17989
+ if ( Array.isArray( selector ) || selector instanceof $ ) {
17990
+ for ( i=0, ien=selector.length ; i<ien ; i++ ) {
17991
+ run( selector[i], inst );
17992
+ }
17993
+ return;
17994
+ }
17995
+
17996
+ if ( selector === null || selector === undefined || selector === '*' ) {
17997
+ // Select all
17998
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
17999
+ ret.push( {
18000
+ inst: inst,
18001
+ node: buttons[i].node
18002
+ } );
18003
+ }
18004
+ }
18005
+ else if ( typeof selector === 'number' ) {
18006
+ // Main button index selector
18007
+ ret.push( {
18008
+ inst: inst,
18009
+ node: inst.s.buttons[ selector ].node
18010
+ } );
18011
+ }
18012
+ else if ( typeof selector === 'string' ) {
18013
+ if ( selector.indexOf( ',' ) !== -1 ) {
18014
+ // Split
18015
+ var a = selector.split(',');
18016
+
18017
+ for ( i=0, ien=a.length ; i<ien ; i++ ) {
18018
+ run( a[i].trim(), inst );
18019
+ }
18020
+ }
18021
+ else if ( selector.match( /^\d+(\-\d+)*$/ ) ) {
18022
+ // Sub-button index selector
18023
+ var indexes = $.map( buttons, function (v) {
18024
+ return v.idx;
18025
+ } );
18026
+
18027
+ ret.push( {
18028
+ inst: inst,
18029
+ node: buttons[ $.inArray( selector, indexes ) ].node
18030
+ } );
18031
+ }
18032
+ else if ( selector.indexOf( ':name' ) !== -1 ) {
18033
+ // Button name selector
18034
+ var name = selector.replace( ':name', '' );
18035
+
18036
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
18037
+ if ( buttons[i].name === name ) {
18038
+ ret.push( {
18039
+ inst: inst,
18040
+ node: buttons[i].node
18041
+ } );
18042
+ }
18043
+ }
18044
+ }
18045
+ else {
18046
+ // jQuery selector on the nodes
18047
+ $( nodes ).filter( selector ).each( function () {
18048
+ ret.push( {
18049
+ inst: inst,
18050
+ node: this
18051
+ } );
18052
+ } );
18053
+ }
18054
+ }
18055
+ else if ( typeof selector === 'object' && selector.nodeName ) {
18056
+ // Node selector
18057
+ var idx = $.inArray( selector, nodes );
18058
+
18059
+ if ( idx !== -1 ) {
18060
+ ret.push( {
18061
+ inst: inst,
18062
+ node: nodes[ idx ]
18063
+ } );
18064
+ }
18065
+ }
18066
+ };
18067
+
18068
+
18069
+ for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
18070
+ var inst = insts[i];
18071
+
18072
+ run( selector, inst );
18073
+ }
18074
+
18075
+ return ret;
18076
+ };
18077
+
18078
+
18079
+ /**
18080
+ * Buttons defaults. For full documentation, please refer to the docs/option
18081
+ * directory or the DataTables site.
18082
+ * @type {Object}
18083
+ * @static
18084
+ */
18085
+ Buttons.defaults = {
18086
+ buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ],
18087
+ name: 'main',
18088
+ tabIndex: 0,
18089
+ dom: {
18090
+ container: {
18091
+ tag: 'div',
18092
+ className: 'dt-buttons'
18093
+ },
18094
+ collection: {
18095
+ tag: 'div',
18096
+ className: ''
18097
+ },
18098
+ button: {
18099
+ // Flash buttons will not work with `<button>` in IE - it has to be `<a>`
18100
+ tag: 'ActiveXObject' in window ?
18101
+ 'a' :
18102
+ 'button',
18103
+ className: 'dt-button',
18104
+ active: 'active',
18105
+ disabled: 'disabled'
18106
+ },
18107
+ buttonLiner: {
18108
+ tag: 'span',
18109
+ className: ''
18110
+ }
18111
+ }
18112
+ };
18113
+
18114
+ /**
18115
+ * Version information
18116
+ * @type {string}
18117
+ * @static
18118
+ */
18119
+ Buttons.version = '1.6.5';
18120
+
18121
+
18122
+ $.extend( _dtButtons, {
18123
+ collection: {
18124
+ text: function ( dt ) {
18125
+ return dt.i18n( 'buttons.collection', 'Collection' );
18126
+ },
18127
+ className: 'buttons-collection',
18128
+ init: function ( dt, button, config ) {
18129
+ button.attr( 'aria-expanded', false );
18130
+ },
18131
+ action: function ( e, dt, button, config ) {
18132
+ e.stopPropagation();
18133
+
18134
+ if ( config._collection.parents('body').length ) {
18135
+ this.popover(false, config);
18136
+ }
18137
+ else {
18138
+ this.popover(config._collection, config);
18139
+ }
18140
+ },
18141
+ attr: {
18142
+ 'aria-haspopup': true
18143
+ }
18144
+ // Also the popover options, defined in Buttons.popover
18145
+ },
18146
+ copy: function ( dt, conf ) {
18147
+ if ( _dtButtons.copyHtml5 ) {
18148
+ return 'copyHtml5';
18149
+ }
18150
+ if ( _dtButtons.copyFlash && _dtButtons.copyFlash.available( dt, conf ) ) {
18151
+ return 'copyFlash';
18152
+ }
18153
+ },
18154
+ csv: function ( dt, conf ) {
18155
+ // Common option that will use the HTML5 or Flash export buttons
18156
+ if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) {
18157
+ return 'csvHtml5';
18158
+ }
18159
+ if ( _dtButtons.csvFlash && _dtButtons.csvFlash.available( dt, conf ) ) {
18160
+ return 'csvFlash';
18161
+ }
18162
+ },
18163
+ excel: function ( dt, conf ) {
18164
+ // Common option that will use the HTML5 or Flash export buttons
18165
+ if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) {
18166
+ return 'excelHtml5';
18167
+ }
18168
+ if ( _dtButtons.excelFlash && _dtButtons.excelFlash.available( dt, conf ) ) {
18169
+ return 'excelFlash';
18170
+ }
18171
+ },
18172
+ pdf: function ( dt, conf ) {
18173
+ // Common option that will use the HTML5 or Flash export buttons
18174
+ if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) {
18175
+ return 'pdfHtml5';
18176
+ }
18177
+ if ( _dtButtons.pdfFlash && _dtButtons.pdfFlash.available( dt, conf ) ) {
18178
+ return 'pdfFlash';
18179
+ }
18180
+ },
18181
+ pageLength: function ( dt ) {
18182
+ var lengthMenu = dt.settings()[0].aLengthMenu;
18183
+ var vals = Array.isArray( lengthMenu[0] ) ? lengthMenu[0] : lengthMenu;
18184
+ var lang = Array.isArray( lengthMenu[0] ) ? lengthMenu[1] : lengthMenu;
18185
+ var text = function ( dt ) {
18186
+ return dt.i18n( 'buttons.pageLength', {
18187
+ "-1": 'Show all rows',
18188
+ _: 'Show %d rows'
18189
+ }, dt.page.len() );
18190
+ };
18191
+
18192
+ return {
18193
+ extend: 'collection',
18194
+ text: text,
18195
+ className: 'buttons-page-length',
18196
+ autoClose: true,
18197
+ buttons: $.map( vals, function ( val, i ) {
18198
+ return {
18199
+ text: lang[i],
18200
+ className: 'button-page-length',
18201
+ action: function ( e, dt ) {
18202
+ dt.page.len( val ).draw();
18203
+ },
18204
+ init: function ( dt, node, conf ) {
18205
+ var that = this;
18206
+ var fn = function () {
18207
+ that.active( dt.page.len() === val );
18208
+ };
18209
+
18210
+ dt.on( 'length.dt'+conf.namespace, fn );
18211
+ fn();
18212
+ },
18213
+ destroy: function ( dt, node, conf ) {
18214
+ dt.off( 'length.dt'+conf.namespace );
18215
+ }
18216
+ };
18217
+ } ),
18218
+ init: function ( dt, node, conf ) {
18219
+ var that = this;
18220
+ dt.on( 'length.dt'+conf.namespace, function () {
18221
+ that.text( conf.text );
18222
+ } );
18223
+ },
18224
+ destroy: function ( dt, node, conf ) {
18225
+ dt.off( 'length.dt'+conf.namespace );
18226
+ }
18227
+ };
18228
+ }
18229
+ } );
18230
+
18231
+
18232
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18233
+ * DataTables API
18234
+ *
18235
+ * For complete documentation, please refer to the docs/api directory or the
18236
+ * DataTables site
18237
+ */
18238
+
18239
+ // Buttons group and individual button selector
18240
+ DataTable.Api.register( 'buttons()', function ( group, selector ) {
18241
+ // Argument shifting
18242
+ if ( selector === undefined ) {
18243
+ selector = group;
18244
+ group = undefined;
18245
+ }
18246
+
18247
+ this.selector.buttonGroup = group;
18248
+
18249
+ var res = this.iterator( true, 'table', function ( ctx ) {
18250
+ if ( ctx._buttons ) {
18251
+ return Buttons.buttonSelector(
18252
+ Buttons.instanceSelector( group, ctx._buttons ),
18253
+ selector
18254
+ );
18255
+ }
18256
+ }, true );
18257
+
18258
+ res._groupSelector = group;
18259
+ return res;
18260
+ } );
18261
+
18262
+ // Individual button selector
18263
+ DataTable.Api.register( 'button()', function ( group, selector ) {
18264
+ // just run buttons() and truncate
18265
+ var buttons = this.buttons( group, selector );
18266
+
18267
+ if ( buttons.length > 1 ) {
18268
+ buttons.splice( 1, buttons.length );
18269
+ }
18270
+
18271
+ return buttons;
18272
+ } );
18273
+
18274
+ // Active buttons
18275
+ DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) {
18276
+ if ( flag === undefined ) {
18277
+ return this.map( function ( set ) {
18278
+ return set.inst.active( set.node );
18279
+ } );
18280
+ }
18281
+
18282
+ return this.each( function ( set ) {
18283
+ set.inst.active( set.node, flag );
18284
+ } );
18285
+ } );
18286
+
18287
+ // Get / set button action
18288
+ DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) {
18289
+ if ( action === undefined ) {
18290
+ return this.map( function ( set ) {
18291
+ return set.inst.action( set.node );
18292
+ } );
18293
+ }
18294
+
18295
+ return this.each( function ( set ) {
18296
+ set.inst.action( set.node, action );
18297
+ } );
18298
+ } );
18299
+
18300
+ // Enable / disable buttons
18301
+ DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) {
18302
+ return this.each( function ( set ) {
18303
+ set.inst.enable( set.node, flag );
18304
+ } );
18305
+ } );
18306
+
18307
+ // Disable buttons
18308
+ DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () {
18309
+ return this.each( function ( set ) {
18310
+ set.inst.disable( set.node );
18311
+ } );
18312
+ } );
18313
+
18314
+ // Get button nodes
18315
+ DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () {
18316
+ var jq = $();
18317
+
18318
+ // jQuery will automatically reduce duplicates to a single entry
18319
+ $( this.each( function ( set ) {
18320
+ jq = jq.add( set.inst.node( set.node ) );
18321
+ } ) );
18322
+
18323
+ return jq;
18324
+ } );
18325
+
18326
+ // Get / set button processing state
18327
+ DataTable.Api.registerPlural( 'buttons().processing()', 'button().processing()', function ( flag ) {
18328
+ if ( flag === undefined ) {
18329
+ return this.map( function ( set ) {
18330
+ return set.inst.processing( set.node );
18331
+ } );
18332
+ }
18333
+
18334
+ return this.each( function ( set ) {
18335
+ set.inst.processing( set.node, flag );
18336
+ } );
18337
+ } );
18338
+
18339
+ // Get / set button text (i.e. the button labels)
18340
+ DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) {
18341
+ if ( label === undefined ) {
18342
+ return this.map( function ( set ) {
18343
+ return set.inst.text( set.node );
18344
+ } );
18345
+ }
18346
+
18347
+ return this.each( function ( set ) {
18348
+ set.inst.text( set.node, label );
18349
+ } );
18350
+ } );
18351
+
18352
+ // Trigger a button's action
18353
+ DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () {
18354
+ return this.each( function ( set ) {
18355
+ set.inst.node( set.node ).trigger( 'click' );
18356
+ } );
18357
+ } );
18358
+
18359
+ // Button resolver to the popover
18360
+ DataTable.Api.register( 'button().popover()', function (content, options) {
18361
+ return this.map( function ( set ) {
18362
+ return set.inst._popover( content, this.button(this[0].node), options );
18363
+ } );
18364
+ } );
18365
+
18366
+ // Get the container elements
18367
+ DataTable.Api.register( 'buttons().containers()', function () {
18368
+ var jq = $();
18369
+ var groupSelector = this._groupSelector;
18370
+
18371
+ // We need to use the group selector directly, since if there are no buttons
18372
+ // the result set will be empty
18373
+ this.iterator( true, 'table', function ( ctx ) {
18374
+ if ( ctx._buttons ) {
18375
+ var insts = Buttons.instanceSelector( groupSelector, ctx._buttons );
18376
+
18377
+ for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
18378
+ jq = jq.add( insts[i].container() );
18379
+ }
18380
+ }
18381
+ } );
18382
+
18383
+ return jq;
18384
+ } );
18385
+
18386
+ DataTable.Api.register( 'buttons().container()', function () {
18387
+ // API level of nesting is `buttons()` so we can zip into the containers method
18388
+ return this.containers().eq(0);
18389
+ } );
18390
+
18391
+ // Add a new button
18392
+ DataTable.Api.register( 'button().add()', function ( idx, conf ) {
18393
+ var ctx = this.context;
18394
+
18395
+ // Don't use `this` as it could be empty - select the instances directly
18396
+ if ( ctx.length ) {
18397
+ var inst = Buttons.instanceSelector( this._groupSelector, ctx[0]._buttons );
18398
+
18399
+ if ( inst.length ) {
18400
+ inst[0].add( conf, idx );
18401
+ }
18402
+ }
18403
+
18404
+ return this.button( this._groupSelector, idx );
18405
+ } );
18406
+
18407
+ // Destroy the button sets selected
18408
+ DataTable.Api.register( 'buttons().destroy()', function () {
18409
+ this.pluck( 'inst' ).unique().each( function ( inst ) {
18410
+ inst.destroy();
18411
+ } );
18412
+
18413
+ return this;
18414
+ } );
18415
+
18416
+ // Remove a button
18417
+ DataTable.Api.registerPlural( 'buttons().remove()', 'buttons().remove()', function () {
18418
+ this.each( function ( set ) {
18419
+ set.inst.remove( set.node );
18420
+ } );
18421
+
18422
+ return this;
18423
+ } );
18424
+
18425
+ // Information box that can be used by buttons
18426
+ var _infoTimer;
18427
+ DataTable.Api.register( 'buttons.info()', function ( title, message, time ) {
18428
+ var that = this;
18429
+
18430
+ if ( title === false ) {
18431
+ this.off('destroy.btn-info');
18432
+ _fadeOut(
18433
+ $('#datatables_buttons_info'),
18434
+ 400,
18435
+ function () {
18436
+ $(this).remove();
18437
+ }
18438
+ );
18439
+ clearTimeout( _infoTimer );
18440
+ _infoTimer = null;
18441
+
18442
+ return this;
18443
+ }
18444
+
18445
+ if ( _infoTimer ) {
18446
+ clearTimeout( _infoTimer );
18447
+ }
18448
+
18449
+ if ( $('#datatables_buttons_info').length ) {
18450
+ $('#datatables_buttons_info').remove();
18451
+ }
18452
+
18453
+ title = title ? '<h2>'+title+'</h2>' : '';
18454
+
18455
+ _fadeIn(
18456
+ $('<div id="datatables_buttons_info" class="dt-button-info"/>')
18457
+ .html( title )
18458
+ .append( $('<div/>')[ typeof message === 'string' ? 'html' : 'append' ]( message ) )
18459
+ .css( 'display', 'none' )
18460
+ .appendTo( 'body' )
18461
+ );
18462
+
18463
+ if ( time !== undefined && time !== 0 ) {
18464
+ _infoTimer = setTimeout( function () {
18465
+ that.buttons.info( false );
18466
+ }, time );
18467
+ }
18468
+
18469
+ this.on('destroy.btn-info', function () {
18470
+ that.buttons.info(false);
18471
+ });
18472
+
18473
+ return this;
18474
+ } );
18475
+
18476
+ // Get data from the table for export - this is common to a number of plug-in
18477
+ // buttons so it is included in the Buttons core library
18478
+ DataTable.Api.register( 'buttons.exportData()', function ( options ) {
18479
+ if ( this.context.length ) {
18480
+ return _exportData( new DataTable.Api( this.context[0] ), options );
18481
+ }
18482
+ } );
18483
+
18484
+ // Get information about the export that is common to many of the export data
18485
+ // types (DRY)
18486
+ DataTable.Api.register( 'buttons.exportInfo()', function ( conf ) {
18487
+ if ( ! conf ) {
18488
+ conf = {};
18489
+ }
18490
+
18491
+ return {
18492
+ filename: _filename( conf ),
18493
+ title: _title( conf ),
18494
+ messageTop: _message(this, conf.message || conf.messageTop, 'top'),
18495
+ messageBottom: _message(this, conf.messageBottom, 'bottom')
18496
+ };
18497
+ } );
18498
+
18499
+
18500
+
18501
+ /**
18502
+ * Get the file name for an exported file.
18503
+ *
18504
+ * @param {object} config Button configuration
18505
+ * @param {boolean} incExtension Include the file name extension
18506
+ */
18507
+ var _filename = function ( config )
18508
+ {
18509
+ // Backwards compatibility
18510
+ var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined && config.title !== null && config.title !== '' ?
18511
+ config.title :
18512
+ config.filename;
18513
+
18514
+ if ( typeof filename === 'function' ) {
18515
+ filename = filename();
18516
+ }
18517
+
18518
+ if ( filename === undefined || filename === null ) {
18519
+ return null;
18520
+ }
18521
+
18522
+ if ( filename.indexOf( '*' ) !== -1 ) {
18523
+ filename = filename.replace( '*', $('head > title').text() ).trim();
18524
+ }
18525
+
18526
+ // Strip characters which the OS will object to
18527
+ filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
18528
+
18529
+ var extension = _stringOrFunction( config.extension );
18530
+ if ( ! extension ) {
18531
+ extension = '';
18532
+ }
18533
+
18534
+ return filename + extension;
18535
+ };
18536
+
18537
+ /**
18538
+ * Simply utility method to allow parameters to be given as a function
18539
+ *
18540
+ * @param {undefined|string|function} option Option
18541
+ * @return {null|string} Resolved value
18542
+ */
18543
+ var _stringOrFunction = function ( option )
18544
+ {
18545
+ if ( option === null || option === undefined ) {
18546
+ return null;
18547
+ }
18548
+ else if ( typeof option === 'function' ) {
18549
+ return option();
18550
+ }
18551
+ return option;
18552
+ };
18553
+
18554
+ /**
18555
+ * Get the title for an exported file.
18556
+ *
18557
+ * @param {object} config Button configuration
18558
+ */
18559
+ var _title = function ( config )
18560
+ {
18561
+ var title = _stringOrFunction( config.title );
18562
+
18563
+ return title === null ?
18564
+ null : title.indexOf( '*' ) !== -1 ?
18565
+ title.replace( '*', $('head > title').text() || 'Exported data' ) :
18566
+ title;
18567
+ };
18568
+
18569
+ var _message = function ( dt, option, position )
18570
+ {
18571
+ var message = _stringOrFunction( option );
18572
+ if ( message === null ) {
18573
+ return null;
18574
+ }
18575
+
18576
+ var caption = $('caption', dt.table().container()).eq(0);
18577
+ if ( message === '*' ) {
18578
+ var side = caption.css( 'caption-side' );
18579
+ if ( side !== position ) {
18580
+ return null;
18581
+ }
18582
+
18583
+ return caption.length ?
18584
+ caption.text() :
18585
+ '';
18586
+ }
18587
+
18588
+ return message;
18589
+ };
18590
+
18591
+
18592
+
18593
+
18594
+
18595
+
18596
+
18597
+ var _exportTextarea = $('<textarea/>')[0];
18598
+ var _exportData = function ( dt, inOpts )
18599
+ {
18600
+ var config = $.extend( true, {}, {
18601
+ rows: null,
18602
+ columns: '',
18603
+ modifier: {
18604
+ search: 'applied',
18605
+ order: 'applied'
18606
+ },
18607
+ orthogonal: 'display',
18608
+ stripHtml: true,
18609
+ stripNewlines: true,
18610
+ decodeEntities: true,
18611
+ trim: true,
18612
+ format: {
18613
+ header: function ( d ) {
18614
+ return strip( d );
18615
+ },
18616
+ footer: function ( d ) {
18617
+ return strip( d );
18618
+ },
18619
+ body: function ( d ) {
18620
+ return strip( d );
18621
+ }
18622
+ },
18623
+ customizeData: null
18624
+ }, inOpts );
18625
+
18626
+ var strip = function ( str ) {
18627
+ if ( typeof str !== 'string' ) {
18628
+ return str;
18629
+ }
18630
+
18631
+ // Always remove script tags
18632
+ str = str.replace( /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '' );
18633
+
18634
+ // Always remove comments
18635
+ str = str.replace( /<!\-\-.*?\-\->/g, '' );
18636
+
18637
+ if ( config.stripHtml ) {
18638
+ str = str.replace( /<([^>'"]*('[^']*'|"[^"]*")?)*>/g, '' );
18639
+ }
18640
+
18641
+ if ( config.trim ) {
18642
+ str = str.replace( /^\s+|\s+$/g, '' );
18643
+ }
18644
+
18645
+ if ( config.stripNewlines ) {
18646
+ str = str.replace( /\n/g, ' ' );
18647
+ }
18648
+
18649
+ if ( config.decodeEntities ) {
18650
+ _exportTextarea.innerHTML = str;
18651
+ str = _exportTextarea.value;
18652
+ }
18653
+
18654
+ return str;
18655
+ };
18656
+
18657
+
18658
+ var header = dt.columns( config.columns ).indexes().map( function (idx) {
18659
+ var el = dt.column( idx ).header();
18660
+ return config.format.header( el.innerHTML, idx, el );
18661
+ } ).toArray();
18662
+
18663
+ var footer = dt.table().footer() ?
18664
+ dt.columns( config.columns ).indexes().map( function (idx) {
18665
+ var el = dt.column( idx ).footer();
18666
+ return config.format.footer( el ? el.innerHTML : '', idx, el );
18667
+ } ).toArray() :
18668
+ null;
18669
+
18670
+ // If Select is available on this table, and any rows are selected, limit the export
18671
+ // to the selected rows. If no rows are selected, all rows will be exported. Specify
18672
+ // a `selected` modifier to control directly.
18673
+ var modifier = $.extend( {}, config.modifier );
18674
+ if ( dt.select && typeof dt.select.info === 'function' && modifier.selected === undefined ) {
18675
+ if ( dt.rows( config.rows, $.extend( { selected: true }, modifier ) ).any() ) {
18676
+ $.extend( modifier, { selected: true } )
18677
+ }
18678
+ }
18679
+
18680
+ var rowIndexes = dt.rows( config.rows, modifier ).indexes().toArray();
18681
+ var selectedCells = dt.cells( rowIndexes, config.columns );
18682
+ var cells = selectedCells
18683
+ .render( config.orthogonal )
18684
+ .toArray();
18685
+ var cellNodes = selectedCells
18686
+ .nodes()
18687
+ .toArray();
18688
+
18689
+ var columns = header.length;
18690
+ var rows = columns > 0 ? cells.length / columns : 0;
18691
+ var body = [];
18692
+ var cellCounter = 0;
18693
+
18694
+ for ( var i=0, ien=rows ; i<ien ; i++ ) {
18695
+ var row = [ columns ];
18696
+
18697
+ for ( var j=0 ; j<columns ; j++ ) {
18698
+ row[j] = config.format.body( cells[ cellCounter ], i, j, cellNodes[ cellCounter ] );
18699
+ cellCounter++;
18700
+ }
18701
+
18702
+ body[i] = row;
18703
+ }
18704
+
18705
+ var data = {
18706
+ header: header,
18707
+ footer: footer,
18708
+ body: body
18709
+ };
18710
+
18711
+ if ( config.customizeData ) {
18712
+ config.customizeData( data );
18713
+ }
18714
+
18715
+ return data;
18716
+ };
18717
+
18718
+
18719
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18720
+ * DataTables interface
18721
+ */
18722
+
18723
+ // Attach to DataTables objects for global access
18724
+ $.fn.dataTable.Buttons = Buttons;
18725
+ $.fn.DataTable.Buttons = Buttons;
18726
+
18727
+
18728
+
18729
+ // DataTables creation - check if the buttons have been defined for this table,
18730
+ // they will have been if the `B` option was used in `dom`, otherwise we should
18731
+ // create the buttons instance here so they can be inserted into the document
18732
+ // using the API. Listen for `init` for compatibility with pre 1.10.10, but to
18733
+ // be removed in future.
18734
+ $(document).on( 'init.dt plugin-init.dt', function (e, settings) {
18735
+ if ( e.namespace !== 'dt' ) {
18736
+ return;
18737
+ }
18738
+
18739
+ var opts = settings.oInit.buttons || DataTable.defaults.buttons;
18740
+
18741
+ if ( opts && ! settings._buttons ) {
18742
+ new Buttons( settings, opts ).container();
18743
+ }
18744
+ } );
18745
+
18746
+ function _init ( settings, options ) {
18747
+ var api = new DataTable.Api( settings );
18748
+ var opts = options
18749
+ ? options
18750
+ : api.init().buttons || DataTable.defaults.buttons;
18751
+
18752
+ return new Buttons( api, opts ).container();
18753
+ }
18754
+
18755
+ // DataTables `dom` feature option
18756
+ DataTable.ext.feature.push( {
18757
+ fnInit: _init,
18758
+ cFeature: "B"
18759
+ } );
18760
+
18761
+ // DataTables 2 layout feature
18762
+ if ( DataTable.ext.features ) {
18763
+ DataTable.ext.features.register( 'buttons', _init );
18764
+ }
18765
+
18766
+
18767
+ return Buttons;
18768
+ }));
18769
+
18770
+
18771
+ /*! ColReorder 1.5.3
18772
+ * ©2010-2020 SpryMedia Ltd - datatables.net/license
18773
+ */
18774
+
18775
+ /**
18776
+ * @summary ColReorder
18777
+ * @description Provide the ability to reorder columns in a DataTable
18778
+ * @version 1.5.3
18779
+ * @file dataTables.colReorder.js
18780
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
18781
+ * @contact www.sprymedia.co.uk/contact
18782
+ * @copyright Copyright 2010-2020 SpryMedia Ltd.
18783
+ *
18784
+ * This source file is free software, available under the following license:
18785
+ * MIT license - http://datatables.net/license/mit
18786
+ *
18787
+ * This source file is distributed in the hope that it will be useful, but
18788
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18789
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
18790
+ *
18791
+ * For details please refer to: http://www.datatables.net
18792
+ */
18793
+ (function( factory ){
18794
+ if ( typeof define === 'function' && define.amd ) {
18795
+ // AMD
18796
+ define( ['jquery', 'datatables.net'], function ( $ ) {
18797
+ return factory( $, window, document );
18798
+ } );
18799
+ }
18800
+ else if ( typeof exports === 'object' ) {
18801
+ // CommonJS
18802
+ module.exports = function (root, $) {
18803
+ if ( ! root ) {
18804
+ root = window;
18805
+ }
18806
+
18807
+ if ( ! $ || ! $.fn.dataTable ) {
18808
+ $ = require('datatables.net')(root, $).$;
18809
+ }
18810
+
18811
+ return factory( $, root, root.document );
18812
+ };
18813
+ }
18814
+ else {
18815
+ // Browser
18816
+ factory( jQuery, window, document );
18817
+ }
18818
+ }(function( $, window, document, undefined ) {
18819
+ 'use strict';
18820
+ var DataTable = $.fn.dataTable;
18821
+
18822
+
18823
+ /**
18824
+ * Switch the key value pairing of an index array to be value key (i.e. the old value is now the
18825
+ * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ].
18826
+ * @method fnInvertKeyValues
18827
+ * @param array aIn Array to switch around
18828
+ * @returns array
18829
+ */
18830
+ function fnInvertKeyValues( aIn )
18831
+ {
18832
+ var aRet=[];
18833
+ for ( var i=0, iLen=aIn.length ; i<iLen ; i++ )
18834
+ {
18835
+ aRet[ aIn[i] ] = i;
18836
+ }
18837
+ return aRet;
18838
+ }
18839
+
18840
+
18841
+ /**
18842
+ * Modify an array by switching the position of two elements
18843
+ * @method fnArraySwitch
18844
+ * @param array aArray Array to consider, will be modified by reference (i.e. no return)
18845
+ * @param int iFrom From point
18846
+ * @param int iTo Insert point
18847
+ * @returns void
18848
+ */
18849
+ function fnArraySwitch( aArray, iFrom, iTo )
18850
+ {
18851
+ var mStore = aArray.splice( iFrom, 1 )[0];
18852
+ aArray.splice( iTo, 0, mStore );
18853
+ }
18854
+
18855
+
18856
+ /**
18857
+ * Switch the positions of nodes in a parent node (note this is specifically designed for
18858
+ * table rows). Note this function considers all element nodes under the parent!
18859
+ * @method fnDomSwitch
18860
+ * @param string sTag Tag to consider
18861
+ * @param int iFrom Element to move
18862
+ * @param int Point to element the element to (before this point), can be null for append
18863
+ * @returns void
18864
+ */
18865
+ function fnDomSwitch( nParent, iFrom, iTo )
18866
+ {
18867
+ var anTags = [];
18868
+ for ( var i=0, iLen=nParent.childNodes.length ; i<iLen ; i++ )
18869
+ {
18870
+ if ( nParent.childNodes[i].nodeType == 1 )
18871
+ {
18872
+ anTags.push( nParent.childNodes[i] );
18873
+ }
18874
+ }
18875
+ var nStore = anTags[ iFrom ];
18876
+
18877
+ if ( iTo !== null )
18878
+ {
18879
+ nParent.insertBefore( nStore, anTags[iTo] );
18880
+ }
18881
+ else
18882
+ {
18883
+ nParent.appendChild( nStore );
18884
+ }
18885
+ }
18886
+
18887
+
18888
+ /**
18889
+ * Plug-in for DataTables which will reorder the internal column structure by taking the column
18890
+ * from one position (iFrom) and insert it into a given point (iTo).
18891
+ * @method $.fn.dataTableExt.oApi.fnColReorder
18892
+ * @param object oSettings DataTables settings object - automatically added by DataTables!
18893
+ * @param int iFrom Take the column to be repositioned from this point
18894
+ * @param int iTo and insert it into this point
18895
+ * @param bool drop Indicate if the reorder is the final one (i.e. a drop)
18896
+ * not a live reorder
18897
+ * @param bool invalidateRows speeds up processing if false passed
18898
+ * @returns void
18899
+ */
18900
+ $.fn.dataTableExt.oApi.fnColReorder = function ( oSettings, iFrom, iTo, drop, invalidateRows )
18901
+ {
18902
+ var i, iLen, j, jLen, jen, iCols=oSettings.aoColumns.length, nTrs, oCol;
18903
+ var attrMap = function ( obj, prop, mapping ) {
18904
+ if ( ! obj[ prop ] || typeof obj[ prop ] === 'function' ) {
18905
+ return;
18906
+ }
18907
+
18908
+ var a = obj[ prop ].split('.');
18909
+ var num = a.shift();
18910
+
18911
+ if ( isNaN( num*1 ) ) {
18912
+ return;
18913
+ }
18914
+
18915
+ obj[ prop ] = mapping[ num*1 ]+'.'+a.join('.');
18916
+ };
18917
+
18918
+ /* Sanity check in the input */
18919
+ if ( iFrom == iTo )
18920
+ {
18921
+ /* Pointless reorder */
18922
+ return;
18923
+ }
18924
+
18925
+ if ( iFrom < 0 || iFrom >= iCols )
18926
+ {
18927
+ this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom );
18928
+ return;
18929
+ }
18930
+
18931
+ if ( iTo < 0 || iTo >= iCols )
18932
+ {
18933
+ this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo );
18934
+ return;
18935
+ }
18936
+
18937
+ /*
18938
+ * Calculate the new column array index, so we have a mapping between the old and new
18939
+ */
18940
+ var aiMapping = [];
18941
+ for ( i=0, iLen=iCols ; i<iLen ; i++ )
18942
+ {
18943
+ aiMapping[i] = i;
18944
+ }
18945
+ fnArraySwitch( aiMapping, iFrom, iTo );
18946
+ var aiInvertMapping = fnInvertKeyValues( aiMapping );
18947
+
18948
+
18949
+ /*
18950
+ * Convert all internal indexing to the new column order indexes
18951
+ */
18952
+ /* Sorting */
18953
+ for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
18954
+ {
18955
+ oSettings.aaSorting[i][0] = aiInvertMapping[ oSettings.aaSorting[i][0] ];
18956
+ }
18957
+
18958
+ /* Fixed sorting */
18959
+ if ( oSettings.aaSortingFixed !== null )
18960
+ {
18961
+ for ( i=0, iLen=oSettings.aaSortingFixed.length ; i<iLen ; i++ )
18962
+ {
18963
+ oSettings.aaSortingFixed[i][0] = aiInvertMapping[ oSettings.aaSortingFixed[i][0] ];
18964
+ }
18965
+ }
18966
+
18967
+ /* Data column sorting (the column which the sort for a given column should take place on) */
18968
+ for ( i=0, iLen=iCols ; i<iLen ; i++ )
18969
+ {
18970
+ oCol = oSettings.aoColumns[i];
18971
+ for ( j=0, jLen=oCol.aDataSort.length ; j<jLen ; j++ )
18972
+ {
18973
+ oCol.aDataSort[j] = aiInvertMapping[ oCol.aDataSort[j] ];
18974
+ }
18975
+
18976
+ // Update the column indexes
18977
+ oCol.idx = aiInvertMapping[ oCol.idx ];
18978
+ }
18979
+
18980
+ // Update 1.10 optimised sort class removal variable
18981
+ $.each( oSettings.aLastSort, function (i, val) {
18982
+ oSettings.aLastSort[i].src = aiInvertMapping[ val.src ];
18983
+ } );
18984
+
18985
+ /* Update the Get and Set functions for each column */
18986
+ for ( i=0, iLen=iCols ; i<iLen ; i++ )
18987
+ {
18988
+ oCol = oSettings.aoColumns[i];
18989
+
18990
+ if ( typeof oCol.mData == 'number' ) {
18991
+ oCol.mData = aiInvertMapping[ oCol.mData ];
18992
+ }
18993
+ else if ( $.isPlainObject( oCol.mData ) ) {
18994
+ // HTML5 data sourced
18995
+ attrMap( oCol.mData, '_', aiInvertMapping );
18996
+ attrMap( oCol.mData, 'filter', aiInvertMapping );
18997
+ attrMap( oCol.mData, 'sort', aiInvertMapping );
18998
+ attrMap( oCol.mData, 'type', aiInvertMapping );
18999
+ }
19000
+ }
19001
+
19002
+ /*
19003
+ * Move the DOM elements
19004
+ */
19005
+ if ( oSettings.aoColumns[iFrom].bVisible )
19006
+ {
19007
+ /* Calculate the current visible index and the point to insert the node before. The insert
19008
+ * before needs to take into account that there might not be an element to insert before,
19009
+ * in which case it will be null, and an appendChild should be used
19010
+ */
19011
+ var iVisibleIndex = this.oApi._fnColumnIndexToVisible( oSettings, iFrom );
19012
+ var iInsertBeforeIndex = null;
19013
+
19014
+ i = iTo < iFrom ? iTo : iTo + 1;
19015
+ while ( iInsertBeforeIndex === null && i < iCols )
19016
+ {
19017
+ iInsertBeforeIndex = this.oApi._fnColumnIndexToVisible( oSettings, i );
19018
+ i++;
19019
+ }
19020
+
19021
+ /* Header */
19022
+ nTrs = oSettings.nTHead.getElementsByTagName('tr');
19023
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
19024
+ {
19025
+ fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );
19026
+ }
19027
+
19028
+ /* Footer */
19029
+ if ( oSettings.nTFoot !== null )
19030
+ {
19031
+ nTrs = oSettings.nTFoot.getElementsByTagName('tr');
19032
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
19033
+ {
19034
+ fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );
19035
+ }
19036
+ }
19037
+
19038
+ /* Body */
19039
+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
19040
+ {
19041
+ if ( oSettings.aoData[i].nTr !== null )
19042
+ {
19043
+ fnDomSwitch( oSettings.aoData[i].nTr, iVisibleIndex, iInsertBeforeIndex );
19044
+ }
19045
+ }
19046
+ }
19047
+
19048
+ /*
19049
+ * Move the internal array elements
19050
+ */
19051
+ /* Columns */
19052
+ fnArraySwitch( oSettings.aoColumns, iFrom, iTo );
19053
+
19054
+ // regenerate the get / set functions
19055
+ for ( i=0, iLen=iCols ; i<iLen ; i++ ) {
19056
+ oSettings.oApi._fnColumnOptions( oSettings, i, {} );
19057
+ }
19058
+
19059
+ /* Search columns */
19060
+ fnArraySwitch( oSettings.aoPreSearchCols, iFrom, iTo );
19061
+
19062
+ /* Array array - internal data anodes cache */
19063
+ for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
19064
+ {
19065
+ var data = oSettings.aoData[i];
19066
+ var cells = data.anCells;
19067
+
19068
+ if ( cells ) {
19069
+ fnArraySwitch( cells, iFrom, iTo );
19070
+
19071
+ // Longer term, should this be moved into the DataTables' invalidate
19072
+ // methods?
19073
+ for ( j=0, jen=cells.length ; j<jen ; j++ ) {
19074
+ if ( cells[j] && cells[j]._DT_CellIndex ) {
19075
+ cells[j]._DT_CellIndex.column = j;
19076
+ }
19077
+ }
19078
+ }
19079
+
19080
+ // For DOM sourced data, the invalidate will reread the cell into
19081
+ // the data array, but for data sources as an array, they need to
19082
+ // be flipped
19083
+ if ( data.src !== 'dom' && Array.isArray( data._aData ) ) {
19084
+ fnArraySwitch( data._aData, iFrom, iTo );
19085
+ }
19086
+ }
19087
+
19088
+ /* Reposition the header elements in the header layout array */
19089
+ for ( i=0, iLen=oSettings.aoHeader.length ; i<iLen ; i++ )
19090
+ {
19091
+ fnArraySwitch( oSettings.aoHeader[i], iFrom, iTo );
19092
+ }
19093
+
19094
+ if ( oSettings.aoFooter !== null )
19095
+ {
19096
+ for ( i=0, iLen=oSettings.aoFooter.length ; i<iLen ; i++ )
19097
+ {
19098
+ fnArraySwitch( oSettings.aoFooter[i], iFrom, iTo );
19099
+ }
19100
+ }
19101
+
19102
+ if ( invalidateRows || invalidateRows === undefined )
19103
+ {
19104
+ $.fn.dataTable.Api( oSettings ).rows().invalidate();
19105
+ }
19106
+
19107
+ /*
19108
+ * Update DataTables' event handlers
19109
+ */
19110
+
19111
+ /* Sort listener */
19112
+ for ( i=0, iLen=iCols ; i<iLen ; i++ )
19113
+ {
19114
+ $(oSettings.aoColumns[i].nTh).off('.DT');
19115
+ this.oApi._fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
19116
+ }
19117
+
19118
+
19119
+ /* Fire an event so other plug-ins can update */
19120
+ $(oSettings.oInstance).trigger( 'column-reorder.dt', [ oSettings, {
19121
+ from: iFrom,
19122
+ to: iTo,
19123
+ mapping: aiInvertMapping,
19124
+ drop: drop,
19125
+
19126
+ // Old style parameters for compatibility
19127
+ iFrom: iFrom,
19128
+ iTo: iTo,
19129
+ aiInvertMapping: aiInvertMapping
19130
+ } ] );
19131
+ };
19132
+
19133
+ /**
19134
+ * ColReorder provides column visibility control for DataTables
19135
+ * @class ColReorder
19136
+ * @constructor
19137
+ * @param {object} dt DataTables settings object
19138
+ * @param {object} opts ColReorder options
19139
+ */
19140
+ var ColReorder = function( dt, opts )
19141
+ {
19142
+ var settings = new $.fn.dataTable.Api( dt ).settings()[0];
19143
+
19144
+ // Ensure that we can't initialise on the same table twice
19145
+ if ( settings._colReorder ) {
19146
+ return settings._colReorder;
19147
+ }
19148
+
19149
+ // Allow the options to be a boolean for defaults
19150
+ if ( opts === true ) {
19151
+ opts = {};
19152
+ }
19153
+
19154
+ // Convert from camelCase to Hungarian, just as DataTables does
19155
+ var camelToHungarian = $.fn.dataTable.camelToHungarian;
19156
+ if ( camelToHungarian ) {
19157
+ camelToHungarian( ColReorder.defaults, ColReorder.defaults, true );
19158
+ camelToHungarian( ColReorder.defaults, opts || {} );
19159
+ }
19160
+
19161
+
19162
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19163
+ * Public class variables
19164
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
19165
+
19166
+ /**
19167
+ * @namespace Settings object which contains customisable information for ColReorder instance
19168
+ */
19169
+ this.s = {
19170
+ /**
19171
+ * DataTables settings object
19172
+ * @property dt
19173
+ * @type Object
19174
+ * @default null
19175
+ */
19176
+ "dt": null,
19177
+
19178
+ /**
19179
+ * Enable flag
19180
+ * @property dt
19181
+ * @type Object
19182
+ * @default null
19183
+ */
19184
+ "enable": null,
19185
+
19186
+ /**
19187
+ * Initialisation object used for this instance
19188
+ * @property init
19189
+ * @type object
19190
+ * @default {}
19191
+ */
19192
+ "init": $.extend( true, {}, ColReorder.defaults, opts ),
19193
+
19194
+ /**
19195
+ * Number of columns to fix (not allow to be reordered)
19196
+ * @property fixed
19197
+ * @type int
19198
+ * @default 0
19199
+ */
19200
+ "fixed": 0,
19201
+
19202
+ /**
19203
+ * Number of columns to fix counting from right (not allow to be reordered)
19204
+ * @property fixedRight
19205
+ * @type int
19206
+ * @default 0
19207
+ */
19208
+ "fixedRight": 0,
19209
+
19210
+ /**
19211
+ * Callback function for once the reorder has been done
19212
+ * @property reorderCallback
19213
+ * @type function
19214
+ * @default null
19215
+ */
19216
+ "reorderCallback": null,
19217
+
19218
+ /**
19219
+ * @namespace Information used for the mouse drag
19220
+ */
19221
+ "mouse": {
19222
+ "startX": -1,
19223
+ "startY": -1,
19224
+ "offsetX": -1,
19225
+ "offsetY": -1,
19226
+ "target": -1,
19227
+ "targetIndex": -1,
19228
+ "fromIndex": -1
19229
+ },
19230
+
19231
+ /**
19232
+ * Information which is used for positioning the insert cusor and knowing where to do the
19233
+ * insert. Array of objects with the properties:
19234
+ * x: x-axis position
19235
+ * to: insert point
19236
+ * @property aoTargets
19237
+ * @type array
19238
+ * @default []
19239
+ */
19240
+ "aoTargets": []
19241
+ };
19242
+
19243
+
19244
+ /**
19245
+ * @namespace Common and useful DOM elements for the class instance
19246
+ */
19247
+ this.dom = {
19248
+ /**
19249
+ * Dragging element (the one the mouse is moving)
19250
+ * @property drag
19251
+ * @type element
19252
+ * @default null
19253
+ */
19254
+ "drag": null,
19255
+
19256
+ /**
19257
+ * The insert cursor
19258
+ * @property pointer
19259
+ * @type element
19260
+ * @default null
19261
+ */
19262
+ "pointer": null
19263
+ };
19264
+
19265
+ /* Constructor logic */
19266
+ this.s.enable = this.s.init.bEnable;
19267
+ this.s.dt = settings;
19268
+ this.s.dt._colReorder = this;
19269
+ this._fnConstruct();
19270
+
19271
+ return this;
19272
+ };
19273
+
19274
+
19275
+
19276
+ $.extend( ColReorder.prototype, {
19277
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19278
+ * Public methods
19279
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
19280
+
19281
+ /**
19282
+ * Enable / disable end user interaction
19283
+ */
19284
+ fnEnable: function ( flag )
19285
+ {
19286
+ if ( flag === false ) {
19287
+ return fnDisable();
19288
+ }
19289
+
19290
+ this.s.enable = true;
19291
+ },
19292
+
19293
+ /**
19294
+ * Disable end user interaction
19295
+ */
19296
+ fnDisable: function ()
19297
+ {
19298
+ this.s.enable = false;
19299
+ },
19300
+
19301
+ /**
19302
+ * Reset the column ordering to the original ordering that was detected on
19303
+ * start up.
19304
+ * @return {this} Returns `this` for chaining.
19305
+ *
19306
+ * @example
19307
+ * // DataTables initialisation with ColReorder
19308
+ * var table = $('#example').dataTable( {
19309
+ * "sDom": 'Rlfrtip'
19310
+ * } );
19311
+ *
19312
+ * // Add click event to a button to reset the ordering
19313
+ * $('#resetOrdering').click( function (e) {
19314
+ * e.preventDefault();
19315
+ * $.fn.dataTable.ColReorder( table ).fnReset();
19316
+ * } );
19317
+ */
19318
+ "fnReset": function ()
19319
+ {
19320
+ this._fnOrderColumns( this.fnOrder() );
19321
+
19322
+ return this;
19323
+ },
19324
+
19325
+ /**
19326
+ * `Deprecated` - Get the current order of the columns, as an array.
19327
+ * @return {array} Array of column identifiers
19328
+ * @deprecated `fnOrder` should be used in preference to this method.
19329
+ * `fnOrder` acts as a getter/setter.
19330
+ */
19331
+ "fnGetCurrentOrder": function ()
19332
+ {
19333
+ return this.fnOrder();
19334
+ },
19335
+
19336
+ /**
19337
+ * Get the current order of the columns, as an array. Note that the values
19338
+ * given in the array are unique identifiers for each column. Currently
19339
+ * these are the original ordering of the columns that was detected on
19340
+ * start up, but this could potentially change in future.
19341
+ * @return {array} Array of column identifiers
19342
+ *
19343
+ * @example
19344
+ * // Get column ordering for the table
19345
+ * var order = $.fn.dataTable.ColReorder( dataTable ).fnOrder();
19346
+ *//**
19347
+ * Set the order of the columns, from the positions identified in the
19348
+ * ordering array given. Note that ColReorder takes a brute force approach
19349
+ * to reordering, so it is possible multiple reordering events will occur
19350
+ * before the final order is settled upon.
19351
+ * @param {array} [set] Array of column identifiers in the new order. Note
19352
+ * that every column must be included, uniquely, in this array.
19353
+ * @return {this} Returns `this` for chaining.
19354
+ *
19355
+ * @example
19356
+ * // Swap the first and second columns
19357
+ * $.fn.dataTable.ColReorder( dataTable ).fnOrder( [1, 0, 2, 3, 4] );
19358
+ *
19359
+ * @example
19360
+ * // Move the first column to the end for the table `#example`
19361
+ * var curr = $.fn.dataTable.ColReorder( '#example' ).fnOrder();
19362
+ * var first = curr.shift();
19363
+ * curr.push( first );
19364
+ * $.fn.dataTable.ColReorder( '#example' ).fnOrder( curr );
19365
+ *
19366
+ * @example
19367
+ * // Reverse the table's order
19368
+ * $.fn.dataTable.ColReorder( '#example' ).fnOrder(
19369
+ * $.fn.dataTable.ColReorder( '#example' ).fnOrder().reverse()
19370
+ * );
19371
+ */
19372
+ "fnOrder": function ( set, original )
19373
+ {
19374
+ var a = [], i, ien, j, jen;
19375
+ var columns = this.s.dt.aoColumns;
19376
+
19377
+ if ( set === undefined ){
19378
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
19379
+ a.push( columns[i]._ColReorder_iOrigCol );
19380
+ }
19381
+
19382
+ return a;
19383
+ }
19384
+
19385
+ // The order given is based on the original indexes, rather than the
19386
+ // existing ones, so we need to translate from the original to current
19387
+ // before then doing the order
19388
+ if ( original ) {
19389
+ var order = this.fnOrder();
19390
+
19391
+ for ( i=0, ien=set.length ; i<ien ; i++ ) {
19392
+ a.push( $.inArray( set[i], order ) );
19393
+ }
19394
+
19395
+ set = a;
19396
+ }
19397
+
19398
+ this._fnOrderColumns( fnInvertKeyValues( set ) );
19399
+
19400
+ return this;
19401
+ },
19402
+
19403
+
19404
+ /**
19405
+ * Convert from the original column index, to the original
19406
+ *
19407
+ * @param {int|array} idx Index(es) to convert
19408
+ * @param {string} dir Transpose direction - `fromOriginal` / `toCurrent`
19409
+ * or `'toOriginal` / `fromCurrent`
19410
+ * @return {int|array} Converted values
19411
+ */
19412
+ fnTranspose: function ( idx, dir )
19413
+ {
19414
+ if ( ! dir ) {
19415
+ dir = 'toCurrent';
19416
+ }
19417
+
19418
+ var order = this.fnOrder();
19419
+ var columns = this.s.dt.aoColumns;
19420
+
19421
+ if ( dir === 'toCurrent' ) {
19422
+ // Given an original index, want the current
19423
+ return ! Array.isArray( idx ) ?
19424
+ $.inArray( idx, order ) :
19425
+ $.map( idx, function ( index ) {
19426
+ return $.inArray( index, order );
19427
+ } );
19428
+ }
19429
+ else {
19430
+ // Given a current index, want the original
19431
+ return ! Array.isArray( idx ) ?
19432
+ columns[idx]._ColReorder_iOrigCol :
19433
+ $.map( idx, function ( index ) {
19434
+ return columns[index]._ColReorder_iOrigCol;
19435
+ } );
19436
+ }
19437
+ },
19438
+
19439
+
19440
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19441
+ * Private methods (they are of course public in JS, but recommended as private)
19442
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
19443
+
19444
+ /**
19445
+ * Constructor logic
19446
+ * @method _fnConstruct
19447
+ * @returns void
19448
+ * @private
19449
+ */
19450
+ "_fnConstruct": function ()
19451
+ {
19452
+ var that = this;
19453
+ var iLen = this.s.dt.aoColumns.length;
19454
+ var table = this.s.dt.nTable;
19455
+ var i;
19456
+
19457
+ /* Columns discounted from reordering - counting left to right */
19458
+ if ( this.s.init.iFixedColumns )
19459
+ {
19460
+ this.s.fixed = this.s.init.iFixedColumns;
19461
+ }
19462
+
19463
+ if ( this.s.init.iFixedColumnsLeft )
19464
+ {
19465
+ this.s.fixed = this.s.init.iFixedColumnsLeft;
19466
+ }
19467
+
19468
+ /* Columns discounted from reordering - counting right to left */
19469
+ this.s.fixedRight = this.s.init.iFixedColumnsRight ?
19470
+ this.s.init.iFixedColumnsRight :
19471
+ 0;
19472
+
19473
+ /* Drop callback initialisation option */
19474
+ if ( this.s.init.fnReorderCallback )
19475
+ {
19476
+ this.s.reorderCallback = this.s.init.fnReorderCallback;
19477
+ }
19478
+
19479
+ /* Add event handlers for the drag and drop, and also mark the original column order */
19480
+ for ( i = 0; i < iLen; i++ )
19481
+ {
19482
+ if ( i > this.s.fixed-1 && i < iLen - this.s.fixedRight )
19483
+ {
19484
+ this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh );
19485
+ }
19486
+
19487
+ /* Mark the original column order for later reference */
19488
+ this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i;
19489
+ }
19490
+
19491
+ /* State saving */
19492
+ this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {
19493
+ that._fnStateSave.call( that, oData );
19494
+ }, "ColReorder_State" );
19495
+
19496
+ /* An initial column order has been specified */
19497
+ var aiOrder = null;
19498
+ if ( this.s.init.aiOrder )
19499
+ {
19500
+ aiOrder = this.s.init.aiOrder.slice();
19501
+ }
19502
+
19503
+ /* State loading, overrides the column order given */
19504
+ if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' &&
19505
+ this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length )
19506
+ {
19507
+ aiOrder = this.s.dt.oLoadedState.ColReorder;
19508
+ }
19509
+
19510
+ /* If we have an order to apply - do so */
19511
+ if ( aiOrder )
19512
+ {
19513
+ /* We might be called during or after the DataTables initialisation. If before, then we need
19514
+ * to wait until the draw is done, if after, then do what we need to do right away
19515
+ */
19516
+ if ( !that.s.dt._bInitComplete )
19517
+ {
19518
+ var bDone = false;
19519
+ $(table).on( 'draw.dt.colReorder', function () {
19520
+ if ( !that.s.dt._bInitComplete && !bDone )
19521
+ {
19522
+ bDone = true;
19523
+ var resort = fnInvertKeyValues( aiOrder );
19524
+ that._fnOrderColumns.call( that, resort );
19525
+ }
19526
+ } );
19527
+ }
19528
+ else
19529
+ {
19530
+ var resort = fnInvertKeyValues( aiOrder );
19531
+ that._fnOrderColumns.call( that, resort );
19532
+ }
19533
+ }
19534
+ else {
19535
+ this._fnSetColumnIndexes();
19536
+ }
19537
+
19538
+ // Destroy clean up
19539
+ $(table).on( 'destroy.dt.colReorder', function () {
19540
+ $(table).off( 'destroy.dt.colReorder draw.dt.colReorder' );
19541
+
19542
+ $.each( that.s.dt.aoColumns, function (i, column) {
19543
+ $(column.nTh).off('.ColReorder');
19544
+ $(column.nTh).removeAttr('data-column-index');
19545
+ } );
19546
+
19547
+ that.s.dt._colReorder = null;
19548
+ that.s = null;
19549
+ } );
19550
+ },
19551
+
19552
+
19553
+ /**
19554
+ * Set the column order from an array
19555
+ * @method _fnOrderColumns
19556
+ * @param array a An array of integers which dictate the column order that should be applied
19557
+ * @returns void
19558
+ * @private
19559
+ */
19560
+ "_fnOrderColumns": function ( a )
19561
+ {
19562
+ var changed = false;
19563
+
19564
+ if ( a.length != this.s.dt.aoColumns.length )
19565
+ {
19566
+ this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "ColReorder - array reorder does not "+
19567
+ "match known number of columns. Skipping." );
19568
+ return;
19569
+ }
19570
+
19571
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
19572
+ {
19573
+ var currIndex = $.inArray( i, a );
19574
+ if ( i != currIndex )
19575
+ {
19576
+ /* Reorder our switching array */
19577
+ fnArraySwitch( a, currIndex, i );
19578
+
19579
+ /* Do the column reorder in the table */
19580
+ this.s.dt.oInstance.fnColReorder( currIndex, i, true, false );
19581
+
19582
+ changed = true;
19583
+ }
19584
+ }
19585
+
19586
+ this._fnSetColumnIndexes();
19587
+
19588
+ // Has anything actually changed? If not, then nothing else to do
19589
+ if ( ! changed ) {
19590
+ return;
19591
+ }
19592
+
19593
+ $.fn.dataTable.Api( this.s.dt ).rows().invalidate();
19594
+
19595
+ /* When scrolling we need to recalculate the column sizes to allow for the shift */
19596
+ if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
19597
+ {
19598
+ this.s.dt.oInstance.fnAdjustColumnSizing( false );
19599
+ }
19600
+
19601
+ /* Save the state */
19602
+ this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );
19603
+
19604
+ if ( this.s.reorderCallback !== null )
19605
+ {
19606
+ this.s.reorderCallback.call( this );
19607
+ }
19608
+ },
19609
+
19610
+
19611
+ /**
19612
+ * Because we change the indexes of columns in the table, relative to their starting point
19613
+ * we need to reorder the state columns to what they are at the starting point so we can
19614
+ * then rearrange them again on state load!
19615
+ * @method _fnStateSave
19616
+ * @param object oState DataTables state
19617
+ * @returns string JSON encoded cookie string for DataTables
19618
+ * @private
19619
+ */
19620
+ "_fnStateSave": function ( oState )
19621
+ {
19622
+ var i, iLen, aCopy, iOrigColumn;
19623
+ var oSettings = this.s.dt;
19624
+ var columns = oSettings.aoColumns;
19625
+
19626
+ oState.ColReorder = [];
19627
+
19628
+ /* Sorting */
19629
+ if ( oState.aaSorting ) {
19630
+ // 1.10.0-
19631
+ for ( i=0 ; i<oState.aaSorting.length ; i++ ) {
19632
+ oState.aaSorting[i][0] = columns[ oState.aaSorting[i][0] ]._ColReorder_iOrigCol;
19633
+ }
19634
+
19635
+ var aSearchCopy = $.extend( true, [], oState.aoSearchCols );
19636
+
19637
+ for ( i=0, iLen=columns.length ; i<iLen ; i++ )
19638
+ {
19639
+ iOrigColumn = columns[i]._ColReorder_iOrigCol;
19640
+
19641
+ /* Column filter */
19642
+ oState.aoSearchCols[ iOrigColumn ] = aSearchCopy[i];
19643
+
19644
+ /* Visibility */
19645
+ oState.abVisCols[ iOrigColumn ] = columns[i].bVisible;
19646
+
19647
+ /* Column reordering */
19648
+ oState.ColReorder.push( iOrigColumn );
19649
+ }
19650
+ }
19651
+ else if ( oState.order ) {
19652
+ // 1.10.1+
19653
+ for ( i=0 ; i<oState.order.length ; i++ ) {
19654
+ oState.order[i][0] = columns[ oState.order[i][0] ]._ColReorder_iOrigCol;
19655
+ }
19656
+
19657
+ var stateColumnsCopy = $.extend( true, [], oState.columns );
19658
+
19659
+ for ( i=0, iLen=columns.length ; i<iLen ; i++ )
19660
+ {
19661
+ iOrigColumn = columns[i]._ColReorder_iOrigCol;
19662
+
19663
+ /* Columns */
19664
+ oState.columns[ iOrigColumn ] = stateColumnsCopy[i];
19665
+
19666
+ /* Column reordering */
19667
+ oState.ColReorder.push( iOrigColumn );
19668
+ }
19669
+ }
19670
+ },
19671
+
19672
+
19673
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19674
+ * Mouse drop and drag
19675
+ */
19676
+
19677
+ /**
19678
+ * Add a mouse down listener to a particluar TH element
19679
+ * @method _fnMouseListener
19680
+ * @param int i Column index
19681
+ * @param element nTh TH element clicked on
19682
+ * @returns void
19683
+ * @private
19684
+ */
19685
+ "_fnMouseListener": function ( i, nTh )
19686
+ {
19687
+ var that = this;
19688
+ $(nTh)
19689
+ .on( 'mousedown.ColReorder', function (e) {
19690
+ if ( that.s.enable && e.which === 1 ) {
19691
+ that._fnMouseDown.call( that, e, nTh );
19692
+ }
19693
+ } )
19694
+ .on( 'touchstart.ColReorder', function (e) {
19695
+ if ( that.s.enable ) {
19696
+ that._fnMouseDown.call( that, e, nTh );
19697
+ }
19698
+ } );
19699
+ },
19700
+
19701
+
19702
+ /**
19703
+ * Mouse down on a TH element in the table header
19704
+ * @method _fnMouseDown
19705
+ * @param event e Mouse event
19706
+ * @param element nTh TH element to be dragged
19707
+ * @returns void
19708
+ * @private
19709
+ */
19710
+ "_fnMouseDown": function ( e, nTh )
19711
+ {
19712
+ var that = this;
19713
+
19714
+ /* Store information about the mouse position */
19715
+ var target = $(e.target).closest('th, td');
19716
+ var offset = target.offset();
19717
+ var idx = parseInt( $(nTh).attr('data-column-index'), 10 );
19718
+
19719
+ if ( idx === undefined ) {
19720
+ return;
19721
+ }
19722
+
19723
+ this.s.mouse.startX = this._fnCursorPosition( e, 'pageX' );
19724
+ this.s.mouse.startY = this._fnCursorPosition( e, 'pageY' );
19725
+ this.s.mouse.offsetX = this._fnCursorPosition( e, 'pageX' ) - offset.left;
19726
+ this.s.mouse.offsetY = this._fnCursorPosition( e, 'pageY' ) - offset.top;
19727
+ this.s.mouse.target = this.s.dt.aoColumns[ idx ].nTh;//target[0];
19728
+ this.s.mouse.targetIndex = idx;
19729
+ this.s.mouse.fromIndex = idx;
19730
+
19731
+ this._fnRegions();
19732
+
19733
+ /* Add event handlers to the document */
19734
+ $(document)
19735
+ .on( 'mousemove.ColReorder touchmove.ColReorder', function (e) {
19736
+ that._fnMouseMove.call( that, e );
19737
+ } )
19738
+ .on( 'mouseup.ColReorder touchend.ColReorder', function (e) {
19739
+ that._fnMouseUp.call( that, e );
19740
+ } );
19741
+ },
19742
+
19743
+
19744
+ /**
19745
+ * Deal with a mouse move event while dragging a node
19746
+ * @method _fnMouseMove
19747
+ * @param event e Mouse event
19748
+ * @returns void
19749
+ * @private
19750
+ */
19751
+ "_fnMouseMove": function ( e )
19752
+ {
19753
+ var that = this;
19754
+
19755
+ if ( this.dom.drag === null )
19756
+ {
19757
+ /* Only create the drag element if the mouse has moved a specific distance from the start
19758
+ * point - this allows the user to make small mouse movements when sorting and not have a
19759
+ * possibly confusing drag element showing up
19760
+ */
19761
+ if ( Math.pow(
19762
+ Math.pow(this._fnCursorPosition( e, 'pageX') - this.s.mouse.startX, 2) +
19763
+ Math.pow(this._fnCursorPosition( e, 'pageY') - this.s.mouse.startY, 2), 0.5 ) < 5 )
19764
+ {
19765
+ return;
19766
+ }
19767
+ this._fnCreateDragNode();
19768
+ }
19769
+
19770
+ /* Position the element - we respect where in the element the click occured */
19771
+ this.dom.drag.css( {
19772
+ left: this._fnCursorPosition( e, 'pageX' ) - this.s.mouse.offsetX,
19773
+ top: this._fnCursorPosition( e, 'pageY' ) - this.s.mouse.offsetY
19774
+ } );
19775
+
19776
+ /* Based on the current mouse position, calculate where the insert should go */
19777
+ var target;
19778
+ var lastToIndex = this.s.mouse.toIndex;
19779
+ var cursorXPosiotion = this._fnCursorPosition(e, 'pageX');
19780
+ var targetsPrev = function (i) {
19781
+ while (i >= 0) {
19782
+ i--;
19783
+
19784
+ if (i <= 0) {
19785
+ return null;
19786
+ }
19787
+
19788
+ if (that.s.aoTargets[i+1].x !== that.s.aoTargets[i].x) {
19789
+ return that.s.aoTargets[i];
19790
+ }
19791
+ }
19792
+ };
19793
+ var firstNotHidden = function () {
19794
+ for (var i=0 ; i<that.s.aoTargets.length-1 ; i++) {
19795
+ if (that.s.aoTargets[i].x !== that.s.aoTargets[i+1].x) {
19796
+ return that.s.aoTargets[i];
19797
+ }
19798
+ }
19799
+ };
19800
+ var lastNotHidden = function () {
19801
+ for (var i=that.s.aoTargets.length-1 ; i>0 ; i--) {
19802
+ if (that.s.aoTargets[i].x !== that.s.aoTargets[i-1].x) {
19803
+ return that.s.aoTargets[i];
19804
+ }
19805
+ }
19806
+ };
19807
+
19808
+ for (var i = 1; i < this.s.aoTargets.length; i++) {
19809
+ var prevTarget = targetsPrev(i);
19810
+ if (! prevTarget) {
19811
+ prevTarget = firstNotHidden();
19812
+ }
19813
+
19814
+ var prevTargetMiddle = prevTarget.x + (this.s.aoTargets[i].x - prevTarget.x) / 2;
19815
+
19816
+ if (this._fnIsLtr()) {
19817
+ if (cursorXPosiotion < prevTargetMiddle ) {
19818
+ target = prevTarget;
19819
+ break;
19820
+ }
19821
+ }
19822
+ else {
19823
+ if (cursorXPosiotion > prevTargetMiddle) {
19824
+ target = prevTarget;
19825
+ break;
19826
+ }
19827
+ }
19828
+ }
19829
+
19830
+ if (target) {
19831
+ this.dom.pointer.css('left', target.x);
19832
+ this.s.mouse.toIndex = target.to;
19833
+ }
19834
+ else {
19835
+ // The insert element wasn't positioned in the array (less than
19836
+ // operator), so we put it at the end
19837
+ this.dom.pointer.css( 'left', lastNotHidden().x );
19838
+ this.s.mouse.toIndex = lastNotHidden().to;
19839
+ }
19840
+
19841
+ // Perform reordering if realtime updating is on and the column has moved
19842
+ if ( this.s.init.bRealtime && lastToIndex !== this.s.mouse.toIndex ) {
19843
+ this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex );
19844
+ this.s.mouse.fromIndex = this.s.mouse.toIndex;
19845
+
19846
+ // Not great for performance, but required to keep everything in alignment
19847
+ if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
19848
+ {
19849
+ this.s.dt.oInstance.fnAdjustColumnSizing( false );
19850
+ }
19851
+
19852
+ this._fnRegions();
19853
+ }
19854
+ },
19855
+
19856
+
19857
+ /**
19858
+ * Finish off the mouse drag and insert the column where needed
19859
+ * @method _fnMouseUp
19860
+ * @param event e Mouse event
19861
+ * @returns void
19862
+ * @private
19863
+ */
19864
+ "_fnMouseUp": function ( e )
19865
+ {
19866
+ var that = this;
19867
+
19868
+ $(document).off( '.ColReorder' );
19869
+
19870
+ if ( this.dom.drag !== null )
19871
+ {
19872
+ /* Remove the guide elements */
19873
+ this.dom.drag.remove();
19874
+ this.dom.pointer.remove();
19875
+ this.dom.drag = null;
19876
+ this.dom.pointer = null;
19877
+
19878
+ /* Actually do the reorder */
19879
+ this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex, true );
19880
+ this._fnSetColumnIndexes();
19881
+
19882
+ /* When scrolling we need to recalculate the column sizes to allow for the shift */
19883
+ if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
19884
+ {
19885
+ this.s.dt.oInstance.fnAdjustColumnSizing( false );
19886
+ }
19887
+
19888
+ /* Save the state */
19889
+ this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );
19890
+
19891
+ if ( this.s.reorderCallback !== null )
19892
+ {
19893
+ this.s.reorderCallback.call( this );
19894
+ }
19895
+ }
19896
+ },
19897
+
19898
+
19899
+ /**
19900
+ * Calculate a cached array with the points of the column inserts, and the
19901
+ * 'to' points
19902
+ * @method _fnRegions
19903
+ * @returns void
19904
+ * @private
19905
+ */
19906
+ "_fnRegions": function ()
19907
+ {
19908
+ var aoColumns = this.s.dt.aoColumns;
19909
+ var isLTR = this._fnIsLtr();
19910
+ this.s.aoTargets.splice(0, this.s.aoTargets.length);
19911
+ var lastBound = $(this.s.dt.nTable).offset().left;
19912
+
19913
+ var aoColumnBounds = [];
19914
+ $.each(aoColumns, function (i, column) {
19915
+ if (column.bVisible && column.nTh.style.display !== 'none') {
19916
+ var nth = $(column.nTh);
19917
+ var bound = nth.offset().left;
19918
+
19919
+ if (isLTR) {
19920
+ bound += nth.outerWidth();
19921
+ }
19922
+
19923
+ aoColumnBounds.push({
19924
+ index: i,
19925
+ bound: bound
19926
+ });
19927
+
19928
+ lastBound = bound;
19929
+ }
19930
+ else {
19931
+ aoColumnBounds.push({
19932
+ index: i,
19933
+ bound: lastBound
19934
+ });
19935
+ }
19936
+ });
19937
+
19938
+ var firstColumn = aoColumnBounds[0];
19939
+ var firstColumnWidth = $(aoColumns[firstColumn.index].nTh).outerWidth();
19940
+
19941
+ this.s.aoTargets.push({
19942
+ to: 0,
19943
+ x: firstColumn.bound - firstColumnWidth
19944
+ });
19945
+
19946
+ for (var i = 0; i < aoColumnBounds.length; i++) {
19947
+ var columnBound = aoColumnBounds[i];
19948
+ var iToPoint = columnBound.index;
19949
+
19950
+ /* For the column / header in question, we want it's position to remain the same if the
19951
+ * position is just to it's immediate left or right, so we only increment the counter for
19952
+ * other columns
19953
+ */
19954
+ if (columnBound.index < this.s.mouse.fromIndex) {
19955
+ iToPoint++;
19956
+ }
19957
+
19958
+ this.s.aoTargets.push({
19959
+ to: iToPoint,
19960
+ x: columnBound.bound
19961
+ });
19962
+ }
19963
+
19964
+ /* Disallow columns for being reordered by drag and drop, counting right to left */
19965
+ if ( this.s.fixedRight !== 0 )
19966
+ {
19967
+ this.s.aoTargets.splice( this.s.aoTargets.length - this.s.fixedRight );
19968
+ }
19969
+
19970
+ /* Disallow columns for being reordered by drag and drop, counting left to right */
19971
+ if ( this.s.fixed !== 0 )
19972
+ {
19973
+ this.s.aoTargets.splice( 0, this.s.fixed );
19974
+ }
19975
+ },
19976
+
19977
+
19978
+ /**
19979
+ * Copy the TH element that is being drags so the user has the idea that they are actually
19980
+ * moving it around the page.
19981
+ * @method _fnCreateDragNode
19982
+ * @returns void
19983
+ * @private
19984
+ */
19985
+ "_fnCreateDragNode": function ()
19986
+ {
19987
+ var scrolling = this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "";
19988
+
19989
+ var origCell = this.s.dt.aoColumns[ this.s.mouse.targetIndex ].nTh;
19990
+ var origTr = origCell.parentNode;
19991
+ var origThead = origTr.parentNode;
19992
+ var origTable = origThead.parentNode;
19993
+ var cloneCell = $(origCell).clone();
19994
+
19995
+ // This is a slightly odd combination of jQuery and DOM, but it is the
19996
+ // fastest and least resource intensive way I could think of cloning
19997
+ // the table with just a single header cell in it.
19998
+ this.dom.drag = $(origTable.cloneNode(false))
19999
+ .addClass( 'DTCR_clonedTable' )
20000
+ .append(
20001
+ $(origThead.cloneNode(false)).append(
20002
+ $(origTr.cloneNode(false)).append(
20003
+ cloneCell[0]
20004
+ )
20005
+ )
20006
+ )
20007
+ .css( {
20008
+ position: 'absolute',
20009
+ top: 0,
20010
+ left: 0,
20011
+ width: $(origCell).outerWidth(),
20012
+ height: $(origCell).outerHeight()
20013
+ } )
20014
+ .appendTo( 'body' );
20015
+
20016
+ this.dom.pointer = $('<div></div>')
20017
+ .addClass( 'DTCR_pointer' )
20018
+ .css( {
20019
+ position: 'absolute',
20020
+ top: scrolling ?
20021
+ $('div.dataTables_scroll', this.s.dt.nTableWrapper).offset().top :
20022
+ $(this.s.dt.nTable).offset().top,
20023
+ height : scrolling ?
20024
+ $('div.dataTables_scroll', this.s.dt.nTableWrapper).height() :
20025
+ $(this.s.dt.nTable).height()
20026
+ } )
20027
+ .appendTo( 'body' );
20028
+ },
20029
+
20030
+
20031
+ /**
20032
+ * Add a data attribute to the column headers, so we know the index of
20033
+ * the row to be reordered. This allows fast detection of the index, and
20034
+ * for this plug-in to work with FixedHeader which clones the nodes.
20035
+ * @private
20036
+ */
20037
+ "_fnSetColumnIndexes": function ()
20038
+ {
20039
+ $.each( this.s.dt.aoColumns, function (i, column) {
20040
+ $(column.nTh).attr('data-column-index', i);
20041
+ } );
20042
+ },
20043
+
20044
+
20045
+ /**
20046
+ * Get cursor position regardless of mouse or touch input
20047
+ * @param {Event} e jQuery Event
20048
+ * @param {string} prop Property to get
20049
+ * @return {number} Value
20050
+ */
20051
+ _fnCursorPosition: function ( e, prop ) {
20052
+ if ( e.type.indexOf('touch') !== -1 ) {
20053
+ return e.originalEvent.touches[0][ prop ];
20054
+ }
20055
+ return e[ prop ];
20056
+ },
20057
+
20058
+ _fnIsLtr: function () {
20059
+ return $(this.s.dt.nTable).css('direction') !== "rtl";
20060
+ }
20061
+ } );
20062
+
20063
+
20064
+
20065
+
20066
+
20067
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20068
+ * Static parameters
20069
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20070
+
20071
+
20072
+ /**
20073
+ * ColReorder default settings for initialisation
20074
+ * @namespace
20075
+ * @static
20076
+ */
20077
+ ColReorder.defaults = {
20078
+ /**
20079
+ * Predefined ordering for the columns that will be applied automatically
20080
+ * on initialisation. If not specified then the order that the columns are
20081
+ * found to be in the HTML is the order used.
20082
+ * @type array
20083
+ * @default null
20084
+ * @static
20085
+ */
20086
+ aiOrder: null,
20087
+
20088
+ /**
20089
+ * ColReorder enable on initialisation
20090
+ * @type boolean
20091
+ * @default true
20092
+ * @static
20093
+ */
20094
+ bEnable: true,
20095
+
20096
+ /**
20097
+ * Redraw the table's column ordering as the end user draws the column
20098
+ * (`true`) or wait until the mouse is released (`false` - default). Note
20099
+ * that this will perform a redraw on each reordering, which involves an
20100
+ * Ajax request each time if you are using server-side processing in
20101
+ * DataTables.
20102
+ * @type boolean
20103
+ * @default false
20104
+ * @static
20105
+ */
20106
+ bRealtime: true,
20107
+
20108
+ /**
20109
+ * Indicate how many columns should be fixed in position (counting from the
20110
+ * left). This will typically be 1 if used, but can be as high as you like.
20111
+ * @type int
20112
+ * @default 0
20113
+ * @static
20114
+ */
20115
+ iFixedColumnsLeft: 0,
20116
+
20117
+ /**
20118
+ * As `iFixedColumnsRight` but counting from the right.
20119
+ * @type int
20120
+ * @default 0
20121
+ * @static
20122
+ */
20123
+ iFixedColumnsRight: 0,
20124
+
20125
+ /**
20126
+ * Callback function that is fired when columns are reordered. The `column-
20127
+ * reorder` event is preferred over this callback
20128
+ * @type function():void
20129
+ * @default null
20130
+ * @static
20131
+ */
20132
+ fnReorderCallback: null
20133
+ };
20134
+
20135
+
20136
+
20137
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20138
+ * Constants
20139
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20140
+
20141
+ /**
20142
+ * ColReorder version
20143
+ * @constant version
20144
+ * @type String
20145
+ * @default As code
20146
+ */
20147
+ ColReorder.version = "1.5.3";
20148
+
20149
+
20150
+
20151
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20152
+ * DataTables interfaces
20153
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20154
+
20155
+ // Expose
20156
+ $.fn.dataTable.ColReorder = ColReorder;
20157
+ $.fn.DataTable.ColReorder = ColReorder;
20158
+
20159
+
20160
+ // Register a new feature with DataTables
20161
+ if ( typeof $.fn.dataTable == "function" &&
20162
+ typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
20163
+ $.fn.dataTableExt.fnVersionCheck('1.10.8') )
20164
+ {
20165
+ $.fn.dataTableExt.aoFeatures.push( {
20166
+ "fnInit": function( settings ) {
20167
+ var table = settings.oInstance;
20168
+
20169
+ if ( ! settings._colReorder ) {
20170
+ var dtInit = settings.oInit;
20171
+ var opts = dtInit.colReorder || dtInit.oColReorder || {};
20172
+
20173
+ new ColReorder( settings, opts );
20174
+ }
20175
+ else {
20176
+ table.oApi._fnLog( settings, 1, "ColReorder attempted to initialise twice. Ignoring second" );
20177
+ }
20178
+
20179
+ return null; /* No node for DataTables to insert */
20180
+ },
20181
+ "cFeature": "R",
20182
+ "sFeature": "ColReorder"
20183
+ } );
20184
+ }
20185
+ else {
20186
+ alert( "Warning: ColReorder requires DataTables 1.10.8 or greater - www.datatables.net/download");
20187
+ }
20188
+
20189
+
20190
+ // Attach a listener to the document which listens for DataTables initialisation
20191
+ // events so we can automatically initialise
20192
+ $(document).on( 'preInit.dt.colReorder', function (e, settings) {
20193
+ if ( e.namespace !== 'dt' ) {
20194
+ return;
20195
+ }
20196
+
20197
+ var init = settings.oInit.colReorder;
20198
+ var defaults = DataTable.defaults.colReorder;
20199
+
20200
+ if ( init || defaults ) {
20201
+ var opts = $.extend( {}, init, defaults );
20202
+
20203
+ if ( init !== false ) {
20204
+ new ColReorder( settings, opts );
20205
+ }
20206
+ }
20207
+ } );
20208
+
20209
+
20210
+ // API augmentation
20211
+ $.fn.dataTable.Api.register( 'colReorder.reset()', function () {
20212
+ return this.iterator( 'table', function ( ctx ) {
20213
+ ctx._colReorder.fnReset();
20214
+ } );
20215
+ } );
20216
+
20217
+ $.fn.dataTable.Api.register( 'colReorder.order()', function ( set, original ) {
20218
+ if ( set ) {
20219
+ return this.iterator( 'table', function ( ctx ) {
20220
+ ctx._colReorder.fnOrder( set, original );
20221
+ } );
20222
+ }
20223
+
20224
+ return this.context.length ?
20225
+ this.context[0]._colReorder.fnOrder() :
20226
+ null;
20227
+ } );
20228
+
20229
+ $.fn.dataTable.Api.register( 'colReorder.transpose()', function ( idx, dir ) {
20230
+ return this.context.length && this.context[0]._colReorder ?
20231
+ this.context[0]._colReorder.fnTranspose( idx, dir ) :
20232
+ idx;
20233
+ } );
20234
+
20235
+ $.fn.dataTable.Api.register( 'colReorder.move()', function( from, to, drop, invalidateRows ) {
20236
+ if (this.context.length) {
20237
+ this.context[0]._colReorder.s.dt.oInstance.fnColReorder( from, to, drop, invalidateRows );
20238
+ this.context[0]._colReorder._fnSetColumnIndexes();
20239
+ }
20240
+ return this;
20241
+ } );
20242
+
20243
+ $.fn.dataTable.Api.register( 'colReorder.enable()', function( flag ) {
20244
+ return this.iterator( 'table', function ( ctx ) {
20245
+ if ( ctx._colReorder ) {
20246
+ ctx._colReorder.fnEnable( flag );
20247
+ }
20248
+ } );
20249
+ } );
20250
+
20251
+ $.fn.dataTable.Api.register( 'colReorder.disable()', function() {
20252
+ return this.iterator( 'table', function ( ctx ) {
20253
+ if ( ctx._colReorder ) {
20254
+ ctx._colReorder.fnDisable();
20255
+ }
20256
+ } );
20257
+ } );
20258
+
20259
+
20260
+ return ColReorder;
20261
+ }));
20262
+
20263
+
20264
+ /*! FixedColumns 3.3.2
20265
+ * ©2010-2020 SpryMedia Ltd - datatables.net/license
20266
+ */
20267
+
20268
+ /**
20269
+ * @summary FixedColumns
20270
+ * @description Freeze columns in place on a scrolling DataTable
20271
+ * @version 3.3.2
20272
+ * @file dataTables.fixedColumns.js
20273
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
20274
+ * @contact www.sprymedia.co.uk/contact
20275
+ * @copyright Copyright 2010-2020 SpryMedia Ltd.
20276
+ *
20277
+ * This source file is free software, available under the following license:
20278
+ * MIT license - http://datatables.net/license/mit
20279
+ *
20280
+ * This source file is distributed in the hope that it will be useful, but
20281
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20282
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20283
+ *
20284
+ * For details please refer to: http://www.datatables.net
20285
+ */
20286
+ (function( factory ){
20287
+ if ( typeof define === 'function' && define.amd ) {
20288
+ // AMD
20289
+ define( ['jquery', 'datatables.net'], function ( $ ) {
20290
+ return factory( $, window, document );
20291
+ } );
20292
+ }
20293
+ else if ( typeof exports === 'object' ) {
20294
+ // CommonJS
20295
+ module.exports = function (root, $) {
20296
+ if ( ! root ) {
20297
+ root = window;
20298
+ }
20299
+
20300
+ if ( ! $ || ! $.fn.dataTable ) {
20301
+ $ = require('datatables.net')(root, $).$;
20302
+ }
20303
+
20304
+ return factory( $, root, root.document );
20305
+ };
20306
+ }
20307
+ else {
20308
+ // Browser
20309
+ factory( jQuery, window, document );
20310
+ }
20311
+ }(function( $, window, document, undefined ) {
20312
+ 'use strict';
20313
+ var DataTable = $.fn.dataTable;
20314
+ var _firefoxScroll;
20315
+
20316
+ /**
20317
+ * When making use of DataTables' x-axis scrolling feature, you may wish to
20318
+ * fix the left most column in place. This plug-in for DataTables provides
20319
+ * exactly this option (note for non-scrolling tables, please use the
20320
+ * FixedHeader plug-in, which can fix headers and footers). Key
20321
+ * features include:
20322
+ *
20323
+ * * Freezes the left or right most columns to the side of the table
20324
+ * * Option to freeze two or more columns
20325
+ * * Full integration with DataTables' scrolling options
20326
+ * * Speed - FixedColumns is fast in its operation
20327
+ *
20328
+ * @class
20329
+ * @constructor
20330
+ * @global
20331
+ * @param {object} dt DataTables instance. With DataTables 1.10 this can also
20332
+ * be a jQuery collection, a jQuery selector, DataTables API instance or
20333
+ * settings object.
20334
+ * @param {object} [init={}] Configuration object for FixedColumns. Options are
20335
+ * defined by {@link FixedColumns.defaults}
20336
+ *
20337
+ * @requires jQuery 1.7+
20338
+ * @requires DataTables 1.8.0+
20339
+ *
20340
+ * @example
20341
+ * var table = $('#example').dataTable( {
20342
+ * "scrollX": "100%"
20343
+ * } );
20344
+ * new $.fn.dataTable.fixedColumns( table );
20345
+ */
20346
+ var FixedColumns = function ( dt, init ) {
20347
+ var that = this;
20348
+
20349
+ /* Sanity check - you just know it will happen */
20350
+ if ( ! ( this instanceof FixedColumns ) ) {
20351
+ alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
20352
+ return;
20353
+ }
20354
+
20355
+ if ( init === undefined || init === true ) {
20356
+ init = {};
20357
+ }
20358
+
20359
+ // Use the DataTables Hungarian notation mapping method, if it exists to
20360
+ // provide forwards compatibility for camel case variables
20361
+ var camelToHungarian = $.fn.dataTable.camelToHungarian;
20362
+ if ( camelToHungarian ) {
20363
+ camelToHungarian( FixedColumns.defaults, FixedColumns.defaults, true );
20364
+ camelToHungarian( FixedColumns.defaults, init );
20365
+ }
20366
+
20367
+ // v1.10 allows the settings object to be got form a number of sources
20368
+ var dtSettings = new $.fn.dataTable.Api( dt ).settings()[0];
20369
+
20370
+ /**
20371
+ * Settings object which contains customisable information for FixedColumns instance
20372
+ * @namespace
20373
+ * @extends FixedColumns.defaults
20374
+ * @private
20375
+ */
20376
+ this.s = {
20377
+ /**
20378
+ * DataTables settings objects
20379
+ * @type object
20380
+ * @default Obtained from DataTables instance
20381
+ */
20382
+ "dt": dtSettings,
20383
+
20384
+ /**
20385
+ * Number of columns in the DataTable - stored for quick access
20386
+ * @type int
20387
+ * @default Obtained from DataTables instance
20388
+ */
20389
+ "iTableColumns": dtSettings.aoColumns.length,
20390
+
20391
+ /**
20392
+ * Original outer widths of the columns as rendered by DataTables - used to calculate
20393
+ * the FixedColumns grid bounding box
20394
+ * @type array.<int>
20395
+ * @default []
20396
+ */
20397
+ "aiOuterWidths": [],
20398
+
20399
+ /**
20400
+ * Original inner widths of the columns as rendered by DataTables - used to apply widths
20401
+ * to the columns
20402
+ * @type array.<int>
20403
+ * @default []
20404
+ */
20405
+ "aiInnerWidths": [],
20406
+
20407
+
20408
+ /**
20409
+ * Is the document layout right-to-left
20410
+ * @type boolean
20411
+ */
20412
+ rtl: $(dtSettings.nTable).css('direction') === 'rtl'
20413
+ };
20414
+
20415
+
20416
+ /**
20417
+ * DOM elements used by the class instance
20418
+ * @namespace
20419
+ * @private
20420
+ *
20421
+ */
20422
+ this.dom = {
20423
+ /**
20424
+ * DataTables scrolling element
20425
+ * @type node
20426
+ * @default null
20427
+ */
20428
+ "scroller": null,
20429
+
20430
+ /**
20431
+ * DataTables header table
20432
+ * @type node
20433
+ * @default null
20434
+ */
20435
+ "header": null,
20436
+
20437
+ /**
20438
+ * DataTables body table
20439
+ * @type node
20440
+ * @default null
20441
+ */
20442
+ "body": null,
20443
+
20444
+ /**
20445
+ * DataTables footer table
20446
+ * @type node
20447
+ * @default null
20448
+ */
20449
+ "footer": null,
20450
+
20451
+ /**
20452
+ * Display grid elements
20453
+ * @namespace
20454
+ */
20455
+ "grid": {
20456
+ /**
20457
+ * Grid wrapper. This is the container element for the 3x3 grid
20458
+ * @type node
20459
+ * @default null
20460
+ */
20461
+ "wrapper": null,
20462
+
20463
+ /**
20464
+ * DataTables scrolling element. This element is the DataTables
20465
+ * component in the display grid (making up the main table - i.e.
20466
+ * not the fixed columns).
20467
+ * @type node
20468
+ * @default null
20469
+ */
20470
+ "dt": null,
20471
+
20472
+ /**
20473
+ * Left fixed column grid components
20474
+ * @namespace
20475
+ */
20476
+ "left": {
20477
+ "wrapper": null,
20478
+ "head": null,
20479
+ "body": null,
20480
+ "foot": null
20481
+ },
20482
+
20483
+ /**
20484
+ * Right fixed column grid components
20485
+ * @namespace
20486
+ */
20487
+ "right": {
20488
+ "wrapper": null,
20489
+ "head": null,
20490
+ "body": null,
20491
+ "foot": null
20492
+ }
20493
+ },
20494
+
20495
+ /**
20496
+ * Cloned table nodes
20497
+ * @namespace
20498
+ */
20499
+ "clone": {
20500
+ /**
20501
+ * Left column cloned table nodes
20502
+ * @namespace
20503
+ */
20504
+ "left": {
20505
+ /**
20506
+ * Cloned header table
20507
+ * @type node
20508
+ * @default null
20509
+ */
20510
+ "header": null,
20511
+
20512
+ /**
20513
+ * Cloned body table
20514
+ * @type node
20515
+ * @default null
20516
+ */
20517
+ "body": null,
20518
+
20519
+ /**
20520
+ * Cloned footer table
20521
+ * @type node
20522
+ * @default null
20523
+ */
20524
+ "footer": null
20525
+ },
20526
+
20527
+ /**
20528
+ * Right column cloned table nodes
20529
+ * @namespace
20530
+ */
20531
+ "right": {
20532
+ /**
20533
+ * Cloned header table
20534
+ * @type node
20535
+ * @default null
20536
+ */
20537
+ "header": null,
20538
+
20539
+ /**
20540
+ * Cloned body table
20541
+ * @type node
20542
+ * @default null
20543
+ */
20544
+ "body": null,
20545
+
20546
+ /**
20547
+ * Cloned footer table
20548
+ * @type node
20549
+ * @default null
20550
+ */
20551
+ "footer": null
20552
+ }
20553
+ }
20554
+ };
20555
+
20556
+ if ( dtSettings._oFixedColumns ) {
20557
+ throw 'FixedColumns already initialised on this table';
20558
+ }
20559
+
20560
+ /* Attach the instance to the DataTables instance so it can be accessed easily */
20561
+ dtSettings._oFixedColumns = this;
20562
+
20563
+ /* Let's do it */
20564
+ if ( ! dtSettings._bInitComplete )
20565
+ {
20566
+ dtSettings.oApi._fnCallbackReg( dtSettings, 'aoInitComplete', function () {
20567
+ that._fnConstruct( init );
20568
+ }, 'FixedColumns' );
20569
+ }
20570
+ else
20571
+ {
20572
+ this._fnConstruct( init );
20573
+ }
20574
+ };
20575
+
20576
+
20577
+
20578
+ $.extend( FixedColumns.prototype , {
20579
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20580
+ * Public methods
20581
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20582
+
20583
+ /**
20584
+ * Update the fixed columns - including headers and footers. Note that FixedColumns will
20585
+ * automatically update the display whenever the host DataTable redraws.
20586
+ * @returns {void}
20587
+ * @example
20588
+ * var table = $('#example').dataTable( {
20589
+ * "scrollX": "100%"
20590
+ * } );
20591
+ * var fc = new $.fn.dataTable.fixedColumns( table );
20592
+ *
20593
+ * // at some later point when the table has been manipulated....
20594
+ * fc.fnUpdate();
20595
+ */
20596
+ "fnUpdate": function ()
20597
+ {
20598
+ this._fnDraw( true );
20599
+ },
20600
+
20601
+
20602
+ /**
20603
+ * Recalculate the resizes of the 3x3 grid that FixedColumns uses for display of the table.
20604
+ * This is useful if you update the width of the table container. Note that FixedColumns will
20605
+ * perform this function automatically when the window.resize event is fired.
20606
+ * @returns {void}
20607
+ * @example
20608
+ * var table = $('#example').dataTable( {
20609
+ * "scrollX": "100%"
20610
+ * } );
20611
+ * var fc = new $.fn.dataTable.fixedColumns( table );
20612
+ *
20613
+ * // Resize the table container and then have FixedColumns adjust its layout....
20614
+ * $('#content').width( 1200 );
20615
+ * fc.fnRedrawLayout();
20616
+ */
20617
+ "fnRedrawLayout": function ()
20618
+ {
20619
+ this._fnColCalc();
20620
+ this._fnGridLayout();
20621
+ this.fnUpdate();
20622
+ },
20623
+
20624
+
20625
+ /**
20626
+ * Mark a row such that it's height should be recalculated when using 'semiauto' row
20627
+ * height matching. This function will have no effect when 'none' or 'auto' row height
20628
+ * matching is used.
20629
+ * @param {Node} nTr TR element that should have it's height recalculated
20630
+ * @returns {void}
20631
+ * @example
20632
+ * var table = $('#example').dataTable( {
20633
+ * "scrollX": "100%"
20634
+ * } );
20635
+ * var fc = new $.fn.dataTable.fixedColumns( table );
20636
+ *
20637
+ * // manipulate the table - mark the row as needing an update then update the table
20638
+ * // this allows the redraw performed by DataTables fnUpdate to recalculate the row
20639
+ * // height
20640
+ * fc.fnRecalculateHeight();
20641
+ * table.fnUpdate( $('#example tbody tr:eq(0)')[0], ["insert date", 1, 2, 3 ... ]);
20642
+ */
20643
+ "fnRecalculateHeight": function ( nTr )
20644
+ {
20645
+ delete nTr._DTTC_iHeight;
20646
+ nTr.style.height = 'auto';
20647
+ },
20648
+
20649
+
20650
+ /**
20651
+ * Set the height of a given row - provides cross browser compatibility
20652
+ * @param {Node} nTarget TR element that should have it's height recalculated
20653
+ * @param {int} iHeight Height in pixels to set
20654
+ * @returns {void}
20655
+ * @example
20656
+ * var table = $('#example').dataTable( {
20657
+ * "scrollX": "100%"
20658
+ * } );
20659
+ * var fc = new $.fn.dataTable.fixedColumns( table );
20660
+ *
20661
+ * // You may want to do this after manipulating a row in the fixed column
20662
+ * fc.fnSetRowHeight( $('#example tbody tr:eq(0)')[0], 50 );
20663
+ */
20664
+ "fnSetRowHeight": function ( nTarget, iHeight )
20665
+ {
20666
+ nTarget.style.height = iHeight+"px";
20667
+ },
20668
+
20669
+
20670
+ /**
20671
+ * Get data index information about a row or cell in the table body.
20672
+ * This function is functionally identical to fnGetPosition in DataTables,
20673
+ * taking the same parameter (TH, TD or TR node) and returning exactly the
20674
+ * the same information (data index information). THe difference between
20675
+ * the two is that this method takes into account the fixed columns in the
20676
+ * table, so you can pass in nodes from the master table, or the cloned
20677
+ * tables and get the index position for the data in the main table.
20678
+ * @param {node} node TR, TH or TD element to get the information about
20679
+ * @returns {int} If nNode is given as a TR, then a single index is
20680
+ * returned, or if given as a cell, an array of [row index, column index
20681
+ * (visible), column index (all)] is given.
20682
+ */
20683
+ "fnGetPosition": function ( node )
20684
+ {
20685
+ var idx;
20686
+ var inst = this.s.dt.oInstance;
20687
+
20688
+ if ( ! $(node).parents('.DTFC_Cloned').length )
20689
+ {
20690
+ // Not in a cloned table
20691
+ return inst.fnGetPosition( node );
20692
+ }
20693
+ else
20694
+ {
20695
+ // Its in the cloned table, so need to look up position
20696
+ if ( node.nodeName.toLowerCase() === 'tr' ) {
20697
+ idx = $(node).index();
20698
+ return inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );
20699
+ }
20700
+ else
20701
+ {
20702
+ var colIdx = $(node).index();
20703
+ idx = $(node.parentNode).index();
20704
+ var row = inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );
20705
+
20706
+ return [
20707
+ row,
20708
+ colIdx,
20709
+ inst.oApi._fnVisibleToColumnIndex( this.s.dt, colIdx )
20710
+ ];
20711
+ }
20712
+ }
20713
+ },
20714
+
20715
+ fnToFixedNode: function ( rowIdx, colIdx )
20716
+ {
20717
+ var found;
20718
+
20719
+ if ( colIdx < this.s.iLeftColumns ) {
20720
+ found = $(this.dom.clone.left.body).find('[data-dt-row='+rowIdx+'][data-dt-column='+colIdx+']');
20721
+ }
20722
+ else if ( colIdx >= this.s.iRightColumns ) {
20723
+ found = $(this.dom.clone.right.body).find('[data-dt-row='+rowIdx+'][data-dt-column='+colIdx+']');
20724
+ }
20725
+
20726
+ if ( found && found.length ) {
20727
+ return found[0];
20728
+ }
20729
+
20730
+ // Fallback - non-fixed node
20731
+ var table = new $.fn.dataTable.Api(this.s.dt);
20732
+ return table.cell(rowIdx, colIdx).node();
20733
+ },
20734
+
20735
+
20736
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20737
+ * Private methods (they are of course public in JS, but recommended as private)
20738
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
20739
+
20740
+ /**
20741
+ * Initialisation for FixedColumns
20742
+ * @param {Object} oInit User settings for initialisation
20743
+ * @returns {void}
20744
+ * @private
20745
+ */
20746
+ "_fnConstruct": function ( oInit )
20747
+ {
20748
+ var i, iLen, iWidth,
20749
+ that = this;
20750
+
20751
+ /* Sanity checking */
20752
+ if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||
20753
+ this.s.dt.oInstance.fnVersionCheck( '1.8.0' ) !== true )
20754
+ {
20755
+ alert( "FixedColumns "+FixedColumns.VERSION+" required DataTables 1.8.0 or later. "+
20756
+ "Please upgrade your DataTables installation" );
20757
+ return;
20758
+ }
20759
+
20760
+ if ( this.s.dt.oScroll.sX === "" )
20761
+ {
20762
+ this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
20763
+ "x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+
20764
+ "column fixing when scrolling is not enabled" );
20765
+ return;
20766
+ }
20767
+
20768
+ /* Apply the settings from the user / defaults */
20769
+ this.s = $.extend( true, this.s, FixedColumns.defaults, oInit );
20770
+
20771
+ /* Set up the DOM as we need it and cache nodes */
20772
+ var classes = this.s.dt.oClasses;
20773
+ this.dom.grid.dt = $(this.s.dt.nTable).parents('div.'+classes.sScrollWrapper)[0];
20774
+ this.dom.scroller = $('div.'+classes.sScrollBody, this.dom.grid.dt )[0];
20775
+
20776
+ /* Set up the DOM that we want for the fixed column layout grid */
20777
+ this._fnColCalc();
20778
+ this._fnGridSetup();
20779
+
20780
+ /* Event handlers */
20781
+ var mouseController;
20782
+ var mouseDown = false;
20783
+
20784
+ // When the mouse is down (drag scroll) the mouse controller cannot
20785
+ // change, as the browser keeps the original element as the scrolling one
20786
+ $(this.s.dt.nTableWrapper).on( 'mousedown.DTFC', function (e) {
20787
+ if ( e.button === 0 ) {
20788
+ mouseDown = true;
20789
+
20790
+ $(document).one( 'mouseup', function () {
20791
+ mouseDown = false;
20792
+ } );
20793
+ }
20794
+ } );
20795
+
20796
+ // When the body is scrolled - scroll the left and right columns
20797
+ $(this.dom.scroller)
20798
+ .on( 'mouseover.DTFC touchstart.DTFC', function () {
20799
+ if ( ! mouseDown ) {
20800
+ mouseController = 'main';
20801
+ }
20802
+ } )
20803
+ .on( 'scroll.DTFC', function (e) {
20804
+ if ( ! mouseController && e.originalEvent ) {
20805
+ mouseController = 'main';
20806
+ }
20807
+
20808
+ if ( mouseController === 'main' || mouseController === 'key' ) {
20809
+ if ( that.s.iLeftColumns > 0 ) {
20810
+ that.dom.grid.left.liner.scrollTop = that.dom.scroller.scrollTop;
20811
+ }
20812
+ if ( that.s.iRightColumns > 0 ) {
20813
+ that.dom.grid.right.liner.scrollTop = that.dom.scroller.scrollTop;
20814
+ }
20815
+ }
20816
+ } );
20817
+
20818
+ var wheelType = 'onwheel' in document.createElement('div') ?
20819
+ 'wheel.DTFC' :
20820
+ 'mousewheel.DTFC';
20821
+
20822
+ if ( that.s.iLeftColumns > 0 ) {
20823
+ // When scrolling the left column, scroll the body and right column
20824
+ $(that.dom.grid.left.liner)
20825
+ .on( 'mouseover.DTFC touchstart.DTFC', function () {
20826
+ if ( ! mouseDown && mouseController !== 'key' ) {
20827
+ mouseController = 'left';
20828
+ }
20829
+ } )
20830
+ .on( 'scroll.DTFC', function ( e ) {
20831
+ if ( ! mouseController && e.originalEvent ) {
20832
+ mouseController = 'left';
20833
+ }
20834
+
20835
+ if ( mouseController === 'left' ) {
20836
+ that.dom.scroller.scrollTop = that.dom.grid.left.liner.scrollTop;
20837
+ if ( that.s.iRightColumns > 0 ) {
20838
+ that.dom.grid.right.liner.scrollTop = that.dom.grid.left.liner.scrollTop;
20839
+ }
20840
+ }
20841
+ } )
20842
+ .on( wheelType, function(e) {
20843
+ mouseController = 'left';
20844
+
20845
+ // Pass horizontal scrolling through
20846
+ var xDelta = e.type === 'wheel' ?
20847
+ -e.originalEvent.deltaX :
20848
+ e.originalEvent.wheelDeltaX;
20849
+ that.dom.scroller.scrollLeft -= xDelta;
20850
+ } );
20851
+
20852
+ // Header will not trigger scroll on left column, but might on `main` (sorting)
20853
+ $(that.dom.grid.left.head).on( 'mouseover.DTFC touchstart.DTFC', function () {
20854
+ mouseController = 'main';
20855
+ });
20856
+ }
20857
+
20858
+ if ( that.s.iRightColumns > 0 ) {
20859
+ // When scrolling the right column, scroll the body and the left column
20860
+ $(that.dom.grid.right.liner)
20861
+ .on( 'mouseover.DTFC touchstart.DTFC', function () {
20862
+ if ( ! mouseDown && mouseController !== 'key' ) {
20863
+ mouseController = 'right';
20864
+ }
20865
+ } )
20866
+ .on( 'scroll.DTFC', function ( e ) {
20867
+ if ( ! mouseController && e.originalEvent ) {
20868
+ mouseController = 'right';
20869
+ }
20870
+
20871
+ if ( mouseController === 'right' ) {
20872
+ that.dom.scroller.scrollTop = that.dom.grid.right.liner.scrollTop;
20873
+ if ( that.s.iLeftColumns > 0 ) {
20874
+ that.dom.grid.left.liner.scrollTop = that.dom.grid.right.liner.scrollTop;
20875
+ }
20876
+ }
20877
+ } )
20878
+ .on( wheelType, function(e) {
20879
+ mouseController = 'right';
20880
+
20881
+ // Pass horizontal scrolling through
20882
+ var xDelta = e.type === 'wheel' ?
20883
+ -e.originalEvent.deltaX :
20884
+ e.originalEvent.wheelDeltaX;
20885
+ that.dom.scroller.scrollLeft -= xDelta;
20886
+ } );
20887
+
20888
+ $(that.dom.grid.right.head).on( 'mouseover.DTFC touchstart.DTFC', function () {
20889
+ mouseController = 'main';
20890
+ });
20891
+ }
20892
+
20893
+ $(window).on( 'resize.DTFC', function () {
20894
+ that._fnGridLayout.call( that );
20895
+ } );
20896
+
20897
+ var bFirstDraw = true;
20898
+ var jqTable = $(this.s.dt.nTable);
20899
+
20900
+ jqTable
20901
+ .on( 'draw.dt.DTFC', function () {
20902
+ that._fnColCalc();
20903
+ that._fnDraw.call( that, bFirstDraw );
20904
+ bFirstDraw = false;
20905
+ } )
20906
+ .on('key-focus.dt.DTFC', function () {
20907
+ // KeyTable navigation needs to be main focused
20908
+ mouseController = 'key';
20909
+ })
20910
+ .on( 'column-sizing.dt.DTFC', function () {
20911
+ that._fnColCalc();
20912
+ that._fnGridLayout( that );
20913
+ } )
20914
+ .on( 'column-visibility.dt.DTFC', function ( e, settings, column, vis, recalc ) {
20915
+ if ( recalc === undefined || recalc ) {
20916
+ that._fnColCalc();
20917
+ that._fnGridLayout( that );
20918
+ that._fnDraw( true );
20919
+ }
20920
+ } )
20921
+ .on( 'select.dt.DTFC deselect.dt.DTFC', function ( e, dt, type, indexes ) {
20922
+ if ( e.namespace === 'dt' ) {
20923
+ that._fnDraw( false );
20924
+ }
20925
+ } )
20926
+ .on( 'position.dts.dt.DTFC', function (e, tableTop) {
20927
+ // Sync up with Scroller
20928
+ if (that.dom.grid.left.body) {
20929
+ $(that.dom.grid.left.body).find('table').eq(0).css('top', tableTop);
20930
+ }
20931
+
20932
+ if (that.dom.grid.right.body) {
20933
+ $(that.dom.grid.right.body).find('table').eq(0).css('top', tableTop);
20934
+ }
20935
+ } )
20936
+ .on( 'destroy.dt.DTFC', function () {
20937
+ jqTable.off( '.DTFC' );
20938
+
20939
+ $(that.dom.scroller).off( '.DTFC' );
20940
+ $(window).off( '.DTFC' );
20941
+ $(that.s.dt.nTableWrapper).off( '.DTFC' );
20942
+
20943
+ $(that.dom.grid.left.liner).off( '.DTFC '+wheelType );
20944
+ $(that.dom.grid.left.wrapper).remove();
20945
+
20946
+ $(that.dom.grid.right.liner).off( '.DTFC '+wheelType );
20947
+ $(that.dom.grid.right.wrapper).remove();
20948
+ } );
20949
+
20950
+ /* Get things right to start with - note that due to adjusting the columns, there must be
20951
+ * another redraw of the main table. It doesn't need to be a full redraw however.
20952
+ */
20953
+ this._fnGridLayout();
20954
+ this.s.dt.oInstance.fnDraw(false);
20955
+ },
20956
+
20957
+
20958
+ /**
20959
+ * Calculate the column widths for the grid layout
20960
+ * @returns {void}
20961
+ * @private
20962
+ */
20963
+ "_fnColCalc": function ()
20964
+ {
20965
+ var that = this;
20966
+ var iLeftWidth = 0;
20967
+ var iRightWidth = 0;
20968
+
20969
+ this.s.aiInnerWidths = [];
20970
+ this.s.aiOuterWidths = [];
20971
+
20972
+ $.each( this.s.dt.aoColumns, function (i, col) {
20973
+ var th = $(col.nTh);
20974
+ var border;
20975
+
20976
+ if ( ! th.filter(':visible').length ) {
20977
+ that.s.aiInnerWidths.push( 0 );
20978
+ that.s.aiOuterWidths.push( 0 );
20979
+ }
20980
+ else
20981
+ {
20982
+ // Inner width is used to assign widths to cells
20983
+ // Outer width is used to calculate the container
20984
+ var iWidth = th.outerWidth();
20985
+
20986
+ // When working with the left most-cell, need to add on the
20987
+ // table's border to the outerWidth, since we need to take
20988
+ // account of it, but it isn't in any cell
20989
+ if ( that.s.aiOuterWidths.length === 0 ) {
20990
+ border = $(that.s.dt.nTable).css('border-left-width');
20991
+ iWidth += typeof border === 'string' && border.indexOf('px') === -1 ?
20992
+ 1 :
20993
+ parseInt( border, 10 );
20994
+ }
20995
+
20996
+ // Likewise with the final column on the right
20997
+ if ( that.s.aiOuterWidths.length === that.s.dt.aoColumns.length-1 ) {
20998
+ border = $(that.s.dt.nTable).css('border-right-width');
20999
+ iWidth += typeof border === 'string' && border.indexOf('px') === -1 ?
21000
+ 1 :
21001
+ parseInt( border, 10 );
21002
+ }
21003
+
21004
+ that.s.aiOuterWidths.push( iWidth );
21005
+ that.s.aiInnerWidths.push( th.width() );
21006
+
21007
+ if ( i < that.s.iLeftColumns )
21008
+ {
21009
+ iLeftWidth += iWidth;
21010
+ }
21011
+
21012
+ if ( that.s.iTableColumns-that.s.iRightColumns <= i )
21013
+ {
21014
+ iRightWidth += iWidth;
21015
+ }
21016
+ }
21017
+ } );
21018
+
21019
+ this.s.iLeftWidth = iLeftWidth;
21020
+ this.s.iRightWidth = iRightWidth;
21021
+ },
21022
+
21023
+
21024
+ /**
21025
+ * Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid
21026
+ * for the left column, the DataTable (for which we just reuse the scrolling element DataTable
21027
+ * puts into the DOM) and the right column. In each of he two fixed column elements there is a
21028
+ * grouping wrapper element and then a head, body and footer wrapper. In each of these we then
21029
+ * place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure.
21030
+ * @returns {void}
21031
+ * @private
21032
+ */
21033
+ "_fnGridSetup": function ()
21034
+ {
21035
+ var that = this;
21036
+ var oOverflow = this._fnDTOverflow();
21037
+ var block;
21038
+
21039
+ this.dom.body = this.s.dt.nTable;
21040
+ this.dom.header = this.s.dt.nTHead.parentNode;
21041
+ this.dom.header.parentNode.parentNode.style.position = "relative";
21042
+
21043
+ var nSWrapper =
21044
+ $('<div class="DTFC_ScrollWrapper" style="position:relative; clear:both;">'+
21045
+ '<div class="DTFC_LeftWrapper" style="position:absolute; top:0; left:0;" aria-hidden="true">'+
21046
+ '<div class="DTFC_LeftHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
21047
+ '<div class="DTFC_LeftBodyWrapper" style="position:relative; top:0; left:0; height:0; overflow:hidden;">'+
21048
+ '<div class="DTFC_LeftBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
21049
+ '</div>'+
21050
+ '<div class="DTFC_LeftFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
21051
+ '</div>'+
21052
+ '<div class="DTFC_RightWrapper" style="position:absolute; top:0; right:0;" aria-hidden="true">'+
21053
+ '<div class="DTFC_RightHeadWrapper" style="position:relative; top:0; left:0;">'+
21054
+ '<div class="DTFC_RightHeadBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
21055
+ '</div>'+
21056
+ '<div class="DTFC_RightBodyWrapper" style="position:relative; top:0; left:0; height:0; overflow:hidden;">'+
21057
+ '<div class="DTFC_RightBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
21058
+ '</div>'+
21059
+ '<div class="DTFC_RightFootWrapper" style="position:relative; top:0; left:0;">'+
21060
+ '<div class="DTFC_RightFootBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
21061
+ '</div>'+
21062
+ '</div>'+
21063
+ '</div>')[0];
21064
+ var nLeft = nSWrapper.childNodes[0];
21065
+ var nRight = nSWrapper.childNodes[1];
21066
+
21067
+ this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt );
21068
+ nSWrapper.appendChild( this.dom.grid.dt );
21069
+
21070
+ this.dom.grid.wrapper = nSWrapper;
21071
+
21072
+ if ( this.s.iLeftColumns > 0 )
21073
+ {
21074
+ this.dom.grid.left.wrapper = nLeft;
21075
+ this.dom.grid.left.head = nLeft.childNodes[0];
21076
+ this.dom.grid.left.body = nLeft.childNodes[1];
21077
+ this.dom.grid.left.liner = $('div.DTFC_LeftBodyLiner', nSWrapper)[0];
21078
+
21079
+ nSWrapper.appendChild( nLeft );
21080
+ }
21081
+
21082
+ if ( this.s.iRightColumns > 0 )
21083
+ {
21084
+ this.dom.grid.right.wrapper = nRight;
21085
+ this.dom.grid.right.head = nRight.childNodes[0];
21086
+ this.dom.grid.right.body = nRight.childNodes[1];
21087
+ this.dom.grid.right.liner = $('div.DTFC_RightBodyLiner', nSWrapper)[0];
21088
+
21089
+ nRight.style.right = oOverflow.bar+"px";
21090
+
21091
+ block = $('div.DTFC_RightHeadBlocker', nSWrapper)[0];
21092
+ block.style.width = oOverflow.bar+"px";
21093
+ block.style.right = -oOverflow.bar+"px";
21094
+ this.dom.grid.right.headBlock = block;
21095
+
21096
+ block = $('div.DTFC_RightFootBlocker', nSWrapper)[0];
21097
+ block.style.width = oOverflow.bar+"px";
21098
+ block.style.right = -oOverflow.bar+"px";
21099
+ this.dom.grid.right.footBlock = block;
21100
+
21101
+ nSWrapper.appendChild( nRight );
21102
+ }
21103
+
21104
+ if ( this.s.dt.nTFoot )
21105
+ {
21106
+ this.dom.footer = this.s.dt.nTFoot.parentNode;
21107
+ if ( this.s.iLeftColumns > 0 )
21108
+ {
21109
+ this.dom.grid.left.foot = nLeft.childNodes[2];
21110
+ }
21111
+ if ( this.s.iRightColumns > 0 )
21112
+ {
21113
+ this.dom.grid.right.foot = nRight.childNodes[2];
21114
+ }
21115
+ }
21116
+
21117
+ // RTL support - swap the position of the left and right columns (#48)
21118
+ if ( this.s.rtl ) {
21119
+ $('div.DTFC_RightHeadBlocker', nSWrapper).css( {
21120
+ left: -oOverflow.bar+'px',
21121
+ right: ''
21122
+ } );
21123
+ }
21124
+ },
21125
+
21126
+
21127
+ /**
21128
+ * Style and position the grid used for the FixedColumns layout
21129
+ * @returns {void}
21130
+ * @private
21131
+ */
21132
+ "_fnGridLayout": function ()
21133
+ {
21134
+ var that = this;
21135
+ var oGrid = this.dom.grid;
21136
+ var iWidth = $(oGrid.wrapper).width();
21137
+ var iBodyHeight = this.s.dt.nTable.parentNode.offsetHeight;
21138
+ var iFullHeight = this.s.dt.nTable.parentNode.parentNode.offsetHeight;
21139
+ var oOverflow = this._fnDTOverflow();
21140
+ var iLeftWidth = this.s.iLeftWidth;
21141
+ var iRightWidth = this.s.iRightWidth;
21142
+ var rtl = $(this.dom.body).css('direction') === 'rtl';
21143
+ var wrapper;
21144
+ var scrollbarAdjust = function ( node, width ) {
21145
+ if ( ! oOverflow.bar ) {
21146
+ // If there is no scrollbar (Macs) we need to hide the auto scrollbar
21147
+ node.style.width = (width+20)+"px";
21148
+ node.style.paddingRight = "20px";
21149
+ node.style.boxSizing = "border-box";
21150
+ }
21151
+ else if ( that._firefoxScrollError() ) {
21152
+ // See the above function for why this is required
21153
+ if ( $(node).height() > 34 ) {
21154
+ node.style.width = (width+oOverflow.bar)+"px";
21155
+ }
21156
+ }
21157
+ else {
21158
+ // Otherwise just overflow by the scrollbar
21159
+ node.style.width = (width+oOverflow.bar)+"px";
21160
+ }
21161
+ };
21162
+
21163
+ // When x scrolling - don't paint the fixed columns over the x scrollbar
21164
+ if ( oOverflow.x )
21165
+ {
21166
+ iBodyHeight -= oOverflow.bar;
21167
+ }
21168
+
21169
+ oGrid.wrapper.style.height = iFullHeight+"px";
21170
+
21171
+ if ( this.s.iLeftColumns > 0 )
21172
+ {
21173
+ wrapper = oGrid.left.wrapper;
21174
+ wrapper.style.width = iLeftWidth+'px';
21175
+ wrapper.style.height = '1px';
21176
+
21177
+ // Swap the position of the left and right columns for rtl (#48)
21178
+ // This is always up against the edge, scrollbar on the far side
21179
+ if ( rtl ) {
21180
+ wrapper.style.left = '';
21181
+ wrapper.style.right = 0;
21182
+ }
21183
+ else {
21184
+ wrapper.style.left = 0;
21185
+ wrapper.style.right = '';
21186
+ }
21187
+
21188
+ oGrid.left.body.style.height = iBodyHeight+"px";
21189
+ if ( oGrid.left.foot ) {
21190
+ oGrid.left.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; // shift footer for scrollbar
21191
+ }
21192
+
21193
+ scrollbarAdjust( oGrid.left.liner, iLeftWidth );
21194
+ oGrid.left.liner.style.height = iBodyHeight+"px";
21195
+ oGrid.left.liner.style.maxHeight = iBodyHeight+"px";
21196
+ }
21197
+
21198
+ if ( this.s.iRightColumns > 0 )
21199
+ {
21200
+ wrapper = oGrid.right.wrapper;
21201
+ wrapper.style.width = iRightWidth+'px';
21202
+ wrapper.style.height = '1px';
21203
+
21204
+ // Need to take account of the vertical scrollbar
21205
+ if ( this.s.rtl ) {
21206
+ wrapper.style.left = oOverflow.y ? oOverflow.bar+'px' : 0;
21207
+ wrapper.style.right = '';
21208
+ }
21209
+ else {
21210
+ wrapper.style.left = '';
21211
+ wrapper.style.right = oOverflow.y ? oOverflow.bar+'px' : 0;
21212
+ }
21213
+
21214
+ oGrid.right.body.style.height = iBodyHeight+"px";
21215
+ if ( oGrid.right.foot ) {
21216
+ oGrid.right.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px";
21217
+ }
21218
+
21219
+ scrollbarAdjust( oGrid.right.liner, iRightWidth );
21220
+ oGrid.right.liner.style.height = iBodyHeight+"px";
21221
+ oGrid.right.liner.style.maxHeight = iBodyHeight+"px";
21222
+
21223
+ oGrid.right.headBlock.style.display = oOverflow.y ? 'block' : 'none';
21224
+ oGrid.right.footBlock.style.display = oOverflow.y ? 'block' : 'none';
21225
+ }
21226
+ },
21227
+
21228
+
21229
+ /**
21230
+ * Get information about the DataTable's scrolling state - specifically if the table is scrolling
21231
+ * on either the x or y axis, and also the scrollbar width.
21232
+ * @returns {object} Information about the DataTables scrolling state with the properties:
21233
+ * 'x', 'y' and 'bar'
21234
+ * @private
21235
+ */
21236
+ "_fnDTOverflow": function ()
21237
+ {
21238
+ var nTable = this.s.dt.nTable;
21239
+ var nTableScrollBody = nTable.parentNode;
21240
+ var out = {
21241
+ "x": false,
21242
+ "y": false,
21243
+ "bar": this.s.dt.oScroll.iBarWidth
21244
+ };
21245
+
21246
+ if ( nTable.offsetWidth > nTableScrollBody.clientWidth )
21247
+ {
21248
+ out.x = true;
21249
+ }
21250
+
21251
+ if ( nTable.offsetHeight > nTableScrollBody.clientHeight )
21252
+ {
21253
+ out.y = true;
21254
+ }
21255
+
21256
+ return out;
21257
+ },
21258
+
21259
+
21260
+ /**
21261
+ * Clone and position the fixed columns
21262
+ * @returns {void}
21263
+ * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
21264
+ * @private
21265
+ */
21266
+ "_fnDraw": function ( bAll )
21267
+ {
21268
+ this._fnGridLayout();
21269
+ this._fnCloneLeft( bAll );
21270
+ this._fnCloneRight( bAll );
21271
+
21272
+ $(this.dom.scroller).trigger('scroll');
21273
+
21274
+ /* Draw callback function */
21275
+ if ( this.s.fnDrawCallback !== null )
21276
+ {
21277
+ this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right );
21278
+ }
21279
+
21280
+ /* Event triggering */
21281
+ $(this).trigger( 'draw.dtfc', {
21282
+ "leftClone": this.dom.clone.left,
21283
+ "rightClone": this.dom.clone.right
21284
+ } );
21285
+ },
21286
+
21287
+
21288
+ /**
21289
+ * Clone the right columns
21290
+ * @returns {void}
21291
+ * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
21292
+ * @private
21293
+ */
21294
+ "_fnCloneRight": function ( bAll )
21295
+ {
21296
+ if ( this.s.iRightColumns <= 0 ) {
21297
+ return;
21298
+ }
21299
+
21300
+ var that = this,
21301
+ i, jq,
21302
+ aiColumns = [];
21303
+
21304
+ for ( i=this.s.iTableColumns-this.s.iRightColumns ; i<this.s.iTableColumns ; i++ ) {
21305
+ if ( this.s.dt.aoColumns[i].bVisible ) {
21306
+ aiColumns.push( i );
21307
+ }
21308
+ }
21309
+
21310
+ this._fnClone( this.dom.clone.right, this.dom.grid.right, aiColumns, bAll );
21311
+ },
21312
+
21313
+
21314
+ /**
21315
+ * Clone the left columns
21316
+ * @returns {void}
21317
+ * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
21318
+ * @private
21319
+ */
21320
+ "_fnCloneLeft": function ( bAll )
21321
+ {
21322
+ if ( this.s.iLeftColumns <= 0 ) {
21323
+ return;
21324
+ }
21325
+
21326
+ var that = this,
21327
+ i, jq,
21328
+ aiColumns = [];
21329
+
21330
+ for ( i=0 ; i<this.s.iLeftColumns ; i++ ) {
21331
+ if ( this.s.dt.aoColumns[i].bVisible ) {
21332
+ aiColumns.push( i );
21333
+ }
21334
+ }
21335
+
21336
+ this._fnClone( this.dom.clone.left, this.dom.grid.left, aiColumns, bAll );
21337
+ },
21338
+
21339
+
21340
+ /**
21341
+ * Make a copy of the layout object for a header or footer element from DataTables. Note that
21342
+ * this method will clone the nodes in the layout object.
21343
+ * @returns {Array} Copy of the layout array
21344
+ * @param {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter)
21345
+ * @param {Object} aiColumns Columns to copy
21346
+ * @param {boolean} events Copy cell events or not
21347
+ * @private
21348
+ */
21349
+ "_fnCopyLayout": function ( aoOriginal, aiColumns, events )
21350
+ {
21351
+ var aReturn = [];
21352
+ var aClones = [];
21353
+ var aCloned = [];
21354
+
21355
+ for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ )
21356
+ {
21357
+ var aRow = [];
21358
+ aRow.nTr = $(aoOriginal[i].nTr).clone(events, false)[0];
21359
+
21360
+ for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ )
21361
+ {
21362
+ if ( $.inArray( j, aiColumns ) === -1 )
21363
+ {
21364
+ continue;
21365
+ }
21366
+
21367
+ var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned );
21368
+ if ( iCloned === -1 )
21369
+ {
21370
+ var nClone = $(aoOriginal[i][j].cell).clone(events, false)[0];
21371
+ aClones.push( nClone );
21372
+ aCloned.push( aoOriginal[i][j].cell );
21373
+
21374
+ aRow.push( {
21375
+ "cell": nClone,
21376
+ "unique": aoOriginal[i][j].unique
21377
+ } );
21378
+ }
21379
+ else
21380
+ {
21381
+ aRow.push( {
21382
+ "cell": aClones[ iCloned ],
21383
+ "unique": aoOriginal[i][j].unique
21384
+ } );
21385
+ }
21386
+ }
21387
+
21388
+ aReturn.push( aRow );
21389
+ }
21390
+
21391
+ return aReturn;
21392
+ },
21393
+
21394
+
21395
+ /**
21396
+ * Clone the DataTable nodes and place them in the DOM (sized correctly)
21397
+ * @returns {void}
21398
+ * @param {Object} oClone Object containing the header, footer and body cloned DOM elements
21399
+ * @param {Object} oGrid Grid object containing the display grid elements for the cloned
21400
+ * column (left or right)
21401
+ * @param {Array} aiColumns Column indexes which should be operated on from the DataTable
21402
+ * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
21403
+ * @private
21404
+ */
21405
+ "_fnClone": function ( oClone, oGrid, aiColumns, bAll )
21406
+ {
21407
+ var that = this,
21408
+ i, iLen, j, jLen, jq, nTarget, iColumn, nClone, iIndex, aoCloneLayout,
21409
+ jqCloneThead, aoFixedHeader,
21410
+ dt = this.s.dt;
21411
+
21412
+ /*
21413
+ * Header
21414
+ */
21415
+ if ( bAll )
21416
+ {
21417
+ $(oClone.header).remove();
21418
+
21419
+ oClone.header = $(this.dom.header).clone(true, false)[0];
21420
+ oClone.header.className += " DTFC_Cloned";
21421
+ oClone.header.style.width = "100%";
21422
+ oGrid.head.appendChild( oClone.header );
21423
+
21424
+ /* Copy the DataTables layout cache for the header for our floating column */
21425
+ aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, true );
21426
+ jqCloneThead = $('>thead', oClone.header);
21427
+ jqCloneThead.empty();
21428
+
21429
+ /* Add the created cloned TR elements to the table */
21430
+ for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
21431
+ {
21432
+ jqCloneThead[0].appendChild( aoCloneLayout[i].nTr );
21433
+ }
21434
+
21435
+ /* Use the handy _fnDrawHead function in DataTables to do the rowspan/colspan
21436
+ * calculations for us
21437
+ */
21438
+ dt.oApi._fnDrawHead( dt, aoCloneLayout, true );
21439
+ }
21440
+ else
21441
+ {
21442
+ /* To ensure that we copy cell classes exactly, regardless of colspan, multiple rows
21443
+ * etc, we make a copy of the header from the DataTable again, but don't insert the
21444
+ * cloned cells, just copy the classes across. To get the matching layout for the
21445
+ * fixed component, we use the DataTables _fnDetectHeader method, allowing 1:1 mapping
21446
+ */
21447
+ aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, false );
21448
+ aoFixedHeader=[];
21449
+
21450
+ dt.oApi._fnDetectHeader( aoFixedHeader, $('>thead', oClone.header)[0] );
21451
+
21452
+ for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
21453
+ {
21454
+ for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
21455
+ {
21456
+ aoFixedHeader[i][j].cell.className = aoCloneLayout[i][j].cell.className;
21457
+
21458
+ // If jQuery UI theming is used we need to copy those elements as well
21459
+ $('span.DataTables_sort_icon', aoFixedHeader[i][j].cell).each( function () {
21460
+ this.className = $('span.DataTables_sort_icon', aoCloneLayout[i][j].cell)[0].className;
21461
+ } );
21462
+ }
21463
+ }
21464
+ }
21465
+ this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header );
21466
+
21467
+ /*
21468
+ * Body
21469
+ */
21470
+ if ( this.s.sHeightMatch == 'auto' )
21471
+ {
21472
+ /* Remove any heights which have been applied already and let the browser figure it out */
21473
+ $('>tbody>tr', that.dom.body).css('height', 'auto');
21474
+ }
21475
+
21476
+ if ( oClone.body !== null )
21477
+ {
21478
+ $(oClone.body).remove();
21479
+ oClone.body = null;
21480
+ }
21481
+
21482
+ oClone.body = $(this.dom.body).clone(true)[0];
21483
+ oClone.body.className += " DTFC_Cloned";
21484
+ oClone.body.style.paddingBottom = dt.oScroll.iBarWidth+"px";
21485
+ oClone.body.style.marginBottom = (dt.oScroll.iBarWidth*2)+"px"; /* For IE */
21486
+ if ( oClone.body.getAttribute('id') !== null )
21487
+ {
21488
+ oClone.body.removeAttribute('id');
21489
+ }
21490
+
21491
+ $('>thead>tr', oClone.body).empty();
21492
+ $('>tfoot', oClone.body).remove();
21493
+
21494
+ var nBody = $('tbody', oClone.body)[0];
21495
+ $(nBody).empty();
21496
+ if ( dt.aiDisplay.length > 0 )
21497
+ {
21498
+ /* Copy the DataTables' header elements to force the column width in exactly the
21499
+ * same way that DataTables does it - have the header element, apply the width and
21500
+ * colapse it down
21501
+ */
21502
+ var nInnerThead = $('>thead>tr', oClone.body)[0];
21503
+ for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
21504
+ {
21505
+ iColumn = aiColumns[iIndex];
21506
+
21507
+ nClone = $(dt.aoColumns[iColumn].nTh).clone(true)[0];
21508
+ nClone.innerHTML = "";
21509
+
21510
+ var oStyle = nClone.style;
21511
+ oStyle.paddingTop = "0";
21512
+ oStyle.paddingBottom = "0";
21513
+ oStyle.borderTopWidth = "0";
21514
+ oStyle.borderBottomWidth = "0";
21515
+ oStyle.height = 0;
21516
+ oStyle.width = that.s.aiInnerWidths[iColumn]+"px";
21517
+
21518
+ nInnerThead.appendChild( nClone );
21519
+ }
21520
+
21521
+ /* Add in the tbody elements, cloning form the master table */
21522
+ $('>tbody>tr', that.dom.body).each( function (z) {
21523
+ var i = that.s.dt.oFeatures.bServerSide===false ?
21524
+ that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z;
21525
+ var aTds = that.s.dt.aoData[ i ].anCells || $(this).children('td, th');
21526
+
21527
+ var n = this.cloneNode(false);
21528
+ n.removeAttribute('id');
21529
+ n.setAttribute( 'data-dt-row', i );
21530
+
21531
+ for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
21532
+ {
21533
+ iColumn = aiColumns[iIndex];
21534
+
21535
+ if ( aTds.length > 0 )
21536
+ {
21537
+ nClone = $( aTds[iColumn] ).clone(true, true)[0];
21538
+ nClone.removeAttribute( 'id' );
21539
+ nClone.setAttribute( 'data-dt-row', i );
21540
+ nClone.setAttribute( 'data-dt-column', iColumn );
21541
+ n.appendChild( nClone );
21542
+ }
21543
+ }
21544
+ nBody.appendChild( n );
21545
+ } );
21546
+ }
21547
+ else
21548
+ {
21549
+ $('>tbody>tr', that.dom.body).each( function (z) {
21550
+ nClone = this.cloneNode(true);
21551
+ nClone.className += ' DTFC_NoData';
21552
+ $('td', nClone).html('');
21553
+ nBody.appendChild( nClone );
21554
+ } );
21555
+ }
21556
+
21557
+ oClone.body.style.width = "100%";
21558
+ oClone.body.style.margin = "0";
21559
+ oClone.body.style.padding = "0";
21560
+
21561
+ // Interop with Scroller - need to use a height forcing element in the
21562
+ // scrolling area in the same way that Scroller does in the body scroll.
21563
+ if ( dt.oScroller !== undefined )
21564
+ {
21565
+ var scrollerForcer = dt.oScroller.dom.force;
21566
+
21567
+ if ( ! oGrid.forcer ) {
21568
+ oGrid.forcer = scrollerForcer.cloneNode( true );
21569
+ oGrid.liner.appendChild( oGrid.forcer );
21570
+ }
21571
+ else {
21572
+ oGrid.forcer.style.height = scrollerForcer.style.height;
21573
+ }
21574
+ }
21575
+
21576
+ oGrid.liner.appendChild( oClone.body );
21577
+
21578
+ this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body );
21579
+
21580
+ /*
21581
+ * Footer
21582
+ */
21583
+ if ( dt.nTFoot !== null )
21584
+ {
21585
+ if ( bAll )
21586
+ {
21587
+ if ( oClone.footer !== null )
21588
+ {
21589
+ oClone.footer.parentNode.removeChild( oClone.footer );
21590
+ }
21591
+ oClone.footer = $(this.dom.footer).clone(true, true)[0];
21592
+ oClone.footer.className += " DTFC_Cloned";
21593
+ oClone.footer.style.width = "100%";
21594
+ oGrid.foot.appendChild( oClone.footer );
21595
+
21596
+ /* Copy the footer just like we do for the header */
21597
+ aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, true );
21598
+ var jqCloneTfoot = $('>tfoot', oClone.footer);
21599
+ jqCloneTfoot.empty();
21600
+
21601
+ for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
21602
+ {
21603
+ jqCloneTfoot[0].appendChild( aoCloneLayout[i].nTr );
21604
+ }
21605
+ dt.oApi._fnDrawHead( dt, aoCloneLayout, true );
21606
+ }
21607
+ else
21608
+ {
21609
+ aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, false );
21610
+ var aoCurrFooter=[];
21611
+
21612
+ dt.oApi._fnDetectHeader( aoCurrFooter, $('>tfoot', oClone.footer)[0] );
21613
+
21614
+ for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
21615
+ {
21616
+ for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
21617
+ {
21618
+ aoCurrFooter[i][j].cell.className = aoCloneLayout[i][j].cell.className;
21619
+ }
21620
+ }
21621
+ }
21622
+ this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer );
21623
+ }
21624
+
21625
+ /* Equalise the column widths between the header footer and body - body get's priority */
21626
+ var anUnique = dt.oApi._fnGetUniqueThs( dt, $('>thead', oClone.header)[0] );
21627
+ $(anUnique).each( function (i) {
21628
+ iColumn = aiColumns[i];
21629
+ this.style.width = that.s.aiInnerWidths[iColumn]+"px";
21630
+ } );
21631
+
21632
+ if ( that.s.dt.nTFoot !== null )
21633
+ {
21634
+ anUnique = dt.oApi._fnGetUniqueThs( dt, $('>tfoot', oClone.footer)[0] );
21635
+ $(anUnique).each( function (i) {
21636
+ iColumn = aiColumns[i];
21637
+ this.style.width = that.s.aiInnerWidths[iColumn]+"px";
21638
+ } );
21639
+ }
21640
+ },
21641
+
21642
+
21643
+ /**
21644
+ * From a given table node (THEAD etc), get a list of TR direct child elements
21645
+ * @param {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element)
21646
+ * @returns {Array} List of TR elements found
21647
+ * @private
21648
+ */
21649
+ "_fnGetTrNodes": function ( nIn )
21650
+ {
21651
+ var aOut = [];
21652
+ for ( var i=0, iLen=nIn.childNodes.length ; i<iLen ; i++ )
21653
+ {
21654
+ if ( nIn.childNodes[i].nodeName.toUpperCase() == "TR" )
21655
+ {
21656
+ aOut.push( nIn.childNodes[i] );
21657
+ }
21658
+ }
21659
+ return aOut;
21660
+ },
21661
+
21662
+
21663
+ /**
21664
+ * Equalise the heights of the rows in a given table node in a cross browser way
21665
+ * @returns {void}
21666
+ * @param {String} nodeName Node type - thead, tbody or tfoot
21667
+ * @param {Node} original Original node to take the heights from
21668
+ * @param {Node} clone Copy the heights to
21669
+ * @private
21670
+ */
21671
+ "_fnEqualiseHeights": function ( nodeName, original, clone )
21672
+ {
21673
+ if ( this.s.sHeightMatch == 'none' && nodeName !== 'thead' && nodeName !== 'tfoot' )
21674
+ {
21675
+ return;
21676
+ }
21677
+
21678
+ var that = this,
21679
+ i, iLen, iHeight, iHeight2, iHeightOriginal, iHeightClone,
21680
+ rootOriginal = original.getElementsByTagName(nodeName)[0],
21681
+ rootClone = clone.getElementsByTagName(nodeName)[0],
21682
+ jqBoxHack = $('>'+nodeName+'>tr:eq(0)', original).children(':first'),
21683
+ iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),
21684
+ anOriginal = this._fnGetTrNodes( rootOriginal ),
21685
+ anClone = this._fnGetTrNodes( rootClone ),
21686
+ heights = [];
21687
+
21688
+ for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
21689
+ {
21690
+ iHeightOriginal = anOriginal[i].offsetHeight;
21691
+ iHeightClone = anClone[i].offsetHeight;
21692
+ iHeight = iHeightClone > iHeightOriginal ? iHeightClone : iHeightOriginal;
21693
+
21694
+ if ( this.s.sHeightMatch == 'semiauto' )
21695
+ {
21696
+ anOriginal[i]._DTTC_iHeight = iHeight;
21697
+ }
21698
+
21699
+ heights.push( iHeight );
21700
+ }
21701
+
21702
+ for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
21703
+ {
21704
+ anClone[i].style.height = heights[i]+"px";
21705
+ anOriginal[i].style.height = heights[i]+"px";
21706
+ }
21707
+ },
21708
+
21709
+ /**
21710
+ * Determine if the UA suffers from Firefox's overflow:scroll scrollbars
21711
+ * not being shown bug.
21712
+ *
21713
+ * Firefox doesn't draw scrollbars, even if it is told to using
21714
+ * overflow:scroll, if the div is less than 34px height. See bugs 292284 and
21715
+ * 781885. Using UA detection here since this is particularly hard to detect
21716
+ * using objects - its a straight up rendering error in Firefox.
21717
+ *
21718
+ * @return {boolean} True if Firefox error is present, false otherwise
21719
+ */
21720
+ _firefoxScrollError: function () {
21721
+ if ( _firefoxScroll === undefined ) {
21722
+ var test = $('<div/>')
21723
+ .css( {
21724
+ position: 'absolute',
21725
+ top: 0,
21726
+ left: 0,
21727
+ height: 10,
21728
+ width: 50,
21729
+ overflow: 'scroll'
21730
+ } )
21731
+ .appendTo( 'body' );
21732
+
21733
+ // Make sure this doesn't apply on Macs with 0 width scrollbars
21734
+ _firefoxScroll = (
21735
+ test[0].clientWidth === test[0].offsetWidth && this._fnDTOverflow().bar !== 0
21736
+ );
21737
+
21738
+ test.remove();
21739
+ }
21740
+
21741
+ return _firefoxScroll;
21742
+ }
21743
+ } );
21744
+
21745
+
21746
+
21747
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21748
+ * Statics
21749
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21750
+
21751
+ /**
21752
+ * FixedColumns default settings for initialisation
21753
+ * @name FixedColumns.defaults
21754
+ * @namespace
21755
+ * @static
21756
+ */
21757
+ FixedColumns.defaults = /** @lends FixedColumns.defaults */{
21758
+ /**
21759
+ * Number of left hand columns to fix in position
21760
+ * @type int
21761
+ * @default 1
21762
+ * @static
21763
+ * @example
21764
+ * var = $('#example').dataTable( {
21765
+ * "scrollX": "100%"
21766
+ * } );
21767
+ * new $.fn.dataTable.fixedColumns( table, {
21768
+ * "leftColumns": 2
21769
+ * } );
21770
+ */
21771
+ "iLeftColumns": 1,
21772
+
21773
+ /**
21774
+ * Number of right hand columns to fix in position
21775
+ * @type int
21776
+ * @default 0
21777
+ * @static
21778
+ * @example
21779
+ * var table = $('#example').dataTable( {
21780
+ * "scrollX": "100%"
21781
+ * } );
21782
+ * new $.fn.dataTable.fixedColumns( table, {
21783
+ * "rightColumns": 1
21784
+ * } );
21785
+ */
21786
+ "iRightColumns": 0,
21787
+
21788
+ /**
21789
+ * Draw callback function which is called when FixedColumns has redrawn the fixed assets
21790
+ * @type function(object, object):void
21791
+ * @default null
21792
+ * @static
21793
+ * @example
21794
+ * var table = $('#example').dataTable( {
21795
+ * "scrollX": "100%"
21796
+ * } );
21797
+ * new $.fn.dataTable.fixedColumns( table, {
21798
+ * "drawCallback": function () {
21799
+ * alert( "FixedColumns redraw" );
21800
+ * }
21801
+ * } );
21802
+ */
21803
+ "fnDrawCallback": null,
21804
+
21805
+ /**
21806
+ * Height matching algorthim to use. This can be "none" which will result in no height
21807
+ * matching being applied by FixedColumns (height matching could be forced by CSS in this
21808
+ * case), "semiauto" whereby the height calculation will be performed once, and the result
21809
+ * cached to be used again (fnRecalculateHeight can be used to force recalculation), or
21810
+ * "auto" when height matching is performed on every draw (slowest but must accurate)
21811
+ * @type string
21812
+ * @default semiauto
21813
+ * @static
21814
+ * @example
21815
+ * var table = $('#example').dataTable( {
21816
+ * "scrollX": "100%"
21817
+ * } );
21818
+ * new $.fn.dataTable.fixedColumns( table, {
21819
+ * "heightMatch": "auto"
21820
+ * } );
21821
+ */
21822
+ "sHeightMatch": "semiauto"
21823
+ };
21824
+
21825
+
21826
+
21827
+
21828
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21829
+ * Constants
21830
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21831
+
21832
+ /**
21833
+ * FixedColumns version
21834
+ * @name FixedColumns.version
21835
+ * @type String
21836
+ * @default See code
21837
+ * @static
21838
+ */
21839
+ FixedColumns.version = "3.3.2";
21840
+
21841
+
21842
+
21843
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21844
+ * DataTables API integration
21845
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21846
+
21847
+ DataTable.Api.register( 'fixedColumns()', function () {
21848
+ return this;
21849
+ } );
21850
+
21851
+ DataTable.Api.register( 'fixedColumns().update()', function () {
21852
+ return this.iterator( 'table', function ( ctx ) {
21853
+ if ( ctx._oFixedColumns ) {
21854
+ ctx._oFixedColumns.fnUpdate();
21855
+ }
21856
+ } );
21857
+ } );
21858
+
21859
+ DataTable.Api.register( 'fixedColumns().relayout()', function () {
21860
+ return this.iterator( 'table', function ( ctx ) {
21861
+ if ( ctx._oFixedColumns ) {
21862
+ ctx._oFixedColumns.fnRedrawLayout();
21863
+ }
21864
+ } );
21865
+ } );
21866
+
21867
+ DataTable.Api.register( 'rows().recalcHeight()', function () {
21868
+ return this.iterator( 'row', function ( ctx, idx ) {
21869
+ if ( ctx._oFixedColumns ) {
21870
+ ctx._oFixedColumns.fnRecalculateHeight( this.row(idx).node() );
21871
+ }
21872
+ } );
21873
+ } );
21874
+
21875
+ DataTable.Api.register( 'fixedColumns().rowIndex()', function ( row ) {
21876
+ row = $(row);
21877
+
21878
+ return row.parents('.DTFC_Cloned').length ?
21879
+ this.rows( { page: 'current' } ).indexes()[ row.index() ] :
21880
+ this.row( row ).index();
21881
+ } );
21882
+
21883
+ DataTable.Api.register( 'fixedColumns().cellIndex()', function ( cell ) {
21884
+ cell = $(cell);
21885
+
21886
+ if ( cell.parents('.DTFC_Cloned').length ) {
21887
+ var rowClonedIdx = cell.parent().index();
21888
+ var rowIdx = this.rows( { page: 'current' } ).indexes()[ rowClonedIdx ];
21889
+ var columnIdx;
21890
+
21891
+ if ( cell.parents('.DTFC_LeftWrapper').length ) {
21892
+ columnIdx = cell.index();
21893
+ }
21894
+ else {
21895
+ var columns = this.columns().flatten().length;
21896
+ columnIdx = columns - this.context[0]._oFixedColumns.s.iRightColumns + cell.index();
21897
+ }
21898
+
21899
+ return {
21900
+ row: rowIdx,
21901
+ column: this.column.index( 'toData', columnIdx ),
21902
+ columnVisible: columnIdx
21903
+ };
21904
+ }
21905
+ else {
21906
+ return this.cell( cell ).index();
21907
+ }
21908
+ } );
21909
+
21910
+ DataTable.Api.registerPlural( 'cells().fixedNodes()', 'cell().fixedNode()', function () {
21911
+ return this.iterator( 'cell', function ( settings, row, column ) {
21912
+ return settings._oFixedColumns
21913
+ ? settings._oFixedColumns.fnToFixedNode( row, column )
21914
+ : this.cell(row, column).node();
21915
+ }, 1 );
21916
+ } );
21917
+
21918
+
21919
+
21920
+
21921
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
21922
+ * Initialisation
21923
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21924
+
21925
+ // Attach a listener to the document which listens for DataTables initialisation
21926
+ // events so we can automatically initialise
21927
+ $(document).on( 'init.dt.fixedColumns', function (e, settings) {
21928
+ if ( e.namespace !== 'dt' ) {
21929
+ return;
21930
+ }
21931
+
21932
+ var init = settings.oInit.fixedColumns;
21933
+ var defaults = DataTable.defaults.fixedColumns;
21934
+
21935
+ if ( init || defaults ) {
21936
+ var opts = $.extend( {}, init, defaults );
21937
+
21938
+ if ( init !== false ) {
21939
+ new FixedColumns( settings, opts );
21940
+ }
21941
+ }
21942
+ } );
21943
+
21944
+
21945
+
21946
+ // Make FixedColumns accessible from the DataTables instance
21947
+ $.fn.dataTable.FixedColumns = FixedColumns;
21948
+ $.fn.DataTable.FixedColumns = FixedColumns;
21949
+
21950
+ return FixedColumns;
21951
+ }));
21952
+
21953
+
21954
+ /*! FixedHeader 3.1.8
21955
+ * ©2009-2021 SpryMedia Ltd - datatables.net/license
21956
+ */
21957
+
21958
+ /**
21959
+ * @summary FixedHeader
21960
+ * @description Fix a table's header or footer, so it is always visible while
21961
+ * scrolling
21962
+ * @version 3.1.8
21963
+ * @file dataTables.fixedHeader.js
21964
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
21965
+ * @contact www.sprymedia.co.uk/contact
21966
+ * @copyright Copyright 2009-2021 SpryMedia Ltd.
21967
+ *
21968
+ * This source file is free software, available under the following license:
21969
+ * MIT license - http://datatables.net/license/mit
21970
+ *
21971
+ * This source file is distributed in the hope that it will be useful, but
21972
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21973
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
21974
+ *
21975
+ * For details please refer to: http://www.datatables.net
21976
+ */
21977
+
21978
+ (function( factory ){
21979
+ if ( typeof define === 'function' && define.amd ) {
21980
+ // AMD
21981
+ define( ['jquery', 'datatables.net'], function ( $ ) {
21982
+ return factory( $, window, document );
21983
+ } );
21984
+ }
21985
+ else if ( typeof exports === 'object' ) {
21986
+ // CommonJS
21987
+ module.exports = function (root, $) {
21988
+ if ( ! root ) {
21989
+ root = window;
21990
+ }
21991
+
21992
+ if ( ! $ || ! $.fn.dataTable ) {
21993
+ $ = require('datatables.net')(root, $).$;
21994
+ }
21995
+
21996
+ return factory( $, root, root.document );
21997
+ };
21998
+ }
21999
+ else {
22000
+ // Browser
22001
+ factory( jQuery, window, document );
22002
+ }
22003
+ }(function( $, window, document, undefined ) {
22004
+ 'use strict';
22005
+ var DataTable = $.fn.dataTable;
22006
+
22007
+
22008
+ var _instCounter = 0;
22009
+
22010
+ var FixedHeader = function ( dt, config ) {
22011
+ // Sanity check - you just know it will happen
22012
+ if ( ! (this instanceof FixedHeader) ) {
22013
+ throw "FixedHeader must be initialised with the 'new' keyword.";
22014
+ }
22015
+
22016
+ // Allow a boolean true for defaults
22017
+ if ( config === true ) {
22018
+ config = {};
22019
+ }
22020
+
22021
+ dt = new DataTable.Api( dt );
22022
+
22023
+ this.c = $.extend( true, {}, FixedHeader.defaults, config );
22024
+
22025
+ this.s = {
22026
+ dt: dt,
22027
+ position: {
22028
+ theadTop: 0,
22029
+ tbodyTop: 0,
22030
+ tfootTop: 0,
22031
+ tfootBottom: 0,
22032
+ width: 0,
22033
+ left: 0,
22034
+ tfootHeight: 0,
22035
+ theadHeight: 0,
22036
+ windowHeight: $(window).height(),
22037
+ visible: true
22038
+ },
22039
+ headerMode: null,
22040
+ footerMode: null,
22041
+ autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
22042
+ namespace: '.dtfc'+(_instCounter++),
22043
+ scrollLeft: {
22044
+ header: -1,
22045
+ footer: -1
22046
+ },
22047
+ enable: true
22048
+ };
22049
+
22050
+ this.dom = {
22051
+ floatingHeader: null,
22052
+ thead: $(dt.table().header()),
22053
+ tbody: $(dt.table().body()),
22054
+ tfoot: $(dt.table().footer()),
22055
+ header: {
22056
+ host: null,
22057
+ floating: null,
22058
+ placeholder: null
22059
+ },
22060
+ footer: {
22061
+ host: null,
22062
+ floating: null,
22063
+ placeholder: null
22064
+ }
22065
+ };
22066
+
22067
+ this.dom.header.host = this.dom.thead.parent();
22068
+ this.dom.footer.host = this.dom.tfoot.parent();
22069
+
22070
+ var dtSettings = dt.settings()[0];
22071
+ if ( dtSettings._fixedHeader ) {
22072
+ throw "FixedHeader already initialised on table "+dtSettings.nTable.id;
22073
+ }
22074
+
22075
+ dtSettings._fixedHeader = this;
22076
+
22077
+ this._constructor();
22078
+ };
22079
+
22080
+
22081
+ /*
22082
+ * Variable: FixedHeader
22083
+ * Purpose: Prototype for FixedHeader
22084
+ * Scope: global
22085
+ */
22086
+ $.extend( FixedHeader.prototype, {
22087
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22088
+ * API methods
22089
+ */
22090
+
22091
+ /**
22092
+ * Kill off FH and any events
22093
+ */
22094
+ destroy: function () {
22095
+ this.s.dt.off( '.dtfc' );
22096
+ $(window).off( this.s.namespace );
22097
+
22098
+ if ( this.c.header ) {
22099
+ this._modeChange( 'in-place', 'header', true );
22100
+ }
22101
+
22102
+ if ( this.c.footer && this.dom.tfoot.length ) {
22103
+ this._modeChange( 'in-place', 'footer', true );
22104
+ }
22105
+ },
22106
+
22107
+ /**
22108
+ * Enable / disable the fixed elements
22109
+ *
22110
+ * @param {boolean} enable `true` to enable, `false` to disable
22111
+ */
22112
+ enable: function ( enable, update )
22113
+ {
22114
+ this.s.enable = enable;
22115
+
22116
+ if ( update || update === undefined ) {
22117
+ this._positions();
22118
+ this._scroll( true );
22119
+ }
22120
+ },
22121
+
22122
+ /**
22123
+ * Get enabled status
22124
+ */
22125
+ enabled: function ()
22126
+ {
22127
+ return this.s.enable;
22128
+ },
22129
+
22130
+ /**
22131
+ * Set header offset
22132
+ *
22133
+ * @param {int} new value for headerOffset
22134
+ */
22135
+ headerOffset: function ( offset )
22136
+ {
22137
+ if ( offset !== undefined ) {
22138
+ this.c.headerOffset = offset;
22139
+ this.update();
22140
+ }
22141
+
22142
+ return this.c.headerOffset;
22143
+ },
22144
+
22145
+ /**
22146
+ * Set footer offset
22147
+ *
22148
+ * @param {int} new value for footerOffset
22149
+ */
22150
+ footerOffset: function ( offset )
22151
+ {
22152
+ if ( offset !== undefined ) {
22153
+ this.c.footerOffset = offset;
22154
+ this.update();
22155
+ }
22156
+
22157
+ return this.c.footerOffset;
22158
+ },
22159
+
22160
+
22161
+ /**
22162
+ * Recalculate the position of the fixed elements and force them into place
22163
+ */
22164
+ update: function ()
22165
+ {
22166
+ var table = this.s.dt.table().node();
22167
+
22168
+ if ( $(table).is(':visible') ) {
22169
+ this.enable( true, false );
22170
+ }
22171
+ else {
22172
+ this.enable( false, false );
22173
+ }
22174
+
22175
+ this._positions();
22176
+ this._scroll( true );
22177
+ },
22178
+
22179
+
22180
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22181
+ * Constructor
22182
+ */
22183
+
22184
+ /**
22185
+ * FixedHeader constructor - adding the required event listeners and
22186
+ * simple initialisation
22187
+ *
22188
+ * @private
22189
+ */
22190
+ _constructor: function ()
22191
+ {
22192
+ var that = this;
22193
+ var dt = this.s.dt;
22194
+
22195
+ $(window)
22196
+ .on( 'scroll'+this.s.namespace, function () {
22197
+ that._scroll();
22198
+ } )
22199
+ .on( 'resize'+this.s.namespace, DataTable.util.throttle( function () {
22200
+ that.s.position.windowHeight = $(window).height();
22201
+ that.update();
22202
+ }, 50 ) );
22203
+
22204
+ var autoHeader = $('.fh-fixedHeader');
22205
+ if ( ! this.c.headerOffset && autoHeader.length ) {
22206
+ this.c.headerOffset = autoHeader.outerHeight();
22207
+ }
22208
+
22209
+ var autoFooter = $('.fh-fixedFooter');
22210
+ if ( ! this.c.footerOffset && autoFooter.length ) {
22211
+ this.c.footerOffset = autoFooter.outerHeight();
22212
+ }
22213
+
22214
+ dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc responsive-display.dt.dtfc', function () {
22215
+ that.update();
22216
+ } );
22217
+
22218
+ dt.on( 'destroy.dtfc', function () {
22219
+ that.destroy();
22220
+ } );
22221
+
22222
+ this._positions();
22223
+ this._scroll();
22224
+ },
22225
+
22226
+
22227
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22228
+ * Private methods
22229
+ */
22230
+
22231
+ /**
22232
+ * Clone a fixed item to act as a place holder for the original element
22233
+ * which is moved into a clone of the table element, and moved around the
22234
+ * document to give the fixed effect.
22235
+ *
22236
+ * @param {string} item 'header' or 'footer'
22237
+ * @param {boolean} force Force the clone to happen, or allow automatic
22238
+ * decision (reuse existing if available)
22239
+ * @private
22240
+ */
22241
+ _clone: function ( item, force )
22242
+ {
22243
+ var dt = this.s.dt;
22244
+ var itemDom = this.dom[ item ];
22245
+ var itemElement = item === 'header' ?
22246
+ this.dom.thead :
22247
+ this.dom.tfoot;
22248
+
22249
+ if ( ! force && itemDom.floating ) {
22250
+ // existing floating element - reuse it
22251
+ itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' );
22252
+ }
22253
+ else {
22254
+ if ( itemDom.floating ) {
22255
+ itemDom.placeholder.remove();
22256
+ this._unsize( item );
22257
+ itemDom.floating.children().detach();
22258
+ itemDom.floating.remove();
22259
+ }
22260
+
22261
+ itemDom.floating = $( dt.table().node().cloneNode( false ) )
22262
+ .css( 'table-layout', 'fixed' )
22263
+ .attr( 'aria-hidden', 'true' )
22264
+ .removeAttr( 'id' )
22265
+ .append( itemElement )
22266
+ .appendTo( 'body' );
22267
+
22268
+ // Insert a fake thead/tfoot into the DataTable to stop it jumping around
22269
+ itemDom.placeholder = itemElement.clone( false );
22270
+ itemDom.placeholder
22271
+ .find( '*[id]' )
22272
+ .removeAttr( 'id' );
22273
+
22274
+ itemDom.host.prepend( itemDom.placeholder );
22275
+
22276
+ // Clone widths
22277
+ this._matchWidths( itemDom.placeholder, itemDom.floating );
22278
+ }
22279
+ },
22280
+
22281
+ /**
22282
+ * Copy widths from the cells in one element to another. This is required
22283
+ * for the footer as the footer in the main table takes its sizes from the
22284
+ * header columns. That isn't present in the footer so to have it still
22285
+ * align correctly, the sizes need to be copied over. It is also required
22286
+ * for the header when auto width is not enabled
22287
+ *
22288
+ * @param {jQuery} from Copy widths from
22289
+ * @param {jQuery} to Copy widths to
22290
+ * @private
22291
+ */
22292
+ _matchWidths: function ( from, to ) {
22293
+ var get = function ( name ) {
22294
+ return $(name, from)
22295
+ .map( function () {
22296
+ return $(this).css('width').replace(/[^\d\.]/g, '') * 1;
22297
+ } ).toArray();
22298
+ };
22299
+
22300
+ var set = function ( name, toWidths ) {
22301
+ $(name, to).each( function ( i ) {
22302
+ $(this).css( {
22303
+ width: toWidths[i],
22304
+ minWidth: toWidths[i]
22305
+ } );
22306
+ } );
22307
+ };
22308
+
22309
+ var thWidths = get( 'th' );
22310
+ var tdWidths = get( 'td' );
22311
+
22312
+ set( 'th', thWidths );
22313
+ set( 'td', tdWidths );
22314
+ },
22315
+
22316
+ /**
22317
+ * Remove assigned widths from the cells in an element. This is required
22318
+ * when inserting the footer back into the main table so the size is defined
22319
+ * by the header columns and also when auto width is disabled in the
22320
+ * DataTable.
22321
+ *
22322
+ * @param {string} item The `header` or `footer`
22323
+ * @private
22324
+ */
22325
+ _unsize: function ( item ) {
22326
+ var el = this.dom[ item ].floating;
22327
+
22328
+ if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) {
22329
+ $('th, td', el).css( {
22330
+ width: '',
22331
+ minWidth: ''
22332
+ } );
22333
+ }
22334
+ else if ( el && item === 'header' ) {
22335
+ $('th, td', el).css( 'min-width', '' );
22336
+ }
22337
+ },
22338
+
22339
+ /**
22340
+ * Reposition the floating elements to take account of horizontal page
22341
+ * scroll
22342
+ *
22343
+ * @param {string} item The `header` or `footer`
22344
+ * @param {int} scrollLeft Document scrollLeft
22345
+ * @private
22346
+ */
22347
+ _horizontal: function ( item, scrollLeft )
22348
+ {
22349
+ var itemDom = this.dom[ item ];
22350
+ var position = this.s.position;
22351
+ var lastScrollLeft = this.s.scrollLeft;
22352
+
22353
+ if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) {
22354
+ itemDom.floating.css( 'left', position.left - scrollLeft );
22355
+
22356
+ lastScrollLeft[ item ] = scrollLeft;
22357
+ }
22358
+ },
22359
+
22360
+ /**
22361
+ * Change from one display mode to another. Each fixed item can be in one
22362
+ * of:
22363
+ *
22364
+ * * `in-place` - In the main DataTable
22365
+ * * `in` - Floating over the DataTable
22366
+ * * `below` - (Header only) Fixed to the bottom of the table body
22367
+ * * `above` - (Footer only) Fixed to the top of the table body
22368
+ *
22369
+ * @param {string} mode Mode that the item should be shown in
22370
+ * @param {string} item 'header' or 'footer'
22371
+ * @param {boolean} forceChange Force a redraw of the mode, even if already
22372
+ * in that mode.
22373
+ * @private
22374
+ */
22375
+ _modeChange: function ( mode, item, forceChange )
22376
+ {
22377
+ var dt = this.s.dt;
22378
+ var itemDom = this.dom[ item ];
22379
+ var position = this.s.position;
22380
+
22381
+ // It isn't trivial to add a !important css attribute...
22382
+ var importantWidth = function (w) {
22383
+ itemDom.floating.attr('style', function(i,s) {
22384
+ return (s || '') + 'width: '+w+'px !important;';
22385
+ });
22386
+ };
22387
+
22388
+ // Record focus. Browser's will cause input elements to loose focus if
22389
+ // they are inserted else where in the doc
22390
+ var tablePart = this.dom[ item==='footer' ? 'tfoot' : 'thead' ];
22391
+ var focus = $.contains( tablePart[0], document.activeElement ) ?
22392
+ document.activeElement :
22393
+ null;
22394
+
22395
+ if ( focus ) {
22396
+ focus.blur();
22397
+ }
22398
+
22399
+ if ( mode === 'in-place' ) {
22400
+ // Insert the header back into the table's real header
22401
+ if ( itemDom.placeholder ) {
22402
+ itemDom.placeholder.remove();
22403
+ itemDom.placeholder = null;
22404
+ }
22405
+
22406
+ this._unsize( item );
22407
+
22408
+ if ( item === 'header' ) {
22409
+ itemDom.host.prepend( tablePart );
22410
+ }
22411
+ else {
22412
+ itemDom.host.append( tablePart );
22413
+ }
22414
+
22415
+ if ( itemDom.floating ) {
22416
+ itemDom.floating.remove();
22417
+ itemDom.floating = null;
22418
+ }
22419
+ }
22420
+ else if ( mode === 'in' ) {
22421
+ // Remove the header from the read header and insert into a fixed
22422
+ // positioned floating table clone
22423
+ this._clone( item, forceChange );
22424
+
22425
+ itemDom.floating
22426
+ .addClass( 'fixedHeader-floating' )
22427
+ .css( item === 'header' ? 'top' : 'bottom', this.c[item+'Offset'] )
22428
+ .css( 'left', position.left+'px' );
22429
+
22430
+ importantWidth(position.width);
22431
+
22432
+ if ( item === 'footer' ) {
22433
+ itemDom.floating.css( 'top', '' );
22434
+ }
22435
+ }
22436
+ else if ( mode === 'below' ) { // only used for the header
22437
+ // Fix the position of the floating header at base of the table body
22438
+ this._clone( item, forceChange );
22439
+
22440
+ itemDom.floating
22441
+ .addClass( 'fixedHeader-locked' )
22442
+ .css( 'top', position.tfootTop - position.theadHeight )
22443
+ .css( 'left', position.left+'px' );
22444
+
22445
+ importantWidth(position.width);
22446
+ }
22447
+ else if ( mode === 'above' ) { // only used for the footer
22448
+ // Fix the position of the floating footer at top of the table body
22449
+ this._clone( item, forceChange );
22450
+
22451
+ itemDom.floating
22452
+ .addClass( 'fixedHeader-locked' )
22453
+ .css( 'top', position.tbodyTop )
22454
+ .css( 'left', position.left+'px' );
22455
+
22456
+ importantWidth(position.width);
22457
+ }
22458
+
22459
+ // Restore focus if it was lost
22460
+ if ( focus && focus !== document.activeElement ) {
22461
+ setTimeout( function () {
22462
+ focus.focus();
22463
+ }, 10 );
22464
+ }
22465
+
22466
+ this.s.scrollLeft.header = -1;
22467
+ this.s.scrollLeft.footer = -1;
22468
+ this.s[item+'Mode'] = mode;
22469
+ },
22470
+
22471
+ /**
22472
+ * Cache the positional information that is required for the mode
22473
+ * calculations that FixedHeader performs.
22474
+ *
22475
+ * @private
22476
+ */
22477
+ _positions: function ()
22478
+ {
22479
+ var dt = this.s.dt;
22480
+ var table = dt.table();
22481
+ var position = this.s.position;
22482
+ var dom = this.dom;
22483
+ var tableNode = $(table.node());
22484
+
22485
+ // Need to use the header and footer that are in the main table,
22486
+ // regardless of if they are clones, since they hold the positions we
22487
+ // want to measure from
22488
+ var thead = tableNode.children('thead');
22489
+ var tfoot = tableNode.children('tfoot');
22490
+ var tbody = dom.tbody;
22491
+
22492
+ position.visible = tableNode.is(':visible');
22493
+ position.width = tableNode.outerWidth();
22494
+ position.left = tableNode.offset().left;
22495
+ position.theadTop = thead.offset().top;
22496
+ position.tbodyTop = tbody.offset().top;
22497
+ position.tbodyHeight = tbody.outerHeight();
22498
+ position.theadHeight = position.tbodyTop - position.theadTop;
22499
+
22500
+ if ( tfoot.length ) {
22501
+ position.tfootTop = tfoot.offset().top;
22502
+ position.tfootBottom = position.tfootTop + tfoot.outerHeight();
22503
+ position.tfootHeight = position.tfootBottom - position.tfootTop;
22504
+ }
22505
+ else {
22506
+ position.tfootTop = position.tbodyTop + tbody.outerHeight();
22507
+ position.tfootBottom = position.tfootTop;
22508
+ position.tfootHeight = position.tfootTop;
22509
+ }
22510
+ },
22511
+
22512
+
22513
+ /**
22514
+ * Mode calculation - determine what mode the fixed items should be placed
22515
+ * into.
22516
+ *
22517
+ * @param {boolean} forceChange Force a redraw of the mode, even if already
22518
+ * in that mode.
22519
+ * @private
22520
+ */
22521
+ _scroll: function ( forceChange )
22522
+ {
22523
+ var windowTop = $(document).scrollTop();
22524
+ var windowLeft = $(document).scrollLeft();
22525
+ var position = this.s.position;
22526
+ var headerMode, footerMode;
22527
+
22528
+ if ( this.c.header ) {
22529
+ if ( ! this.s.enable ) {
22530
+ headerMode = 'in-place';
22531
+ }
22532
+ else if ( ! position.visible || windowTop <= position.theadTop - this.c.headerOffset ) {
22533
+ headerMode = 'in-place';
22534
+ }
22535
+ else if ( windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset ) {
22536
+ headerMode = 'in';
22537
+ }
22538
+ else {
22539
+ headerMode = 'below';
22540
+ }
22541
+
22542
+ if ( forceChange || headerMode !== this.s.headerMode ) {
22543
+ this._modeChange( headerMode, 'header', forceChange );
22544
+ }
22545
+
22546
+ this._horizontal( 'header', windowLeft );
22547
+ }
22548
+
22549
+ if ( this.c.footer && this.dom.tfoot.length ) {
22550
+ if ( ! this.s.enable ) {
22551
+ footerMode = 'in-place';
22552
+ }
22553
+ else if ( ! position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset ) {
22554
+ footerMode = 'in-place';
22555
+ }
22556
+ else if ( position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset ) {
22557
+ footerMode = 'in';
22558
+ }
22559
+ else {
22560
+ footerMode = 'above';
22561
+ }
22562
+
22563
+ if ( forceChange || footerMode !== this.s.footerMode ) {
22564
+ this._modeChange( footerMode, 'footer', forceChange );
22565
+ }
22566
+
22567
+ this._horizontal( 'footer', windowLeft );
22568
+ }
22569
+ }
22570
+ } );
22571
+
22572
+
22573
+ /**
22574
+ * Version
22575
+ * @type {String}
22576
+ * @static
22577
+ */
22578
+ FixedHeader.version = "3.1.8";
22579
+
22580
+ /**
22581
+ * Defaults
22582
+ * @type {Object}
22583
+ * @static
22584
+ */
22585
+ FixedHeader.defaults = {
22586
+ header: true,
22587
+ footer: false,
22588
+ headerOffset: 0,
22589
+ footerOffset: 0
22590
+ };
22591
+
22592
+
22593
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22594
+ * DataTables interfaces
22595
+ */
22596
+
22597
+ // Attach for constructor access
22598
+ $.fn.dataTable.FixedHeader = FixedHeader;
22599
+ $.fn.DataTable.FixedHeader = FixedHeader;
22600
+
22601
+
22602
+ // DataTables creation - check if the FixedHeader option has been defined on the
22603
+ // table and if so, initialise
22604
+ $(document).on( 'init.dt.dtfh', function (e, settings, json) {
22605
+ if ( e.namespace !== 'dt' ) {
22606
+ return;
22607
+ }
22608
+
22609
+ var init = settings.oInit.fixedHeader;
22610
+ var defaults = DataTable.defaults.fixedHeader;
22611
+
22612
+ if ( (init || defaults) && ! settings._fixedHeader ) {
22613
+ var opts = $.extend( {}, defaults, init );
22614
+
22615
+ if ( init !== false ) {
22616
+ new FixedHeader( settings, opts );
22617
+ }
22618
+ }
22619
+ } );
22620
+
22621
+ // DataTables API methods
22622
+ DataTable.Api.register( 'fixedHeader()', function () {} );
22623
+
22624
+ DataTable.Api.register( 'fixedHeader.adjust()', function () {
22625
+ return this.iterator( 'table', function ( ctx ) {
22626
+ var fh = ctx._fixedHeader;
22627
+
22628
+ if ( fh ) {
22629
+ fh.update();
22630
+ }
22631
+ } );
22632
+ } );
22633
+
22634
+ DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) {
22635
+ return this.iterator( 'table', function ( ctx ) {
22636
+ var fh = ctx._fixedHeader;
22637
+
22638
+ flag = ( flag !== undefined ? flag : true );
22639
+ if ( fh && flag !== fh.enabled() ) {
22640
+ fh.enable( flag );
22641
+ }
22642
+ } );
22643
+ } );
22644
+
22645
+ DataTable.Api.register( 'fixedHeader.enabled()', function () {
22646
+ if ( this.context.length ) {
22647
+ var fh = this.context[0]._fixedHeader;
22648
+
22649
+ if ( fh ) {
22650
+ return fh.enabled();
22651
+ }
22652
+ }
22653
+
22654
+ return false;
22655
+ } );
22656
+
22657
+ DataTable.Api.register( 'fixedHeader.disable()', function ( ) {
22658
+ return this.iterator( 'table', function ( ctx ) {
22659
+ var fh = ctx._fixedHeader;
22660
+
22661
+ if ( fh && fh.enabled() ) {
22662
+ fh.enable( false );
22663
+ }
22664
+ } );
22665
+ } );
22666
+
22667
+ $.each( ['header', 'footer'], function ( i, el ) {
22668
+ DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) {
22669
+ var ctx = this.context;
22670
+
22671
+ if ( offset === undefined ) {
22672
+ return ctx.length && ctx[0]._fixedHeader ?
22673
+ ctx[0]._fixedHeader[el +'Offset']() :
22674
+ undefined;
22675
+ }
22676
+
22677
+ return this.iterator( 'table', function ( ctx ) {
22678
+ var fh = ctx._fixedHeader;
22679
+
22680
+ if ( fh ) {
22681
+ fh[ el +'Offset' ]( offset );
22682
+ }
22683
+ } );
22684
+ } );
22685
+ } );
22686
+
22687
+
22688
+ return FixedHeader;
22689
+ }));
22690
+
22691
+
22692
+ /*! KeyTable 2.6.1
22693
+ * ©2009-2021 SpryMedia Ltd - datatables.net/license
22694
+ */
22695
+
22696
+ /**
22697
+ * @summary KeyTable
22698
+ * @description Spreadsheet like keyboard navigation for DataTables
22699
+ * @version 2.6.1
22700
+ * @file dataTables.keyTable.js
22701
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
22702
+ * @contact www.sprymedia.co.uk/contact
22703
+ * @copyright Copyright 2009-2021 SpryMedia Ltd.
22704
+ *
22705
+ * This source file is free software, available under the following license:
22706
+ * MIT license - http://datatables.net/license/mit
22707
+ *
22708
+ * This source file is distributed in the hope that it will be useful, but
22709
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22710
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
22711
+ *
22712
+ * For details please refer to: http://www.datatables.net
22713
+ */
22714
+
22715
+ (function( factory ){
22716
+ if ( typeof define === 'function' && define.amd ) {
22717
+ // AMD
22718
+ define( ['jquery', 'datatables.net'], function ( $ ) {
22719
+ return factory( $, window, document );
22720
+ } );
22721
+ }
22722
+ else if ( typeof exports === 'object' ) {
22723
+ // CommonJS
22724
+ module.exports = function (root, $) {
22725
+ if ( ! root ) {
22726
+ root = window;
22727
+ }
22728
+
22729
+ if ( ! $ || ! $.fn.dataTable ) {
22730
+ $ = require('datatables.net')(root, $).$;
22731
+ }
22732
+
22733
+ return factory( $, root, root.document );
22734
+ };
22735
+ }
22736
+ else {
22737
+ // Browser
22738
+ factory( jQuery, window, document );
22739
+ }
22740
+ }(function( $, window, document, undefined ) {
22741
+ 'use strict';
22742
+ var DataTable = $.fn.dataTable;
22743
+ var namespaceCounter = 0;
22744
+ var editorNamespaceCounter = 0;
22745
+
22746
+
22747
+ var KeyTable = function ( dt, opts ) {
22748
+ // Sanity check that we are using DataTables 1.10 or newer
22749
+ if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
22750
+ throw 'KeyTable requires DataTables 1.10.8 or newer';
22751
+ }
22752
+
22753
+ // User and defaults configuration object
22754
+ this.c = $.extend( true, {},
22755
+ DataTable.defaults.keyTable,
22756
+ KeyTable.defaults,
22757
+ opts
22758
+ );
22759
+
22760
+ // Internal settings
22761
+ this.s = {
22762
+ /** @type {DataTable.Api} DataTables' API instance */
22763
+ dt: new DataTable.Api( dt ),
22764
+
22765
+ enable: true,
22766
+
22767
+ /** @type {bool} Flag for if a draw is triggered by focus */
22768
+ focusDraw: false,
22769
+
22770
+ /** @type {bool} Flag to indicate when waiting for a draw to happen.
22771
+ * Will ignore key presses at this point
22772
+ */
22773
+ waitingForDraw: false,
22774
+
22775
+ /** @type {object} Information about the last cell that was focused */
22776
+ lastFocus: null,
22777
+
22778
+ /** @type {string} Unique namespace per instance */
22779
+ namespace: '.keyTable-'+(namespaceCounter++),
22780
+
22781
+ /** @type {Node} Input element for tabbing into the table */
22782
+ tabInput: null
22783
+ };
22784
+
22785
+ // DOM items
22786
+ this.dom = {
22787
+
22788
+ };
22789
+
22790
+ // Check if row reorder has already been initialised on this table
22791
+ var settings = this.s.dt.settings()[0];
22792
+ var exisiting = settings.keytable;
22793
+ if ( exisiting ) {
22794
+ return exisiting;
22795
+ }
22796
+
22797
+ settings.keytable = this;
22798
+ this._constructor();
22799
+ };
22800
+
22801
+
22802
+ $.extend( KeyTable.prototype, {
22803
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22804
+ * API methods for DataTables API interface
22805
+ */
22806
+
22807
+ /**
22808
+ * Blur the table's cell focus
22809
+ */
22810
+ blur: function ()
22811
+ {
22812
+ this._blur();
22813
+ },
22814
+
22815
+ /**
22816
+ * Enable cell focus for the table
22817
+ *
22818
+ * @param {string} state Can be `true`, `false` or `-string navigation-only`
22819
+ */
22820
+ enable: function ( state )
22821
+ {
22822
+ this.s.enable = state;
22823
+ },
22824
+
22825
+ /**
22826
+ * Get enable status
22827
+ */
22828
+ enabled: function () {
22829
+ return this.s.enable;
22830
+ },
22831
+
22832
+ /**
22833
+ * Focus on a cell
22834
+ * @param {integer} row Row index
22835
+ * @param {integer} column Column index
22836
+ */
22837
+ focus: function ( row, column )
22838
+ {
22839
+ this._focus( this.s.dt.cell( row, column ) );
22840
+ },
22841
+
22842
+ /**
22843
+ * Is the cell focused
22844
+ * @param {object} cell Cell index to check
22845
+ * @returns {boolean} true if focused, false otherwise
22846
+ */
22847
+ focused: function ( cell )
22848
+ {
22849
+ var lastFocus = this.s.lastFocus;
22850
+
22851
+ if ( ! lastFocus ) {
22852
+ return false;
22853
+ }
22854
+
22855
+ var lastIdx = this.s.lastFocus.cell.index();
22856
+ return cell.row === lastIdx.row && cell.column === lastIdx.column;
22857
+ },
22858
+
22859
+
22860
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22861
+ * Constructor
22862
+ */
22863
+
22864
+ /**
22865
+ * Initialise the KeyTable instance
22866
+ *
22867
+ * @private
22868
+ */
22869
+ _constructor: function ()
22870
+ {
22871
+ this._tabInput();
22872
+
22873
+ var that = this;
22874
+ var dt = this.s.dt;
22875
+ var table = $( dt.table().node() );
22876
+ var namespace = this.s.namespace;
22877
+ var editorBlock = false;
22878
+
22879
+ // Need to be able to calculate the cell positions relative to the table
22880
+ if ( table.css('position') === 'static' ) {
22881
+ table.css( 'position', 'relative' );
22882
+ }
22883
+
22884
+ // Click to focus
22885
+ $( dt.table().body() ).on( 'click'+namespace, 'th, td', function (e) {
22886
+ if ( that.s.enable === false ) {
22887
+ return;
22888
+ }
22889
+
22890
+ var cell = dt.cell( this );
22891
+
22892
+ if ( ! cell.any() ) {
22893
+ return;
22894
+ }
22895
+
22896
+ that._focus( cell, null, false, e );
22897
+ } );
22898
+
22899
+ // Key events
22900
+ $( document ).on( 'keydown'+namespace, function (e) {
22901
+ if ( ! editorBlock ) {
22902
+ that._key( e );
22903
+ }
22904
+ } );
22905
+
22906
+ // Click blur
22907
+ if ( this.c.blurable ) {
22908
+ $( document ).on( 'mousedown'+namespace, function ( e ) {
22909
+ // Click on the search input will blur focus
22910
+ if ( $(e.target).parents( '.dataTables_filter' ).length ) {
22911
+ that._blur();
22912
+ }
22913
+
22914
+ // If the click was inside the DataTables container, don't blur
22915
+ if ( $(e.target).parents().filter( dt.table().container() ).length ) {
22916
+ return;
22917
+ }
22918
+
22919
+ // Don't blur in Editor form
22920
+ if ( $(e.target).parents('div.DTE').length ) {
22921
+ return;
22922
+ }
22923
+
22924
+ // Or an Editor date input
22925
+ if (
22926
+ $(e.target).parents('div.editor-datetime').length ||
22927
+ $(e.target).parents('div.dt-datetime').length
22928
+ ) {
22929
+ return;
22930
+ }
22931
+
22932
+ //If the click was inside the fixed columns container, don't blur
22933
+ if ( $(e.target).parents().filter('.DTFC_Cloned').length ) {
22934
+ return;
22935
+ }
22936
+
22937
+ that._blur();
22938
+ } );
22939
+ }
22940
+
22941
+ if ( this.c.editor ) {
22942
+ var editor = this.c.editor;
22943
+
22944
+ // Need to disable KeyTable when the main editor is shown
22945
+ editor.on( 'open.keyTableMain', function (e, mode, action) {
22946
+ if ( mode !== 'inline' && that.s.enable ) {
22947
+ that.enable( false );
22948
+
22949
+ editor.one( 'close'+namespace, function () {
22950
+ that.enable( true );
22951
+ } );
22952
+ }
22953
+ } );
22954
+
22955
+ if ( this.c.editOnFocus ) {
22956
+ dt.on( 'key-focus'+namespace+' key-refocus'+namespace, function ( e, dt, cell, orig ) {
22957
+ that._editor( null, orig, true );
22958
+ } );
22959
+ }
22960
+
22961
+ // Activate Editor when a key is pressed (will be ignored, if
22962
+ // already active).
22963
+ dt.on( 'key'+namespace, function ( e, dt, key, cell, orig ) {
22964
+ that._editor( key, orig, false );
22965
+ } );
22966
+
22967
+ // Active editing on double click - it will already have focus from
22968
+ // the click event handler above
22969
+ $( dt.table().body() ).on( 'dblclick'+namespace, 'th, td', function (e) {
22970
+ if ( that.s.enable === false ) {
22971
+ return;
22972
+ }
22973
+
22974
+ var cell = dt.cell( this );
22975
+
22976
+ if ( ! cell.any() ) {
22977
+ return;
22978
+ }
22979
+
22980
+ if ( that.s.lastFocus && this !== that.s.lastFocus.cell.node() ) {
22981
+ return;
22982
+ }
22983
+
22984
+ that._editor( null, e, true );
22985
+ } );
22986
+
22987
+ // While Editor is busy processing, we don't want to process any key events
22988
+ editor
22989
+ .on('preSubmit', function () {
22990
+ editorBlock = true;
22991
+ } )
22992
+ .on('preSubmitCancelled', function () {
22993
+ editorBlock = false;
22994
+ } )
22995
+ .on('submitComplete', function () {
22996
+ editorBlock = false;
22997
+ } );
22998
+ }
22999
+
23000
+ // Stave saving
23001
+ if ( dt.settings()[0].oFeatures.bStateSave ) {
23002
+ dt.on( 'stateSaveParams'+namespace, function (e, s, d) {
23003
+ d.keyTable = that.s.lastFocus ?
23004
+ that.s.lastFocus.cell.index() :
23005
+ null;
23006
+ } );
23007
+ }
23008
+
23009
+ dt.on( 'column-visibility'+namespace, function (e) {
23010
+ that._tabInput();
23011
+ } );
23012
+
23013
+ // Redraw - retain focus on the current cell
23014
+ dt.on( 'draw'+namespace, function (e) {
23015
+ that._tabInput();
23016
+
23017
+ if ( that.s.focusDraw ) {
23018
+ return;
23019
+ }
23020
+
23021
+ var lastFocus = that.s.lastFocus;
23022
+
23023
+ if ( lastFocus ) {
23024
+ var relative = that.s.lastFocus.relative;
23025
+ var info = dt.page.info();
23026
+ var row = relative.row + info.start;
23027
+
23028
+ if ( info.recordsDisplay === 0 ) {
23029
+ return;
23030
+ }
23031
+
23032
+ // Reverse if needed
23033
+ if ( row >= info.recordsDisplay ) {
23034
+ row = info.recordsDisplay - 1;
23035
+ }
23036
+
23037
+ that._focus( row, relative.column, true, e );
23038
+ }
23039
+ } );
23040
+
23041
+ // Clipboard support
23042
+ if ( this.c.clipboard ) {
23043
+ this._clipboard();
23044
+ }
23045
+
23046
+ dt.on( 'destroy'+namespace, function () {
23047
+ that._blur( true );
23048
+
23049
+ // Event tidy up
23050
+ dt.off( namespace );
23051
+
23052
+ $( dt.table().body() )
23053
+ .off( 'click'+namespace, 'th, td' )
23054
+ .off( 'dblclick'+namespace, 'th, td' );
23055
+
23056
+ $( document )
23057
+ .off( 'mousedown'+namespace )
23058
+ .off( 'keydown'+namespace )
23059
+ .off( 'copy'+namespace )
23060
+ .off( 'paste'+namespace );
23061
+ } );
23062
+
23063
+ // Initial focus comes from state or options
23064
+ var state = dt.state.loaded();
23065
+
23066
+ if ( state && state.keyTable ) {
23067
+ // Wait until init is done
23068
+ dt.one( 'init', function () {
23069
+ var cell = dt.cell( state.keyTable );
23070
+
23071
+ // Ensure that the saved cell still exists
23072
+ if ( cell.any() ) {
23073
+ cell.focus();
23074
+ }
23075
+ } );
23076
+ }
23077
+ else if ( this.c.focus ) {
23078
+ dt.cell( this.c.focus ).focus();
23079
+ }
23080
+ },
23081
+
23082
+
23083
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23084
+ * Private methods
23085
+ */
23086
+
23087
+ /**
23088
+ * Blur the control
23089
+ *
23090
+ * @param {boolean} [noEvents=false] Don't trigger updates / events (for destroying)
23091
+ * @private
23092
+ */
23093
+ _blur: function (noEvents)
23094
+ {
23095
+ if ( ! this.s.enable || ! this.s.lastFocus ) {
23096
+ return;
23097
+ }
23098
+
23099
+ var cell = this.s.lastFocus.cell;
23100
+
23101
+ $( cell.node() ).removeClass( this.c.className );
23102
+ this.s.lastFocus = null;
23103
+
23104
+ if ( ! noEvents ) {
23105
+ this._updateFixedColumns(cell.index().column);
23106
+
23107
+ this._emitEvent( 'key-blur', [ this.s.dt, cell ] );
23108
+ }
23109
+ },
23110
+
23111
+
23112
+ /**
23113
+ * Clipboard interaction handlers
23114
+ *
23115
+ * @private
23116
+ */
23117
+ _clipboard: function () {
23118
+ var dt = this.s.dt;
23119
+ var that = this;
23120
+ var namespace = this.s.namespace;
23121
+
23122
+ // IE8 doesn't support getting selected text
23123
+ if ( ! window.getSelection ) {
23124
+ return;
23125
+ }
23126
+
23127
+ $(document).on( 'copy'+namespace, function (ejq) {
23128
+ var e = ejq.originalEvent;
23129
+ var selection = window.getSelection().toString();
23130
+ var focused = that.s.lastFocus;
23131
+
23132
+ // Only copy cell text to clipboard if there is no other selection
23133
+ // and there is a focused cell
23134
+ if ( ! selection && focused ) {
23135
+ e.clipboardData.setData(
23136
+ 'text/plain',
23137
+ focused.cell.render( that.c.clipboardOrthogonal )
23138
+ );
23139
+ e.preventDefault();
23140
+ }
23141
+ } );
23142
+
23143
+ $(document).on( 'paste'+namespace, function (ejq) {
23144
+ var e = ejq.originalEvent;
23145
+ var focused = that.s.lastFocus;
23146
+ var activeEl = document.activeElement;
23147
+ var editor = that.c.editor;
23148
+ var pastedText;
23149
+
23150
+ if ( focused && (! activeEl || activeEl.nodeName.toLowerCase() === 'body') ) {
23151
+ e.preventDefault();
23152
+
23153
+ if ( window.clipboardData && window.clipboardData.getData ) {
23154
+ // IE
23155
+ pastedText = window.clipboardData.getData('Text');
23156
+ }
23157
+ else if ( e.clipboardData && e.clipboardData.getData ) {
23158
+ // Everything else
23159
+ pastedText = e.clipboardData.getData('text/plain');
23160
+ }
23161
+
23162
+ if ( editor ) {
23163
+ // Got Editor - need to activate inline editing,
23164
+ // set the value and submit
23165
+ editor
23166
+ .inline( focused.cell.index() )
23167
+ .set( editor.displayed()[0], pastedText )
23168
+ .submit();
23169
+ }
23170
+ else {
23171
+ // No editor, so just dump the data in
23172
+ focused.cell.data( pastedText );
23173
+ dt.draw(false);
23174
+ }
23175
+ }
23176
+ } );
23177
+ },
23178
+
23179
+
23180
+ /**
23181
+ * Get an array of the column indexes that KeyTable can operate on. This
23182
+ * is a merge of the user supplied columns and the visible columns.
23183
+ *
23184
+ * @private
23185
+ */
23186
+ _columns: function ()
23187
+ {
23188
+ var dt = this.s.dt;
23189
+ var user = dt.columns( this.c.columns ).indexes();
23190
+ var out = [];
23191
+
23192
+ dt.columns( ':visible' ).every( function (i) {
23193
+ if ( user.indexOf( i ) !== -1 ) {
23194
+ out.push( i );
23195
+ }
23196
+ } );
23197
+
23198
+ return out;
23199
+ },
23200
+
23201
+
23202
+ /**
23203
+ * Perform excel like navigation for Editor by triggering an edit on key
23204
+ * press
23205
+ *
23206
+ * @param {integer} key Key code for the pressed key
23207
+ * @param {object} orig Original event
23208
+ * @private
23209
+ */
23210
+ _editor: function ( key, orig, hardEdit )
23211
+ {
23212
+ // If nothing focused, we can't take any action
23213
+ if (! this.s.lastFocus) {
23214
+ return;
23215
+ }
23216
+
23217
+ // DataTables draw event
23218
+ if (orig && orig.type === 'draw') {
23219
+ return;
23220
+ }
23221
+
23222
+ var that = this;
23223
+ var dt = this.s.dt;
23224
+ var editor = this.c.editor;
23225
+ var editCell = this.s.lastFocus.cell;
23226
+ var namespace = this.s.namespace + 'e' + editorNamespaceCounter++;
23227
+
23228
+ // Do nothing if there is already an inline edit in this cell
23229
+ if ( $('div.DTE', editCell.node()).length ) {
23230
+ return;
23231
+ }
23232
+
23233
+ // Don't activate Editor on control key presses
23234
+ if ( key !== null && (
23235
+ (key >= 0x00 && key <= 0x09) ||
23236
+ key === 0x0b ||
23237
+ key === 0x0c ||
23238
+ (key >= 0x0e && key <= 0x1f) ||
23239
+ (key >= 0x70 && key <= 0x7b) ||
23240
+ (key >= 0x7f && key <= 0x9f)
23241
+ ) ) {
23242
+ return;
23243
+ }
23244
+
23245
+ if ( orig ) {
23246
+ orig.stopPropagation();
23247
+
23248
+ // Return key should do nothing - for textareas it would empty the
23249
+ // contents
23250
+ if ( key === 13 ) {
23251
+ orig.preventDefault();
23252
+ }
23253
+ }
23254
+
23255
+ var editInline = function () {
23256
+ editor
23257
+ .one( 'open'+namespace, function () {
23258
+ // Remove cancel open
23259
+ editor.off( 'cancelOpen'+namespace );
23260
+
23261
+ // Excel style - select all text
23262
+ if ( ! hardEdit ) {
23263
+ $('div.DTE_Field_InputControl input, div.DTE_Field_InputControl textarea').select();
23264
+ }
23265
+
23266
+ // Reduce the keys the Keys listens for
23267
+ dt.keys.enable( hardEdit ? 'tab-only' : 'navigation-only' );
23268
+
23269
+ // On blur of the navigation submit
23270
+ dt.on( 'key-blur.editor', function (e, dt, cell) {
23271
+ if ( editor.displayed() && cell.node() === editCell.node() ) {
23272
+ editor.submit();
23273
+ }
23274
+ } );
23275
+
23276
+ // Highlight the cell a different colour on full edit
23277
+ if ( hardEdit ) {
23278
+ $( dt.table().container() ).addClass('dtk-focus-alt');
23279
+ }
23280
+
23281
+ // If the dev cancels the submit, we need to return focus
23282
+ editor.on( 'preSubmitCancelled'+namespace, function () {
23283
+ setTimeout( function () {
23284
+ that._focus( editCell, null, false );
23285
+ }, 50 );
23286
+ } );
23287
+
23288
+ editor.on( 'submitUnsuccessful'+namespace, function () {
23289
+ that._focus( editCell, null, false );
23290
+ } );
23291
+
23292
+ // Restore full key navigation on close
23293
+ editor.one( 'close'+namespace, function () {
23294
+ dt.keys.enable( true );
23295
+ dt.off( 'key-blur.editor' );
23296
+ editor.off( namespace );
23297
+ $( dt.table().container() ).removeClass('dtk-focus-alt');
23298
+
23299
+ if (that.s.returnSubmit) {
23300
+ that.s.returnSubmit = false;
23301
+ that._emitEvent( 'key-return-submit', [dt, editCell] );
23302
+ }
23303
+ } );
23304
+ } )
23305
+ .one( 'cancelOpen'+namespace, function () {
23306
+ // `preOpen` can cancel the display of the form, so it
23307
+ // might be that the open event handler isn't needed
23308
+ editor.off( namespace );
23309
+ } )
23310
+ .inline( editCell.index() );
23311
+ };
23312
+
23313
+ // Editor 1.7 listens for `return` on keyup, so if return is the trigger
23314
+ // key, we need to wait for `keyup` otherwise Editor would just submit
23315
+ // the content triggered by this keypress.
23316
+ if ( key === 13 ) {
23317
+ hardEdit = true;
23318
+
23319
+ $(document).one( 'keyup', function () { // immediately removed
23320
+ editInline();
23321
+ } );
23322
+ }
23323
+ else {
23324
+ editInline();
23325
+ }
23326
+ },
23327
+
23328
+
23329
+ /**
23330
+ * Emit an event on the DataTable for listeners
23331
+ *
23332
+ * @param {string} name Event name
23333
+ * @param {array} args Event arguments
23334
+ * @private
23335
+ */
23336
+ _emitEvent: function ( name, args )
23337
+ {
23338
+ this.s.dt.iterator( 'table', function ( ctx, i ) {
23339
+ $(ctx.nTable).triggerHandler( name, args );
23340
+ } );
23341
+ },
23342
+
23343
+
23344
+ /**
23345
+ * Focus on a particular cell, shifting the table's paging if required
23346
+ *
23347
+ * @param {DataTables.Api|integer} row Can be given as an API instance that
23348
+ * contains the cell to focus or as an integer. As the latter it is the
23349
+ * visible row index (from the whole data set) - NOT the data index
23350
+ * @param {integer} [column] Not required if a cell is given as the first
23351
+ * parameter. Otherwise this is the column data index for the cell to
23352
+ * focus on
23353
+ * @param {boolean} [shift=true] Should the viewport be moved to show cell
23354
+ * @private
23355
+ */
23356
+ _focus: function ( row, column, shift, originalEvent )
23357
+ {
23358
+ var that = this;
23359
+ var dt = this.s.dt;
23360
+ var pageInfo = dt.page.info();
23361
+ var lastFocus = this.s.lastFocus;
23362
+
23363
+ if ( ! originalEvent) {
23364
+ originalEvent = null;
23365
+ }
23366
+
23367
+ if ( ! this.s.enable ) {
23368
+ return;
23369
+ }
23370
+
23371
+ if ( typeof row !== 'number' ) {
23372
+ // Its an API instance - check that there is actually a row
23373
+ if ( ! row.any() ) {
23374
+ return;
23375
+ }
23376
+
23377
+ // Convert the cell to a row and column
23378
+ var index = row.index();
23379
+ column = index.column;
23380
+ row = dt
23381
+ .rows( { filter: 'applied', order: 'applied' } )
23382
+ .indexes()
23383
+ .indexOf( index.row );
23384
+
23385
+ // Don't focus rows that were filtered out.
23386
+ if ( row < 0 ) {
23387
+ return;
23388
+ }
23389
+
23390
+ // For server-side processing normalise the row by adding the start
23391
+ // point, since `rows().indexes()` includes only rows that are
23392
+ // available at the client-side
23393
+ if ( pageInfo.serverSide ) {
23394
+ row += pageInfo.start;
23395
+ }
23396
+ }
23397
+
23398
+ // Is the row on the current page? If not, we need to redraw to show the
23399
+ // page
23400
+ if ( pageInfo.length !== -1 && (row < pageInfo.start || row >= pageInfo.start+pageInfo.length) ) {
23401
+ this.s.focusDraw = true;
23402
+ this.s.waitingForDraw = true;
23403
+
23404
+ dt
23405
+ .one( 'draw', function () {
23406
+ that.s.focusDraw = false;
23407
+ that.s.waitingForDraw = false;
23408
+ that._focus( row, column, undefined, originalEvent );
23409
+ } )
23410
+ .page( Math.floor( row / pageInfo.length ) )
23411
+ .draw( false );
23412
+
23413
+ return;
23414
+ }
23415
+
23416
+ // In the available columns?
23417
+ if ( $.inArray( column, this._columns() ) === -1 ) {
23418
+ return;
23419
+ }
23420
+
23421
+ // De-normalise the server-side processing row, so we select the row
23422
+ // in its displayed position
23423
+ if ( pageInfo.serverSide ) {
23424
+ row -= pageInfo.start;
23425
+ }
23426
+
23427
+ // Get the cell from the current position - ignoring any cells which might
23428
+ // not have been rendered (therefore can't use `:eq()` selector).
23429
+ var cells = dt.cells( null, column, {search: 'applied', order: 'applied'} ).flatten();
23430
+ var cell = dt.cell( cells[ row ] );
23431
+
23432
+ if ( lastFocus ) {
23433
+ // Don't trigger a refocus on the same cell
23434
+ if ( lastFocus.node === cell.node() ) {
23435
+ this._emitEvent( 'key-refocus', [ this.s.dt, cell, originalEvent || null ] );
23436
+ return;
23437
+ }
23438
+
23439
+ // Otherwise blur the old focus
23440
+ this._blur();
23441
+ }
23442
+
23443
+ // Clear focus from other tables
23444
+ this._removeOtherFocus();
23445
+
23446
+ var node = $( cell.node() );
23447
+ node.addClass( this.c.className );
23448
+
23449
+ this._updateFixedColumns(column);
23450
+
23451
+ // Shift viewpoint and page to make cell visible
23452
+ if ( shift === undefined || shift === true ) {
23453
+ this._scroll( $(window), $(document.body), node, 'offset' );
23454
+
23455
+ var bodyParent = dt.table().body().parentNode;
23456
+ if ( bodyParent !== dt.table().header().parentNode ) {
23457
+ var parent = $(bodyParent.parentNode);
23458
+
23459
+ this._scroll( parent, parent, node, 'position' );
23460
+ }
23461
+ }
23462
+
23463
+ // Event and finish
23464
+ this.s.lastFocus = {
23465
+ cell: cell,
23466
+ node: cell.node(),
23467
+ relative: {
23468
+ row: dt.rows( { page: 'current' } ).indexes().indexOf( cell.index().row ),
23469
+ column: cell.index().column
23470
+ }
23471
+ };
23472
+
23473
+ this._emitEvent( 'key-focus', [ this.s.dt, cell, originalEvent || null ] );
23474
+ dt.state.save();
23475
+ },
23476
+
23477
+
23478
+ /**
23479
+ * Handle key press
23480
+ *
23481
+ * @param {object} e Event
23482
+ * @private
23483
+ */
23484
+ _key: function ( e )
23485
+ {
23486
+ // If we are waiting for a draw to happen from another key event, then
23487
+ // do nothing for this new key press.
23488
+ if ( this.s.waitingForDraw ) {
23489
+ e.preventDefault();
23490
+ return;
23491
+ }
23492
+
23493
+ var enable = this.s.enable;
23494
+ this.s.returnSubmit = (enable === 'navigation-only' || enable === 'tab-only') && e.keyCode === 13
23495
+ ? true
23496
+ : false;
23497
+
23498
+ var navEnable = enable === true || enable === 'navigation-only';
23499
+ if ( ! enable ) {
23500
+ return;
23501
+ }
23502
+
23503
+ if ( (e.keyCode === 0 || e.ctrlKey || e.metaKey || e.altKey) && !(e.ctrlKey && e.altKey) ) {
23504
+ return;
23505
+ }
23506
+
23507
+ // If not focused, then there is no key action to take
23508
+ var lastFocus = this.s.lastFocus;
23509
+ if ( ! lastFocus ) {
23510
+ return;
23511
+ }
23512
+
23513
+ // And the last focus still exists!
23514
+ if ( ! this.s.dt.cell(lastFocus.node).any() ) {
23515
+ this.s.lastFocus = null;
23516
+ return;
23517
+ }
23518
+
23519
+ var that = this;
23520
+ var dt = this.s.dt;
23521
+ var scrolling = this.s.dt.settings()[0].oScroll.sY ? true : false;
23522
+
23523
+ // If we are not listening for this key, do nothing
23524
+ if ( this.c.keys && $.inArray( e.keyCode, this.c.keys ) === -1 ) {
23525
+ return;
23526
+ }
23527
+
23528
+ switch( e.keyCode ) {
23529
+ case 9: // tab
23530
+ // `enable` can be tab-only
23531
+ this._shift( e, e.shiftKey ? 'left' : 'right', true );
23532
+ break;
23533
+
23534
+ case 27: // esc
23535
+ if ( this.s.blurable && enable === true ) {
23536
+ this._blur();
23537
+ }
23538
+ break;
23539
+
23540
+ case 33: // page up (previous page)
23541
+ case 34: // page down (next page)
23542
+ if ( navEnable && !scrolling ) {
23543
+ e.preventDefault();
23544
+
23545
+ dt
23546
+ .page( e.keyCode === 33 ? 'previous' : 'next' )
23547
+ .draw( false );
23548
+ }
23549
+ break;
23550
+
23551
+ case 35: // end (end of current page)
23552
+ case 36: // home (start of current page)
23553
+ if ( navEnable ) {
23554
+ e.preventDefault();
23555
+ var indexes = dt.cells( {page: 'current'} ).indexes();
23556
+ var colIndexes = this._columns();
23557
+
23558
+ this._focus( dt.cell(
23559
+ indexes[ e.keyCode === 35 ? indexes.length-1 : colIndexes[0] ]
23560
+ ), null, true, e );
23561
+ }
23562
+ break;
23563
+
23564
+ case 37: // left arrow
23565
+ if ( navEnable ) {
23566
+ this._shift( e, 'left' );
23567
+ }
23568
+ break;
23569
+
23570
+ case 38: // up arrow
23571
+ if ( navEnable ) {
23572
+ this._shift( e, 'up' );
23573
+ }
23574
+ break;
23575
+
23576
+ case 39: // right arrow
23577
+ if ( navEnable ) {
23578
+ this._shift( e, 'right' );
23579
+ }
23580
+ break;
23581
+
23582
+ case 40: // down arrow
23583
+ if ( navEnable ) {
23584
+ this._shift( e, 'down' );
23585
+ }
23586
+ break;
23587
+
23588
+ case 113: // F2 - Excel like hard edit
23589
+ if ( this.c.editor ) {
23590
+ this._editor(null, e, true);
23591
+ break;
23592
+ }
23593
+ // else fallthrough
23594
+
23595
+ default:
23596
+ // Everything else - pass through only when fully enabled
23597
+ if ( enable === true ) {
23598
+ this._emitEvent( 'key', [ dt, e.keyCode, this.s.lastFocus.cell, e ] );
23599
+ }
23600
+ break;
23601
+ }
23602
+ },
23603
+
23604
+ /**
23605
+ * Remove focus from all tables other than this one
23606
+ */
23607
+ _removeOtherFocus: function ()
23608
+ {
23609
+ var thisTable = this.s.dt.table().node();
23610
+
23611
+ $.fn.dataTable.tables({api:true}).iterator('table', function (settings) {
23612
+ if (this.table().node() !== thisTable) {
23613
+ this.cell.blur();
23614
+ }
23615
+ });
23616
+ },
23617
+
23618
+ /**
23619
+ * Scroll a container to make a cell visible in it. This can be used for
23620
+ * both DataTables scrolling and native window scrolling.
23621
+ *
23622
+ * @param {jQuery} container Scrolling container
23623
+ * @param {jQuery} scroller Item being scrolled
23624
+ * @param {jQuery} cell Cell in the scroller
23625
+ * @param {string} posOff `position` or `offset` - which to use for the
23626
+ * calculation. `offset` for the document, otherwise `position`
23627
+ * @private
23628
+ */
23629
+ _scroll: function ( container, scroller, cell, posOff )
23630
+ {
23631
+ var offset = cell[posOff]();
23632
+ var height = cell.outerHeight();
23633
+ var width = cell.outerWidth();
23634
+
23635
+ var scrollTop = scroller.scrollTop();
23636
+ var scrollLeft = scroller.scrollLeft();
23637
+ var containerHeight = container.height();
23638
+ var containerWidth = container.width();
23639
+
23640
+ // If Scroller is being used, the table can be `position: absolute` and that
23641
+ // needs to be taken account of in the offset. If no Scroller, this will be 0
23642
+ if ( posOff === 'position' ) {
23643
+ offset.top += parseInt( cell.closest('table').css('top'), 10 );
23644
+ }
23645
+
23646
+ // Top correction
23647
+ if ( offset.top < scrollTop ) {
23648
+ scroller.scrollTop( offset.top );
23649
+ }
23650
+
23651
+ // Left correction
23652
+ if ( offset.left < scrollLeft ) {
23653
+ scroller.scrollLeft( offset.left );
23654
+ }
23655
+
23656
+ // Bottom correction
23657
+ if ( offset.top + height > scrollTop + containerHeight && height < containerHeight ) {
23658
+ scroller.scrollTop( offset.top + height - containerHeight );
23659
+ }
23660
+
23661
+ // Right correction
23662
+ if ( offset.left + width > scrollLeft + containerWidth && width < containerWidth ) {
23663
+ scroller.scrollLeft( offset.left + width - containerWidth );
23664
+ }
23665
+ },
23666
+
23667
+
23668
+ /**
23669
+ * Calculate a single offset movement in the table - up, down, left and
23670
+ * right and then perform the focus if possible
23671
+ *
23672
+ * @param {object} e Event object
23673
+ * @param {string} direction Movement direction
23674
+ * @param {boolean} keyBlurable `true` if the key press can result in the
23675
+ * table being blurred. This is so arrow keys won't blur the table, but
23676
+ * tab will.
23677
+ * @private
23678
+ */
23679
+ _shift: function ( e, direction, keyBlurable )
23680
+ {
23681
+ var that = this;
23682
+ var dt = this.s.dt;
23683
+ var pageInfo = dt.page.info();
23684
+ var rows = pageInfo.recordsDisplay;
23685
+ var columns = this._columns();
23686
+ var last = this.s.lastFocus;
23687
+ if ( ! last ) {
23688
+ return;
23689
+ }
23690
+
23691
+ var currentCell = last.cell;
23692
+ if ( ! currentCell ) {
23693
+ return;
23694
+ }
23695
+
23696
+ var currRow = dt
23697
+ .rows( { filter: 'applied', order: 'applied' } )
23698
+ .indexes()
23699
+ .indexOf( currentCell.index().row );
23700
+
23701
+ // When server-side processing, `rows().indexes()` only gives the rows
23702
+ // that are available at the client-side, so we need to normalise the
23703
+ // row's current position by the display start point
23704
+ if ( pageInfo.serverSide ) {
23705
+ currRow += pageInfo.start;
23706
+ }
23707
+
23708
+ var currCol = dt
23709
+ .columns( columns )
23710
+ .indexes()
23711
+ .indexOf( currentCell.index().column );
23712
+
23713
+ var
23714
+ row = currRow,
23715
+ column = columns[ currCol ]; // row is the display, column is an index
23716
+
23717
+ if ( direction === 'right' ) {
23718
+ if ( currCol >= columns.length - 1 ) {
23719
+ row++;
23720
+ column = columns[0];
23721
+ }
23722
+ else {
23723
+ column = columns[ currCol+1 ];
23724
+ }
23725
+ }
23726
+ else if ( direction === 'left' ) {
23727
+ if ( currCol === 0 ) {
23728
+ row--;
23729
+ column = columns[ columns.length - 1 ];
23730
+ }
23731
+ else {
23732
+ column = columns[ currCol-1 ];
23733
+ }
23734
+ }
23735
+ else if ( direction === 'up' ) {
23736
+ row--;
23737
+ }
23738
+ else if ( direction === 'down' ) {
23739
+ row++;
23740
+ }
23741
+
23742
+ if ( row >= 0 && row < rows && $.inArray( column, columns ) !== -1 ) {
23743
+ if (e) {
23744
+ e.preventDefault();
23745
+ }
23746
+
23747
+ this._focus( row, column, true, e );
23748
+ }
23749
+ else if ( ! keyBlurable || ! this.c.blurable ) {
23750
+ // No new focus, but if the table isn't blurable, then don't loose
23751
+ // focus
23752
+ if (e) {
23753
+ e.preventDefault();
23754
+ }
23755
+ }
23756
+ else {
23757
+ this._blur();
23758
+ }
23759
+ },
23760
+
23761
+
23762
+ /**
23763
+ * Create and insert a hidden input element that can receive focus on behalf
23764
+ * of the table
23765
+ *
23766
+ * @private
23767
+ */
23768
+ _tabInput: function ()
23769
+ {
23770
+ var that = this;
23771
+ var dt = this.s.dt;
23772
+ var tabIndex = this.c.tabIndex !== null ?
23773
+ this.c.tabIndex :
23774
+ dt.settings()[0].iTabIndex;
23775
+
23776
+ if ( tabIndex == -1 ) {
23777
+ return;
23778
+ }
23779
+
23780
+ // Only create the input element once on first class
23781
+ if (! this.s.tabInput) {
23782
+ var div = $('<div><input type="text" tabindex="'+tabIndex+'"/></div>')
23783
+ .css( {
23784
+ position: 'absolute',
23785
+ height: 1,
23786
+ width: 0,
23787
+ overflow: 'hidden'
23788
+ } );
23789
+
23790
+ div.children().on( 'focus', function (e) {
23791
+ var cell = dt.cell(':eq(0)', that._columns(), {page: 'current'});
23792
+
23793
+ if ( cell.any() ) {
23794
+ that._focus( cell, null, true, e );
23795
+ }
23796
+ } );
23797
+
23798
+ this.s.tabInput = div;
23799
+ }
23800
+
23801
+ // Insert the input element into the first cell in the table's body
23802
+ var cell = this.s.dt.cell(':eq(0)', '0:visible', {page: 'current', order: 'current'}).node();
23803
+ if (cell) {
23804
+ $(cell).prepend(this.s.tabInput);
23805
+ }
23806
+ },
23807
+
23808
+ /**
23809
+ * Update fixed columns if they are enabled and if the cell we are
23810
+ * focusing is inside a fixed column
23811
+ * @param {integer} column Index of the column being changed
23812
+ * @private
23813
+ */
23814
+ _updateFixedColumns: function( column )
23815
+ {
23816
+ var dt = this.s.dt;
23817
+ var settings = dt.settings()[0];
23818
+
23819
+ if ( settings._oFixedColumns ) {
23820
+ var leftCols = settings._oFixedColumns.s.iLeftColumns;
23821
+ var rightCols = settings.aoColumns.length - settings._oFixedColumns.s.iRightColumns;
23822
+
23823
+ if (column < leftCols || column >= rightCols) {
23824
+ dt.fixedColumns().update();
23825
+ }
23826
+ }
23827
+ }
23828
+ } );
23829
+
23830
+
23831
+ /**
23832
+ * KeyTable default settings for initialisation
23833
+ *
23834
+ * @namespace
23835
+ * @name KeyTable.defaults
23836
+ * @static
23837
+ */
23838
+ KeyTable.defaults = {
23839
+ /**
23840
+ * Can focus be removed from the table
23841
+ * @type {Boolean}
23842
+ */
23843
+ blurable: true,
23844
+
23845
+ /**
23846
+ * Class to give to the focused cell
23847
+ * @type {String}
23848
+ */
23849
+ className: 'focus',
23850
+
23851
+ /**
23852
+ * Enable or disable clipboard support
23853
+ * @type {Boolean}
23854
+ */
23855
+ clipboard: true,
23856
+
23857
+ /**
23858
+ * Orthogonal data that should be copied to clipboard
23859
+ * @type {string}
23860
+ */
23861
+ clipboardOrthogonal: 'display',
23862
+
23863
+ /**
23864
+ * Columns that can be focused. This is automatically merged with the
23865
+ * visible columns as only visible columns can gain focus.
23866
+ * @type {String}
23867
+ */
23868
+ columns: '', // all
23869
+
23870
+ /**
23871
+ * Editor instance to automatically perform Excel like navigation
23872
+ * @type {Editor}
23873
+ */
23874
+ editor: null,
23875
+
23876
+ /**
23877
+ * Trigger editing immediately on focus
23878
+ * @type {boolean}
23879
+ */
23880
+ editOnFocus: false,
23881
+
23882
+ /**
23883
+ * Select a cell to automatically select on start up. `null` for no
23884
+ * automatic selection
23885
+ * @type {cell-selector}
23886
+ */
23887
+ focus: null,
23888
+
23889
+ /**
23890
+ * Array of keys to listen for
23891
+ * @type {null|array}
23892
+ */
23893
+ keys: null,
23894
+
23895
+ /**
23896
+ * Tab index for where the table should sit in the document's tab flow
23897
+ * @type {integer|null}
23898
+ */
23899
+ tabIndex: null
23900
+ };
23901
+
23902
+
23903
+
23904
+ KeyTable.version = "2.6.1";
23905
+
23906
+
23907
+ $.fn.dataTable.KeyTable = KeyTable;
23908
+ $.fn.DataTable.KeyTable = KeyTable;
23909
+
23910
+
23911
+ DataTable.Api.register( 'cell.blur()', function () {
23912
+ return this.iterator( 'table', function (ctx) {
23913
+ if ( ctx.keytable ) {
23914
+ ctx.keytable.blur();
23915
+ }
23916
+ } );
23917
+ } );
23918
+
23919
+ DataTable.Api.register( 'cell().focus()', function () {
23920
+ return this.iterator( 'cell', function (ctx, row, column) {
23921
+ if ( ctx.keytable ) {
23922
+ ctx.keytable.focus( row, column );
23923
+ }
23924
+ } );
23925
+ } );
23926
+
23927
+ DataTable.Api.register( 'keys.disable()', function () {
23928
+ return this.iterator( 'table', function (ctx) {
23929
+ if ( ctx.keytable ) {
23930
+ ctx.keytable.enable( false );
23931
+ }
23932
+ } );
23933
+ } );
23934
+
23935
+ DataTable.Api.register( 'keys.enable()', function ( opts ) {
23936
+ return this.iterator( 'table', function (ctx) {
23937
+ if ( ctx.keytable ) {
23938
+ ctx.keytable.enable( opts === undefined ? true : opts );
23939
+ }
23940
+ } );
23941
+ } );
23942
+
23943
+ DataTable.Api.register( 'keys.enabled()', function ( opts ) {
23944
+ var ctx = this.context;
23945
+
23946
+ if (ctx.length) {
23947
+ return ctx[0].keytable
23948
+ ? ctx[0].keytable.enabled()
23949
+ : false;
23950
+ }
23951
+
23952
+ return false;
23953
+ } );
23954
+
23955
+ DataTable.Api.register( 'keys.move()', function ( dir ) {
23956
+ return this.iterator( 'table', function (ctx) {
23957
+ if ( ctx.keytable ) {
23958
+ ctx.keytable._shift( null, dir, false );
23959
+ }
23960
+ } );
23961
+ } );
23962
+
23963
+ // Cell selector
23964
+ DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
23965
+ var focused = opts.focused;
23966
+ var kt = settings.keytable;
23967
+ var out = [];
23968
+
23969
+ if ( ! kt || focused === undefined ) {
23970
+ return cells;
23971
+ }
23972
+
23973
+ for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
23974
+ if ( (focused === true && kt.focused( cells[i] ) ) ||
23975
+ (focused === false && ! kt.focused( cells[i] ) )
23976
+ ) {
23977
+ out.push( cells[i] );
23978
+ }
23979
+ }
23980
+
23981
+ return out;
23982
+ } );
23983
+
23984
+
23985
+ // Attach a listener to the document which listens for DataTables initialisation
23986
+ // events so we can automatically initialise
23987
+ $(document).on( 'preInit.dt.dtk', function (e, settings, json) {
23988
+ if ( e.namespace !== 'dt' ) {
23989
+ return;
23990
+ }
23991
+
23992
+ var init = settings.oInit.keys;
23993
+ var defaults = DataTable.defaults.keys;
23994
+
23995
+ if ( init || defaults ) {
23996
+ var opts = $.extend( {}, defaults, init );
23997
+
23998
+ if ( init !== false ) {
23999
+ new KeyTable( settings, opts );
24000
+ }
24001
+ }
24002
+ } );
24003
+
24004
+
24005
+ return KeyTable;
24006
+ }));
24007
+
24008
+
24009
+ /*! Responsive 2.2.7
24010
+ * 2014-2021 SpryMedia Ltd - datatables.net/license
24011
+ */
24012
+
24013
+ /**
24014
+ * @summary Responsive
24015
+ * @description Responsive tables plug-in for DataTables
24016
+ * @version 2.2.7
24017
+ * @file dataTables.responsive.js
24018
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
24019
+ * @contact www.sprymedia.co.uk/contact
24020
+ * @copyright Copyright 2014-2021 SpryMedia Ltd.
24021
+ *
24022
+ * This source file is free software, available under the following license:
24023
+ * MIT license - http://datatables.net/license/mit
24024
+ *
24025
+ * This source file is distributed in the hope that it will be useful, but
24026
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24027
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
24028
+ *
24029
+ * For details please refer to: http://www.datatables.net
24030
+ */
24031
+ (function( factory ){
24032
+ if ( typeof define === 'function' && define.amd ) {
24033
+ // AMD
24034
+ define( ['jquery', 'datatables.net'], function ( $ ) {
24035
+ return factory( $, window, document );
24036
+ } );
24037
+ }
24038
+ else if ( typeof exports === 'object' ) {
24039
+ // CommonJS
24040
+ module.exports = function (root, $) {
24041
+ if ( ! root ) {
24042
+ root = window;
24043
+ }
24044
+
24045
+ if ( ! $ || ! $.fn.dataTable ) {
24046
+ $ = require('datatables.net')(root, $).$;
24047
+ }
24048
+
24049
+ return factory( $, root, root.document );
24050
+ };
24051
+ }
24052
+ else {
24053
+ // Browser
24054
+ factory( jQuery, window, document );
24055
+ }
24056
+ }(function( $, window, document, undefined ) {
24057
+ 'use strict';
24058
+ var DataTable = $.fn.dataTable;
24059
+
24060
+
24061
+ /**
24062
+ * Responsive is a plug-in for the DataTables library that makes use of
24063
+ * DataTables' ability to change the visibility of columns, changing the
24064
+ * visibility of columns so the displayed columns fit into the table container.
24065
+ * The end result is that complex tables will be dynamically adjusted to fit
24066
+ * into the viewport, be it on a desktop, tablet or mobile browser.
24067
+ *
24068
+ * Responsive for DataTables has two modes of operation, which can used
24069
+ * individually or combined:
24070
+ *
24071
+ * * Class name based control - columns assigned class names that match the
24072
+ * breakpoint logic can be shown / hidden as required for each breakpoint.
24073
+ * * Automatic control - columns are automatically hidden when there is no
24074
+ * room left to display them. Columns removed from the right.
24075
+ *
24076
+ * In additional to column visibility control, Responsive also has built into
24077
+ * options to use DataTables' child row display to show / hide the information
24078
+ * from the table that has been hidden. There are also two modes of operation
24079
+ * for this child row display:
24080
+ *
24081
+ * * Inline - when the control element that the user can use to show / hide
24082
+ * child rows is displayed inside the first column of the table.
24083
+ * * Column - where a whole column is dedicated to be the show / hide control.
24084
+ *
24085
+ * Initialisation of Responsive is performed by:
24086
+ *
24087
+ * * Adding the class `responsive` or `dt-responsive` to the table. In this case
24088
+ * Responsive will automatically be initialised with the default configuration
24089
+ * options when the DataTable is created.
24090
+ * * Using the `responsive` option in the DataTables configuration options. This
24091
+ * can also be used to specify the configuration options, or simply set to
24092
+ * `true` to use the defaults.
24093
+ *
24094
+ * @class
24095
+ * @param {object} settings DataTables settings object for the host table
24096
+ * @param {object} [opts] Configuration options
24097
+ * @requires jQuery 1.7+
24098
+ * @requires DataTables 1.10.3+
24099
+ *
24100
+ * @example
24101
+ * $('#example').DataTable( {
24102
+ * responsive: true
24103
+ * } );
24104
+ * } );
24105
+ */
24106
+ var Responsive = function ( settings, opts ) {
24107
+ // Sanity check that we are using DataTables 1.10 or newer
24108
+ if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.10' ) ) {
24109
+ throw 'DataTables Responsive requires DataTables 1.10.10 or newer';
24110
+ }
24111
+
24112
+ this.s = {
24113
+ dt: new DataTable.Api( settings ),
24114
+ columns: [],
24115
+ current: []
24116
+ };
24117
+
24118
+ // Check if responsive has already been initialised on this table
24119
+ if ( this.s.dt.settings()[0].responsive ) {
24120
+ return;
24121
+ }
24122
+
24123
+ // details is an object, but for simplicity the user can give it as a string
24124
+ // or a boolean
24125
+ if ( opts && typeof opts.details === 'string' ) {
24126
+ opts.details = { type: opts.details };
24127
+ }
24128
+ else if ( opts && opts.details === false ) {
24129
+ opts.details = { type: false };
24130
+ }
24131
+ else if ( opts && opts.details === true ) {
24132
+ opts.details = { type: 'inline' };
24133
+ }
24134
+
24135
+ this.c = $.extend( true, {}, Responsive.defaults, DataTable.defaults.responsive, opts );
24136
+ settings.responsive = this;
24137
+ this._constructor();
24138
+ };
24139
+
24140
+ $.extend( Responsive.prototype, {
24141
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24142
+ * Constructor
24143
+ */
24144
+
24145
+ /**
24146
+ * Initialise the Responsive instance
24147
+ *
24148
+ * @private
24149
+ */
24150
+ _constructor: function ()
24151
+ {
24152
+ var that = this;
24153
+ var dt = this.s.dt;
24154
+ var dtPrivateSettings = dt.settings()[0];
24155
+ var oldWindowWidth = $(window).innerWidth();
24156
+
24157
+ dt.settings()[0]._responsive = this;
24158
+
24159
+ // Use DataTables' throttle function to avoid processor thrashing on
24160
+ // resize
24161
+ $(window).on( 'resize.dtr orientationchange.dtr', DataTable.util.throttle( function () {
24162
+ // iOS has a bug whereby resize can fire when only scrolling
24163
+ // See: http://stackoverflow.com/questions/8898412
24164
+ var width = $(window).innerWidth();
24165
+
24166
+ if ( width !== oldWindowWidth ) {
24167
+ that._resize();
24168
+ oldWindowWidth = width;
24169
+ }
24170
+ } ) );
24171
+
24172
+ // DataTables doesn't currently trigger an event when a row is added, so
24173
+ // we need to hook into its private API to enforce the hidden rows when
24174
+ // new data is added
24175
+ dtPrivateSettings.oApi._fnCallbackReg( dtPrivateSettings, 'aoRowCreatedCallback', function (tr, data, idx) {
24176
+ if ( $.inArray( false, that.s.current ) !== -1 ) {
24177
+ $('>td, >th', tr).each( function ( i ) {
24178
+ var idx = dt.column.index( 'toData', i );
24179
+
24180
+ if ( that.s.current[idx] === false ) {
24181
+ $(this).css('display', 'none');
24182
+ }
24183
+ } );
24184
+ }
24185
+ } );
24186
+
24187
+ // Destroy event handler
24188
+ dt.on( 'destroy.dtr', function () {
24189
+ dt.off( '.dtr' );
24190
+ $( dt.table().body() ).off( '.dtr' );
24191
+ $(window).off( 'resize.dtr orientationchange.dtr' );
24192
+ dt.cells('.dtr-control').nodes().to$().removeClass('dtr-control');
24193
+
24194
+ // Restore the columns that we've hidden
24195
+ $.each( that.s.current, function ( i, val ) {
24196
+ if ( val === false ) {
24197
+ that._setColumnVis( i, true );
24198
+ }
24199
+ } );
24200
+ } );
24201
+
24202
+ // Reorder the breakpoints array here in case they have been added out
24203
+ // of order
24204
+ this.c.breakpoints.sort( function (a, b) {
24205
+ return a.width < b.width ? 1 :
24206
+ a.width > b.width ? -1 : 0;
24207
+ } );
24208
+
24209
+ this._classLogic();
24210
+ this._resizeAuto();
24211
+
24212
+ // Details handler
24213
+ var details = this.c.details;
24214
+
24215
+ if ( details.type !== false ) {
24216
+ that._detailsInit();
24217
+
24218
+ // DataTables will trigger this event on every column it shows and
24219
+ // hides individually
24220
+ dt.on( 'column-visibility.dtr', function () {
24221
+ // Use a small debounce to allow multiple columns to be set together
24222
+ if ( that._timer ) {
24223
+ clearTimeout( that._timer );
24224
+ }
24225
+
24226
+ that._timer = setTimeout( function () {
24227
+ that._timer = null;
24228
+
24229
+ that._classLogic();
24230
+ that._resizeAuto();
24231
+ that._resize(true);
24232
+
24233
+ that._redrawChildren();
24234
+ }, 100 );
24235
+ } );
24236
+
24237
+ // Redraw the details box on each draw which will happen if the data
24238
+ // has changed. This is used until DataTables implements a native
24239
+ // `updated` event for rows
24240
+ dt.on( 'draw.dtr', function () {
24241
+ that._redrawChildren();
24242
+ } );
24243
+
24244
+ $(dt.table().node()).addClass( 'dtr-'+details.type );
24245
+ }
24246
+
24247
+ dt.on( 'column-reorder.dtr', function (e, settings, details) {
24248
+ that._classLogic();
24249
+ that._resizeAuto();
24250
+ that._resize(true);
24251
+ } );
24252
+
24253
+ // Change in column sizes means we need to calc
24254
+ dt.on( 'column-sizing.dtr', function () {
24255
+ that._resizeAuto();
24256
+ that._resize();
24257
+ });
24258
+
24259
+ // On Ajax reload we want to reopen any child rows which are displayed
24260
+ // by responsive
24261
+ dt.on( 'preXhr.dtr', function () {
24262
+ var rowIds = [];
24263
+ dt.rows().every( function () {
24264
+ if ( this.child.isShown() ) {
24265
+ rowIds.push( this.id(true) );
24266
+ }
24267
+ } );
24268
+
24269
+ dt.one( 'draw.dtr', function () {
24270
+ that._resizeAuto();
24271
+ that._resize();
24272
+
24273
+ dt.rows( rowIds ).every( function () {
24274
+ that._detailsDisplay( this, false );
24275
+ } );
24276
+ } );
24277
+ });
24278
+
24279
+ dt
24280
+ .on( 'draw.dtr', function () {
24281
+ that._controlClass();
24282
+ })
24283
+ .on( 'init.dtr', function (e, settings, details) {
24284
+ if ( e.namespace !== 'dt' ) {
24285
+ return;
24286
+ }
24287
+
24288
+ that._resizeAuto();
24289
+ that._resize();
24290
+
24291
+ // If columns were hidden, then DataTables needs to adjust the
24292
+ // column sizing
24293
+ if ( $.inArray( false, that.s.current ) ) {
24294
+ dt.columns.adjust();
24295
+ }
24296
+ } );
24297
+
24298
+ // First pass - draw the table for the current viewport size
24299
+ this._resize();
24300
+ },
24301
+
24302
+
24303
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24304
+ * Private methods
24305
+ */
24306
+
24307
+ /**
24308
+ * Calculate the visibility for the columns in a table for a given
24309
+ * breakpoint. The result is pre-determined based on the class logic if
24310
+ * class names are used to control all columns, but the width of the table
24311
+ * is also used if there are columns which are to be automatically shown
24312
+ * and hidden.
24313
+ *
24314
+ * @param {string} breakpoint Breakpoint name to use for the calculation
24315
+ * @return {array} Array of boolean values initiating the visibility of each
24316
+ * column.
24317
+ * @private
24318
+ */
24319
+ _columnsVisiblity: function ( breakpoint )
24320
+ {
24321
+ var dt = this.s.dt;
24322
+ var columns = this.s.columns;
24323
+ var i, ien;
24324
+
24325
+ // Create an array that defines the column ordering based first on the
24326
+ // column's priority, and secondly the column index. This allows the
24327
+ // columns to be removed from the right if the priority matches
24328
+ var order = columns
24329
+ .map( function ( col, idx ) {
24330
+ return {
24331
+ columnIdx: idx,
24332
+ priority: col.priority
24333
+ };
24334
+ } )
24335
+ .sort( function ( a, b ) {
24336
+ if ( a.priority !== b.priority ) {
24337
+ return a.priority - b.priority;
24338
+ }
24339
+ return a.columnIdx - b.columnIdx;
24340
+ } );
24341
+
24342
+ // Class logic - determine which columns are in this breakpoint based
24343
+ // on the classes. If no class control (i.e. `auto`) then `-` is used
24344
+ // to indicate this to the rest of the function
24345
+ var display = $.map( columns, function ( col, i ) {
24346
+ if ( dt.column(i).visible() === false ) {
24347
+ return 'not-visible';
24348
+ }
24349
+ return col.auto && col.minWidth === null ?
24350
+ false :
24351
+ col.auto === true ?
24352
+ '-' :
24353
+ $.inArray( breakpoint, col.includeIn ) !== -1;
24354
+ } );
24355
+
24356
+ // Auto column control - first pass: how much width is taken by the
24357
+ // ones that must be included from the non-auto columns
24358
+ var requiredWidth = 0;
24359
+ for ( i=0, ien=display.length ; i<ien ; i++ ) {
24360
+ if ( display[i] === true ) {
24361
+ requiredWidth += columns[i].minWidth;
24362
+ }
24363
+ }
24364
+
24365
+ // Second pass, use up any remaining width for other columns. For
24366
+ // scrolling tables we need to subtract the width of the scrollbar. It
24367
+ // may not be requires which makes this sub-optimal, but it would
24368
+ // require another full redraw to make complete use of those extra few
24369
+ // pixels
24370
+ var scrolling = dt.settings()[0].oScroll;
24371
+ var bar = scrolling.sY || scrolling.sX ? scrolling.iBarWidth : 0;
24372
+ var widthAvailable = dt.table().container().offsetWidth - bar;
24373
+ var usedWidth = widthAvailable - requiredWidth;
24374
+
24375
+ // Control column needs to always be included. This makes it sub-
24376
+ // optimal in terms of using the available with, but to stop layout
24377
+ // thrashing or overflow. Also we need to account for the control column
24378
+ // width first so we know how much width is available for the other
24379
+ // columns, since the control column might not be the first one shown
24380
+ for ( i=0, ien=display.length ; i<ien ; i++ ) {
24381
+ if ( columns[i].control ) {
24382
+ usedWidth -= columns[i].minWidth;
24383
+ }
24384
+ }
24385
+
24386
+ // Allow columns to be shown (counting by priority and then right to
24387
+ // left) until we run out of room
24388
+ var empty = false;
24389
+ for ( i=0, ien=order.length ; i<ien ; i++ ) {
24390
+ var colIdx = order[i].columnIdx;
24391
+
24392
+ if ( display[colIdx] === '-' && ! columns[colIdx].control && columns[colIdx].minWidth ) {
24393
+ // Once we've found a column that won't fit we don't let any
24394
+ // others display either, or columns might disappear in the
24395
+ // middle of the table
24396
+ if ( empty || usedWidth - columns[colIdx].minWidth < 0 ) {
24397
+ empty = true;
24398
+ display[colIdx] = false;
24399
+ }
24400
+ else {
24401
+ display[colIdx] = true;
24402
+ }
24403
+
24404
+ usedWidth -= columns[colIdx].minWidth;
24405
+ }
24406
+ }
24407
+
24408
+ // Determine if the 'control' column should be shown (if there is one).
24409
+ // This is the case when there is a hidden column (that is not the
24410
+ // control column). The two loops look inefficient here, but they are
24411
+ // trivial and will fly through. We need to know the outcome from the
24412
+ // first , before the action in the second can be taken
24413
+ var showControl = false;
24414
+
24415
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
24416
+ if ( ! columns[i].control && ! columns[i].never && display[i] === false ) {
24417
+ showControl = true;
24418
+ break;
24419
+ }
24420
+ }
24421
+
24422
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
24423
+ if ( columns[i].control ) {
24424
+ display[i] = showControl;
24425
+ }
24426
+
24427
+ // Replace not visible string with false from the control column detection above
24428
+ if ( display[i] === 'not-visible' ) {
24429
+ display[i] = false;
24430
+ }
24431
+ }
24432
+
24433
+ // Finally we need to make sure that there is at least one column that
24434
+ // is visible
24435
+ if ( $.inArray( true, display ) === -1 ) {
24436
+ display[0] = true;
24437
+ }
24438
+
24439
+ return display;
24440
+ },
24441
+
24442
+
24443
+ /**
24444
+ * Create the internal `columns` array with information about the columns
24445
+ * for the table. This includes determining which breakpoints the column
24446
+ * will appear in, based upon class names in the column, which makes up the
24447
+ * vast majority of this method.
24448
+ *
24449
+ * @private
24450
+ */
24451
+ _classLogic: function ()
24452
+ {
24453
+ var that = this;
24454
+ var calc = {};
24455
+ var breakpoints = this.c.breakpoints;
24456
+ var dt = this.s.dt;
24457
+ var columns = dt.columns().eq(0).map( function (i) {
24458
+ var column = this.column(i);
24459
+ var className = column.header().className;
24460
+ var priority = dt.settings()[0].aoColumns[i].responsivePriority;
24461
+ var dataPriority = column.header().getAttribute('data-priority');
24462
+
24463
+ if ( priority === undefined ) {
24464
+ priority = dataPriority === undefined || dataPriority === null?
24465
+ 10000 :
24466
+ dataPriority * 1;
24467
+ }
24468
+
24469
+ return {
24470
+ className: className,
24471
+ includeIn: [],
24472
+ auto: false,
24473
+ control: false,
24474
+ never: className.match(/\bnever\b/) ? true : false,
24475
+ priority: priority
24476
+ };
24477
+ } );
24478
+
24479
+ // Simply add a breakpoint to `includeIn` array, ensuring that there are
24480
+ // no duplicates
24481
+ var add = function ( colIdx, name ) {
24482
+ var includeIn = columns[ colIdx ].includeIn;
24483
+
24484
+ if ( $.inArray( name, includeIn ) === -1 ) {
24485
+ includeIn.push( name );
24486
+ }
24487
+ };
24488
+
24489
+ var column = function ( colIdx, name, operator, matched ) {
24490
+ var size, i, ien;
24491
+
24492
+ if ( ! operator ) {
24493
+ columns[ colIdx ].includeIn.push( name );
24494
+ }
24495
+ else if ( operator === 'max-' ) {
24496
+ // Add this breakpoint and all smaller
24497
+ size = that._find( name ).width;
24498
+
24499
+ for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
24500
+ if ( breakpoints[i].width <= size ) {
24501
+ add( colIdx, breakpoints[i].name );
24502
+ }
24503
+ }
24504
+ }
24505
+ else if ( operator === 'min-' ) {
24506
+ // Add this breakpoint and all larger
24507
+ size = that._find( name ).width;
24508
+
24509
+ for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
24510
+ if ( breakpoints[i].width >= size ) {
24511
+ add( colIdx, breakpoints[i].name );
24512
+ }
24513
+ }
24514
+ }
24515
+ else if ( operator === 'not-' ) {
24516
+ // Add all but this breakpoint
24517
+ for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
24518
+ if ( breakpoints[i].name.indexOf( matched ) === -1 ) {
24519
+ add( colIdx, breakpoints[i].name );
24520
+ }
24521
+ }
24522
+ }
24523
+ };
24524
+
24525
+ // Loop over each column and determine if it has a responsive control
24526
+ // class
24527
+ columns.each( function ( col, i ) {
24528
+ var classNames = col.className.split(' ');
24529
+ var hasClass = false;
24530
+
24531
+ // Split the class name up so multiple rules can be applied if needed
24532
+ for ( var k=0, ken=classNames.length ; k<ken ; k++ ) {
24533
+ var className = classNames[k].trim();
24534
+
24535
+ if ( className === 'all' ) {
24536
+ // Include in all
24537
+ hasClass = true;
24538
+ col.includeIn = $.map( breakpoints, function (a) {
24539
+ return a.name;
24540
+ } );
24541
+ return;
24542
+ }
24543
+ else if ( className === 'none' || col.never ) {
24544
+ // Include in none (default) and no auto
24545
+ hasClass = true;
24546
+ return;
24547
+ }
24548
+ else if ( className === 'control' || className === 'dtr-control' ) {
24549
+ // Special column that is only visible, when one of the other
24550
+ // columns is hidden. This is used for the details control
24551
+ hasClass = true;
24552
+ col.control = true;
24553
+ return;
24554
+ }
24555
+
24556
+ $.each( breakpoints, function ( j, breakpoint ) {
24557
+ // Does this column have a class that matches this breakpoint?
24558
+ var brokenPoint = breakpoint.name.split('-');
24559
+ var re = new RegExp( '(min\\-|max\\-|not\\-)?('+brokenPoint[0]+')(\\-[_a-zA-Z0-9])?' );
24560
+ var match = className.match( re );
24561
+
24562
+ if ( match ) {
24563
+ hasClass = true;
24564
+
24565
+ if ( match[2] === brokenPoint[0] && match[3] === '-'+brokenPoint[1] ) {
24566
+ // Class name matches breakpoint name fully
24567
+ column( i, breakpoint.name, match[1], match[2]+match[3] );
24568
+ }
24569
+ else if ( match[2] === brokenPoint[0] && ! match[3] ) {
24570
+ // Class name matched primary breakpoint name with no qualifier
24571
+ column( i, breakpoint.name, match[1], match[2] );
24572
+ }
24573
+ }
24574
+ } );
24575
+ }
24576
+
24577
+ // If there was no control class, then automatic sizing is used
24578
+ if ( ! hasClass ) {
24579
+ col.auto = true;
24580
+ }
24581
+ } );
24582
+
24583
+ this.s.columns = columns;
24584
+ },
24585
+
24586
+ /**
24587
+ * Update the cells to show the correct control class / button
24588
+ * @private
24589
+ */
24590
+ _controlClass: function ()
24591
+ {
24592
+ if ( this.c.details.type === 'inline' ) {
24593
+ var dt = this.s.dt;
24594
+ var columnsVis = this.s.current;
24595
+ var firstVisible = $.inArray(true, columnsVis);
24596
+
24597
+ // Remove from any cells which shouldn't have it
24598
+ dt.cells(
24599
+ null,
24600
+ function(idx) {
24601
+ return idx !== firstVisible;
24602
+ },
24603
+ {page: 'current'}
24604
+ )
24605
+ .nodes()
24606
+ .to$()
24607
+ .filter('.dtr-control')
24608
+ .removeClass('dtr-control');
24609
+
24610
+ dt.cells(null, firstVisible, {page: 'current'})
24611
+ .nodes()
24612
+ .to$()
24613
+ .addClass('dtr-control');
24614
+ }
24615
+ },
24616
+
24617
+ /**
24618
+ * Show the details for the child row
24619
+ *
24620
+ * @param {DataTables.Api} row API instance for the row
24621
+ * @param {boolean} update Update flag
24622
+ * @private
24623
+ */
24624
+ _detailsDisplay: function ( row, update )
24625
+ {
24626
+ var that = this;
24627
+ var dt = this.s.dt;
24628
+ var details = this.c.details;
24629
+
24630
+ if ( details && details.type !== false ) {
24631
+ var res = details.display( row, update, function () {
24632
+ return details.renderer(
24633
+ dt, row[0], that._detailsObj(row[0])
24634
+ );
24635
+ } );
24636
+
24637
+ if ( res === true || res === false ) {
24638
+ $(dt.table().node()).triggerHandler( 'responsive-display.dt', [dt, row, res, update] );
24639
+ }
24640
+ }
24641
+ },
24642
+
24643
+
24644
+ /**
24645
+ * Initialisation for the details handler
24646
+ *
24647
+ * @private
24648
+ */
24649
+ _detailsInit: function ()
24650
+ {
24651
+ var that = this;
24652
+ var dt = this.s.dt;
24653
+ var details = this.c.details;
24654
+
24655
+ // The inline type always uses the first child as the target
24656
+ if ( details.type === 'inline' ) {
24657
+ details.target = 'td.dtr-control, th.dtr-control';
24658
+ }
24659
+
24660
+ // Keyboard accessibility
24661
+ dt.on( 'draw.dtr', function () {
24662
+ that._tabIndexes();
24663
+ } );
24664
+ that._tabIndexes(); // Initial draw has already happened
24665
+
24666
+ $( dt.table().body() ).on( 'keyup.dtr', 'td, th', function (e) {
24667
+ if ( e.keyCode === 13 && $(this).data('dtr-keyboard') ) {
24668
+ $(this).click();
24669
+ }
24670
+ } );
24671
+
24672
+ // type.target can be a string jQuery selector or a column index
24673
+ var target = details.target;
24674
+ var selector = typeof target === 'string' ? target : 'td, th';
24675
+
24676
+ if ( target !== undefined || target !== null ) {
24677
+ // Click handler to show / hide the details rows when they are available
24678
+ $( dt.table().body() )
24679
+ .on( 'click.dtr mousedown.dtr mouseup.dtr', selector, function (e) {
24680
+ // If the table is not collapsed (i.e. there is no hidden columns)
24681
+ // then take no action
24682
+ if ( ! $(dt.table().node()).hasClass('collapsed' ) ) {
24683
+ return;
24684
+ }
24685
+
24686
+ // Check that the row is actually a DataTable's controlled node
24687
+ if ( $.inArray( $(this).closest('tr').get(0), dt.rows().nodes().toArray() ) === -1 ) {
24688
+ return;
24689
+ }
24690
+
24691
+ // For column index, we determine if we should act or not in the
24692
+ // handler - otherwise it is already okay
24693
+ if ( typeof target === 'number' ) {
24694
+ var targetIdx = target < 0 ?
24695
+ dt.columns().eq(0).length + target :
24696
+ target;
24697
+
24698
+ if ( dt.cell( this ).index().column !== targetIdx ) {
24699
+ return;
24700
+ }
24701
+ }
24702
+
24703
+ // $().closest() includes itself in its check
24704
+ var row = dt.row( $(this).closest('tr') );
24705
+
24706
+ // Check event type to do an action
24707
+ if ( e.type === 'click' ) {
24708
+ // The renderer is given as a function so the caller can execute it
24709
+ // only when they need (i.e. if hiding there is no point is running
24710
+ // the renderer)
24711
+ that._detailsDisplay( row, false );
24712
+ }
24713
+ else if ( e.type === 'mousedown' ) {
24714
+ // For mouse users, prevent the focus ring from showing
24715
+ $(this).css('outline', 'none');
24716
+ }
24717
+ else if ( e.type === 'mouseup' ) {
24718
+ // And then re-allow at the end of the click
24719
+ $(this).trigger('blur').css('outline', '');
24720
+ }
24721
+ } );
24722
+ }
24723
+ },
24724
+
24725
+
24726
+ /**
24727
+ * Get the details to pass to a renderer for a row
24728
+ * @param {int} rowIdx Row index
24729
+ * @private
24730
+ */
24731
+ _detailsObj: function ( rowIdx )
24732
+ {
24733
+ var that = this;
24734
+ var dt = this.s.dt;
24735
+
24736
+ return $.map( this.s.columns, function( col, i ) {
24737
+ // Never and control columns should not be passed to the renderer
24738
+ if ( col.never || col.control ) {
24739
+ return;
24740
+ }
24741
+
24742
+ var dtCol = dt.settings()[0].aoColumns[ i ];
24743
+
24744
+ return {
24745
+ className: dtCol.sClass,
24746
+ columnIndex: i,
24747
+ data: dt.cell( rowIdx, i ).render( that.c.orthogonal ),
24748
+ hidden: dt.column( i ).visible() && !that.s.current[ i ],
24749
+ rowIndex: rowIdx,
24750
+ title: dtCol.sTitle !== null ?
24751
+ dtCol.sTitle :
24752
+ $(dt.column(i).header()).text()
24753
+ };
24754
+ } );
24755
+ },
24756
+
24757
+
24758
+ /**
24759
+ * Find a breakpoint object from a name
24760
+ *
24761
+ * @param {string} name Breakpoint name to find
24762
+ * @return {object} Breakpoint description object
24763
+ * @private
24764
+ */
24765
+ _find: function ( name )
24766
+ {
24767
+ var breakpoints = this.c.breakpoints;
24768
+
24769
+ for ( var i=0, ien=breakpoints.length ; i<ien ; i++ ) {
24770
+ if ( breakpoints[i].name === name ) {
24771
+ return breakpoints[i];
24772
+ }
24773
+ }
24774
+ },
24775
+
24776
+
24777
+ /**
24778
+ * Re-create the contents of the child rows as the display has changed in
24779
+ * some way.
24780
+ *
24781
+ * @private
24782
+ */
24783
+ _redrawChildren: function ()
24784
+ {
24785
+ var that = this;
24786
+ var dt = this.s.dt;
24787
+
24788
+ dt.rows( {page: 'current'} ).iterator( 'row', function ( settings, idx ) {
24789
+ var row = dt.row( idx );
24790
+
24791
+ that._detailsDisplay( dt.row( idx ), true );
24792
+ } );
24793
+ },
24794
+
24795
+
24796
+ /**
24797
+ * Alter the table display for a resized viewport. This involves first
24798
+ * determining what breakpoint the window currently is in, getting the
24799
+ * column visibilities to apply and then setting them.
24800
+ *
24801
+ * @param {boolean} forceRedraw Force a redraw
24802
+ * @private
24803
+ */
24804
+ _resize: function (forceRedraw)
24805
+ {
24806
+ var that = this;
24807
+ var dt = this.s.dt;
24808
+ var width = $(window).innerWidth();
24809
+ var breakpoints = this.c.breakpoints;
24810
+ var breakpoint = breakpoints[0].name;
24811
+ var columns = this.s.columns;
24812
+ var i, ien;
24813
+ var oldVis = this.s.current.slice();
24814
+
24815
+ // Determine what breakpoint we are currently at
24816
+ for ( i=breakpoints.length-1 ; i>=0 ; i-- ) {
24817
+ if ( width <= breakpoints[i].width ) {
24818
+ breakpoint = breakpoints[i].name;
24819
+ break;
24820
+ }
24821
+ }
24822
+
24823
+ // Show the columns for that break point
24824
+ var columnsVis = this._columnsVisiblity( breakpoint );
24825
+ this.s.current = columnsVis;
24826
+
24827
+ // Set the class before the column visibility is changed so event
24828
+ // listeners know what the state is. Need to determine if there are
24829
+ // any columns that are not visible but can be shown
24830
+ var collapsedClass = false;
24831
+
24832
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
24833
+ if ( columnsVis[i] === false && ! columns[i].never && ! columns[i].control && ! dt.column(i).visible() === false ) {
24834
+ collapsedClass = true;
24835
+ break;
24836
+ }
24837
+ }
24838
+
24839
+ $( dt.table().node() ).toggleClass( 'collapsed', collapsedClass );
24840
+
24841
+ var changed = false;
24842
+ var visible = 0;
24843
+
24844
+ dt.columns().eq(0).each( function ( colIdx, i ) {
24845
+ if ( columnsVis[i] === true ) {
24846
+ visible++;
24847
+ }
24848
+
24849
+ if ( forceRedraw || columnsVis[i] !== oldVis[i] ) {
24850
+ changed = true;
24851
+ that._setColumnVis( colIdx, columnsVis[i] );
24852
+ }
24853
+ } );
24854
+
24855
+ if ( changed ) {
24856
+ this._redrawChildren();
24857
+
24858
+ // Inform listeners of the change
24859
+ $(dt.table().node()).trigger( 'responsive-resize.dt', [dt, this.s.current] );
24860
+
24861
+ // If no records, update the "No records" display element
24862
+ if ( dt.page.info().recordsDisplay === 0 ) {
24863
+ $('td', dt.table().body()).eq(0).attr('colspan', visible);
24864
+ }
24865
+ }
24866
+
24867
+ that._controlClass();
24868
+ },
24869
+
24870
+
24871
+ /**
24872
+ * Determine the width of each column in the table so the auto column hiding
24873
+ * has that information to work with. This method is never going to be 100%
24874
+ * perfect since column widths can change slightly per page, but without
24875
+ * seriously compromising performance this is quite effective.
24876
+ *
24877
+ * @private
24878
+ */
24879
+ _resizeAuto: function ()
24880
+ {
24881
+ var dt = this.s.dt;
24882
+ var columns = this.s.columns;
24883
+
24884
+ // Are we allowed to do auto sizing?
24885
+ if ( ! this.c.auto ) {
24886
+ return;
24887
+ }
24888
+
24889
+ // Are there any columns that actually need auto-sizing, or do they all
24890
+ // have classes defined
24891
+ if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) {
24892
+ return;
24893
+ }
24894
+
24895
+ // Need to restore all children. They will be reinstated by a re-render
24896
+ if ( ! $.isEmptyObject( _childNodeStore ) ) {
24897
+ $.each( _childNodeStore, function ( key ) {
24898
+ var idx = key.split('-');
24899
+
24900
+ _childNodesRestore( dt, idx[0]*1, idx[1]*1 );
24901
+ } );
24902
+ }
24903
+
24904
+ // Clone the table with the current data in it
24905
+ var tableWidth = dt.table().node().offsetWidth;
24906
+ var columnWidths = dt.columns;
24907
+ var clonedTable = dt.table().node().cloneNode( false );
24908
+ var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable );
24909
+ var clonedBody = $( dt.table().body() ).clone( false, false ).empty().appendTo( clonedTable ); // use jQuery because of IE8
24910
+
24911
+ clonedTable.style.width = 'auto';
24912
+
24913
+ // Header
24914
+ var headerCells = dt.columns()
24915
+ .header()
24916
+ .filter( function (idx) {
24917
+ return dt.column(idx).visible();
24918
+ } )
24919
+ .to$()
24920
+ .clone( false )
24921
+ .css( 'display', 'table-cell' )
24922
+ .css( 'width', 'auto' )
24923
+ .css( 'min-width', 0 );
24924
+
24925
+ // Body rows - we don't need to take account of DataTables' column
24926
+ // visibility since we implement our own here (hence the `display` set)
24927
+ $(clonedBody)
24928
+ .append( $(dt.rows( { page: 'current' } ).nodes()).clone( false ) )
24929
+ .find( 'th, td' ).css( 'display', '' );
24930
+
24931
+ // Footer
24932
+ var footer = dt.table().footer();
24933
+ if ( footer ) {
24934
+ var clonedFooter = $( footer.cloneNode( false ) ).appendTo( clonedTable );
24935
+ var footerCells = dt.columns()
24936
+ .footer()
24937
+ .filter( function (idx) {
24938
+ return dt.column(idx).visible();
24939
+ } )
24940
+ .to$()
24941
+ .clone( false )
24942
+ .css( 'display', 'table-cell' );
24943
+
24944
+ $('<tr/>')
24945
+ .append( footerCells )
24946
+ .appendTo( clonedFooter );
24947
+ }
24948
+
24949
+ $('<tr/>')
24950
+ .append( headerCells )
24951
+ .appendTo( clonedHeader );
24952
+
24953
+ // In the inline case extra padding is applied to the first column to
24954
+ // give space for the show / hide icon. We need to use this in the
24955
+ // calculation
24956
+ if ( this.c.details.type === 'inline' ) {
24957
+ $(clonedTable).addClass( 'dtr-inline collapsed' );
24958
+ }
24959
+
24960
+ // It is unsafe to insert elements with the same name into the DOM
24961
+ // multiple times. For example, cloning and inserting a checked radio
24962
+ // clears the chcecked state of the original radio.
24963
+ $( clonedTable ).find( '[name]' ).removeAttr( 'name' );
24964
+
24965
+ // A position absolute table would take the table out of the flow of
24966
+ // our container element, bypassing the height and width (Scroller)
24967
+ $( clonedTable ).css( 'position', 'relative' )
24968
+
24969
+ var inserted = $('<div/>')
24970
+ .css( {
24971
+ width: 1,
24972
+ height: 1,
24973
+ overflow: 'hidden',
24974
+ clear: 'both'
24975
+ } )
24976
+ .append( clonedTable );
24977
+
24978
+ inserted.insertBefore( dt.table().node() );
24979
+
24980
+ // The cloned header now contains the smallest that each column can be
24981
+ headerCells.each( function (i) {
24982
+ var idx = dt.column.index( 'fromVisible', i );
24983
+ columns[ idx ].minWidth = this.offsetWidth || 0;
24984
+ } );
24985
+
24986
+ inserted.remove();
24987
+ },
24988
+
24989
+ /**
24990
+ * Get the state of the current hidden columns - controlled by Responsive only
24991
+ */
24992
+ _responsiveOnlyHidden: function ()
24993
+ {
24994
+ var dt = this.s.dt;
24995
+
24996
+ return $.map( this.s.current, function (v, i) {
24997
+ // If the column is hidden by DataTables then it can't be hidden by
24998
+ // Responsive!
24999
+ if ( dt.column(i).visible() === false ) {
25000
+ return true;
25001
+ }
25002
+ return v;
25003
+ } );
25004
+ },
25005
+
25006
+ /**
25007
+ * Set a column's visibility.
25008
+ *
25009
+ * We don't use DataTables' column visibility controls in order to ensure
25010
+ * that column visibility can Responsive can no-exist. Since only IE8+ is
25011
+ * supported (and all evergreen browsers of course) the control of the
25012
+ * display attribute works well.
25013
+ *
25014
+ * @param {integer} col Column index
25015
+ * @param {boolean} showHide Show or hide (true or false)
25016
+ * @private
25017
+ */
25018
+ _setColumnVis: function ( col, showHide )
25019
+ {
25020
+ var dt = this.s.dt;
25021
+ var display = showHide ? '' : 'none'; // empty string will remove the attr
25022
+
25023
+ $( dt.column( col ).header() ).css( 'display', display );
25024
+ $( dt.column( col ).footer() ).css( 'display', display );
25025
+ dt.column( col ).nodes().to$().css( 'display', display );
25026
+
25027
+ // If the are child nodes stored, we might need to reinsert them
25028
+ if ( ! $.isEmptyObject( _childNodeStore ) ) {
25029
+ dt.cells( null, col ).indexes().each( function (idx) {
25030
+ _childNodesRestore( dt, idx.row, idx.column );
25031
+ } );
25032
+ }
25033
+ },
25034
+
25035
+
25036
+ /**
25037
+ * Update the cell tab indexes for keyboard accessibility. This is called on
25038
+ * every table draw - that is potentially inefficient, but also the least
25039
+ * complex option given that column visibility can change on the fly. Its a
25040
+ * shame user-focus was removed from CSS 3 UI, as it would have solved this
25041
+ * issue with a single CSS statement.
25042
+ *
25043
+ * @private
25044
+ */
25045
+ _tabIndexes: function ()
25046
+ {
25047
+ var dt = this.s.dt;
25048
+ var cells = dt.cells( { page: 'current' } ).nodes().to$();
25049
+ var ctx = dt.settings()[0];
25050
+ var target = this.c.details.target;
25051
+
25052
+ cells.filter( '[data-dtr-keyboard]' ).removeData( '[data-dtr-keyboard]' );
25053
+
25054
+ if ( typeof target === 'number' ) {
25055
+ dt.cells( null, target, { page: 'current' } ).nodes().to$()
25056
+ .attr( 'tabIndex', ctx.iTabIndex )
25057
+ .data( 'dtr-keyboard', 1 );
25058
+ }
25059
+ else {
25060
+ // This is a bit of a hack - we need to limit the selected nodes to just
25061
+ // those of this table
25062
+ if ( target === 'td:first-child, th:first-child' ) {
25063
+ target = '>td:first-child, >th:first-child';
25064
+ }
25065
+
25066
+ $( target, dt.rows( { page: 'current' } ).nodes() )
25067
+ .attr( 'tabIndex', ctx.iTabIndex )
25068
+ .data( 'dtr-keyboard', 1 );
25069
+ }
25070
+ }
25071
+ } );
25072
+
25073
+
25074
+ /**
25075
+ * List of default breakpoints. Each item in the array is an object with two
25076
+ * properties:
25077
+ *
25078
+ * * `name` - the breakpoint name.
25079
+ * * `width` - the breakpoint width
25080
+ *
25081
+ * @name Responsive.breakpoints
25082
+ * @static
25083
+ */
25084
+ Responsive.breakpoints = [
25085
+ { name: 'desktop', width: Infinity },
25086
+ { name: 'tablet-l', width: 1024 },
25087
+ { name: 'tablet-p', width: 768 },
25088
+ { name: 'mobile-l', width: 480 },
25089
+ { name: 'mobile-p', width: 320 }
25090
+ ];
25091
+
25092
+
25093
+ /**
25094
+ * Display methods - functions which define how the hidden data should be shown
25095
+ * in the table.
25096
+ *
25097
+ * @namespace
25098
+ * @name Responsive.defaults
25099
+ * @static
25100
+ */
25101
+ Responsive.display = {
25102
+ childRow: function ( row, update, render ) {
25103
+ if ( update ) {
25104
+ if ( $(row.node()).hasClass('parent') ) {
25105
+ row.child( render(), 'child' ).show();
25106
+
25107
+ return true;
25108
+ }
25109
+ }
25110
+ else {
25111
+ if ( ! row.child.isShown() ) {
25112
+ row.child( render(), 'child' ).show();
25113
+ $( row.node() ).addClass( 'parent' );
25114
+
25115
+ return true;
25116
+ }
25117
+ else {
25118
+ row.child( false );
25119
+ $( row.node() ).removeClass( 'parent' );
25120
+
25121
+ return false;
25122
+ }
25123
+ }
25124
+ },
25125
+
25126
+ childRowImmediate: function ( row, update, render ) {
25127
+ if ( (! update && row.child.isShown()) || ! row.responsive.hasHidden() ) {
25128
+ // User interaction and the row is show, or nothing to show
25129
+ row.child( false );
25130
+ $( row.node() ).removeClass( 'parent' );
25131
+
25132
+ return false;
25133
+ }
25134
+ else {
25135
+ // Display
25136
+ row.child( render(), 'child' ).show();
25137
+ $( row.node() ).addClass( 'parent' );
25138
+
25139
+ return true;
25140
+ }
25141
+ },
25142
+
25143
+ // This is a wrapper so the modal options for Bootstrap and jQuery UI can
25144
+ // have options passed into them. This specific one doesn't need to be a
25145
+ // function but it is for consistency in the `modal` name
25146
+ modal: function ( options ) {
25147
+ return function ( row, update, render ) {
25148
+ if ( ! update ) {
25149
+ // Show a modal
25150
+ var close = function () {
25151
+ modal.remove(); // will tidy events for us
25152
+ $(document).off( 'keypress.dtr' );
25153
+ };
25154
+
25155
+ var modal = $('<div class="dtr-modal"/>')
25156
+ .append( $('<div class="dtr-modal-display"/>')
25157
+ .append( $('<div class="dtr-modal-content"/>')
25158
+ .append( render() )
25159
+ )
25160
+ .append( $('<div class="dtr-modal-close">&times;</div>' )
25161
+ .click( function () {
25162
+ close();
25163
+ } )
25164
+ )
25165
+ )
25166
+ .append( $('<div class="dtr-modal-background"/>')
25167
+ .click( function () {
25168
+ close();
25169
+ } )
25170
+ )
25171
+ .appendTo( 'body' );
25172
+
25173
+ $(document).on( 'keyup.dtr', function (e) {
25174
+ if ( e.keyCode === 27 ) {
25175
+ e.stopPropagation();
25176
+
25177
+ close();
25178
+ }
25179
+ } );
25180
+ }
25181
+ else {
25182
+ $('div.dtr-modal-content')
25183
+ .empty()
25184
+ .append( render() );
25185
+ }
25186
+
25187
+ if ( options && options.header ) {
25188
+ $('div.dtr-modal-content').prepend(
25189
+ '<h2>'+options.header( row )+'</h2>'
25190
+ );
25191
+ }
25192
+ };
25193
+ }
25194
+ };
25195
+
25196
+
25197
+ var _childNodeStore = {};
25198
+
25199
+ function _childNodes( dt, row, col ) {
25200
+ var name = row+'-'+col;
25201
+
25202
+ if ( _childNodeStore[ name ] ) {
25203
+ return _childNodeStore[ name ];
25204
+ }
25205
+
25206
+ // https://jsperf.com/childnodes-array-slice-vs-loop
25207
+ var nodes = [];
25208
+ var children = dt.cell( row, col ).node().childNodes;
25209
+ for ( var i=0, ien=children.length ; i<ien ; i++ ) {
25210
+ nodes.push( children[i] );
25211
+ }
25212
+
25213
+ _childNodeStore[ name ] = nodes;
25214
+
25215
+ return nodes;
25216
+ }
25217
+
25218
+ function _childNodesRestore( dt, row, col ) {
25219
+ var name = row+'-'+col;
25220
+
25221
+ if ( ! _childNodeStore[ name ] ) {
25222
+ return;
25223
+ }
25224
+
25225
+ var node = dt.cell( row, col ).node();
25226
+ var store = _childNodeStore[ name ];
25227
+ var parent = store[0].parentNode;
25228
+ var parentChildren = parent.childNodes;
25229
+ var a = [];
25230
+
25231
+ for ( var i=0, ien=parentChildren.length ; i<ien ; i++ ) {
25232
+ a.push( parentChildren[i] );
25233
+ }
25234
+
25235
+ for ( var j=0, jen=a.length ; j<jen ; j++ ) {
25236
+ node.appendChild( a[j] );
25237
+ }
25238
+
25239
+ _childNodeStore[ name ] = undefined;
25240
+ }
25241
+
25242
+
25243
+ /**
25244
+ * Display methods - functions which define how the hidden data should be shown
25245
+ * in the table.
25246
+ *
25247
+ * @namespace
25248
+ * @name Responsive.defaults
25249
+ * @static
25250
+ */
25251
+ Responsive.renderer = {
25252
+ listHiddenNodes: function () {
25253
+ return function ( api, rowIdx, columns ) {
25254
+ var ul = $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>');
25255
+ var found = false;
25256
+
25257
+ var data = $.each( columns, function ( i, col ) {
25258
+ if ( col.hidden ) {
25259
+ var klass = col.className ?
25260
+ 'class="'+ col.className +'"' :
25261
+ '';
25262
+
25263
+ $(
25264
+ '<li '+klass+' data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
25265
+ '<span class="dtr-title">'+
25266
+ col.title+
25267
+ '</span> '+
25268
+ '</li>'
25269
+ )
25270
+ .append( $('<span class="dtr-data"/>').append( _childNodes( api, col.rowIndex, col.columnIndex ) ) )// api.cell( col.rowIndex, col.columnIndex ).node().childNodes ) )
25271
+ .appendTo( ul );
25272
+
25273
+ found = true;
25274
+ }
25275
+ } );
25276
+
25277
+ return found ?
25278
+ ul :
25279
+ false;
25280
+ };
25281
+ },
25282
+
25283
+ listHidden: function () {
25284
+ return function ( api, rowIdx, columns ) {
25285
+ var data = $.map( columns, function ( col ) {
25286
+ var klass = col.className ?
25287
+ 'class="'+ col.className +'"' :
25288
+ '';
25289
+
25290
+ return col.hidden ?
25291
+ '<li '+klass+' data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
25292
+ '<span class="dtr-title">'+
25293
+ col.title+
25294
+ '</span> '+
25295
+ '<span class="dtr-data">'+
25296
+ col.data+
25297
+ '</span>'+
25298
+ '</li>' :
25299
+ '';
25300
+ } ).join('');
25301
+
25302
+ return data ?
25303
+ $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>').append( data ) :
25304
+ false;
25305
+ }
25306
+ },
25307
+
25308
+ tableAll: function ( options ) {
25309
+ options = $.extend( {
25310
+ tableClass: ''
25311
+ }, options );
25312
+
25313
+ return function ( api, rowIdx, columns ) {
25314
+ var data = $.map( columns, function ( col ) {
25315
+ var klass = col.className ?
25316
+ 'class="'+ col.className +'"' :
25317
+ '';
25318
+
25319
+ return '<tr '+klass+' data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
25320
+ '<td>'+col.title+':'+'</td> '+
25321
+ '<td>'+col.data+'</td>'+
25322
+ '</tr>';
25323
+ } ).join('');
25324
+
25325
+ return $('<table class="'+options.tableClass+' dtr-details" width="100%"/>').append( data );
25326
+ }
25327
+ }
25328
+ };
25329
+
25330
+ /**
25331
+ * Responsive default settings for initialisation
25332
+ *
25333
+ * @namespace
25334
+ * @name Responsive.defaults
25335
+ * @static
25336
+ */
25337
+ Responsive.defaults = {
25338
+ /**
25339
+ * List of breakpoints for the instance. Note that this means that each
25340
+ * instance can have its own breakpoints. Additionally, the breakpoints
25341
+ * cannot be changed once an instance has been creased.
25342
+ *
25343
+ * @type {Array}
25344
+ * @default Takes the value of `Responsive.breakpoints`
25345
+ */
25346
+ breakpoints: Responsive.breakpoints,
25347
+
25348
+ /**
25349
+ * Enable / disable auto hiding calculations. It can help to increase
25350
+ * performance slightly if you disable this option, but all columns would
25351
+ * need to have breakpoint classes assigned to them
25352
+ *
25353
+ * @type {Boolean}
25354
+ * @default `true`
25355
+ */
25356
+ auto: true,
25357
+
25358
+ /**
25359
+ * Details control. If given as a string value, the `type` property of the
25360
+ * default object is set to that value, and the defaults used for the rest
25361
+ * of the object - this is for ease of implementation.
25362
+ *
25363
+ * The object consists of the following properties:
25364
+ *
25365
+ * * `display` - A function that is used to show and hide the hidden details
25366
+ * * `renderer` - function that is called for display of the child row data.
25367
+ * The default function will show the data from the hidden columns
25368
+ * * `target` - Used as the selector for what objects to attach the child
25369
+ * open / close to
25370
+ * * `type` - `false` to disable the details display, `inline` or `column`
25371
+ * for the two control types
25372
+ *
25373
+ * @type {Object|string}
25374
+ */
25375
+ details: {
25376
+ display: Responsive.display.childRow,
25377
+
25378
+ renderer: Responsive.renderer.listHidden(),
25379
+
25380
+ target: 0,
25381
+
25382
+ type: 'inline'
25383
+ },
25384
+
25385
+ /**
25386
+ * Orthogonal data request option. This is used to define the data type
25387
+ * requested when Responsive gets the data to show in the child row.
25388
+ *
25389
+ * @type {String}
25390
+ */
25391
+ orthogonal: 'display'
25392
+ };
25393
+
25394
+
25395
+ /*
25396
+ * API
25397
+ */
25398
+ var Api = $.fn.dataTable.Api;
25399
+
25400
+ // Doesn't do anything - work around for a bug in DT... Not documented
25401
+ Api.register( 'responsive()', function () {
25402
+ return this;
25403
+ } );
25404
+
25405
+ Api.register( 'responsive.index()', function ( li ) {
25406
+ li = $(li);
25407
+
25408
+ return {
25409
+ column: li.data('dtr-index'),
25410
+ row: li.parent().data('dtr-index')
25411
+ };
25412
+ } );
25413
+
25414
+ Api.register( 'responsive.rebuild()', function () {
25415
+ return this.iterator( 'table', function ( ctx ) {
25416
+ if ( ctx._responsive ) {
25417
+ ctx._responsive._classLogic();
25418
+ }
25419
+ } );
25420
+ } );
25421
+
25422
+ Api.register( 'responsive.recalc()', function () {
25423
+ return this.iterator( 'table', function ( ctx ) {
25424
+ if ( ctx._responsive ) {
25425
+ ctx._responsive._resizeAuto();
25426
+ ctx._responsive._resize();
25427
+ }
25428
+ } );
25429
+ } );
25430
+
25431
+ Api.register( 'responsive.hasHidden()', function () {
25432
+ var ctx = this.context[0];
25433
+
25434
+ return ctx._responsive ?
25435
+ $.inArray( false, ctx._responsive._responsiveOnlyHidden() ) !== -1 :
25436
+ false;
25437
+ } );
25438
+
25439
+ Api.registerPlural( 'columns().responsiveHidden()', 'column().responsiveHidden()', function () {
25440
+ return this.iterator( 'column', function ( settings, column ) {
25441
+ return settings._responsive ?
25442
+ settings._responsive._responsiveOnlyHidden()[ column ] :
25443
+ false;
25444
+ }, 1 );
25445
+ } );
25446
+
25447
+
25448
+ /**
25449
+ * Version information
25450
+ *
25451
+ * @name Responsive.version
25452
+ * @static
25453
+ */
25454
+ Responsive.version = '2.2.7';
25455
+
25456
+
25457
+ $.fn.dataTable.Responsive = Responsive;
25458
+ $.fn.DataTable.Responsive = Responsive;
25459
+
25460
+ // Attach a listener to the document which listens for DataTables initialisation
25461
+ // events so we can automatically initialise
25462
+ $(document).on( 'preInit.dt.dtr', function (e, settings, json) {
25463
+ if ( e.namespace !== 'dt' ) {
25464
+ return;
25465
+ }
25466
+
25467
+ if ( $(settings.nTable).hasClass( 'responsive' ) ||
25468
+ $(settings.nTable).hasClass( 'dt-responsive' ) ||
25469
+ settings.oInit.responsive ||
25470
+ DataTable.defaults.responsive
25471
+ ) {
25472
+ var init = settings.oInit.responsive;
25473
+
25474
+ if ( init !== false ) {
25475
+ new Responsive( settings, $.isPlainObject( init ) ? init : {} );
25476
+ }
25477
+ }
25478
+ } );
25479
+
25480
+
25481
+ return Responsive;
25482
+ }));
25483
+
25484
+
25485
+ /*! RowReorder 1.2.7
25486
+ * 2015-2020 SpryMedia Ltd - datatables.net/license
25487
+ */
25488
+
25489
+ /**
25490
+ * @summary RowReorder
25491
+ * @description Row reordering extension for DataTables
25492
+ * @version 1.2.7
25493
+ * @file dataTables.rowReorder.js
25494
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
25495
+ * @contact www.sprymedia.co.uk/contact
25496
+ * @copyright Copyright 2015-2020 SpryMedia Ltd.
25497
+ *
25498
+ * This source file is free software, available under the following license:
25499
+ * MIT license - http://datatables.net/license/mit
25500
+ *
25501
+ * This source file is distributed in the hope that it will be useful, but
25502
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25503
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
25504
+ *
25505
+ * For details please refer to: http://www.datatables.net
25506
+ */
25507
+
25508
+ (function( factory ){
25509
+ if ( typeof define === 'function' && define.amd ) {
25510
+ // AMD
25511
+ define( ['jquery', 'datatables.net'], function ( $ ) {
25512
+ return factory( $, window, document );
25513
+ } );
25514
+ }
25515
+ else if ( typeof exports === 'object' ) {
25516
+ // CommonJS
25517
+ module.exports = function (root, $) {
25518
+ if ( ! root ) {
25519
+ root = window;
25520
+ }
25521
+
25522
+ if ( ! $ || ! $.fn.dataTable ) {
25523
+ $ = require('datatables.net')(root, $).$;
25524
+ }
25525
+
25526
+ return factory( $, root, root.document );
25527
+ };
25528
+ }
25529
+ else {
25530
+ // Browser
25531
+ factory( jQuery, window, document );
25532
+ }
25533
+ }(function( $, window, document, undefined ) {
25534
+ 'use strict';
25535
+ var DataTable = $.fn.dataTable;
25536
+
25537
+
25538
+ /**
25539
+ * RowReorder provides the ability in DataTables to click and drag rows to
25540
+ * reorder them. When a row is dropped the data for the rows effected will be
25541
+ * updated to reflect the change. Normally this data point should also be the
25542
+ * column being sorted upon in the DataTable but this does not need to be the
25543
+ * case. RowReorder implements a "data swap" method - so the rows being
25544
+ * reordered take the value of the data point from the row that used to occupy
25545
+ * the row's new position.
25546
+ *
25547
+ * Initialisation is done by either:
25548
+ *
25549
+ * * `rowReorder` parameter in the DataTable initialisation object
25550
+ * * `new $.fn.dataTable.RowReorder( table, opts )` after DataTables
25551
+ * initialisation.
25552
+ *
25553
+ * @class
25554
+ * @param {object} settings DataTables settings object for the host table
25555
+ * @param {object} [opts] Configuration options
25556
+ * @requires jQuery 1.7+
25557
+ * @requires DataTables 1.10.7+
25558
+ */
25559
+ var RowReorder = function ( dt, opts ) {
25560
+ // Sanity check that we are using DataTables 1.10 or newer
25561
+ if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
25562
+ throw 'DataTables RowReorder requires DataTables 1.10.8 or newer';
25563
+ }
25564
+
25565
+ // User and defaults configuration object
25566
+ this.c = $.extend( true, {},
25567
+ DataTable.defaults.rowReorder,
25568
+ RowReorder.defaults,
25569
+ opts
25570
+ );
25571
+
25572
+ // Internal settings
25573
+ this.s = {
25574
+ /** @type {integer} Scroll body top cache */
25575
+ bodyTop: null,
25576
+
25577
+ /** @type {DataTable.Api} DataTables' API instance */
25578
+ dt: new DataTable.Api( dt ),
25579
+
25580
+ /** @type {function} Data fetch function */
25581
+ getDataFn: DataTable.ext.oApi._fnGetObjectDataFn( this.c.dataSrc ),
25582
+
25583
+ /** @type {array} Pixel positions for row insertion calculation */
25584
+ middles: null,
25585
+
25586
+ /** @type {Object} Cached dimension information for use in the mouse move event handler */
25587
+ scroll: {},
25588
+
25589
+ /** @type {integer} Interval object used for smooth scrolling */
25590
+ scrollInterval: null,
25591
+
25592
+ /** @type {function} Data set function */
25593
+ setDataFn: DataTable.ext.oApi._fnSetObjectDataFn( this.c.dataSrc ),
25594
+
25595
+ /** @type {Object} Mouse down information */
25596
+ start: {
25597
+ top: 0,
25598
+ left: 0,
25599
+ offsetTop: 0,
25600
+ offsetLeft: 0,
25601
+ nodes: []
25602
+ },
25603
+
25604
+ /** @type {integer} Window height cached value */
25605
+ windowHeight: 0,
25606
+
25607
+ /** @type {integer} Document outer height cached value */
25608
+ documentOuterHeight: 0,
25609
+
25610
+ /** @type {integer} DOM clone outer height cached value */
25611
+ domCloneOuterHeight: 0
25612
+ };
25613
+
25614
+ // DOM items
25615
+ this.dom = {
25616
+ /** @type {jQuery} Cloned row being moved around */
25617
+ clone: null,
25618
+
25619
+ /** @type {jQuery} DataTables scrolling container */
25620
+ dtScroll: $('div.dataTables_scrollBody', this.s.dt.table().container())
25621
+ };
25622
+
25623
+ // Check if row reorder has already been initialised on this table
25624
+ var settings = this.s.dt.settings()[0];
25625
+ var exisiting = settings.rowreorder;
25626
+
25627
+ if ( exisiting ) {
25628
+ return exisiting;
25629
+ }
25630
+
25631
+ if ( !this.dom.dtScroll.length ) {
25632
+ this.dom.dtScroll = $(this.s.dt.table().container(), 'tbody')
25633
+ }
25634
+
25635
+ settings.rowreorder = this;
25636
+ this._constructor();
25637
+ };
25638
+
25639
+
25640
+ $.extend( RowReorder.prototype, {
25641
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25642
+ * Constructor
25643
+ */
25644
+
25645
+ /**
25646
+ * Initialise the RowReorder instance
25647
+ *
25648
+ * @private
25649
+ */
25650
+ _constructor: function ()
25651
+ {
25652
+ var that = this;
25653
+ var dt = this.s.dt;
25654
+ var table = $( dt.table().node() );
25655
+
25656
+ // Need to be able to calculate the row positions relative to the table
25657
+ if ( table.css('position') === 'static' ) {
25658
+ table.css( 'position', 'relative' );
25659
+ }
25660
+
25661
+ // listen for mouse down on the target column - we have to implement
25662
+ // this rather than using HTML5 drag and drop as drag and drop doesn't
25663
+ // appear to work on table rows at this time. Also mobile browsers are
25664
+ // not supported.
25665
+ // Use `table().container()` rather than just the table node for IE8 -
25666
+ // otherwise it only works once...
25667
+ $(dt.table().container()).on( 'mousedown.rowReorder touchstart.rowReorder', this.c.selector, function (e) {
25668
+ if ( ! that.c.enable ) {
25669
+ return;
25670
+ }
25671
+
25672
+ // Ignore excluded children of the selector
25673
+ if ( $(e.target).is(that.c.excludedChildren) ) {
25674
+ return true;
25675
+ }
25676
+
25677
+ var tr = $(this).closest('tr');
25678
+ var row = dt.row( tr );
25679
+
25680
+ // Double check that it is a DataTable row
25681
+ if ( row.any() ) {
25682
+ that._emitEvent( 'pre-row-reorder', {
25683
+ node: row.node(),
25684
+ index: row.index()
25685
+ } );
25686
+
25687
+ that._mouseDown( e, tr );
25688
+ return false;
25689
+ }
25690
+ } );
25691
+
25692
+ dt.on( 'destroy.rowReorder', function () {
25693
+ $(dt.table().container()).off( '.rowReorder' );
25694
+ dt.off( '.rowReorder' );
25695
+ } );
25696
+ },
25697
+
25698
+
25699
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25700
+ * Private methods
25701
+ */
25702
+
25703
+ /**
25704
+ * Cache the measurements that RowReorder needs in the mouse move handler
25705
+ * to attempt to speed things up, rather than reading from the DOM.
25706
+ *
25707
+ * @private
25708
+ */
25709
+ _cachePositions: function ()
25710
+ {
25711
+ var dt = this.s.dt;
25712
+
25713
+ // Frustratingly, if we add `position:relative` to the tbody, the
25714
+ // position is still relatively to the parent. So we need to adjust
25715
+ // for that
25716
+ var headerHeight = $( dt.table().node() ).find('thead').outerHeight();
25717
+
25718
+ // Need to pass the nodes through jQuery to get them in document order,
25719
+ // not what DataTables thinks it is, since we have been altering the
25720
+ // order
25721
+ var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
25722
+ var middles = $.map( nodes, function ( node, i ) {
25723
+ var top = $(node).position().top - headerHeight;
25724
+
25725
+ return (top + top + $(node).outerHeight() ) / 2;
25726
+ } );
25727
+
25728
+ this.s.middles = middles;
25729
+ this.s.bodyTop = $( dt.table().body() ).offset().top;
25730
+ this.s.windowHeight = $(window).height();
25731
+ this.s.documentOuterHeight = $(document).outerHeight();
25732
+ },
25733
+
25734
+
25735
+ /**
25736
+ * Clone a row so it can be floated around the screen
25737
+ *
25738
+ * @param {jQuery} target Node to be cloned
25739
+ * @private
25740
+ */
25741
+ _clone: function ( target )
25742
+ {
25743
+ var dt = this.s.dt;
25744
+ var clone = $( dt.table().node().cloneNode(false) )
25745
+ .addClass( 'dt-rowReorder-float' )
25746
+ .append('<tbody/>')
25747
+ .append( target.clone( false ) );
25748
+
25749
+ // Match the table and column widths - read all sizes before setting
25750
+ // to reduce reflows
25751
+ var tableWidth = target.outerWidth();
25752
+ var tableHeight = target.outerHeight();
25753
+ var sizes = target.children().map( function () {
25754
+ return $(this).width();
25755
+ } );
25756
+
25757
+ clone
25758
+ .width( tableWidth )
25759
+ .height( tableHeight )
25760
+ .find('tr').children().each( function (i) {
25761
+ this.style.width = sizes[i]+'px';
25762
+ } );
25763
+
25764
+ // Insert into the document to have it floating around
25765
+ clone.appendTo( 'body' );
25766
+
25767
+ this.dom.clone = clone;
25768
+ this.s.domCloneOuterHeight = clone.outerHeight();
25769
+ },
25770
+
25771
+
25772
+ /**
25773
+ * Update the cloned item's position in the document
25774
+ *
25775
+ * @param {object} e Event giving the mouse's position
25776
+ * @private
25777
+ */
25778
+ _clonePosition: function ( e )
25779
+ {
25780
+ var start = this.s.start;
25781
+ var topDiff = this._eventToPage( e, 'Y' ) - start.top;
25782
+ var leftDiff = this._eventToPage( e, 'X' ) - start.left;
25783
+ var snap = this.c.snapX;
25784
+ var left;
25785
+ var top = topDiff + start.offsetTop;
25786
+
25787
+ if ( snap === true ) {
25788
+ left = start.offsetLeft;
25789
+ }
25790
+ else if ( typeof snap === 'number' ) {
25791
+ left = start.offsetLeft + snap;
25792
+ }
25793
+ else {
25794
+ left = leftDiff + start.offsetLeft;
25795
+ }
25796
+
25797
+ if(top < 0) {
25798
+ top = 0
25799
+ }
25800
+ else if(top + this.s.domCloneOuterHeight > this.s.documentOuterHeight) {
25801
+ top = this.s.documentOuterHeight - this.s.domCloneOuterHeight;
25802
+ }
25803
+
25804
+ this.dom.clone.css( {
25805
+ top: top,
25806
+ left: left
25807
+ } );
25808
+ },
25809
+
25810
+
25811
+ /**
25812
+ * Emit an event on the DataTable for listeners
25813
+ *
25814
+ * @param {string} name Event name
25815
+ * @param {array} args Event arguments
25816
+ * @private
25817
+ */
25818
+ _emitEvent: function ( name, args )
25819
+ {
25820
+ this.s.dt.iterator( 'table', function ( ctx, i ) {
25821
+ $(ctx.nTable).triggerHandler( name+'.dt', args );
25822
+ } );
25823
+ },
25824
+
25825
+
25826
+ /**
25827
+ * Get pageX/Y position from an event, regardless of if it is a mouse or
25828
+ * touch event.
25829
+ *
25830
+ * @param {object} e Event
25831
+ * @param {string} pos X or Y (must be a capital)
25832
+ * @private
25833
+ */
25834
+ _eventToPage: function ( e, pos )
25835
+ {
25836
+ if ( e.type.indexOf( 'touch' ) !== -1 ) {
25837
+ return e.originalEvent.touches[0][ 'page'+pos ];
25838
+ }
25839
+
25840
+ return e[ 'page'+pos ];
25841
+ },
25842
+
25843
+
25844
+ /**
25845
+ * Mouse down event handler. Read initial positions and add event handlers
25846
+ * for the move.
25847
+ *
25848
+ * @param {object} e Mouse event
25849
+ * @param {jQuery} target TR element that is to be moved
25850
+ * @private
25851
+ */
25852
+ _mouseDown: function ( e, target )
25853
+ {
25854
+ var that = this;
25855
+ var dt = this.s.dt;
25856
+ var start = this.s.start;
25857
+
25858
+ var offset = target.offset();
25859
+ start.top = this._eventToPage( e, 'Y' );
25860
+ start.left = this._eventToPage( e, 'X' );
25861
+ start.offsetTop = offset.top;
25862
+ start.offsetLeft = offset.left;
25863
+ start.nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
25864
+
25865
+ this._cachePositions();
25866
+ this._clone( target );
25867
+ this._clonePosition( e );
25868
+
25869
+ this.dom.target = target;
25870
+ target.addClass( 'dt-rowReorder-moving' );
25871
+
25872
+ $( document )
25873
+ .on( 'mouseup.rowReorder touchend.rowReorder', function (e) {
25874
+ that._mouseUp(e);
25875
+ } )
25876
+ .on( 'mousemove.rowReorder touchmove.rowReorder', function (e) {
25877
+ that._mouseMove(e);
25878
+ } );
25879
+
25880
+ // Check if window is x-scrolling - if not, disable it for the duration
25881
+ // of the drag
25882
+ if ( $(window).width() === $(document).width() ) {
25883
+ $(document.body).addClass( 'dt-rowReorder-noOverflow' );
25884
+ }
25885
+
25886
+ // Cache scrolling information so mouse move doesn't need to read.
25887
+ // This assumes that the window and DT scroller will not change size
25888
+ // during an row drag, which I think is a fair assumption
25889
+ var scrollWrapper = this.dom.dtScroll;
25890
+ this.s.scroll = {
25891
+ windowHeight: $(window).height(),
25892
+ windowWidth: $(window).width(),
25893
+ dtTop: scrollWrapper.length ? scrollWrapper.offset().top : null,
25894
+ dtLeft: scrollWrapper.length ? scrollWrapper.offset().left : null,
25895
+ dtHeight: scrollWrapper.length ? scrollWrapper.outerHeight() : null,
25896
+ dtWidth: scrollWrapper.length ? scrollWrapper.outerWidth() : null
25897
+ };
25898
+ },
25899
+
25900
+
25901
+ /**
25902
+ * Mouse move event handler - move the cloned row and shuffle the table's
25903
+ * rows if required.
25904
+ *
25905
+ * @param {object} e Mouse event
25906
+ * @private
25907
+ */
25908
+ _mouseMove: function ( e )
25909
+ {
25910
+ this._clonePosition( e );
25911
+
25912
+ // Transform the mouse position into a position in the table's body
25913
+ var bodyY = this._eventToPage( e, 'Y' ) - this.s.bodyTop;
25914
+ var middles = this.s.middles;
25915
+ var insertPoint = null;
25916
+ var dt = this.s.dt;
25917
+
25918
+ // Determine where the row should be inserted based on the mouse
25919
+ // position
25920
+ for ( var i=0, ien=middles.length ; i<ien ; i++ ) {
25921
+ if ( bodyY < middles[i] ) {
25922
+ insertPoint = i;
25923
+ break;
25924
+ }
25925
+ }
25926
+
25927
+ if ( insertPoint === null ) {
25928
+ insertPoint = middles.length;
25929
+ }
25930
+
25931
+ // Perform the DOM shuffle if it has changed from last time
25932
+ if ( this.s.lastInsert === null || this.s.lastInsert !== insertPoint ) {
25933
+ var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
25934
+
25935
+ if ( insertPoint > this.s.lastInsert ) {
25936
+ this.dom.target.insertAfter( nodes[ insertPoint-1 ] );
25937
+ }
25938
+ else {
25939
+ this.dom.target.insertBefore( nodes[ insertPoint ] );
25940
+ }
25941
+
25942
+ this._cachePositions();
25943
+
25944
+ this.s.lastInsert = insertPoint;
25945
+ }
25946
+
25947
+ this._shiftScroll( e );
25948
+ },
25949
+
25950
+
25951
+ /**
25952
+ * Mouse up event handler - release the event handlers and perform the
25953
+ * table updates
25954
+ *
25955
+ * @param {object} e Mouse event
25956
+ * @private
25957
+ */
25958
+ _mouseUp: function ( e )
25959
+ {
25960
+ var that = this;
25961
+ var dt = this.s.dt;
25962
+ var i, ien;
25963
+ var dataSrc = this.c.dataSrc;
25964
+
25965
+ this.dom.clone.remove();
25966
+ this.dom.clone = null;
25967
+
25968
+ this.dom.target.removeClass( 'dt-rowReorder-moving' );
25969
+ //this.dom.target = null;
25970
+
25971
+ $(document).off( '.rowReorder' );
25972
+ $(document.body).removeClass( 'dt-rowReorder-noOverflow' );
25973
+
25974
+ clearInterval( this.s.scrollInterval );
25975
+ this.s.scrollInterval = null;
25976
+
25977
+ // Calculate the difference
25978
+ var startNodes = this.s.start.nodes;
25979
+ var endNodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
25980
+ var idDiff = {};
25981
+ var fullDiff = [];
25982
+ var diffNodes = [];
25983
+ var getDataFn = this.s.getDataFn;
25984
+ var setDataFn = this.s.setDataFn;
25985
+
25986
+ for ( i=0, ien=startNodes.length ; i<ien ; i++ ) {
25987
+ if ( startNodes[i] !== endNodes[i] ) {
25988
+ var id = dt.row( endNodes[i] ).id();
25989
+ var endRowData = dt.row( endNodes[i] ).data();
25990
+ var startRowData = dt.row( startNodes[i] ).data();
25991
+
25992
+ if ( id ) {
25993
+ idDiff[ id ] = getDataFn( startRowData );
25994
+ }
25995
+
25996
+ fullDiff.push( {
25997
+ node: endNodes[i],
25998
+ oldData: getDataFn( endRowData ),
25999
+ newData: getDataFn( startRowData ),
26000
+ newPosition: i,
26001
+ oldPosition: $.inArray( endNodes[i], startNodes )
26002
+ } );
26003
+
26004
+ diffNodes.push( endNodes[i] );
26005
+ }
26006
+ }
26007
+
26008
+ // Create event args
26009
+ var eventArgs = [ fullDiff, {
26010
+ dataSrc: dataSrc,
26011
+ nodes: diffNodes,
26012
+ values: idDiff,
26013
+ triggerRow: dt.row( this.dom.target ),
26014
+ originalEvent: e
26015
+ } ];
26016
+
26017
+ // Emit event
26018
+ this._emitEvent( 'row-reorder', eventArgs );
26019
+
26020
+ var update = function () {
26021
+ if ( that.c.update ) {
26022
+ for ( i=0, ien=fullDiff.length ; i<ien ; i++ ) {
26023
+ var row = dt.row( fullDiff[i].node );
26024
+ var rowData = row.data();
26025
+
26026
+ setDataFn( rowData, fullDiff[i].newData );
26027
+
26028
+ // Invalidate the cell that has the same data source as the dataSrc
26029
+ dt.columns().every( function () {
26030
+ if ( this.dataSrc() === dataSrc ) {
26031
+ dt.cell( fullDiff[i].node, this.index() ).invalidate( 'data' );
26032
+ }
26033
+ } );
26034
+ }
26035
+
26036
+ // Trigger row reordered event
26037
+ that._emitEvent( 'row-reordered', eventArgs );
26038
+
26039
+ dt.draw( false );
26040
+ }
26041
+ };
26042
+
26043
+ // Editor interface
26044
+ if ( this.c.editor ) {
26045
+ // Disable user interaction while Editor is submitting
26046
+ this.c.enable = false;
26047
+
26048
+ this.c.editor
26049
+ .edit(
26050
+ diffNodes,
26051
+ false,
26052
+ $.extend( {submit: 'changed'}, this.c.formOptions )
26053
+ )
26054
+ .multiSet( dataSrc, idDiff )
26055
+ .one( 'preSubmitCancelled.rowReorder', function () {
26056
+ that.c.enable = true;
26057
+ that.c.editor.off( '.rowReorder' );
26058
+ dt.draw( false );
26059
+ } )
26060
+ .one( 'submitUnsuccessful.rowReorder', function () {
26061
+ dt.draw( false );
26062
+ } )
26063
+ .one( 'submitSuccess.rowReorder', function () {
26064
+ update();
26065
+ } )
26066
+ .one( 'submitComplete', function () {
26067
+ that.c.enable = true;
26068
+ that.c.editor.off( '.rowReorder' );
26069
+ } )
26070
+ .submit();
26071
+ }
26072
+ else {
26073
+ update();
26074
+ }
26075
+ },
26076
+
26077
+
26078
+ /**
26079
+ * Move the window and DataTables scrolling during a drag to scroll new
26080
+ * content into view.
26081
+ *
26082
+ * This matches the `_shiftScroll` method used in AutoFill, but only
26083
+ * horizontal scrolling is considered here.
26084
+ *
26085
+ * @param {object} e Mouse move event object
26086
+ * @private
26087
+ */
26088
+ _shiftScroll: function ( e )
26089
+ {
26090
+ var that = this;
26091
+ var dt = this.s.dt;
26092
+ var scroll = this.s.scroll;
26093
+ var runInterval = false;
26094
+ var scrollSpeed = 5;
26095
+ var buffer = 65;
26096
+ var
26097
+ windowY = e.pageY - document.body.scrollTop,
26098
+ windowVert,
26099
+ dtVert;
26100
+
26101
+ // Window calculations - based on the mouse position in the window,
26102
+ // regardless of scrolling
26103
+ if ( windowY < $(window).scrollTop() + buffer ) {
26104
+ windowVert = scrollSpeed * -1;
26105
+ }
26106
+ else if ( windowY > scroll.windowHeight + $(window).scrollTop() - buffer ) {
26107
+ windowVert = scrollSpeed;
26108
+ }
26109
+
26110
+ // DataTables scrolling calculations - based on the table's position in
26111
+ // the document and the mouse position on the page
26112
+ if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
26113
+ dtVert = scrollSpeed * -1;
26114
+ }
26115
+ else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
26116
+ dtVert = scrollSpeed;
26117
+ }
26118
+
26119
+ // This is where it gets interesting. We want to continue scrolling
26120
+ // without requiring a mouse move, so we need an interval to be
26121
+ // triggered. The interval should continue until it is no longer needed,
26122
+ // but it must also use the latest scroll commands (for example consider
26123
+ // that the mouse might move from scrolling up to scrolling left, all
26124
+ // with the same interval running. We use the `scroll` object to "pass"
26125
+ // this information to the interval. Can't use local variables as they
26126
+ // wouldn't be the ones that are used by an already existing interval!
26127
+ if ( windowVert || dtVert ) {
26128
+ scroll.windowVert = windowVert;
26129
+ scroll.dtVert = dtVert;
26130
+ runInterval = true;
26131
+ }
26132
+ else if ( this.s.scrollInterval ) {
26133
+ // Don't need to scroll - remove any existing timer
26134
+ clearInterval( this.s.scrollInterval );
26135
+ this.s.scrollInterval = null;
26136
+ }
26137
+
26138
+ // If we need to run the interval to scroll and there is no existing
26139
+ // interval (if there is an existing one, it will continue to run)
26140
+ if ( ! this.s.scrollInterval && runInterval ) {
26141
+ this.s.scrollInterval = setInterval( function () {
26142
+ // Don't need to worry about setting scroll <0 or beyond the
26143
+ // scroll bound as the browser will just reject that.
26144
+ if ( scroll.windowVert ) {
26145
+ var top = $(document).scrollTop();
26146
+ $(document).scrollTop(top + scroll.windowVert);
26147
+
26148
+ if ( top !== $(document).scrollTop() ) {
26149
+ var move = parseFloat(that.dom.clone.css("top"));
26150
+ that.dom.clone.css("top", move + scroll.windowVert);
26151
+ }
26152
+ }
26153
+
26154
+ // DataTables scrolling
26155
+ if ( scroll.dtVert ) {
26156
+ var scroller = that.dom.dtScroll[0];
26157
+
26158
+ if ( scroll.dtVert ) {
26159
+ scroller.scrollTop += scroll.dtVert;
26160
+ }
26161
+ }
26162
+ }, 20 );
26163
+ }
26164
+ }
26165
+ } );
26166
+
26167
+
26168
+
26169
+ /**
26170
+ * RowReorder default settings for initialisation
26171
+ *
26172
+ * @namespace
26173
+ * @name RowReorder.defaults
26174
+ * @static
26175
+ */
26176
+ RowReorder.defaults = {
26177
+ /**
26178
+ * Data point in the host row's data source object for where to get and set
26179
+ * the data to reorder. This will normally also be the sorting column.
26180
+ *
26181
+ * @type {Number}
26182
+ */
26183
+ dataSrc: 0,
26184
+
26185
+ /**
26186
+ * Editor instance that will be used to perform the update
26187
+ *
26188
+ * @type {DataTable.Editor}
26189
+ */
26190
+ editor: null,
26191
+
26192
+ /**
26193
+ * Enable / disable RowReorder's user interaction
26194
+ * @type {Boolean}
26195
+ */
26196
+ enable: true,
26197
+
26198
+ /**
26199
+ * Form options to pass to Editor when submitting a change in the row order.
26200
+ * See the Editor `from-options` object for details of the options
26201
+ * available.
26202
+ * @type {Object}
26203
+ */
26204
+ formOptions: {},
26205
+
26206
+ /**
26207
+ * Drag handle selector. This defines the element that when dragged will
26208
+ * reorder a row.
26209
+ *
26210
+ * @type {String}
26211
+ */
26212
+ selector: 'td:first-child',
26213
+
26214
+ /**
26215
+ * Optionally lock the dragged row's x-position. This can be `true` to
26216
+ * fix the position match the host table's, `false` to allow free movement
26217
+ * of the row, or a number to define an offset from the host table.
26218
+ *
26219
+ * @type {Boolean|number}
26220
+ */
26221
+ snapX: false,
26222
+
26223
+ /**
26224
+ * Update the table's data on drop
26225
+ *
26226
+ * @type {Boolean}
26227
+ */
26228
+ update: true,
26229
+
26230
+ /**
26231
+ * Selector for children of the drag handle selector that mouseDown events
26232
+ * will be passed through to and drag will not activate
26233
+ *
26234
+ * @type {String}
26235
+ */
26236
+ excludedChildren: 'a'
26237
+ };
26238
+
26239
+
26240
+ /*
26241
+ * API
26242
+ */
26243
+ var Api = $.fn.dataTable.Api;
26244
+
26245
+ // Doesn't do anything - work around for a bug in DT... Not documented
26246
+ Api.register( 'rowReorder()', function () {
26247
+ return this;
26248
+ } );
26249
+
26250
+ Api.register( 'rowReorder.enable()', function ( toggle ) {
26251
+ if ( toggle === undefined ) {
26252
+ toggle = true;
26253
+ }
26254
+
26255
+ return this.iterator( 'table', function ( ctx ) {
26256
+ if ( ctx.rowreorder ) {
26257
+ ctx.rowreorder.c.enable = toggle;
26258
+ }
26259
+ } );
26260
+ } );
26261
+
26262
+ Api.register( 'rowReorder.disable()', function () {
26263
+ return this.iterator( 'table', function ( ctx ) {
26264
+ if ( ctx.rowreorder ) {
26265
+ ctx.rowreorder.c.enable = false;
26266
+ }
26267
+ } );
26268
+ } );
26269
+
26270
+
26271
+ /**
26272
+ * Version information
26273
+ *
26274
+ * @name RowReorder.version
26275
+ * @static
26276
+ */
26277
+ RowReorder.version = '1.2.6';
26278
+
26279
+
26280
+ $.fn.dataTable.RowReorder = RowReorder;
26281
+ $.fn.DataTable.RowReorder = RowReorder;
26282
+
26283
+ // Attach a listener to the document which listens for DataTables initialisation
26284
+ // events so we can automatically initialise
26285
+ $(document).on( 'init.dt.dtr', function (e, settings, json) {
26286
+ if ( e.namespace !== 'dt' ) {
26287
+ return;
26288
+ }
26289
+
26290
+ var init = settings.oInit.rowReorder;
26291
+ var defaults = DataTable.defaults.rowReorder;
26292
+
26293
+ if ( init || defaults ) {
26294
+ var opts = $.extend( {}, init, defaults );
26295
+
26296
+ if ( init !== false ) {
26297
+ new RowReorder( settings, opts );
26298
+ }
26299
+ }
26300
+ } );
26301
+
26302
+
26303
+ return RowReorder;
26304
+ }));
26305
+
26306
+
26307
+ /*! Scroller 2.0.3
26308
+ * ©2011-2020 SpryMedia Ltd - datatables.net/license
26309
+ */
26310
+
26311
+ /**
26312
+ * @summary Scroller
26313
+ * @description Virtual rendering for DataTables
26314
+ * @version 2.0.3
26315
+ * @file dataTables.scroller.js
26316
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
26317
+ * @contact www.sprymedia.co.uk/contact
26318
+ * @copyright Copyright 2011-2020 SpryMedia Ltd.
26319
+ *
26320
+ * This source file is free software, available under the following license:
26321
+ * MIT license - http://datatables.net/license/mit
26322
+ *
26323
+ * This source file is distributed in the hope that it will be useful, but
26324
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26325
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
26326
+ *
26327
+ * For details please refer to: http://www.datatables.net
26328
+ */
26329
+
26330
+ (function( factory ){
26331
+ if ( typeof define === 'function' && define.amd ) {
26332
+ // AMD
26333
+ define( ['jquery', 'datatables.net'], function ( $ ) {
26334
+ return factory( $, window, document );
26335
+ } );
26336
+ }
26337
+ else if ( typeof exports === 'object' ) {
26338
+ // CommonJS
26339
+ module.exports = function (root, $) {
26340
+ if ( ! root ) {
26341
+ root = window;
26342
+ }
26343
+
26344
+ if ( ! $ || ! $.fn.dataTable ) {
26345
+ $ = require('datatables.net')(root, $).$;
26346
+ }
26347
+
26348
+ return factory( $, root, root.document );
26349
+ };
26350
+ }
26351
+ else {
26352
+ // Browser
26353
+ factory( jQuery, window, document );
26354
+ }
26355
+ }(function( $, window, document, undefined ) {
26356
+ 'use strict';
26357
+ var DataTable = $.fn.dataTable;
26358
+
26359
+
26360
+ /**
26361
+ * Scroller is a virtual rendering plug-in for DataTables which allows large
26362
+ * datasets to be drawn on screen every quickly. What the virtual rendering means
26363
+ * is that only the visible portion of the table (and a bit to either side to make
26364
+ * the scrolling smooth) is drawn, while the scrolling container gives the
26365
+ * visual impression that the whole table is visible. This is done by making use
26366
+ * of the pagination abilities of DataTables and moving the table around in the
26367
+ * scrolling container DataTables adds to the page. The scrolling container is
26368
+ * forced to the height it would be for the full table display using an extra
26369
+ * element.
26370
+ *
26371
+ * Note that rows in the table MUST all be the same height. Information in a cell
26372
+ * which expands on to multiple lines will cause some odd behaviour in the scrolling.
26373
+ *
26374
+ * Scroller is initialised by simply including the letter 'S' in the sDom for the
26375
+ * table you want to have this feature enabled on. Note that the 'S' must come
26376
+ * AFTER the 't' parameter in `dom`.
26377
+ *
26378
+ * Key features include:
26379
+ * <ul class="limit_length">
26380
+ * <li>Speed! The aim of Scroller for DataTables is to make rendering large data sets fast</li>
26381
+ * <li>Full compatibility with deferred rendering in DataTables for maximum speed</li>
26382
+ * <li>Display millions of rows</li>
26383
+ * <li>Integration with state saving in DataTables (scrolling position is saved)</li>
26384
+ * <li>Easy to use</li>
26385
+ * </ul>
26386
+ *
26387
+ * @class
26388
+ * @constructor
26389
+ * @global
26390
+ * @param {object} dt DataTables settings object or API instance
26391
+ * @param {object} [opts={}] Configuration object for FixedColumns. Options
26392
+ * are defined by {@link Scroller.defaults}
26393
+ *
26394
+ * @requires jQuery 1.7+
26395
+ * @requires DataTables 1.10.0+
26396
+ *
26397
+ * @example
26398
+ * $(document).ready(function() {
26399
+ * $('#example').DataTable( {
26400
+ * "scrollY": "200px",
26401
+ * "ajax": "media/dataset/large.txt",
26402
+ * "scroller": true,
26403
+ * "deferRender": true
26404
+ * } );
26405
+ * } );
26406
+ */
26407
+ var Scroller = function ( dt, opts ) {
26408
+ /* Sanity check - you just know it will happen */
26409
+ if ( ! (this instanceof Scroller) ) {
26410
+ alert( "Scroller warning: Scroller must be initialised with the 'new' keyword." );
26411
+ return;
26412
+ }
26413
+
26414
+ if ( opts === undefined ) {
26415
+ opts = {};
26416
+ }
26417
+
26418
+ var dtApi = $.fn.dataTable.Api( dt );
26419
+
26420
+ /**
26421
+ * Settings object which contains customisable information for the Scroller instance
26422
+ * @namespace
26423
+ * @private
26424
+ * @extends Scroller.defaults
26425
+ */
26426
+ this.s = {
26427
+ /**
26428
+ * DataTables settings object
26429
+ * @type object
26430
+ * @default Passed in as first parameter to constructor
26431
+ */
26432
+ dt: dtApi.settings()[0],
26433
+
26434
+ /**
26435
+ * DataTables API instance
26436
+ * @type DataTable.Api
26437
+ */
26438
+ dtApi: dtApi,
26439
+
26440
+ /**
26441
+ * Pixel location of the top of the drawn table in the viewport
26442
+ * @type int
26443
+ * @default 0
26444
+ */
26445
+ tableTop: 0,
26446
+
26447
+ /**
26448
+ * Pixel location of the bottom of the drawn table in the viewport
26449
+ * @type int
26450
+ * @default 0
26451
+ */
26452
+ tableBottom: 0,
26453
+
26454
+ /**
26455
+ * Pixel location of the boundary for when the next data set should be loaded and drawn
26456
+ * when scrolling up the way.
26457
+ * @type int
26458
+ * @default 0
26459
+ * @private
26460
+ */
26461
+ redrawTop: 0,
26462
+
26463
+ /**
26464
+ * Pixel location of the boundary for when the next data set should be loaded and drawn
26465
+ * when scrolling down the way. Note that this is actually calculated as the offset from
26466
+ * the top.
26467
+ * @type int
26468
+ * @default 0
26469
+ * @private
26470
+ */
26471
+ redrawBottom: 0,
26472
+
26473
+ /**
26474
+ * Auto row height or not indicator
26475
+ * @type bool
26476
+ * @default 0
26477
+ */
26478
+ autoHeight: true,
26479
+
26480
+ /**
26481
+ * Number of rows calculated as visible in the visible viewport
26482
+ * @type int
26483
+ * @default 0
26484
+ */
26485
+ viewportRows: 0,
26486
+
26487
+ /**
26488
+ * setTimeout reference for state saving, used when state saving is enabled in the DataTable
26489
+ * and when the user scrolls the viewport in order to stop the cookie set taking too much
26490
+ * CPU!
26491
+ * @type int
26492
+ * @default 0
26493
+ */
26494
+ stateTO: null,
26495
+
26496
+ stateSaveThrottle: function () {},
26497
+
26498
+ /**
26499
+ * setTimeout reference for the redraw, used when server-side processing is enabled in the
26500
+ * DataTables in order to prevent DoSing the server
26501
+ * @type int
26502
+ * @default null
26503
+ */
26504
+ drawTO: null,
26505
+
26506
+ heights: {
26507
+ jump: null,
26508
+ page: null,
26509
+ virtual: null,
26510
+ scroll: null,
26511
+
26512
+ /**
26513
+ * Height of rows in the table
26514
+ * @type int
26515
+ * @default 0
26516
+ */
26517
+ row: null,
26518
+
26519
+ /**
26520
+ * Pixel height of the viewport
26521
+ * @type int
26522
+ * @default 0
26523
+ */
26524
+ viewport: null,
26525
+ labelFactor: 1
26526
+ },
26527
+
26528
+ topRowFloat: 0,
26529
+ scrollDrawDiff: null,
26530
+ loaderVisible: false,
26531
+ forceReposition: false,
26532
+ baseRowTop: 0,
26533
+ baseScrollTop: 0,
26534
+ mousedown: false,
26535
+ lastScrollTop: 0
26536
+ };
26537
+
26538
+ // @todo The defaults should extend a `c` property and the internal settings
26539
+ // only held in the `s` property. At the moment they are mixed
26540
+ this.s = $.extend( this.s, Scroller.oDefaults, opts );
26541
+
26542
+ // Workaround for row height being read from height object (see above comment)
26543
+ this.s.heights.row = this.s.rowHeight;
26544
+
26545
+ /**
26546
+ * DOM elements used by the class instance
26547
+ * @private
26548
+ * @namespace
26549
+ *
26550
+ */
26551
+ this.dom = {
26552
+ "force": document.createElement('div'),
26553
+ "label": $('<div class="dts_label">0</div>'),
26554
+ "scroller": null,
26555
+ "table": null,
26556
+ "loader": null
26557
+ };
26558
+
26559
+ // Attach the instance to the DataTables instance so it can be accessed in
26560
+ // future. Don't initialise Scroller twice on the same table
26561
+ if ( this.s.dt.oScroller ) {
26562
+ return;
26563
+ }
26564
+
26565
+ this.s.dt.oScroller = this;
26566
+
26567
+ /* Let's do it */
26568
+ this.construct();
26569
+ };
26570
+
26571
+
26572
+
26573
+ $.extend( Scroller.prototype, {
26574
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26575
+ * Public methods - to be exposed via the DataTables API
26576
+ */
26577
+
26578
+ /**
26579
+ * Calculate and store information about how many rows are to be displayed
26580
+ * in the scrolling viewport, based on current dimensions in the browser's
26581
+ * rendering. This can be particularly useful if the table is initially
26582
+ * drawn in a hidden element - for example in a tab.
26583
+ * @param {bool} [redraw=true] Redraw the table automatically after the recalculation, with
26584
+ * the new dimensions forming the basis for the draw.
26585
+ * @returns {void}
26586
+ */
26587
+ measure: function ( redraw )
26588
+ {
26589
+ if ( this.s.autoHeight )
26590
+ {
26591
+ this._calcRowHeight();
26592
+ }
26593
+
26594
+ var heights = this.s.heights;
26595
+
26596
+ if ( heights.row ) {
26597
+ heights.viewport = this._parseHeight($(this.dom.scroller).css('max-height'));
26598
+
26599
+ this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1;
26600
+ this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer;
26601
+ }
26602
+
26603
+ var label = this.dom.label.outerHeight();
26604
+ heights.labelFactor = (heights.viewport-label) / heights.scroll;
26605
+
26606
+ if ( redraw === undefined || redraw )
26607
+ {
26608
+ this.s.dt.oInstance.fnDraw( false );
26609
+ }
26610
+ },
26611
+
26612
+ /**
26613
+ * Get information about current displayed record range. This corresponds to
26614
+ * the information usually displayed in the "Info" block of the table.
26615
+ *
26616
+ * @returns {object} info as an object:
26617
+ * {
26618
+ * start: {int}, // the 0-indexed record at the top of the viewport
26619
+ * end: {int}, // the 0-indexed record at the bottom of the viewport
26620
+ * }
26621
+ */
26622
+ pageInfo: function()
26623
+ {
26624
+ var
26625
+ dt = this.s.dt,
26626
+ iScrollTop = this.dom.scroller.scrollTop,
26627
+ iTotal = dt.fnRecordsDisplay(),
26628
+ iPossibleEnd = Math.ceil(this.pixelsToRow(iScrollTop + this.s.heights.viewport, false, this.s.ani));
26629
+
26630
+ return {
26631
+ start: Math.floor(this.pixelsToRow(iScrollTop, false, this.s.ani)),
26632
+ end: iTotal < iPossibleEnd ? iTotal-1 : iPossibleEnd-1
26633
+ };
26634
+ },
26635
+
26636
+ /**
26637
+ * Calculate the row number that will be found at the given pixel position
26638
+ * (y-scroll).
26639
+ *
26640
+ * Please note that when the height of the full table exceeds 1 million
26641
+ * pixels, Scroller switches into a non-linear mode for the scrollbar to fit
26642
+ * all of the records into a finite area, but this function returns a linear
26643
+ * value (relative to the last non-linear positioning).
26644
+ * @param {int} pixels Offset from top to calculate the row number of
26645
+ * @param {int} [intParse=true] If an integer value should be returned
26646
+ * @param {int} [virtual=false] Perform the calculations in the virtual domain
26647
+ * @returns {int} Row index
26648
+ */
26649
+ pixelsToRow: function ( pixels, intParse, virtual )
26650
+ {
26651
+ var diff = pixels - this.s.baseScrollTop;
26652
+ var row = virtual ?
26653
+ (this._domain( 'physicalToVirtual', this.s.baseScrollTop ) + diff) / this.s.heights.row :
26654
+ ( diff / this.s.heights.row ) + this.s.baseRowTop;
26655
+
26656
+ return intParse || intParse === undefined ?
26657
+ parseInt( row, 10 ) :
26658
+ row;
26659
+ },
26660
+
26661
+ /**
26662
+ * Calculate the pixel position from the top of the scrolling container for
26663
+ * a given row
26664
+ * @param {int} iRow Row number to calculate the position of
26665
+ * @returns {int} Pixels
26666
+ */
26667
+ rowToPixels: function ( rowIdx, intParse, virtual )
26668
+ {
26669
+ var pixels;
26670
+ var diff = rowIdx - this.s.baseRowTop;
26671
+
26672
+ if ( virtual ) {
26673
+ pixels = this._domain( 'virtualToPhysical', this.s.baseScrollTop );
26674
+ pixels += diff * this.s.heights.row;
26675
+ }
26676
+ else {
26677
+ pixels = this.s.baseScrollTop;
26678
+ pixels += diff * this.s.heights.row;
26679
+ }
26680
+
26681
+ return intParse || intParse === undefined ?
26682
+ parseInt( pixels, 10 ) :
26683
+ pixels;
26684
+ },
26685
+
26686
+
26687
+ /**
26688
+ * Calculate the row number that will be found at the given pixel position (y-scroll)
26689
+ * @param {int} row Row index to scroll to
26690
+ * @param {bool} [animate=true] Animate the transition or not
26691
+ * @returns {void}
26692
+ */
26693
+ scrollToRow: function ( row, animate )
26694
+ {
26695
+ var that = this;
26696
+ var ani = false;
26697
+ var px = this.rowToPixels( row );
26698
+
26699
+ // We need to know if the table will redraw or not before doing the
26700
+ // scroll. If it will not redraw, then we need to use the currently
26701
+ // displayed table, and scroll with the physical pixels. Otherwise, we
26702
+ // need to calculate the table's new position from the virtual
26703
+ // transform.
26704
+ var preRows = ((this.s.displayBuffer-1)/2) * this.s.viewportRows;
26705
+ var drawRow = row - preRows;
26706
+ if ( drawRow < 0 ) {
26707
+ drawRow = 0;
26708
+ }
26709
+
26710
+ if ( (px > this.s.redrawBottom || px < this.s.redrawTop) && this.s.dt._iDisplayStart !== drawRow ) {
26711
+ ani = true;
26712
+ px = this._domain( 'virtualToPhysical', row * this.s.heights.row );
26713
+
26714
+ // If we need records outside the current draw region, but the new
26715
+ // scrolling position is inside that (due to the non-linear nature
26716
+ // for larger numbers of records), we need to force position update.
26717
+ if ( this.s.redrawTop < px && px < this.s.redrawBottom ) {
26718
+ this.s.forceReposition = true;
26719
+ animate = false;
26720
+ }
26721
+ }
26722
+
26723
+ if ( animate === undefined || animate )
26724
+ {
26725
+ this.s.ani = ani;
26726
+ $(this.dom.scroller).animate( {
26727
+ "scrollTop": px
26728
+ }, function () {
26729
+ // This needs to happen after the animation has completed and
26730
+ // the final scroll event fired
26731
+ setTimeout( function () {
26732
+ that.s.ani = false;
26733
+ }, 250 );
26734
+ } );
26735
+ }
26736
+ else
26737
+ {
26738
+ $(this.dom.scroller).scrollTop( px );
26739
+ }
26740
+ },
26741
+
26742
+
26743
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26744
+ * Constructor
26745
+ */
26746
+
26747
+ /**
26748
+ * Initialisation for Scroller
26749
+ * @returns {void}
26750
+ * @private
26751
+ */
26752
+ construct: function ()
26753
+ {
26754
+ var that = this;
26755
+ var dt = this.s.dtApi;
26756
+
26757
+ /* Sanity check */
26758
+ if ( !this.s.dt.oFeatures.bPaginate ) {
26759
+ this.s.dt.oApi._fnLog( this.s.dt, 0, 'Pagination must be enabled for Scroller' );
26760
+ return;
26761
+ }
26762
+
26763
+ /* Insert a div element that we can use to force the DT scrolling container to
26764
+ * the height that would be required if the whole table was being displayed
26765
+ */
26766
+ this.dom.force.style.position = "relative";
26767
+ this.dom.force.style.top = "0px";
26768
+ this.dom.force.style.left = "0px";
26769
+ this.dom.force.style.width = "1px";
26770
+
26771
+ this.dom.scroller = $('div.'+this.s.dt.oClasses.sScrollBody, this.s.dt.nTableWrapper)[0];
26772
+ this.dom.scroller.appendChild( this.dom.force );
26773
+ this.dom.scroller.style.position = "relative";
26774
+
26775
+ this.dom.table = $('>table', this.dom.scroller)[0];
26776
+ this.dom.table.style.position = "absolute";
26777
+ this.dom.table.style.top = "0px";
26778
+ this.dom.table.style.left = "0px";
26779
+
26780
+ // Add class to 'announce' that we are a Scroller table
26781
+ $(dt.table().container()).addClass('dts DTS');
26782
+
26783
+ // Add a 'loading' indicator
26784
+ if ( this.s.loadingIndicator )
26785
+ {
26786
+ this.dom.loader = $('<div class="dataTables_processing dts_loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>')
26787
+ .css('display', 'none');
26788
+
26789
+ $(this.dom.scroller.parentNode)
26790
+ .css('position', 'relative')
26791
+ .append( this.dom.loader );
26792
+ }
26793
+
26794
+ this.dom.label.appendTo(this.dom.scroller);
26795
+
26796
+ /* Initial size calculations */
26797
+ if ( this.s.heights.row && this.s.heights.row != 'auto' )
26798
+ {
26799
+ this.s.autoHeight = false;
26800
+ }
26801
+
26802
+ // Scrolling callback to see if a page change is needed
26803
+ this.s.ingnoreScroll = true;
26804
+ $(this.dom.scroller).on( 'scroll.dt-scroller', function (e) {
26805
+ that._scroll.call( that );
26806
+ } );
26807
+
26808
+ // In iOS we catch the touchstart event in case the user tries to scroll
26809
+ // while the display is already scrolling
26810
+ $(this.dom.scroller).on('touchstart.dt-scroller', function () {
26811
+ that._scroll.call( that );
26812
+ } );
26813
+
26814
+ $(this.dom.scroller)
26815
+ .on('mousedown.dt-scroller', function () {
26816
+ that.s.mousedown = true;
26817
+ })
26818
+ .on('mouseup.dt-scroller', function () {
26819
+ that.s.labelVisible = false;
26820
+ that.s.mousedown = false;
26821
+ that.dom.label.css('display', 'none');
26822
+ });
26823
+
26824
+ // On resize, update the information element, since the number of rows shown might change
26825
+ $(window).on( 'resize.dt-scroller', function () {
26826
+ that.measure( false );
26827
+ that._info();
26828
+ } );
26829
+
26830
+ // Add a state saving parameter to the DT state saving so we can restore the exact
26831
+ // position of the scrolling.
26832
+ var initialStateSave = true;
26833
+ var loadedState = dt.state.loaded();
26834
+
26835
+ dt.on( 'stateSaveParams.scroller', function ( e, settings, data ) {
26836
+ if ( initialStateSave && loadedState ) {
26837
+ data.scroller = loadedState.scroller;
26838
+ initialStateSave = false;
26839
+ }
26840
+ else {
26841
+ // Need to used the saved position on init
26842
+ data.scroller = {
26843
+ topRow: that.s.topRowFloat,
26844
+ baseScrollTop: that.s.baseScrollTop,
26845
+ baseRowTop: that.s.baseRowTop,
26846
+ scrollTop: that.s.lastScrollTop
26847
+ };
26848
+ }
26849
+ } );
26850
+
26851
+ if ( loadedState && loadedState.scroller ) {
26852
+ this.s.topRowFloat = loadedState.scroller.topRow;
26853
+ this.s.baseScrollTop = loadedState.scroller.baseScrollTop;
26854
+ this.s.baseRowTop = loadedState.scroller.baseRowTop;
26855
+ }
26856
+
26857
+ this.measure( false );
26858
+
26859
+ that.s.stateSaveThrottle = that.s.dt.oApi._fnThrottle( function () {
26860
+ that.s.dtApi.state.save();
26861
+ }, 500 );
26862
+
26863
+ dt.on( 'init.scroller', function () {
26864
+ that.measure( false );
26865
+
26866
+ // Setting to `jump` will instruct _draw to calculate the scroll top
26867
+ // position
26868
+ that.s.scrollType = 'jump';
26869
+ that._draw();
26870
+
26871
+ // Update the scroller when the DataTable is redrawn
26872
+ dt.on( 'draw.scroller', function () {
26873
+ that._draw();
26874
+ });
26875
+ } );
26876
+
26877
+ // Set height before the draw happens, allowing everything else to update
26878
+ // on draw complete without worry for roder.
26879
+ dt.on( 'preDraw.dt.scroller', function () {
26880
+ that._scrollForce();
26881
+ } );
26882
+
26883
+ // Destructor
26884
+ dt.on( 'destroy.scroller', function () {
26885
+ $(window).off( 'resize.dt-scroller' );
26886
+ $(that.dom.scroller).off('.dt-scroller');
26887
+ $(that.s.dt.nTable).off( '.scroller' );
26888
+
26889
+ $(that.s.dt.nTableWrapper).removeClass('DTS');
26890
+ $('div.DTS_Loading', that.dom.scroller.parentNode).remove();
26891
+
26892
+ that.dom.table.style.position = "";
26893
+ that.dom.table.style.top = "";
26894
+ that.dom.table.style.left = "";
26895
+ } );
26896
+ },
26897
+
26898
+
26899
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26900
+ * Private methods
26901
+ */
26902
+
26903
+ /**
26904
+ * Automatic calculation of table row height. This is just a little tricky here as using
26905
+ * initialisation DataTables has tale the table out of the document, so we need to create
26906
+ * a new table and insert it into the document, calculate the row height and then whip the
26907
+ * table out.
26908
+ * @returns {void}
26909
+ * @private
26910
+ */
26911
+ _calcRowHeight: function ()
26912
+ {
26913
+ var dt = this.s.dt;
26914
+ var origTable = dt.nTable;
26915
+ var nTable = origTable.cloneNode( false );
26916
+ var tbody = $('<tbody/>').appendTo( nTable );
26917
+ var container = $(
26918
+ '<div class="'+dt.oClasses.sWrapper+' DTS">'+
26919
+ '<div class="'+dt.oClasses.sScrollWrapper+'">'+
26920
+ '<div class="'+dt.oClasses.sScrollBody+'"></div>'+
26921
+ '</div>'+
26922
+ '</div>'
26923
+ );
26924
+
26925
+ // Want 3 rows in the sizing table so :first-child and :last-child
26926
+ // CSS styles don't come into play - take the size of the middle row
26927
+ $('tbody tr:lt(4)', origTable).clone().appendTo( tbody );
26928
+ var rowsCount = $('tr', tbody).length;
26929
+
26930
+ if ( rowsCount === 1 ) {
26931
+ tbody.prepend('<tr><td>&#160;</td></tr>');
26932
+ tbody.append('<tr><td>&#160;</td></tr>');
26933
+ }
26934
+ else {
26935
+ for (; rowsCount < 3; rowsCount++) {
26936
+ tbody.append('<tr><td>&#160;</td></tr>');
26937
+ }
26938
+ }
26939
+
26940
+ $('div.'+dt.oClasses.sScrollBody, container).append( nTable );
26941
+
26942
+ // If initialised using `dom`, use the holding element as the insert point
26943
+ var insertEl = this.s.dt.nHolding || origTable.parentNode;
26944
+
26945
+ if ( ! $(insertEl).is(':visible') ) {
26946
+ insertEl = 'body';
26947
+ }
26948
+
26949
+ // Remove form element links as they might select over others (particularly radio and checkboxes)
26950
+ container.find("input").removeAttr("name");
26951
+
26952
+ container.appendTo( insertEl );
26953
+ this.s.heights.row = $('tr', tbody).eq(1).outerHeight();
26954
+
26955
+ container.remove();
26956
+ },
26957
+
26958
+ /**
26959
+ * Draw callback function which is fired when the DataTable is redrawn. The main function of
26960
+ * this method is to position the drawn table correctly the scrolling container for the rows
26961
+ * that is displays as a result of the scrolling position.
26962
+ * @returns {void}
26963
+ * @private
26964
+ */
26965
+ _draw: function ()
26966
+ {
26967
+ var
26968
+ that = this,
26969
+ heights = this.s.heights,
26970
+ iScrollTop = this.dom.scroller.scrollTop,
26971
+ iTableHeight = $(this.s.dt.nTable).height(),
26972
+ displayStart = this.s.dt._iDisplayStart,
26973
+ displayLen = this.s.dt._iDisplayLength,
26974
+ displayEnd = this.s.dt.fnRecordsDisplay();
26975
+
26976
+ // Disable the scroll event listener while we are updating the DOM
26977
+ this.s.skip = true;
26978
+
26979
+ // If paging is reset
26980
+ if ( (this.s.dt.bSorted || this.s.dt.bFiltered) && displayStart === 0 && !this.s.dt._drawHold ) {
26981
+ this.s.topRowFloat = 0;
26982
+ }
26983
+
26984
+ iScrollTop = this.s.scrollType === 'jump' ?
26985
+ this._domain( 'virtualToPhysical', this.s.topRowFloat * heights.row ) :
26986
+ iScrollTop;
26987
+
26988
+ // Store positional information so positional calculations can be based
26989
+ // upon the current table draw position
26990
+ this.s.baseScrollTop = iScrollTop;
26991
+ this.s.baseRowTop = this.s.topRowFloat;
26992
+
26993
+ // Position the table in the virtual scroller
26994
+ var tableTop = iScrollTop - ((this.s.topRowFloat - displayStart) * heights.row);
26995
+ if ( displayStart === 0 ) {
26996
+ tableTop = 0;
26997
+ }
26998
+ else if ( displayStart + displayLen >= displayEnd ) {
26999
+ tableTop = heights.scroll - iTableHeight;
27000
+ }
27001
+
27002
+ this.dom.table.style.top = tableTop+'px';
27003
+
27004
+ /* Cache some information for the scroller */
27005
+ this.s.tableTop = tableTop;
27006
+ this.s.tableBottom = iTableHeight + this.s.tableTop;
27007
+
27008
+ // Calculate the boundaries for where a redraw will be triggered by the
27009
+ // scroll event listener
27010
+ var boundaryPx = (iScrollTop - this.s.tableTop) * this.s.boundaryScale;
27011
+ this.s.redrawTop = iScrollTop - boundaryPx;
27012
+ this.s.redrawBottom = iScrollTop + boundaryPx > heights.scroll - heights.viewport - heights.row ?
27013
+ heights.scroll - heights.viewport - heights.row :
27014
+ iScrollTop + boundaryPx;
27015
+
27016
+ this.s.skip = false;
27017
+
27018
+ // Restore the scrolling position that was saved by DataTable's state
27019
+ // saving Note that this is done on the second draw when data is Ajax
27020
+ // sourced, and the first draw when DOM soured
27021
+ if ( this.s.dt.oFeatures.bStateSave && this.s.dt.oLoadedState !== null &&
27022
+ typeof this.s.dt.oLoadedState.scroller != 'undefined' )
27023
+ {
27024
+ // A quirk of DataTables is that the draw callback will occur on an
27025
+ // empty set if Ajax sourced, but not if server-side processing.
27026
+ var ajaxSourced = (this.s.dt.sAjaxSource || that.s.dt.ajax) && ! this.s.dt.oFeatures.bServerSide ?
27027
+ true :
27028
+ false;
27029
+
27030
+ if ( ( ajaxSourced && this.s.dt.iDraw == 2) ||
27031
+ (!ajaxSourced && this.s.dt.iDraw == 1) )
27032
+ {
27033
+ setTimeout( function () {
27034
+ $(that.dom.scroller).scrollTop( that.s.dt.oLoadedState.scroller.scrollTop );
27035
+
27036
+ // In order to prevent layout thrashing we need another
27037
+ // small delay
27038
+ setTimeout( function () {
27039
+ that.s.ingnoreScroll = false;
27040
+ }, 0 );
27041
+ }, 0 );
27042
+ }
27043
+ }
27044
+ else {
27045
+ that.s.ingnoreScroll = false;
27046
+ }
27047
+
27048
+ // Because of the order of the DT callbacks, the info update will
27049
+ // take precedence over the one we want here. So a 'thread' break is
27050
+ // needed. Only add the thread break if bInfo is set
27051
+ if ( this.s.dt.oFeatures.bInfo ) {
27052
+ setTimeout( function () {
27053
+ that._info.call( that );
27054
+ }, 0 );
27055
+ }
27056
+
27057
+ // Hide the loading indicator
27058
+ if ( this.dom.loader && this.s.loaderVisible ) {
27059
+ this.dom.loader.css( 'display', 'none' );
27060
+ this.s.loaderVisible = false;
27061
+ }
27062
+ },
27063
+
27064
+ /**
27065
+ * Convert from one domain to another. The physical domain is the actual
27066
+ * pixel count on the screen, while the virtual is if we had browsers which
27067
+ * had scrolling containers of infinite height (i.e. the absolute value)
27068
+ *
27069
+ * @param {string} dir Domain transform direction, `virtualToPhysical` or
27070
+ * `physicalToVirtual`
27071
+ * @returns {number} Calculated transform
27072
+ * @private
27073
+ */
27074
+ _domain: function ( dir, val )
27075
+ {
27076
+ var heights = this.s.heights;
27077
+ var diff;
27078
+ var magic = 10000; // the point at which the non-linear calculations start to happen
27079
+
27080
+ // If the virtual and physical height match, then we use a linear
27081
+ // transform between the two, allowing the scrollbar to be linear
27082
+ if ( heights.virtual === heights.scroll ) {
27083
+ return val;
27084
+ }
27085
+
27086
+ // In the first 10k pixels and the last 10k pixels, we want the scrolling
27087
+ // to be linear. After that it can be non-linear. It would be unusual for
27088
+ // anyone to mouse wheel through that much.
27089
+ if ( val < magic ) {
27090
+ return val;
27091
+ }
27092
+ else if ( dir === 'virtualToPhysical' && val >= heights.virtual - magic ) {
27093
+ diff = heights.virtual - val;
27094
+ return heights.scroll - diff;
27095
+ }
27096
+ else if ( dir === 'physicalToVirtual' && val >= heights.scroll - magic ) {
27097
+ diff = heights.scroll - val;
27098
+ return heights.virtual - diff;
27099
+ }
27100
+
27101
+ // Otherwise, we want a non-linear scrollbar to take account of the
27102
+ // redrawing regions at the start and end of the table, otherwise these
27103
+ // can stutter badly - on large tables 30px (for example) scroll might
27104
+ // be hundreds of rows, so the table would be redrawing every few px at
27105
+ // the start and end. Use a simple linear eq. to stop this, effectively
27106
+ // causing a kink in the scrolling ratio. It does mean the scrollbar is
27107
+ // non-linear, but with such massive data sets, the scrollbar is going
27108
+ // to be a best guess anyway
27109
+ var m = (heights.virtual - magic - magic) / (heights.scroll - magic - magic);
27110
+ var c = magic - (m*magic);
27111
+
27112
+ return dir === 'virtualToPhysical' ?
27113
+ (val-c) / m :
27114
+ (m*val) + c;
27115
+ },
27116
+
27117
+ /**
27118
+ * Update any information elements that are controlled by the DataTable based on the scrolling
27119
+ * viewport and what rows are visible in it. This function basically acts in the same way as
27120
+ * _fnUpdateInfo in DataTables, and effectively replaces that function.
27121
+ * @returns {void}
27122
+ * @private
27123
+ */
27124
+ _info: function ()
27125
+ {
27126
+ if ( !this.s.dt.oFeatures.bInfo )
27127
+ {
27128
+ return;
27129
+ }
27130
+
27131
+ var
27132
+ dt = this.s.dt,
27133
+ language = dt.oLanguage,
27134
+ iScrollTop = this.dom.scroller.scrollTop,
27135
+ iStart = Math.floor( this.pixelsToRow(iScrollTop, false, this.s.ani)+1 ),
27136
+ iMax = dt.fnRecordsTotal(),
27137
+ iTotal = dt.fnRecordsDisplay(),
27138
+ iPossibleEnd = Math.ceil( this.pixelsToRow(iScrollTop+this.s.heights.viewport, false, this.s.ani) ),
27139
+ iEnd = iTotal < iPossibleEnd ? iTotal : iPossibleEnd,
27140
+ sStart = dt.fnFormatNumber( iStart ),
27141
+ sEnd = dt.fnFormatNumber( iEnd ),
27142
+ sMax = dt.fnFormatNumber( iMax ),
27143
+ sTotal = dt.fnFormatNumber( iTotal ),
27144
+ sOut;
27145
+
27146
+ if ( dt.fnRecordsDisplay() === 0 &&
27147
+ dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
27148
+ {
27149
+ /* Empty record set */
27150
+ sOut = language.sInfoEmpty+ language.sInfoPostFix;
27151
+ }
27152
+ else if ( dt.fnRecordsDisplay() === 0 )
27153
+ {
27154
+ /* Empty record set after filtering */
27155
+ sOut = language.sInfoEmpty +' '+
27156
+ language.sInfoFiltered.replace('_MAX_', sMax)+
27157
+ language.sInfoPostFix;
27158
+ }
27159
+ else if ( dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
27160
+ {
27161
+ /* Normal record set */
27162
+ sOut = language.sInfo.
27163
+ replace('_START_', sStart).
27164
+ replace('_END_', sEnd).
27165
+ replace('_MAX_', sMax).
27166
+ replace('_TOTAL_', sTotal)+
27167
+ language.sInfoPostFix;
27168
+ }
27169
+ else
27170
+ {
27171
+ /* Record set after filtering */
27172
+ sOut = language.sInfo.
27173
+ replace('_START_', sStart).
27174
+ replace('_END_', sEnd).
27175
+ replace('_MAX_', sMax).
27176
+ replace('_TOTAL_', sTotal) +' '+
27177
+ language.sInfoFiltered.replace(
27178
+ '_MAX_',
27179
+ dt.fnFormatNumber(dt.fnRecordsTotal())
27180
+ )+
27181
+ language.sInfoPostFix;
27182
+ }
27183
+
27184
+ var callback = language.fnInfoCallback;
27185
+ if ( callback ) {
27186
+ sOut = callback.call( dt.oInstance,
27187
+ dt, iStart, iEnd, iMax, iTotal, sOut
27188
+ );
27189
+ }
27190
+
27191
+ var n = dt.aanFeatures.i;
27192
+ if ( typeof n != 'undefined' )
27193
+ {
27194
+ for ( var i=0, iLen=n.length ; i<iLen ; i++ )
27195
+ {
27196
+ $(n[i]).html( sOut );
27197
+ }
27198
+ }
27199
+
27200
+ // DT doesn't actually (yet) trigger this event, but it will in future
27201
+ $(dt.nTable).triggerHandler( 'info.dt' );
27202
+ },
27203
+
27204
+ /**
27205
+ * Parse CSS height property string as number
27206
+ *
27207
+ * An attempt is made to parse the string as a number. Currently supported units are 'px',
27208
+ * 'vh', and 'rem'. 'em' is partially supported; it works as long as the parent element's
27209
+ * font size matches the body element. Zero is returned for unrecognized strings.
27210
+ * @param {string} cssHeight CSS height property string
27211
+ * @returns {number} height
27212
+ * @private
27213
+ */
27214
+ _parseHeight: function(cssHeight) {
27215
+ var height;
27216
+ var matches = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(px|em|rem|vh)$/.exec(cssHeight);
27217
+
27218
+ if (matches === null) {
27219
+ return 0;
27220
+ }
27221
+
27222
+ var value = parseFloat(matches[1]);
27223
+ var unit = matches[2];
27224
+
27225
+ if ( unit === 'px' ) {
27226
+ height = value;
27227
+ }
27228
+ else if ( unit === 'vh' ) {
27229
+ height = ( value / 100 ) * $(window).height();
27230
+ }
27231
+ else if ( unit === 'rem' ) {
27232
+ height = value * parseFloat($(':root').css('font-size'));
27233
+ }
27234
+ else if ( unit === 'em' ) {
27235
+ height = value * parseFloat($('body').css('font-size'));
27236
+ }
27237
+
27238
+ return height ?
27239
+ height :
27240
+ 0;
27241
+ },
27242
+
27243
+ /**
27244
+ * Scrolling function - fired whenever the scrolling position is changed.
27245
+ * This method needs to use the stored values to see if the table should be
27246
+ * redrawn as we are moving towards the end of the information that is
27247
+ * currently drawn or not. If needed, then it will redraw the table based on
27248
+ * the new position.
27249
+ * @returns {void}
27250
+ * @private
27251
+ */
27252
+ _scroll: function ()
27253
+ {
27254
+ var
27255
+ that = this,
27256
+ heights = this.s.heights,
27257
+ iScrollTop = this.dom.scroller.scrollTop,
27258
+ iTopRow;
27259
+
27260
+ if ( this.s.skip ) {
27261
+ return;
27262
+ }
27263
+
27264
+ if ( this.s.ingnoreScroll ) {
27265
+ return;
27266
+ }
27267
+
27268
+ if ( iScrollTop === this.s.lastScrollTop ) {
27269
+ return;
27270
+ }
27271
+
27272
+ /* If the table has been sorted or filtered, then we use the redraw that
27273
+ * DataTables as done, rather than performing our own
27274
+ */
27275
+ if ( this.s.dt.bFiltered || this.s.dt.bSorted ) {
27276
+ this.s.lastScrollTop = 0;
27277
+ return;
27278
+ }
27279
+
27280
+ /* Update the table's information display for what is now in the viewport */
27281
+ this._info();
27282
+
27283
+ /* We don't want to state save on every scroll event - that's heavy
27284
+ * handed, so use a timeout to update the state saving only when the
27285
+ * scrolling has finished
27286
+ */
27287
+ clearTimeout( this.s.stateTO );
27288
+ this.s.stateTO = setTimeout( function () {
27289
+ that.s.dtApi.state.save();
27290
+ }, 250 );
27291
+
27292
+ this.s.scrollType = Math.abs(iScrollTop - this.s.lastScrollTop) > heights.viewport ?
27293
+ 'jump' :
27294
+ 'cont';
27295
+
27296
+ this.s.topRowFloat = this.s.scrollType === 'cont' ?
27297
+ this.pixelsToRow( iScrollTop, false, false ) :
27298
+ this._domain( 'physicalToVirtual', iScrollTop ) / heights.row;
27299
+
27300
+ if ( this.s.topRowFloat < 0 ) {
27301
+ this.s.topRowFloat = 0;
27302
+ }
27303
+
27304
+ /* Check if the scroll point is outside the trigger boundary which would required
27305
+ * a DataTables redraw
27306
+ */
27307
+ if ( this.s.forceReposition || iScrollTop < this.s.redrawTop || iScrollTop > this.s.redrawBottom ) {
27308
+ var preRows = Math.ceil( ((this.s.displayBuffer-1)/2) * this.s.viewportRows );
27309
+
27310
+ iTopRow = parseInt(this.s.topRowFloat, 10) - preRows;
27311
+ this.s.forceReposition = false;
27312
+
27313
+ if ( iTopRow <= 0 ) {
27314
+ /* At the start of the table */
27315
+ iTopRow = 0;
27316
+ }
27317
+ else if ( iTopRow + this.s.dt._iDisplayLength > this.s.dt.fnRecordsDisplay() ) {
27318
+ /* At the end of the table */
27319
+ iTopRow = this.s.dt.fnRecordsDisplay() - this.s.dt._iDisplayLength;
27320
+ if ( iTopRow < 0 ) {
27321
+ iTopRow = 0;
27322
+ }
27323
+ }
27324
+ else if ( iTopRow % 2 !== 0 ) {
27325
+ // For the row-striping classes (odd/even) we want only to start
27326
+ // on evens otherwise the stripes will change between draws and
27327
+ // look rubbish
27328
+ iTopRow++;
27329
+ }
27330
+
27331
+ // Store calcuated value, in case the following condition is not met, but so
27332
+ // that the draw function will still use it.
27333
+ this.s.targetTop = iTopRow;
27334
+
27335
+ if ( iTopRow != this.s.dt._iDisplayStart ) {
27336
+ /* Cache the new table position for quick lookups */
27337
+ this.s.tableTop = $(this.s.dt.nTable).offset().top;
27338
+ this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
27339
+
27340
+ var draw = function () {
27341
+ that.s.dt._iDisplayStart = that.s.targetTop;
27342
+ that.s.dt.oApi._fnDraw( that.s.dt );
27343
+ };
27344
+
27345
+ /* Do the DataTables redraw based on the calculated start point - note that when
27346
+ * using server-side processing we introduce a small delay to not DoS the server...
27347
+ */
27348
+ if ( this.s.dt.oFeatures.bServerSide ) {
27349
+ this.s.forceReposition = true;
27350
+
27351
+ clearTimeout( this.s.drawTO );
27352
+ this.s.drawTO = setTimeout( draw, this.s.serverWait );
27353
+ }
27354
+ else {
27355
+ draw();
27356
+ }
27357
+
27358
+ if ( this.dom.loader && ! this.s.loaderVisible ) {
27359
+ this.dom.loader.css( 'display', 'block' );
27360
+ this.s.loaderVisible = true;
27361
+ }
27362
+ }
27363
+ }
27364
+ else {
27365
+ this.s.topRowFloat = this.pixelsToRow( iScrollTop, false, true );
27366
+ }
27367
+
27368
+ this.s.lastScrollTop = iScrollTop;
27369
+ this.s.stateSaveThrottle();
27370
+
27371
+ if ( this.s.scrollType === 'jump' && this.s.mousedown ) {
27372
+ this.s.labelVisible = true;
27373
+ }
27374
+ if (this.s.labelVisible) {
27375
+ this.dom.label
27376
+ .html( this.s.dt.fnFormatNumber( parseInt( this.s.topRowFloat, 10 )+1 ) )
27377
+ .css( 'top', iScrollTop + (iScrollTop * heights.labelFactor ) )
27378
+ .css( 'display', 'block' );
27379
+ }
27380
+ },
27381
+
27382
+ /**
27383
+ * Force the scrolling container to have height beyond that of just the
27384
+ * table that has been drawn so the user can scroll the whole data set.
27385
+ *
27386
+ * Note that if the calculated required scrolling height exceeds a maximum
27387
+ * value (1 million pixels - hard-coded) the forcing element will be set
27388
+ * only to that maximum value and virtual / physical domain transforms will
27389
+ * be used to allow Scroller to display tables of any number of records.
27390
+ * @returns {void}
27391
+ * @private
27392
+ */
27393
+ _scrollForce: function ()
27394
+ {
27395
+ var heights = this.s.heights;
27396
+ var max = 1000000;
27397
+
27398
+ heights.virtual = heights.row * this.s.dt.fnRecordsDisplay();
27399
+ heights.scroll = heights.virtual;
27400
+
27401
+ if ( heights.scroll > max ) {
27402
+ heights.scroll = max;
27403
+ }
27404
+
27405
+ // Minimum height so there is always a row visible (the 'no rows found'
27406
+ // if reduced to zero filtering)
27407
+ this.dom.force.style.height = heights.scroll > this.s.heights.row ?
27408
+ heights.scroll+'px' :
27409
+ this.s.heights.row+'px';
27410
+ }
27411
+ } );
27412
+
27413
+
27414
+
27415
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27416
+ * Statics
27417
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27418
+
27419
+
27420
+ /**
27421
+ * Scroller default settings for initialisation
27422
+ * @namespace
27423
+ * @name Scroller.defaults
27424
+ * @static
27425
+ */
27426
+ Scroller.defaults = {
27427
+ /**
27428
+ * Scroller uses the boundary scaling factor to decide when to redraw the table - which it
27429
+ * typically does before you reach the end of the currently loaded data set (in order to
27430
+ * allow the data to look continuous to a user scrolling through the data). If given as 0
27431
+ * then the table will be redrawn whenever the viewport is scrolled, while 1 would not
27432
+ * redraw the table until the currently loaded data has all been shown. You will want
27433
+ * something in the middle - the default factor of 0.5 is usually suitable.
27434
+ * @type float
27435
+ * @default 0.5
27436
+ * @static
27437
+ */
27438
+ boundaryScale: 0.5,
27439
+
27440
+ /**
27441
+ * The display buffer is what Scroller uses to calculate how many rows it should pre-fetch
27442
+ * for scrolling. Scroller automatically adjusts DataTables' display length to pre-fetch
27443
+ * rows that will be shown in "near scrolling" (i.e. just beyond the current display area).
27444
+ * The value is based upon the number of rows that can be displayed in the viewport (i.e.
27445
+ * a value of 1), and will apply the display range to records before before and after the
27446
+ * current viewport - i.e. a factor of 3 will allow Scroller to pre-fetch 1 viewport's worth
27447
+ * of rows before the current viewport, the current viewport's rows and 1 viewport's worth
27448
+ * of rows after the current viewport. Adjusting this value can be useful for ensuring
27449
+ * smooth scrolling based on your data set.
27450
+ * @type int
27451
+ * @default 7
27452
+ * @static
27453
+ */
27454
+ displayBuffer: 9,
27455
+
27456
+ /**
27457
+ * Show (or not) the loading element in the background of the table. Note that you should
27458
+ * include the dataTables.scroller.css file for this to be displayed correctly.
27459
+ * @type boolean
27460
+ * @default false
27461
+ * @static
27462
+ */
27463
+ loadingIndicator: false,
27464
+
27465
+ /**
27466
+ * Scroller will attempt to automatically calculate the height of rows for it's internal
27467
+ * calculations. However the height that is used can be overridden using this parameter.
27468
+ * @type int|string
27469
+ * @default auto
27470
+ * @static
27471
+ */
27472
+ rowHeight: "auto",
27473
+
27474
+ /**
27475
+ * When using server-side processing, Scroller will wait a small amount of time to allow
27476
+ * the scrolling to finish before requesting more data from the server. This prevents
27477
+ * you from DoSing your own server! The wait time can be configured by this parameter.
27478
+ * @type int
27479
+ * @default 200
27480
+ * @static
27481
+ */
27482
+ serverWait: 200
27483
+ };
27484
+
27485
+ Scroller.oDefaults = Scroller.defaults;
27486
+
27487
+
27488
+
27489
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27490
+ * Constants
27491
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27492
+
27493
+ /**
27494
+ * Scroller version
27495
+ * @type String
27496
+ * @default See code
27497
+ * @name Scroller.version
27498
+ * @static
27499
+ */
27500
+ Scroller.version = "2.0.3";
27501
+
27502
+
27503
+
27504
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
27505
+ * Initialisation
27506
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
27507
+
27508
+ // Attach a listener to the document which listens for DataTables initialisation
27509
+ // events so we can automatically initialise
27510
+ $(document).on( 'preInit.dt.dtscroller', function (e, settings) {
27511
+ if ( e.namespace !== 'dt' ) {
27512
+ return;
27513
+ }
27514
+
27515
+ var init = settings.oInit.scroller;
27516
+ var defaults = DataTable.defaults.scroller;
27517
+
27518
+ if ( init || defaults ) {
27519
+ var opts = $.extend( {}, init, defaults );
27520
+
27521
+ if ( init !== false ) {
27522
+ new Scroller( settings, opts );
27523
+ }
27524
+ }
27525
+ } );
27526
+
27527
+
27528
+ // Attach Scroller to DataTables so it can be accessed as an 'extra'
27529
+ $.fn.dataTable.Scroller = Scroller;
27530
+ $.fn.DataTable.Scroller = Scroller;
27531
+
27532
+
27533
+ // DataTables 1.10 API method aliases
27534
+ var Api = $.fn.dataTable.Api;
27535
+
27536
+ Api.register( 'scroller()', function () {
27537
+ return this;
27538
+ } );
27539
+
27540
+ // Undocumented and deprecated - is it actually useful at all?
27541
+ Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
27542
+ var ctx = this.context;
27543
+
27544
+ if ( ctx.length && ctx[0].oScroller ) {
27545
+ return ctx[0].oScroller.rowToPixels( rowIdx, intParse, virtual );
27546
+ }
27547
+ // undefined
27548
+ } );
27549
+
27550
+ // Undocumented and deprecated - is it actually useful at all?
27551
+ Api.register( 'scroller().pixelsToRow()', function ( pixels, intParse, virtual ) {
27552
+ var ctx = this.context;
27553
+
27554
+ if ( ctx.length && ctx[0].oScroller ) {
27555
+ return ctx[0].oScroller.pixelsToRow( pixels, intParse, virtual );
27556
+ }
27557
+ // undefined
27558
+ } );
27559
+
27560
+ // `scroller().scrollToRow()` is undocumented and deprecated. Use `scroller.toPosition()
27561
+ Api.register( ['scroller().scrollToRow()', 'scroller.toPosition()'], function ( idx, ani ) {
27562
+ this.iterator( 'table', function ( ctx ) {
27563
+ if ( ctx.oScroller ) {
27564
+ ctx.oScroller.scrollToRow( idx, ani );
27565
+ }
27566
+ } );
27567
+
27568
+ return this;
27569
+ } );
27570
+
27571
+ Api.register( 'row().scrollTo()', function ( ani ) {
27572
+ var that = this;
27573
+
27574
+ this.iterator( 'row', function ( ctx, rowIdx ) {
27575
+ if ( ctx.oScroller ) {
27576
+ var displayIdx = that
27577
+ .rows( { order: 'applied', search: 'applied' } )
27578
+ .indexes()
27579
+ .indexOf( rowIdx );
27580
+
27581
+ ctx.oScroller.scrollToRow( displayIdx, ani );
27582
+ }
27583
+ } );
27584
+
27585
+ return this;
27586
+ } );
27587
+
27588
+ Api.register( 'scroller.measure()', function ( redraw ) {
27589
+ this.iterator( 'table', function ( ctx ) {
27590
+ if ( ctx.oScroller ) {
27591
+ ctx.oScroller.measure( redraw );
27592
+ }
27593
+ } );
27594
+
27595
+ return this;
27596
+ } );
27597
+
27598
+ Api.register( 'scroller.page()', function() {
27599
+ var ctx = this.context;
27600
+
27601
+ if ( ctx.length && ctx[0].oScroller ) {
27602
+ return ctx[0].oScroller.pageInfo();
27603
+ }
27604
+ // undefined
27605
+ } );
27606
+
27607
+ return Scroller;
27608
+ }));
27609
+
27610
+
27611
+ /*! Select for DataTables 1.3.1
27612
+ * 2015-2019 SpryMedia Ltd - datatables.net/license/mit
27613
+ */
27614
+
27615
+ /**
27616
+ * @summary Select for DataTables
27617
+ * @description A collection of API methods, events and buttons for DataTables
27618
+ * that provides selection options of the items in a DataTable
27619
+ * @version 1.3.1
27620
+ * @file dataTables.select.js
27621
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
27622
+ * @contact datatables.net/forums
27623
+ * @copyright Copyright 2015-2019 SpryMedia Ltd.
27624
+ *
27625
+ * This source file is free software, available under the following license:
27626
+ * MIT license - http://datatables.net/license/mit
27627
+ *
27628
+ * This source file is distributed in the hope that it will be useful, but
27629
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
27630
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
27631
+ *
27632
+ * For details please refer to: http://www.datatables.net/extensions/select
27633
+ */
27634
+ (function( factory ){
27635
+ if ( typeof define === 'function' && define.amd ) {
27636
+ // AMD
27637
+ define( ['jquery', 'datatables.net'], function ( $ ) {
27638
+ return factory( $, window, document );
27639
+ } );
27640
+ }
27641
+ else if ( typeof exports === 'object' ) {
27642
+ // CommonJS
27643
+ module.exports = function (root, $) {
27644
+ if ( ! root ) {
27645
+ root = window;
27646
+ }
27647
+
27648
+ if ( ! $ || ! $.fn.dataTable ) {
27649
+ $ = require('datatables.net')(root, $).$;
27650
+ }
27651
+
27652
+ return factory( $, root, root.document );
27653
+ };
27654
+ }
27655
+ else {
27656
+ // Browser
27657
+ factory( jQuery, window, document );
27658
+ }
27659
+ }(function( $, window, document, undefined ) {
27660
+ 'use strict';
27661
+ var DataTable = $.fn.dataTable;
27662
+
27663
+
27664
+ // Version information for debugger
27665
+ DataTable.select = {};
27666
+
27667
+ DataTable.select.version = '1.3.1';
27668
+
27669
+ DataTable.select.init = function ( dt ) {
27670
+ var ctx = dt.settings()[0];
27671
+ var init = ctx.oInit.select;
27672
+ var defaults = DataTable.defaults.select;
27673
+ var opts = init === undefined ?
27674
+ defaults :
27675
+ init;
27676
+
27677
+ // Set defaults
27678
+ var items = 'row';
27679
+ var style = 'api';
27680
+ var blurable = false;
27681
+ var toggleable = true;
27682
+ var info = true;
27683
+ var selector = 'td, th';
27684
+ var className = 'selected';
27685
+ var setStyle = false;
27686
+
27687
+ ctx._select = {};
27688
+
27689
+ // Initialisation customisations
27690
+ if ( opts === true ) {
27691
+ style = 'os';
27692
+ setStyle = true;
27693
+ }
27694
+ else if ( typeof opts === 'string' ) {
27695
+ style = opts;
27696
+ setStyle = true;
27697
+ }
27698
+ else if ( $.isPlainObject( opts ) ) {
27699
+ if ( opts.blurable !== undefined ) {
27700
+ blurable = opts.blurable;
27701
+ }
27702
+
27703
+ if ( opts.toggleable !== undefined ) {
27704
+ toggleable = opts.toggleable;
27705
+ }
27706
+
27707
+ if ( opts.info !== undefined ) {
27708
+ info = opts.info;
27709
+ }
27710
+
27711
+ if ( opts.items !== undefined ) {
27712
+ items = opts.items;
27713
+ }
27714
+
27715
+ if ( opts.style !== undefined ) {
27716
+ style = opts.style;
27717
+ setStyle = true;
27718
+ }
27719
+ else {
27720
+ style = 'os';
27721
+ setStyle = true;
27722
+ }
27723
+
27724
+ if ( opts.selector !== undefined ) {
27725
+ selector = opts.selector;
27726
+ }
27727
+
27728
+ if ( opts.className !== undefined ) {
27729
+ className = opts.className;
27730
+ }
27731
+ }
27732
+
27733
+ dt.select.selector( selector );
27734
+ dt.select.items( items );
27735
+ dt.select.style( style );
27736
+ dt.select.blurable( blurable );
27737
+ dt.select.toggleable( toggleable );
27738
+ dt.select.info( info );
27739
+ ctx._select.className = className;
27740
+
27741
+
27742
+ // Sort table based on selected rows. Requires Select Datatables extension
27743
+ $.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) {
27744
+ return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) {
27745
+ if ( settings._select.items === 'row' ) {
27746
+ return $( td ).parent().hasClass( settings._select.className );
27747
+ } else if ( settings._select.items === 'cell' ) {
27748
+ return $( td ).hasClass( settings._select.className );
27749
+ }
27750
+ return false;
27751
+ });
27752
+ };
27753
+
27754
+ // If the init options haven't enabled select, but there is a selectable
27755
+ // class name, then enable
27756
+ if ( ! setStyle && $( dt.table().node() ).hasClass( 'selectable' ) )