diff --git a/htdocs/includes/jquery/plugins/datatables/Contributing.md b/htdocs/includes/jquery/plugins/datatables/Contributing.md new file mode 100644 index 00000000000..17a8f1f10b1 --- /dev/null +++ b/htdocs/includes/jquery/plugins/datatables/Contributing.md @@ -0,0 +1,18 @@ +# Support requests + +Please direct support requests to the [DataTables forums](https://datatables.net/forums), ensuring that you provide a link to a test page that shows the problem and a full description of the issue. If you require urgent help, [priority support](https://datatables.net/support) is available. + + +# Contributing code + +If you are thinking of contributing code to DataTables, first of all, thank you! All fixes, patches and enhancements to DataTables are very warmly welcomed. In order to keep thing manageable, there are a number of guidelines that should be followed in order to ensure that your modification is included in DataTables as quickly as possible: + +1. Make contributions in the DataTables/DataTablesSrc repo. Changes to the built files in the built repo (DataTables/DataTables) will not be accepted since they would be overwritten by the next build! + +2. Follow the style of the code in the existing files. They might not be to everyone's tastes, but consistency is key for a mature project like DataTables. DataTables doesn't have a coding standards document, but simple common sense of following the same style as in the existing files is ideal. For example use tabs not spaces (as you will see all source files use tabs). + +3. Link to a test page showing the bug you are fixing or the feature you are adding. This allows to me to quickly identify what is being changed and why. Don't worry about being verbose in pull requests - its much better to know exactly what is changing and why! + +4. DataTables is a large and complex project and it isn't always possible or suitable to pull in every suggested change. Please don't be offended if a pull request is not merged in, it will explained why not if this is the case. Also it isn't always possible to fully check and test pull requests as quickly as I would like due to other commitments. Again this is no reflection on your pull request, just the busy life that we all lead! If you have any questions about your potential contribution and its place in the DataTables project structure, please ask ahead of time in the [DataTables forums](//datatables.net/forums). + +5. Pull requests will only be accepted if you acknowledge that your contribution is offered under and will be made available under the project's existing license (MIT). If your initial pull request doesn't explicitly acknowledge this I'll ask before it is pulled in. \ No newline at end of file diff --git a/htdocs/includes/jquery/plugins/datatables/Readme.md b/htdocs/includes/jquery/plugins/datatables/Readme.md new file mode 100644 index 00000000000..dd952d84ce8 --- /dev/null +++ b/htdocs/includes/jquery/plugins/datatables/Readme.md @@ -0,0 +1,53 @@ +# DataTables plug-in for jQuery + +DataTables is a table enhancing plug-in for the [jQuery](//jquery.com) Javascript library, adding sorting, paging and filtering abilities to plain HTML tables with minimal effort. The stated goal of DataTables is: + +> To enhance the accessibility of data in HTML tables. + +To meet this goal, DataTables is developed with two distinct groups of users in mind: + +* You the developers using DataTables. For developers DataTables provides a wide array of options for how data should be obtained, displayed and acted upon, along with an extensive API for accessing and manipulating the table. + +* End users. For those using the interface DataTables presents, actions to get the most from the information contained in tables, such as sorting and filtering, along with paging and scrolling of the data in table, are easy to use, intuitive and fast. + + +## Installation + +In most cases, to use DataTables all you need to do is include jQuery, the DataTables Javascript and DataTables CSS files in your HTML page. See the [DataTables manual](http://datatables.net/manual/installation#Including-Javascript-/-CSS) for details on how to do this using the latest version of DataTables. + + +## Usage + +In its simplest case, DataTables can be initialised with a single line of Javascript: + +```js +$('table').dataTable(); +``` + +where the jQuery selector is used to obtain a reference to the table you want to enhance with DataTables. Optional configuration parameters can be passed in to DataTables to have it perform certain actions by using a configuration object as the parameter passed in to the DataTables constructor. For example: + +```js +$('table').dataTable( { + paginate: false, + scrollY: 300 +} ); +``` + +will disable paging and enable scrolling. + +A full list of the options available for DataTables are available in the [documentation](//datatables.net). + + +## Documentation + +Full documentation of the DataTables options, API and pug-in interface are available on the [DataTables web-site](//datatables.net). The site also contains information on the wide variety of plug-ins that are available for DataTables, which can be used to enhance and customise your table even further. + + +## Support + +Support for DataTables is available through the [DataTables forums](//datatables.net/forums) and [commercial support options](//datatables.net/support) are available. + + +## License + +DataTables is release under the [MIT license](//datatables.net/license). You are free to use, modify and distribute this software, as long as the copyright header is left intact (specifically the comment block which starts with `/*!`. diff --git a/htdocs/includes/jquery/plugins/datatables/Readme.txt b/htdocs/includes/jquery/plugins/datatables/Readme.txt deleted file mode 100644 index 96a47cbde67..00000000000 --- a/htdocs/includes/jquery/plugins/datatables/Readme.txt +++ /dev/null @@ -1,11 +0,0 @@ -This DataTables plugin (v1.9.x) for jQuery was developed out of the desire to allow highly configurable access to HTML tables with advanced access features. - -For detailed installation, usage and API instructions, please refer to the DataTables web-pages: http://www.datatables.net - -Questions, feature requests and bug reports (etc) can all be asked on the DataTables forums: http://www.datatables.net/forums/ - -The DataTables source can be found in the media/js/ directory of this archive. - -DataTables is released with dual licensing, using the GPL v2 (license-gpl2.txt) and an BSD style license (license-bsd.txt). You may select which of the two licenses you wish to use DataTables under. Please see the corresponding license file for details of these licenses. You are free to use, modify and distribute this software, but all copyright information must remain. - -If you discover any bugs in DataTables, have any suggestions for improvements or even if you just like using it, please free to get in touch with me: www.datatables.net/contact \ No newline at end of file diff --git a/htdocs/includes/jquery/plugins/datatables/css/jquery.dataTables.css b/htdocs/includes/jquery/plugins/datatables/css/jquery.dataTables.css deleted file mode 100644 index b257b33bee5..00000000000 --- a/htdocs/includes/jquery/plugins/datatables/css/jquery.dataTables.css +++ /dev/null @@ -1,221 +0,0 @@ - -/* - * Table - */ -table.dataTable { - margin: 0 auto; - clear: both; - width: 100%; -} - -table.dataTable thead th { - padding: 3px 18px 3px 10px; - border-bottom: 1px solid black; - font-weight: bold; - cursor: pointer; - *cursor: hand; -} - -table.dataTable tfoot th { - padding: 3px 18px 3px 10px; - border-top: 1px solid black; - font-weight: bold; -} - -table.dataTable td { - padding: 3px 10px; -} - -table.dataTable td.center, -table.dataTable td.dataTables_empty { - text-align: center; -} - -table.dataTable tr.odd { background-color: #E2E4FF; } -table.dataTable tr.even { background-color: white; } - -table.dataTable tr.odd td.sorting_1 { background-color: #D3D6FF; } -table.dataTable tr.odd td.sorting_2 { background-color: #DADCFF; } -table.dataTable tr.odd td.sorting_3 { background-color: #E0E2FF; } -table.dataTable tr.even td.sorting_1 { background-color: #EAEBFF; } -table.dataTable tr.even td.sorting_2 { background-color: #F2F3FF; } -table.dataTable tr.even td.sorting_3 { background-color: #F9F9FF; } - - -/* - * Table wrapper - */ -.dataTables_wrapper { - position: relative; - clear: both; - *zoom: 1; -} - - -/* - * Page length menu - */ -.dataTables_length { - float: left; -} - - -/* - * Filter - */ -.dataTables_filter { - float: right; - text-align: right; -} - - -/* - * Table information - */ -.dataTables_info { - clear: both; - float: left; -} - - -/* - * Pagination - */ -.dataTables_paginate { - float: right; - text-align: right; -} - -/* Two button pagination - previous / next */ -.paginate_disabled_previous, -.paginate_enabled_previous, -.paginate_disabled_next, -.paginate_enabled_next { - height: 19px; - float: left; - cursor: pointer; - *cursor: hand; - color: #111 !important; -} -.paginate_disabled_previous:hover, -.paginate_enabled_previous:hover, -.paginate_disabled_next:hover, -.paginate_enabled_next:hover { - text-decoration: none !important; -} -.paginate_disabled_previous:active, -.paginate_enabled_previous:active, -.paginate_disabled_next:active, -.paginate_enabled_next:active { - outline: none; -} - -.paginate_disabled_previous, -.paginate_disabled_next { - color: #666 !important; -} -.paginate_disabled_previous, -.paginate_enabled_previous { - padding-left: 23px; -} -.paginate_disabled_next, -.paginate_enabled_next { - padding-right: 23px; - margin-left: 10px; -} - -.paginate_enabled_previous { background: url('../images/back_enabled.png') no-repeat top left; } -.paginate_enabled_previous:hover { background: url('../images/back_enabled_hover.png') no-repeat top left; } -.paginate_disabled_previous { background: url('../images/back_disabled.png') no-repeat top left; } - -.paginate_enabled_next { background: url('../images/forward_enabled.png') no-repeat top right; } -.paginate_enabled_next:hover { background: url('../images/forward_enabled_hover.png') no-repeat top right; } -.paginate_disabled_next { background: url('../images/forward_disabled.png') no-repeat top right; } - -/* Full number pagination */ -.paging_full_numbers { - height: 22px; - line-height: 22px; -} -.paging_full_numbers a:active { - outline: none -} -.paging_full_numbers a:hover { - text-decoration: none; -} - -.paging_full_numbers a.paginate_button, -.paging_full_numbers a.paginate_active { - border: 1px solid #aaa; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; - padding: 2px 5px; - margin: 0 3px; - cursor: pointer; - *cursor: hand; - /* DOL_LDR CHANGE color: #333 !important; */ -} - -.paging_full_numbers a.paginate_button { - background-color: #ddd; -} - -.paging_full_numbers a.paginate_button:hover { - background-color: #ccc; - text-decoration: none !important; -} - -.paging_full_numbers a.paginate_active { - background-color: #99B3FF; -} - - -/* - * Processing indicator - */ -.dataTables_processing { - position: absolute; - top: 50%; - left: 50%; - width: 250px; - height: 30px; - margin-left: -125px; - margin-top: -15px; - padding: 14px 0 2px 0; - border: 1px solid #ddd; - text-align: center; - color: #999; - font-size: 14px; - background-color: white; -} - - -/* - * Sorting - */ -.sorting { background: url('../images/sort_both.png') no-repeat center right; } -.sorting_asc { background: url('../images/sort_asc.png') no-repeat center right; } -.sorting_desc { background: url('../images/sort_desc.png') no-repeat center right; } - -.sorting_asc_disabled { background: url('../images/sort_asc_disabled.png') no-repeat center right; } -.sorting_desc_disabled { background: url('../images/sort_desc_disabled.png') no-repeat center right; } - -table.dataTable thead th:active, -table.dataTable thead td:active { - outline: none; -} - - -/* - * Scrolling - */ -.dataTables_scroll { - clear: both; -} - -.dataTables_scrollBody { - *margin-top: -1px; - -webkit-overflow-scrolling: touch; -} - diff --git a/htdocs/includes/jquery/plugins/datatables/css/jquery.dataTables_jui.css b/htdocs/includes/jquery/plugins/datatables/css/jquery.dataTables_jui.css deleted file mode 100644 index a210af51ae9..00000000000 --- a/htdocs/includes/jquery/plugins/datatables/css/jquery.dataTables_jui.css +++ /dev/null @@ -1,501 +0,0 @@ -/* - * File: demo_table_jui.css - * CVS: $Id$ - * Description: CSS descriptions for DataTables demo pages - * Author: Allan Jardine - * Created: Tue May 12 06:47:22 BST 2009 - * Modified: $Date$ by $Author$ - * Language: CSS - * Project: DataTables - * - * Copyright 2009 Allan Jardine. All Rights Reserved. - * - * *************************************************************************** - * DESCRIPTION - * - * The styles given here are suitable for the demos that are used with the standard DataTables - * distribution (see www.datatables.net). You will most likely wish to modify these styles to - * meet the layout requirements of your site. - * - * Common issues: - * 'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is - * no conflict between the two pagination types. If you want to use full_numbers pagination - * ensure that you either have "example_alt_pagination" as a body class name, or better yet, - * modify that selector. - * Note that the path used for Images is relative. All images are by default located in - * ../images/ - relative to this CSS file. - */ - - -/* - * jQuery UI specific styling - */ - -.paging_two_button .ui-button { - float: left; - cursor: pointer; - * cursor: hand; -} - -.paging_full_numbers .ui-button { - padding: 2px 6px; - margin: 0; - cursor: pointer; - * cursor: hand; - color: #333 !important; -} - -.dataTables_paginate .ui-button { - margin-right: -0.1em !important; -} - -.paging_full_numbers { - width: 350px !important; -} - -.dataTables_wrapper .ui-toolbar { - padding: 5px; -} - -.dataTables_paginate { - width: auto; -} - -.dataTables_info { - padding-top: 3px; -} - -table.display thead th { - padding: 3px 0px 3px 10px; - cursor: pointer; - * cursor: hand; -} - -div.dataTables_wrapper .ui-widget-header { - font-weight: normal; -} - - -/* - * Sort arrow icon positioning - */ -table.display thead th div.DataTables_sort_wrapper { - position: relative; - padding-right: 20px; -} - -table.display thead th div.DataTables_sort_wrapper span { - position: absolute; - top: 50%; - margin-top: -8px; - right: 0; -} - - - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * - * Everything below this line is the same as demo_table.css. This file is - * required for 'cleanliness' of the markup - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * DataTables features - */ - -.dataTables_wrapper { - position: relative; - clear: both; -} - -.dataTables_processing { - position: absolute; - top: 0px; - left: 50%; - width: 250px; - margin-left: -125px; - border: 1px solid #ddd; - text-align: center; - color: #999; - font-size: 11px; - padding: 2px 0; -} - -.dataTables_length { - width: 40%; - float: left; -} - -.dataTables_filter { - width: 50%; - float: right; - text-align: right; -} - -.dataTables_info { - width: 50%; - float: left; -} - -.dataTables_paginate { - float: right; - text-align: right; -} - - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * DataTables display - */ -table.display { - margin: 0 auto; - width: 100%; - clear: both; - border-collapse: collapse; -} - -table.display tfoot th { - padding: 3px 0px 3px 10px; - font-weight: bold; - font-weight: normal; -} - -table.display tr.heading2 td { - border-bottom: 1px solid #aaa; -} - -table.display td { - padding: 3px 10px; -} - -table.display td.center { - text-align: center; -} - - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * DataTables sorting - */ - -.sorting_asc { - background: url('../images/sort_asc.png') no-repeat center right; -} - -.sorting_desc { - background: url('../images/sort_desc.png') no-repeat center right; -} - -.sorting { - background: url('../images/sort_both.png') no-repeat center right; -} - -.sorting_asc_disabled { - background: url('../images/sort_asc_disabled.png') no-repeat center right; -} - -.sorting_desc_disabled { - background: url('../images/sort_desc_disabled.png') no-repeat center right; -} - - - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * DataTables row classes - */ -table.display tr.odd.gradeA { - background-color: #ddffdd; -} - -table.display tr.even.gradeA { - background-color: #eeffee; -} - - - - -table.display tr.odd.gradeA { - background-color: #ddffdd; -} - -table.display tr.even.gradeA { - background-color: #eeffee; -} - -table.display tr.odd.gradeC { - background-color: #ddddff; -} - -table.display tr.even.gradeC { - background-color: #eeeeff; -} - -table.display tr.odd.gradeX { - background-color: #ffdddd; -} - -table.display tr.even.gradeX { - background-color: #ffeeee; -} - -table.display tr.odd.gradeU { - background-color: #ddd; -} - -table.display tr.even.gradeU { - background-color: #eee; -} - - -tr.odd { - background-color: #E2E4FF; -} - -tr.even { - background-color: white; -} - - - - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Misc - */ -.dataTables_scroll { - clear: both; -} - -.dataTables_scrollBody { - -webkit-overflow-scrolling: touch; -} - -.top, .bottom { - padding: 15px; - background-color: #F5F5F5; - border: 1px solid #CCCCCC; -} - -.top .dataTables_info { - float: none; -} - -.clear { - clear: both; -} - -.dataTables_empty { - text-align: center; -} - -tfoot input { - margin: 0.5em 0; - width: 100%; - color: #444; -} - -tfoot input.search_init { - color: #999; -} - -td.group { - background-color: #d1cfd0; - border-bottom: 2px solid #A19B9E; - border-top: 2px solid #A19B9E; -} - -td.details { - background-color: #d1cfd0; - border: 2px solid #A19B9E; -} - - -.example_alt_pagination div.dataTables_info { - width: 40%; -} - -.paging_full_numbers a.paginate_button, - .paging_full_numbers a.paginate_active { - border: 1px solid #aaa; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - padding: 2px 5px; - margin: 0 3px; - cursor: pointer; - *cursor: hand; - color: #333 !important; -} - -.paging_full_numbers a.paginate_button { - background-color: #ddd; -} - -.paging_full_numbers a.paginate_button:hover { - background-color: #ccc; - text-decoration: none !important; -} - -.paging_full_numbers a.paginate_active { - background-color: #99B3FF; -} - -table.display tr.even.row_selected td { - background-color: #B0BED9; -} - -table.display tr.odd.row_selected td { - background-color: #9FAFD1; -} - - -/* - * Sorting classes for columns - */ -/* For the standard odd/even */ -tr.odd td.sorting_1 { - background-color: #D3D6FF; -} - -tr.odd td.sorting_2 { - background-color: #DADCFF; -} - -tr.odd td.sorting_3 { - background-color: #E0E2FF; -} - -tr.even td.sorting_1 { - background-color: #EAEBFF; -} - -tr.even td.sorting_2 { - background-color: #F2F3FF; -} - -tr.even td.sorting_3 { - background-color: #F9F9FF; -} - - -/* For the Conditional-CSS grading rows */ -/* - Colour calculations (based off the main row colours) - Level 1: - dd > c4 - ee > d5 - Level 2: - dd > d1 - ee > e2 - */ -tr.odd.gradeA td.sorting_1 { - background-color: #c4ffc4; -} - -tr.odd.gradeA td.sorting_2 { - background-color: #d1ffd1; -} - -tr.odd.gradeA td.sorting_3 { - background-color: #d1ffd1; -} - -tr.even.gradeA td.sorting_1 { - background-color: #d5ffd5; -} - -tr.even.gradeA td.sorting_2 { - background-color: #e2ffe2; -} - -tr.even.gradeA td.sorting_3 { - background-color: #e2ffe2; -} - -tr.odd.gradeC td.sorting_1 { - background-color: #c4c4ff; -} - -tr.odd.gradeC td.sorting_2 { - background-color: #d1d1ff; -} - -tr.odd.gradeC td.sorting_3 { - background-color: #d1d1ff; -} - -tr.even.gradeC td.sorting_1 { - background-color: #d5d5ff; -} - -tr.even.gradeC td.sorting_2 { - background-color: #e2e2ff; -} - -tr.even.gradeC td.sorting_3 { - background-color: #e2e2ff; -} - -tr.odd.gradeX td.sorting_1 { - background-color: #ffc4c4; -} - -tr.odd.gradeX td.sorting_2 { - background-color: #ffd1d1; -} - -tr.odd.gradeX td.sorting_3 { - background-color: #ffd1d1; -} - -tr.even.gradeX td.sorting_1 { - background-color: #ffd5d5; -} - -tr.even.gradeX td.sorting_2 { - background-color: #ffe2e2; -} - -tr.even.gradeX td.sorting_3 { - background-color: #ffe2e2; -} - -tr.odd.gradeU td.sorting_1 { - background-color: #c4c4c4; -} - -tr.odd.gradeU td.sorting_2 { - background-color: #d1d1d1; -} - -tr.odd.gradeU td.sorting_3 { - background-color: #d1d1d1; -} - -tr.even.gradeU td.sorting_1 { - background-color: #d5d5d5; -} - -tr.even.gradeU td.sorting_2 { - background-color: #e2e2e2; -} - -tr.even.gradeU td.sorting_3 { - background-color: #e2e2e2; -} - - -/* - * Row highlighting example - */ -.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted { - background-color: #ECFFB3; -} - -.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted { - background-color: #E6FF99; -} \ No newline at end of file diff --git a/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/Readme.txt b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/Readme.txt new file mode 100644 index 00000000000..017d1d0d5f1 --- /dev/null +++ b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/Readme.txt @@ -0,0 +1,38 @@ +# AutoFill + +AutoFill gives an Excel like option to a DataTable to click and drag over multiple cells, filling in information over the selected cells and incrementing numbers as needed. Key features include: + +* Click and drag cell content insertion +* Automatic incrementing of numeric information +* Enable and disable on any column +* Detailed callback functions for customisation +* Support for both DataTables and browser window scrolling + + +# Installation + +To use AutoFill, first download DataTables ( http://datatables.net/download ) and place the unzipped AutoFill package into a `extensions` directory in the DataTables package. This will allow the pages in the examples to operate correctly. To see the examples running, open the `examples` directory in your web-browser. + + +# Basic usage + +AutoFill is initialised using the `$.fn.dataTable.AutoFill` constructor. For example: + +```js +$(document).ready( function () { + var table = $('#example').dataTable(); + new $.fn.dataTable.AutoFill( table ); +} ); +``` + + +# Documentation / support + +* Documentation: http://datatables.net/extensions/autofill/ +* DataTables support forums: http://datatables.net/forums + + +# GitHub + +If you fancy getting involved with the development of AutoFill and help make it better, please refer to its GitHub repo: https://github.com/DataTables/AutoFill + diff --git a/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/css/dataTables.autoFill.css b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/css/dataTables.autoFill.css new file mode 100644 index 00000000000..cab59b3a059 --- /dev/null +++ b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/css/dataTables.autoFill.css @@ -0,0 +1,24 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * AutoFill styles + */ + +div.AutoFill_filler { + display: none; + position: absolute; + height: 14px; + width: 14px; + background: url(../images/filler.png) no-repeat center center; + z-index: 1002; +} + +div.AutoFill_border { + display: none; + position: absolute; + background-color: #0063dc; + z-index: 1001; + + box-shadow: 0px 0px 5px #76b4ff; + -moz-box-shadow: 0px 0px 5px #76b4ff; + -webkit-box-shadow: 0px 0px 5px #76b4ff; +} + diff --git a/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/css/dataTables.autoFill.min.css b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/css/dataTables.autoFill.min.css new file mode 100644 index 00000000000..b485f2163fc --- /dev/null +++ b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/css/dataTables.autoFill.min.css @@ -0,0 +1 @@ +div.AutoFill_filler{display:none;position:absolute;height:14px;width:14px;background:url(../images/filler.png) no-repeat center center;z-index:1002}div.AutoFill_border{display:none;position:absolute;background-color:#0063dc;z-index:1001;box-shadow:0px 0px 5px #76b4ff;-moz-box-shadow:0px 0px 5px #76b4ff;-webkit-box-shadow:0px 0px 5px #76b4ff} diff --git a/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/images/filler.png b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/images/filler.png new file mode 100644 index 00000000000..f2af65d8c38 Binary files /dev/null and b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/images/filler.png differ diff --git a/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/js/dataTables.autoFill.js b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/js/dataTables.autoFill.js new file mode 100755 index 00000000000..6bbfa35b937 --- /dev/null +++ b/htdocs/includes/jquery/plugins/datatables/extensions/AutoFill/js/dataTables.autoFill.js @@ -0,0 +1,855 @@ +/*! AutoFill 1.2.1 + * ©2008-2014 SpryMedia Ltd - datatables.net/license + */ + +/** + * @summary AutoFill + * @description Add Excel like click and drag auto-fill options to DataTables + * @version 1.2.1 + * @file dataTables.autoFill.js + * @author SpryMedia Ltd (www.sprymedia.co.uk) + * @contact www.sprymedia.co.uk/contact + * @copyright Copyright 2010-2014 SpryMedia Ltd. + * + * This source file is free software, available under the following license: + * MIT license - http://datatables.net/license/mit + * + * This source file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. + * + * For details please refer to: http://www.datatables.net + */ + +(function( window, document, undefined ) { + +var factory = function( $, DataTable ) { +"use strict"; + +/** + * AutoFill provides Excel like auto-fill features for a DataTable + * + * @class AutoFill + * @constructor + * @param {object} oTD DataTables settings object + * @param {object} oConfig Configuration object for AutoFill + */ +var AutoFill = function( oDT, oConfig ) +{ + /* Sanity check that we are a new instance */ + if ( ! (this instanceof AutoFill) ) { + throw( "Warning: AutoFill must be initialised with the keyword 'new'" ); + } + + if ( ! $.fn.dataTableExt.fnVersionCheck('1.7.0') ) { + throw( "Warning: AutoFill requires DataTables 1.7 or greater"); + } + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Public class variables + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + this.c = {}; + + /** + * @namespace Settings object which contains customisable information for AutoFill instance + */ + this.s = { + /** + * @namespace Cached information about the little dragging icon (the filler) + */ + "filler": { + "height": 0, + "width": 0 + }, + + /** + * @namespace Cached information about the border display + */ + "border": { + "width": 2 + }, + + /** + * @namespace Store for live information for the current drag + */ + "drag": { + "startX": -1, + "startY": -1, + "startTd": null, + "endTd": null, + "dragging": false + }, + + /** + * @namespace Data cache for information that we need for scrolling the screen when we near + * the edges + */ + "screen": { + "interval": null, + "y": 0, + "height": 0, + "scrollTop": 0 + }, + + /** + * @namespace Data cache for the position of the DataTables scrolling element (when scrolling + * is enabled) + */ + "scroller": { + "top": 0, + "bottom": 0 + }, + + /** + * @namespace Information stored for each column. An array of objects + */ + "columns": [] + }; + + + /** + * @namespace Common and useful DOM elements for the class instance + */ + this.dom = { + "table": null, + "filler": null, + "borderTop": null, + "borderRight": null, + "borderBottom": null, + "borderLeft": null, + "currentTarget": null + }; + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Public class methods + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * Retreieve the settings object from an instance + * @method fnSettings + * @returns {object} AutoFill settings object + */ + this.fnSettings = function () { + return this.s; + }; + + + /* Constructor logic */ + this._fnInit( oDT, oConfig ); + return this; +}; + + + +AutoFill.prototype = { + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Private methods (they are of course public in JS, but recommended as private) + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * Initialisation + * @method _fnInit + * @param {object} dt DataTables settings object + * @param {object} config Configuration object for AutoFill + * @returns void + */ + "_fnInit": function ( dt, config ) + { + var + that = this, + i, iLen; + + // Use DataTables API to get the settings allowing selectors, instances + // etc to be used, or for backwards compatibility get from the old + // fnSettings method + this.s.dt = DataTable.Api ? + new DataTable.Api( dt ).settings()[0] : + dt.fnSettings(); + this.s.init = config || {}; + this.dom.table = this.s.dt.nTable; + + $.extend( true, this.c, AutoFill.defaults, config ); + + /* Add and configure the columns */ + this._initColumns(); + + /* Auto Fill click and drag icon */ + var filler = $('
', { + 'class': 'AutoFill_filler' + } ) + .appendTo( 'body' ); + this.dom.filler = filler[0]; + + // Get the height / width of the click element + this.s.filler.height = filler.height(); + this.s.filler.width = filler.width(); + filler[0].style.display = "none"; + + /* Border display - one div for each side. We can't just use a single + * one with a border, as we want the events to effectively pass through + * the transparent bit of the box + */ + var border; + var appender = document.body; + if ( that.s.dt.oScroll.sY !== "" ) { + that.s.dt.nTable.parentNode.style.position = "relative"; + appender = that.s.dt.nTable.parentNode; + } + + border = $('
', { + "class": "AutoFill_border" + } ); + this.dom.borderTop = border.clone().appendTo( appender )[0]; + this.dom.borderRight = border.clone().appendTo( appender )[0]; + this.dom.borderBottom = border.clone().appendTo( appender )[0]; + this.dom.borderLeft = border.clone().appendTo( appender )[0]; + + /* Events */ + filler.on( 'mousedown.DTAF', function (e) { + this.onselectstart = function() { return false; }; + that._fnFillerDragStart.call( that, e ); + return false; + } ); + + $('tbody', this.dom.table).on( + 'mouseover.DTAF mouseout.DTAF', + '>tr>td, >tr>th', + function (e) { + that._fnFillerDisplay.call( that, e ); + } + ); + + $(this.dom.table).on( 'destroy.dt.DTAF', function () { + filler.off( 'mousedown.DTAF' ).remove(); + $('tbody', this.dom.table).off( 'mouseover.DTAF mouseout.DTAF' ); + } ); + }, + + + _initColumns: function ( ) + { + var that = this; + var i, ien; + var dt = this.s.dt; + var config = this.s.init; + + for ( i=0, ien=dt.aoColumns.length ; i offsetEnd.left) { + x1 = offsetEnd.left - border; + x2 = offsetStart.left + $(nStart).outerWidth(); + width = offsetStart.left + $(nStart).outerWidth() - offsetEnd.left + (2*border); + } + + if ( this.s.dt.oScroll.sY !== "" ) + { + /* The border elements are inside the DT scroller - so position relative to that */ + var + offsetScroll = $(this.s.dt.nTable.parentNode).offset(), + scrollTop = $(this.s.dt.nTable.parentNode).scrollTop(), + scrollLeft = $(this.s.dt.nTable.parentNode).scrollLeft(); + + x1 -= offsetScroll.left - scrollLeft; + x2 -= offsetScroll.left - scrollLeft; + y1 -= offsetScroll.top - scrollTop; + y2 -= offsetScroll.top - scrollTop; + } + + /* Top */ + oStyle = this.dom.borderTop.style; + oStyle.top = y1+"px"; + oStyle.left = x1+"px"; + oStyle.height = this.s.border.width+"px"; + oStyle.width = width+"px"; + + /* Bottom */ + oStyle = this.dom.borderBottom.style; + oStyle.top = y2+"px"; + oStyle.left = x1+"px"; + oStyle.height = this.s.border.width+"px"; + oStyle.width = width+"px"; + + /* Left */ + oStyle = this.dom.borderLeft.style; + oStyle.top = y1+"px"; + oStyle.left = x1+"px"; + oStyle.height = height+"px"; + oStyle.width = this.s.border.width+"px"; + + /* Right */ + oStyle = this.dom.borderRight.style; + oStyle.top = y1+"px"; + oStyle.left = x2+"px"; + oStyle.height = height+"px"; + oStyle.width = this.s.border.width+"px"; + }, + + + /** + * Mouse down event handler for starting a drag + * @method _fnFillerDragStart + * @param {Object} e Event object + * @returns void + */ + "_fnFillerDragStart": function (e) + { + var that = this; + var startingTd = this.dom.currentTarget; + + this.s.drag.dragging = true; + + that.dom.borderTop.style.display = "block"; + that.dom.borderRight.style.display = "block"; + that.dom.borderBottom.style.display = "block"; + that.dom.borderLeft.style.display = "block"; + + var coords = this._fnTargetCoords( startingTd ); + this.s.drag.startX = coords.x; + this.s.drag.startY = coords.y; + + this.s.drag.startTd = startingTd; + this.s.drag.endTd = startingTd; + + this._fnUpdateBorder( startingTd, startingTd ); + + $(document).bind('mousemove.AutoFill', function (e) { + that._fnFillerDragMove.call( that, e ); + } ); + + $(document).bind('mouseup.AutoFill', function (e) { + that._fnFillerFinish.call( that, e ); + } ); + + /* Scrolling information cache */ + this.s.screen.y = e.pageY; + this.s.screen.height = $(window).height(); + this.s.screen.scrollTop = $(document).scrollTop(); + + if ( this.s.dt.oScroll.sY !== "" ) + { + this.s.scroller.top = $(this.s.dt.nTable.parentNode).offset().top; + this.s.scroller.bottom = this.s.scroller.top + $(this.s.dt.nTable.parentNode).height(); + } + + /* Scrolling handler - we set an interval (which is cancelled on mouse up) which will fire + * regularly and see if we need to do any scrolling + */ + this.s.screen.interval = setInterval( function () { + var iScrollTop = $(document).scrollTop(); + var iScrollDelta = iScrollTop - that.s.screen.scrollTop; + that.s.screen.y += iScrollDelta; + + if ( that.s.screen.height - that.s.screen.y + iScrollTop < 50 ) + { + $('html, body').animate( { + "scrollTop": iScrollTop + 50 + }, 240, 'linear' ); + } + else if ( that.s.screen.y - iScrollTop < 50 ) + { + $('html, body').animate( { + "scrollTop": iScrollTop - 50 + }, 240, 'linear' ); + } + + if ( that.s.dt.oScroll.sY !== "" ) + { + if ( that.s.screen.y > that.s.scroller.bottom - 50 ) + { + $(that.s.dt.nTable.parentNode).animate( { + "scrollTop": $(that.s.dt.nTable.parentNode).scrollTop() + 50 + }, 240, 'linear' ); + } + else if ( that.s.screen.y < that.s.scroller.top + 50 ) + { + $(that.s.dt.nTable.parentNode).animate( { + "scrollTop": $(that.s.dt.nTable.parentNode).scrollTop() - 50 + }, 240, 'linear' ); + } + } + }, 250 ); + }, + + + /** + * Mouse move event handler for during a move. See if we want to update the display based on the + * new cursor position + * @method _fnFillerDragMove + * @param {Object} e Event object + * @returns void + */ + "_fnFillerDragMove": function (e) + { + if ( e.target && e.target.nodeName.toUpperCase() == "TD" && + e.target != this.s.drag.endTd ) + { + var coords = this._fnTargetCoords( e.target ); + + if ( this.c.mode == "y" && coords.x != this.s.drag.startX ) + { + e.target = $('tbody>tr:eq('+coords.y+')>td:eq('+this.s.drag.startX+')', this.dom.table)[0]; + } + if ( this.c.mode == "x" && coords.y != this.s.drag.startY ) + { + e.target = $('tbody>tr:eq('+this.s.drag.startY+')>td:eq('+coords.x+')', this.dom.table)[0]; + } + + if ( this.c.mode == "either") + { + if(coords.x != this.s.drag.startX ) + { + e.target = $('tbody>tr:eq('+this.s.drag.startY+')>td:eq('+coords.x+')', this.dom.table)[0]; + } + else if ( coords.y != this.s.drag.startY ) { + e.target = $('tbody>tr:eq('+coords.y+')>td:eq('+this.s.drag.startX+')', this.dom.table)[0]; + } + } + + // update coords + if ( this.c.mode !== "both" ) { + coords = this._fnTargetCoords( e.target ); + } + + var drag = this.s.drag; + drag.endTd = e.target; + + if ( coords.y >= this.s.drag.startY ) { + this._fnUpdateBorder( drag.startTd, drag.endTd ); + } + else { + this._fnUpdateBorder( drag.endTd, drag.startTd ); + } + this._fnFillerPosition( e.target ); + } + + /* Update the screen information so we can perform scrolling */ + this.s.screen.y = e.pageY; + this.s.screen.scrollTop = $(document).scrollTop(); + + if ( this.s.dt.oScroll.sY !== "" ) + { + this.s.scroller.scrollTop = $(this.s.dt.nTable.parentNode).scrollTop(); + this.s.scroller.top = $(this.s.dt.nTable.parentNode).offset().top; + this.s.scroller.bottom = this.s.scroller.top + $(this.s.dt.nTable.parentNode).height(); + } + }, + + + /** + * Mouse release handler - end the drag and take action to update the cells with the needed values + * @method _fnFillerFinish + * @param {Object} e Event object + * @returns void + */ + "_fnFillerFinish": function (e) + { + var that = this, i, iLen, j; + + $(document).unbind('mousemove.AutoFill mouseup.AutoFill'); + + this.dom.borderTop.style.display = "none"; + this.dom.borderRight.style.display = "none"; + this.dom.borderBottom.style.display = "none"; + this.dom.borderLeft.style.display = "none"; + + this.s.drag.dragging = false; + + clearInterval( this.s.screen.interval ); + + var cells = []; + var table = this.dom.table; + var coordsStart = this._fnTargetCoords( this.s.drag.startTd ); + var coordsEnd = this._fnTargetCoords( this.s.drag.endTd ); + var columnIndex = function ( visIdx ) { + return that.s.dt.oApi._fnVisibleToColumnIndex( that.s.dt, visIdx ); + }; + + // xxx - urgh - there must be a way of reducing this... + if ( coordsStart.y <= coordsEnd.y ) { + for ( i=coordsStart.y ; i<=coordsEnd.y ; i++ ) { + if ( coordsStart.x <= coordsEnd.x ) { + for ( j=coordsStart.x ; j<=coordsEnd.x ; j++ ) { + cells.push( { + node: $('tbody>tr:eq('+i+')>td:eq('+j+')', table)[0], + x: j - coordsStart.x, + y: i - coordsStart.y, + colIdx: columnIndex( j ) + } ); + } + } + else { + for ( j=coordsStart.x ; j>=coordsEnd.x ; j-- ) { + cells.push( { + node: $('tbody>tr:eq('+i+')>td:eq('+j+')', table)[0], + x: j - coordsStart.x, + y: i - coordsStart.y, + colIdx: columnIndex( j ) + } ); + } + } + } + } + else { + for ( i=coordsStart.y ; i>=coordsEnd.y ; i-- ) { + if ( coordsStart.x <= coordsEnd.x ) { + for ( j=coordsStart.x ; j<=coordsEnd.x ; j++ ) { + cells.push( { + node: $('tbody>tr:eq('+i+')>td:eq('+j+')', table)[0], + x: j - coordsStart.x, + y: i - coordsStart.y, + colIdx: columnIndex( j ) + } ); + } + } + else { + for ( j=coordsStart.x ; j>=coordsEnd.x ; j-- ) { + cells.push( { + node: $('tbody>tr:eq('+i+')>td:eq('+j+')', table)[0], + x: coordsStart.x - j, + y: coordsStart.y - i, + colIdx: columnIndex( j ) + } ); + } + } + } + } + + // An auto-fill requires 2 or more cells + if ( cells.length <= 1 ) { + return; + } + + var edited = []; + var previous; + + for ( i=0, iLen=cells.length ; i",{"class":"AutoFill_filler"}).appendTo("body");this.dom.filler=e[0];this.s.filler.height=e.height();this.s.filler.width=e.width();e[0].style.display= +"none";var g,f=j.body;""!==a.s.dt.oScroll.sY&&(a.s.dt.nTable.parentNode.style.position="relative",f=a.s.dt.nTable.parentNode);g=c("
",{"class":"AutoFill_border"});this.dom.borderTop=g.clone().appendTo(f)[0];this.dom.borderRight=g.clone().appendTo(f)[0];this.dom.borderBottom=g.clone().appendTo(f)[0];this.dom.borderLeft=g.clone().appendTo(f)[0];e.on("mousedown.DTAF",function(b){this.onselectstart=function(){return false};a._fnFillerDragStart.call(a,b);return false});c("tbody",this.dom.table).on("mouseover.DTAF mouseout.DTAF", +">tr>td, >tr>th",function(b){a._fnFillerDisplay.call(a,b)});c(this.dom.table).on("destroy.dt.DTAF",function(){e.off("mousedown.DTAF").remove();c("tbody",this.dom.table).off("mouseover.DTAF mouseout.DTAF")})},_initColumns:function(){var d=this,b,a,e=this.s.dt,g=this.s.init;b=0;for(a=e.aoColumns.length;bg.left&&(f=g.left-a,i=e.left+c(d).outerWidth(),j=e.left+c(d).outerWidth()-g.left+2*a);""!==this.s.dt.oScroll.sY&&(a=c(this.s.dt.nTable.parentNode).offset(),e=c(this.s.dt.nTable.parentNode).scrollTop(), +g=c(this.s.dt.nTable.parentNode).scrollLeft(),f-=a.left-g,i-=a.left-g,n-=a.top-e,h-=a.top-e);a=this.dom.borderTop.style;a.top=n+"px";a.left=f+"px";a.height=this.s.border.width+"px";a.width=j+"px";a=this.dom.borderBottom.style;a.top=h+"px";a.left=f+"px";a.height=this.s.border.width+"px";a.width=j+"px";a=this.dom.borderLeft.style;a.top=n+"px";a.left=f+"px";a.height=k+"px";a.width=this.s.border.width+"px";a=this.dom.borderRight.style;a.top=n+"px";a.left=i+"px";a.height=k+"px";a.width=this.s.border.width+ +"px"},_fnFillerDragStart:function(d){var b=this,a=this.dom.currentTarget;this.s.drag.dragging=!0;b.dom.borderTop.style.display="block";b.dom.borderRight.style.display="block";b.dom.borderBottom.style.display="block";b.dom.borderLeft.style.display="block";var e=this._fnTargetCoords(a);this.s.drag.startX=e.x;this.s.drag.startY=e.y;this.s.drag.startTd=a;this.s.drag.endTd=a;this._fnUpdateBorder(a,a);c(j).bind("mousemove.AutoFill",function(a){b._fnFillerDragMove.call(b,a)});c(j).bind("mouseup.AutoFill", +function(a){b._fnFillerFinish.call(b,a)});this.s.screen.y=d.pageY;this.s.screen.height=c(o).height();this.s.screen.scrollTop=c(j).scrollTop();""!==this.s.dt.oScroll.sY&&(this.s.scroller.top=c(this.s.dt.nTable.parentNode).offset().top,this.s.scroller.bottom=this.s.scroller.top+c(this.s.dt.nTable.parentNode).height());this.s.screen.interval=setInterval(function(){var a=c(j).scrollTop();b.s.screen.y=b.s.screen.y+(a-b.s.screen.scrollTop);b.s.screen.height-b.s.screen.y+a<50?c("html, body").animate({scrollTop:a+ +50},240,"linear"):b.s.screen.y-a<50&&c("html, body").animate({scrollTop:a-50},240,"linear");b.s.dt.oScroll.sY!==""&&(b.s.screen.y>b.s.scroller.bottom-50?c(b.s.dt.nTable.parentNode).animate({scrollTop:c(b.s.dt.nTable.parentNode).scrollTop()+50},240,"linear"):b.s.screen.ytr:eq("+b.y+")>td:eq("+this.s.drag.startX+")",this.dom.table)[0]);"x"==this.c.mode&&b.y!=this.s.drag.startY&&(d.target=c("tbody>tr:eq("+this.s.drag.startY+")>td:eq("+b.x+")",this.dom.table)[0]);"either"==this.c.mode&&(b.x!=this.s.drag.startX?d.target=c("tbody>tr:eq("+this.s.drag.startY+")>td:eq("+b.x+")",this.dom.table)[0]:b.y!=this.s.drag.startY&&(d.target=c("tbody>tr:eq("+b.y+")>td:eq("+this.s.drag.startX+ +")",this.dom.table)[0]));"both"!==this.c.mode&&(b=this._fnTargetCoords(d.target));var a=this.s.drag;a.endTd=d.target;b.y>=this.s.drag.startY?this._fnUpdateBorder(a.startTd,a.endTd):this._fnUpdateBorder(a.endTd,a.startTd);this._fnFillerPosition(d.target)}this.s.screen.y=d.pageY;this.s.screen.scrollTop=c(j).scrollTop();""!==this.s.dt.oScroll.sY&&(this.s.scroller.scrollTop=c(this.s.dt.nTable.parentNode).scrollTop(),this.s.scroller.top=c(this.s.dt.nTable.parentNode).offset().top,this.s.scroller.bottom= +this.s.scroller.top+c(this.s.dt.nTable.parentNode).height())},_fnFillerFinish:function(){var d=this,b,a;c(j).unbind("mousemove.AutoFill mouseup.AutoFill");this.dom.borderTop.style.display="none";this.dom.borderRight.style.display="none";this.dom.borderBottom.style.display="none";this.dom.borderLeft.style.display="none";this.s.drag.dragging=!1;clearInterval(this.s.screen.interval);var e=[],g=this.dom.table,f=this._fnTargetCoords(this.s.drag.startTd),i=this._fnTargetCoords(this.s.drag.endTd),h=function(a){return d.s.dt.oApi._fnVisibleToColumnIndex(d.s.dt, +a)};if(f.y<=i.y)for(b=f.y;b<=i.y;b++)if(f.x<=i.x)for(a=f.x;a<=i.x;a++)e.push({node:c("tbody>tr:eq("+b+")>td:eq("+a+")",g)[0],x:a-f.x,y:b-f.y,colIdx:h(a)});else for(a=f.x;a>=i.x;a--)e.push({node:c("tbody>tr:eq("+b+")>td:eq("+a+")",g)[0],x:a-f.x,y:b-f.y,colIdx:h(a)});else for(b=f.y;b>=i.y;b--)if(f.x<=i.x)for(a=f.x;a<=i.x;a++)e.push({node:c("tbody>tr:eq("+b+")>td:eq("+a+")",g)[0],x:a-f.x,y:b-f.y,colIdx:h(a)});else for(a=f.x;a>=i.x;a--)e.push({node:c("tbody>tr:eq("+b+")>td:eq("+a+")",g)[0],x:f.x-a,y:f.y- +b,colIdx:h(a)});if(!(1>=e.length)){var g=[],m;b=0;for(a=e.length;bg||0>f?-1:1)):a===m?b:a}}};return h};"function"===typeof define&&define.amd? +define(["jquery","datatables"],l):"object"===typeof exports?l(require("jquery"),require("datatables")):jQuery&&!jQuery.fn.dataTable.AutoFill&&l(jQuery,jQuery.fn.dataTable)})(window,document); diff --git a/htdocs/includes/jquery/plugins/datatables/extras/ColVis/License.txt b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/License.txt similarity index 100% rename from htdocs/includes/jquery/plugins/datatables/extras/ColVis/License.txt rename to htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/License.txt diff --git a/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/Readme.md b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/Readme.md new file mode 100644 index 00000000000..217dd60e2c8 --- /dev/null +++ b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/Readme.md @@ -0,0 +1,39 @@ +# ColReorder + +ColReorder adds the ability for the end user to click and drag column headers to reorder a table as they see fit, to DataTables. Key features include: + +* Very easy integration with DataTables +* Tight integration with all other DataTables plug-ins +* The ability to exclude the first (or more) column from being movable +* Predefine a column order +* Save staving integration with DataTables + + +# Installation + +To use ColReorder, first download DataTables ( http://datatables.net/download ) and place the unzipped ColReorder package into a `extensions` directory in the DataTables package. This will allow the pages in the examples to operate correctly. To see the examples running, open the `examples` directory in your web-browser. + + +# Basic usage + +ColReorder is initialised using the `$.fn.dataTable.ColReorder` constructor. For example: + +```js +$(document).ready( function () { + $('#example').DataTable(); + + new $.fn.dataTable.ColReorder( table ); +} ); +``` + + +# Documentation / support + +* Documentation: http://datatables.net/extensions/colreorder/ +* DataTables support forums: http://datatables.net/forums + + +# GitHub + +If you fancy getting involved with the development of ColReorder and help make it better, please refer to its GitHub repo: https://github.com/DataTables/ColReorder + diff --git a/htdocs/includes/jquery/plugins/datatables/extras/ColReorder/css/ColReorder.css b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/css/dataTables.colReorder.css similarity index 50% rename from htdocs/includes/jquery/plugins/datatables/extras/ColReorder/css/ColReorder.css rename to htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/css/dataTables.colReorder.css index 9c597e34871..bdd6aa0b9ad 100644 --- a/htdocs/includes/jquery/plugins/datatables/extras/ColReorder/css/ColReorder.css +++ b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/css/dataTables.colReorder.css @@ -3,7 +3,7 @@ */ table.DTCR_clonedTable { - background-color: white; + background-color: rgba(255, 255, 255, 0.7); z-index: 202; } @@ -11,11 +11,4 @@ div.DTCR_pointer { width: 1px; background-color: #0259C4; z-index: 201; -} - -body.alt div.DTCR_pointer { - margin-top: -15px; - margin-left: -9px; - width: 18px; - background: url('../images/insert.png') no-repeat top left; } \ No newline at end of file diff --git a/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/css/dataTables.colReorder.min.css b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/css/dataTables.colReorder.min.css new file mode 100644 index 00000000000..77b230e0136 --- /dev/null +++ b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/css/dataTables.colReorder.min.css @@ -0,0 +1 @@ +table.DTCR_clonedTable{background-color:rgba(255,255,255,0.7);z-index:202}div.DTCR_pointer{width:1px;background-color:#0259C4;z-index:201} diff --git a/htdocs/includes/jquery/plugins/datatables/extras/ColReorder/images/insert.png b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/images/insert.png similarity index 100% rename from htdocs/includes/jquery/plugins/datatables/extras/ColReorder/images/insert.png rename to htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/images/insert.png diff --git a/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/js/dataTables.colReorder.js b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/js/dataTables.colReorder.js new file mode 100755 index 00000000000..c3b2f1e2adc --- /dev/null +++ b/htdocs/includes/jquery/plugins/datatables/extensions/ColReorder/js/dataTables.colReorder.js @@ -0,0 +1,1372 @@ +/*! ColReorder 1.1.3 + * ©2010-2014 SpryMedia Ltd - datatables.net/license + */ + +/** + * @summary ColReorder + * @description Provide the ability to reorder columns in a DataTable + * @version 1.1.3 + * @file dataTables.colReorder.js + * @author SpryMedia Ltd (www.sprymedia.co.uk) + * @contact www.sprymedia.co.uk/contact + * @copyright Copyright 2010-2014 SpryMedia Ltd. + * + * This source file is free software, available under the following license: + * MIT license - http://datatables.net/license/mit + * + * This source file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. + * + * For details please refer to: http://www.datatables.net + */ + +(function(window, document, undefined) { + + +/** + * Switch the key value pairing of an index array to be value key (i.e. the old value is now the + * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ]. + * @method fnInvertKeyValues + * @param array aIn Array to switch around + * @returns array + */ +function fnInvertKeyValues( aIn ) +{ + var aRet=[]; + for ( var i=0, iLen=aIn.length ; i= iCols ) + { + this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom ); + return; + } + + if ( iTo < 0 || iTo >= iCols ) + { + this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo ); + return; + } + + /* + * Calculate the new column array index, so we have a mapping between the old and new + */ + var aiMapping = []; + for ( i=0, iLen=iCols ; i this.s.fixed-1 && i < iLen - this.s.fixedRight ) + { + this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh ); + } + + /* Mark the original column order for later reference */ + this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i; + } + + /* State saving */ + this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) { + that._fnStateSave.call( that, oData ); + }, "ColReorder_State" ); + + /* An initial column order has been specified */ + var aiOrder = null; + if ( this.s.init.aiOrder ) + { + aiOrder = this.s.init.aiOrder.slice(); + } + + /* State loading, overrides the column order given */ + if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' && + this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length ) + { + aiOrder = this.s.dt.oLoadedState.ColReorder; + } + + /* If we have an order to apply - do so */ + if ( aiOrder ) + { + /* We might be called during or after the DataTables initialisation. If before, then we need + * to wait until the draw is done, if after, then do what we need to do right away + */ + if ( !that.s.dt._bInitComplete ) + { + var bDone = false; + this.s.dt.aoDrawCallback.push( { + "fn": function () { + if ( !that.s.dt._bInitComplete && !bDone ) + { + bDone = true; + var resort = fnInvertKeyValues( aiOrder ); + that._fnOrderColumns.call( that, resort ); + } + }, + "sName": "ColReorder_Pre" + } ); + } + else + { + var resort = fnInvertKeyValues( aiOrder ); + that._fnOrderColumns.call( that, resort ); + } + } + else { + this._fnSetColumnIndexes(); + } + }, + + + /** + * Set the column order from an array + * @method _fnOrderColumns + * @param array a An array of integers which dictate the column order that should be applied + * @returns void + * @private + */ + "_fnOrderColumns": function ( a ) + { + if ( a.length != this.s.dt.aoColumns.length ) + { + this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "ColReorder - array reorder does not "+ + "match known number of columns. Skipping." ); + return; + } + + for ( var i=0, iLen=a.length ; i
') + .addClass( 'DTCR_pointer' ) + .css( { + position: 'absolute', + top: scrolling ? + $('div.dataTables_scroll', this.s.dt.nTableWrapper).offset().top : + $(this.s.dt.nTable).offset().top, + height : scrolling ? + $('div.dataTables_scroll', this.s.dt.nTableWrapper).height() : + $(this.s.dt.nTable).height() + } ) + .appendTo( 'body' ); + }, + + /** + * Clean up ColReorder memory references and event handlers + * @method _fnDestroy + * @returns void + * @private + */ + "_fnDestroy": function () + { + var i, iLen; + + for ( i=0, iLen=this.s.dt.aoDrawCallback.length ; ib||b>=n)this.oApi._fnLog(a,1,"ColReorder 'from' index is out of bounds: "+b);else if(0>e||e>=n)this.oApi._fnLog(a,1,"ColReorder 'to' index is out of bounds: "+e);else{f=[];c=0;for(g=n;cthis.s.fixed-1&&eMath.pow(Math.pow(a.pageX-this.s.mouse.startX,2)+Math.pow(a.pageY-this.s.mouse.startY,2),0.5))return;this._fnCreateDragNode()}this.dom.drag.css({left:a.pageX-this.s.mouse.offsetX,top:a.pageY-this.s.mouse.offsetY});for(var b=!1,e=this.s.mouse.toIndex,d=1,c=this.s.aoTargets.length;d
").addClass("DTCR_pointer").css({position:"absolute",top:a?d("div.dataTables_scroll",this.s.dt.nTableWrapper).offset().top:d(this.s.dt.nTable).offset().top,height:a?d("div.dataTables_scroll",this.s.dt.nTableWrapper).height(): +d(this.s.dt.nTable).height()}).appendTo("body")},_fnDestroy:function(){var a,b;a=0;for(b=this.s.dt.aoDrawCallback.length;a + * @default [] + */ + "aiOuterWidths": [], + + /** + * Original inner widths of the columns as rendered by DataTables - used to apply widths + * to the columns + * @type array. + * @default [] + */ + "aiInnerWidths": [] + }; + + + /** + * DOM elements used by the class instance + * @namespace + * @private + * + */ + this.dom = { + /** + * DataTables scrolling element + * @type node + * @default null + */ + "scroller": null, + + /** + * DataTables header table + * @type node + * @default null + */ + "header": null, + + /** + * DataTables body table + * @type node + * @default null + */ + "body": null, + + /** + * DataTables footer table + * @type node + * @default null + */ + "footer": null, + + /** + * Display grid elements + * @namespace + */ + "grid": { + /** + * Grid wrapper. This is the container element for the 3x3 grid + * @type node + * @default null + */ + "wrapper": null, + + /** + * DataTables scrolling element. This element is the DataTables + * component in the display grid (making up the main table - i.e. + * not the fixed columns). + * @type node + * @default null + */ + "dt": null, + + /** + * Left fixed column grid components + * @namespace + */ + "left": { + "wrapper": null, + "head": null, + "body": null, + "foot": null + }, + + /** + * Right fixed column grid components + * @namespace + */ + "right": { + "wrapper": null, + "head": null, + "body": null, + "foot": null + } + }, + + /** + * Cloned table nodes + * @namespace + */ + "clone": { + /** + * Left column cloned table nodes + * @namespace + */ + "left": { + /** + * Cloned header table + * @type node + * @default null + */ + "header": null, + + /** + * Cloned body table + * @type node + * @default null + */ + "body": null, + + /** + * Cloned footer table + * @type node + * @default null + */ + "footer": null + }, + + /** + * Right column cloned table nodes + * @namespace + */ + "right": { + /** + * Cloned header table + * @type node + * @default null + */ + "header": null, + + /** + * Cloned body table + * @type node + * @default null + */ + "body": null, + + /** + * Cloned footer table + * @type node + * @default null + */ + "footer": null + } + } + }; + + /* Attach the instance to the DataTables instance so it can be accessed easily */ + dtSettings._oFixedColumns = this; + + /* Let's do it */ + if ( ! dtSettings._bInitComplete ) + { + dtSettings.oApi._fnCallbackReg( dtSettings, 'aoInitComplete', function () { + that._fnConstruct( init ); + }, 'FixedColumns' ); + } + else + { + this._fnConstruct( init ); + } +}; + + + +FixedColumns.prototype = /** @lends FixedColumns.prototype */{ + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Public methods + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * Update the fixed columns - including headers and footers. Note that FixedColumns will + * automatically update the display whenever the host DataTable redraws. + * @returns {void} + * @example + * var table = $('#example').dataTable( { + * "scrollX": "100%" + * } ); + * var fc = new $.fn.dataTable.fixedColumns( table ); + * + * // at some later point when the table has been manipulated.... + * fc.fnUpdate(); + */ + "fnUpdate": function () + { + this._fnDraw( true ); + }, + + + /** + * Recalculate the resizes of the 3x3 grid that FixedColumns uses for display of the table. + * This is useful if you update the width of the table container. Note that FixedColumns will + * perform this function automatically when the window.resize event is fired. + * @returns {void} + * @example + * var table = $('#example').dataTable( { + * "scrollX": "100%" + * } ); + * var fc = new $.fn.dataTable.fixedColumns( table ); + * + * // Resize the table container and then have FixedColumns adjust its layout.... + * $('#content').width( 1200 ); + * fc.fnRedrawLayout(); + */ + "fnRedrawLayout": function () + { + this._fnColCalc(); + this._fnGridLayout(); + this.fnUpdate(); + }, + + + /** + * Mark a row such that it's height should be recalculated when using 'semiauto' row + * height matching. This function will have no effect when 'none' or 'auto' row height + * matching is used. + * @param {Node} nTr TR element that should have it's height recalculated + * @returns {void} + * @example + * var table = $('#example').dataTable( { + * "scrollX": "100%" + * } ); + * var fc = new $.fn.dataTable.fixedColumns( table ); + * + * // manipulate the table - mark the row as needing an update then update the table + * // this allows the redraw performed by DataTables fnUpdate to recalculate the row + * // height + * fc.fnRecalculateHeight(); + * table.fnUpdate( $('#example tbody tr:eq(0)')[0], ["insert date", 1, 2, 3 ... ]); + */ + "fnRecalculateHeight": function ( nTr ) + { + delete nTr._DTTC_iHeight; + nTr.style.height = 'auto'; + }, + + + /** + * Set the height of a given row - provides cross browser compatibility + * @param {Node} nTarget TR element that should have it's height recalculated + * @param {int} iHeight Height in pixels to set + * @returns {void} + * @example + * var table = $('#example').dataTable( { + * "scrollX": "100%" + * } ); + * var fc = new $.fn.dataTable.fixedColumns( table ); + * + * // You may want to do this after manipulating a row in the fixed column + * fc.fnSetRowHeight( $('#example tbody tr:eq(0)')[0], 50 ); + */ + "fnSetRowHeight": function ( nTarget, iHeight ) + { + nTarget.style.height = iHeight+"px"; + }, + + + /** + * Get data index information about a row or cell in the table body. + * This function is functionally identical to fnGetPosition in DataTables, + * taking the same parameter (TH, TD or TR node) and returning exactly the + * the same information (data index information). THe difference between + * the two is that this method takes into account the fixed columns in the + * table, so you can pass in nodes from the master table, or the cloned + * tables and get the index position for the data in the main table. + * @param {node} node TR, TH or TD element to get the information about + * @returns {int} If nNode is given as a TR, then a single index is + * returned, or if given as a cell, an array of [row index, column index + * (visible), column index (all)] is given. + */ + "fnGetPosition": function ( node ) + { + var idx; + var inst = this.s.dt.oInstance; + + if ( ! $(node).parents('.DTFC_Cloned').length ) + { + // Not in a cloned table + return inst.fnGetPosition( node ); + } + else + { + // Its in the cloned table, so need to look up position + if ( node.nodeName.toLowerCase() === 'tr' ) { + idx = $(node).index(); + return inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] ); + } + else + { + var colIdx = $(node).index(); + idx = $(node.parentNode).index(); + var row = inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] ); + + return [ + row, + colIdx, + inst.oApi._fnVisibleToColumnIndex( this.s.dt, colIdx ) + ]; + } + } + }, + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Private methods (they are of course public in JS, but recommended as private) + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * Initialisation for FixedColumns + * @param {Object} oInit User settings for initialisation + * @returns {void} + * @private + */ + "_fnConstruct": function ( oInit ) + { + var i, iLen, iWidth, + that = this; + + /* Sanity checking */ + if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' || + this.s.dt.oInstance.fnVersionCheck( '1.8.0' ) !== true ) + { + alert( "FixedColumns "+FixedColumns.VERSION+" required DataTables 1.8.0 or later. "+ + "Please upgrade your DataTables installation" ); + return; + } + + if ( this.s.dt.oScroll.sX === "" ) + { + this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+ + "x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+ + "column fixing when scrolling is not enabled" ); + return; + } + + /* Apply the settings from the user / defaults */ + this.s = $.extend( true, this.s, FixedColumns.defaults, oInit ); + + /* Set up the DOM as we need it and cache nodes */ + var classes = this.s.dt.oClasses; + this.dom.grid.dt = $(this.s.dt.nTable).parents('div.'+classes.sScrollWrapper)[0]; + this.dom.scroller = $('div.'+classes.sScrollBody, this.dom.grid.dt )[0]; + + /* Set up the DOM that we want for the fixed column layout grid */ + this._fnColCalc(); + this._fnGridSetup(); + + /* Event handlers */ + var mouseController; + + // When the body is scrolled - scroll the left and right columns + $(this.dom.scroller) + .on( 'mouseover.DTFC touchstart.DTFC', function () { + mouseController = 'main'; + } ) + .on( 'scroll.DTFC', function () { + if ( mouseController === 'main' ) { + if ( that.s.iLeftColumns > 0 ) { + that.dom.grid.left.liner.scrollTop = that.dom.scroller.scrollTop; + } + if ( that.s.iRightColumns > 0 ) { + that.dom.grid.right.liner.scrollTop = that.dom.scroller.scrollTop; + } + } + } ); + + var wheelType = 'onwheel' in document.createElement('div') ? + 'wheel.DTFC' : + 'mousewheel.DTFC'; + + if ( that.s.iLeftColumns > 0 ) { + // When scrolling the left column, scroll the body and right column + $(that.dom.grid.left.liner) + .on( 'mouseover.DTFC touchstart.DTFC', function () { + mouseController = 'left'; + } ) + .on( 'scroll.DTFC', function () { + if ( mouseController === 'left' ) { + that.dom.scroller.scrollTop = that.dom.grid.left.liner.scrollTop; + if ( that.s.iRightColumns > 0 ) { + that.dom.grid.right.liner.scrollTop = that.dom.grid.left.liner.scrollTop; + } + } + } ) + .on( wheelType, function(e) { // xxx update the destroy as well + // Pass horizontal scrolling through + var xDelta = e.type === 'wheel' ? + -e.originalEvent.deltaX : + e.originalEvent.wheelDeltaX; + that.dom.scroller.scrollLeft -= xDelta; + } ); + } + + if ( that.s.iRightColumns > 0 ) { + // When scrolling the right column, scroll the body and the left column + $(that.dom.grid.right.liner) + .on( 'mouseover.DTFC touchstart.DTFC', function () { + mouseController = 'right'; + } ) + .on( 'scroll.DTFC', function () { + if ( mouseController === 'right' ) { + that.dom.scroller.scrollTop = that.dom.grid.right.liner.scrollTop; + if ( that.s.iLeftColumns > 0 ) { + that.dom.grid.left.liner.scrollTop = that.dom.grid.right.liner.scrollTop; + } + } + } ) + .on( wheelType, function(e) { + // Pass horizontal scrolling through + var xDelta = e.type === 'wheel' ? + -e.originalEvent.deltaX : + e.originalEvent.wheelDeltaX; + that.dom.scroller.scrollLeft -= xDelta; + } ); + } + + $(window).on( 'resize.DTFC', function () { + that._fnGridLayout.call( that ); + } ); + + var bFirstDraw = true; + var jqTable = $(this.s.dt.nTable); + + jqTable + .on( 'draw.dt.DTFC', function () { + that._fnDraw.call( that, bFirstDraw ); + bFirstDraw = false; + } ) + .on( 'column-sizing.dt.DTFC', function () { + that._fnColCalc(); + that._fnGridLayout( that ); + } ) + .on( 'column-visibility.dt.DTFC', function () { + that._fnColCalc(); + that._fnGridLayout( that ); + that._fnDraw( true ); + } ) + .on( 'destroy.dt.DTFC', function () { + jqTable.off( 'column-sizing.dt.DTFC destroy.dt.DTFC draw.dt.DTFC' ); + + $(that.dom.scroller).off( 'scroll.DTFC mouseover.DTFC' ); + $(window).off( 'resize.DTFC' ); + + $(that.dom.grid.left.liner).off( 'scroll.DTFC mouseover.DTFC '+wheelType ); + $(that.dom.grid.left.wrapper).remove(); + + $(that.dom.grid.right.liner).off( 'scroll.DTFC mouseover.DTFC '+wheelType ); + $(that.dom.grid.right.wrapper).remove(); + } ); + + /* Get things right to start with - note that due to adjusting the columns, there must be + * another redraw of the main table. It doesn't need to be a full redraw however. + */ + this._fnGridLayout(); + this.s.dt.oInstance.fnDraw(false); + }, + + + /** + * Calculate the column widths for the grid layout + * @returns {void} + * @private + */ + "_fnColCalc": function () + { + var that = this; + var iLeftWidth = 0; + var iRightWidth = 0; + + this.s.aiInnerWidths = []; + this.s.aiOuterWidths = []; + + $.each( this.s.dt.aoColumns, function (i, col) { + var th = $(col.nTh); + var border; + + if ( ! th.filter(':visible').length ) { + that.s.aiInnerWidths.push( 0 ); + that.s.aiOuterWidths.push( 0 ); + } + else + { + // Inner width is used to assign widths to cells + // Outer width is used to calculate the container + var iWidth = th.outerWidth(); + + // When working with the left most-cell, need to add on the + // table's border to the outerWidth, since we need to take + // account of it, but it isn't in any cell + if ( that.s.aiOuterWidths.length === 0 ) { + border = $(that.s.dt.nTable).css('border-left-width'); + iWidth += typeof border === 'string' ? 1 : parseInt( border, 10 ); + } + + // Likewise with the final column on the right + if ( that.s.aiOuterWidths.length === that.s.dt.aoColumns.length-1 ) { + border = $(that.s.dt.nTable).css('border-right-width'); + iWidth += typeof border === 'string' ? 1 : parseInt( border, 10 ); + } + + that.s.aiOuterWidths.push( iWidth ); + that.s.aiInnerWidths.push( th.width() ); + + if ( i < that.s.iLeftColumns ) + { + iLeftWidth += iWidth; + } + + if ( that.s.iTableColumns-that.s.iRightColumns <= i ) + { + iRightWidth += iWidth; + } + } + } ); + + this.s.iLeftWidth = iLeftWidth; + this.s.iRightWidth = iRightWidth; + }, + + + /** + * Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid + * for the left column, the DataTable (for which we just reuse the scrolling element DataTable + * puts into the DOM) and the right column. In each of he two fixed column elements there is a + * grouping wrapper element and then a head, body and footer wrapper. In each of these we then + * place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure. + * @returns {void} + * @private + */ + "_fnGridSetup": function () + { + var that = this; + var oOverflow = this._fnDTOverflow(); + var block; + + this.dom.body = this.s.dt.nTable; + this.dom.header = this.s.dt.nTHead.parentNode; + this.dom.header.parentNode.parentNode.style.position = "relative"; + + var nSWrapper = + $('
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
'+ + '
')[0]; + var nLeft = nSWrapper.childNodes[0]; + var nRight = nSWrapper.childNodes[1]; + + this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt ); + nSWrapper.appendChild( this.dom.grid.dt ); + + this.dom.grid.wrapper = nSWrapper; + + if ( this.s.iLeftColumns > 0 ) + { + this.dom.grid.left.wrapper = nLeft; + this.dom.grid.left.head = nLeft.childNodes[0]; + this.dom.grid.left.body = nLeft.childNodes[1]; + this.dom.grid.left.liner = $('div.DTFC_LeftBodyLiner', nSWrapper)[0]; + + nSWrapper.appendChild( nLeft ); + } + + if ( this.s.iRightColumns > 0 ) + { + this.dom.grid.right.wrapper = nRight; + this.dom.grid.right.head = nRight.childNodes[0]; + this.dom.grid.right.body = nRight.childNodes[1]; + this.dom.grid.right.liner = $('div.DTFC_RightBodyLiner', nSWrapper)[0]; + + block = $('div.DTFC_RightHeadBlocker', nSWrapper)[0]; + block.style.width = oOverflow.bar+"px"; + block.style.right = -oOverflow.bar+"px"; + this.dom.grid.right.headBlock = block; + + block = $('div.DTFC_RightFootBlocker', nSWrapper)[0]; + block.style.width = oOverflow.bar+"px"; + block.style.right = -oOverflow.bar+"px"; + this.dom.grid.right.footBlock = block; + + nSWrapper.appendChild( nRight ); + } + + if ( this.s.dt.nTFoot ) + { + this.dom.footer = this.s.dt.nTFoot.parentNode; + if ( this.s.iLeftColumns > 0 ) + { + this.dom.grid.left.foot = nLeft.childNodes[2]; + } + if ( this.s.iRightColumns > 0 ) + { + this.dom.grid.right.foot = nRight.childNodes[2]; + } + } + }, + + + /** + * Style and position the grid used for the FixedColumns layout + * @returns {void} + * @private + */ + "_fnGridLayout": function () + { + var oGrid = this.dom.grid; + var iWidth = $(oGrid.wrapper).width(); + var iBodyHeight = $(this.s.dt.nTable.parentNode).outerHeight(); + var iFullHeight = $(this.s.dt.nTable.parentNode.parentNode).outerHeight(); + var oOverflow = this._fnDTOverflow(); + var + iLeftWidth = this.s.iLeftWidth, + iRightWidth = this.s.iRightWidth, + iRight; + var scrollbarAdjust = function ( node, width ) { + if ( ! oOverflow.bar ) { + // If there is no scrollbar (Macs) we need to hide the auto scrollbar + node.style.width = (width+20)+"px"; + node.style.paddingRight = "20px"; + node.style.boxSizing = "border-box"; + } + else { + // Otherwise just overflow by the scrollbar + node.style.width = (width+oOverflow.bar)+"px"; + } + }; + + // When x scrolling - don't paint the fixed columns over the x scrollbar + if ( oOverflow.x ) + { + iBodyHeight -= oOverflow.bar; + } + + oGrid.wrapper.style.height = iFullHeight+"px"; + + if ( this.s.iLeftColumns > 0 ) + { + oGrid.left.wrapper.style.width = iLeftWidth+"px"; + oGrid.left.wrapper.style.height = "1px"; + oGrid.left.body.style.height = iBodyHeight+"px"; + if ( oGrid.left.foot ) { + oGrid.left.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; // shift footer for scrollbar + } + + scrollbarAdjust( oGrid.left.liner, iLeftWidth ); + oGrid.left.liner.style.height = iBodyHeight+"px"; + } + + if ( this.s.iRightColumns > 0 ) + { + iRight = iWidth - iRightWidth; + if ( oOverflow.y ) + { + iRight -= oOverflow.bar; + } + + oGrid.right.wrapper.style.width = iRightWidth+"px"; + oGrid.right.wrapper.style.left = iRight+"px"; + oGrid.right.wrapper.style.height = "1px"; + oGrid.right.body.style.height = iBodyHeight+"px"; + if ( oGrid.right.foot ) { + oGrid.right.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; + } + + scrollbarAdjust( oGrid.right.liner, iRightWidth ); + oGrid.right.liner.style.height = iBodyHeight+"px"; + + oGrid.right.headBlock.style.display = oOverflow.y ? 'block' : 'none'; + oGrid.right.footBlock.style.display = oOverflow.y ? 'block' : 'none'; + } + }, + + + /** + * Get information about the DataTable's scrolling state - specifically if the table is scrolling + * on either the x or y axis, and also the scrollbar width. + * @returns {object} Information about the DataTables scrolling state with the properties: + * 'x', 'y' and 'bar' + * @private + */ + "_fnDTOverflow": function () + { + var nTable = this.s.dt.nTable; + var nTableScrollBody = nTable.parentNode; + var out = { + "x": false, + "y": false, + "bar": this.s.dt.oScroll.iBarWidth + }; + + if ( nTable.offsetWidth > nTableScrollBody.clientWidth ) + { + out.x = true; + } + + if ( nTable.offsetHeight > nTableScrollBody.clientHeight ) + { + out.y = true; + } + + return out; + }, + + + /** + * Clone and position the fixed columns + * @returns {void} + * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true) + * @private + */ + "_fnDraw": function ( bAll ) + { + this._fnGridLayout(); + this._fnCloneLeft( bAll ); + this._fnCloneRight( bAll ); + + /* Draw callback function */ + if ( this.s.fnDrawCallback !== null ) + { + this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right ); + } + + /* Event triggering */ + $(this).trigger( 'draw.dtfc', { + "leftClone": this.dom.clone.left, + "rightClone": this.dom.clone.right + } ); + }, + + + /** + * Clone the right columns + * @returns {void} + * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true) + * @private + */ + "_fnCloneRight": function ( bAll ) + { + if ( this.s.iRightColumns <= 0 ) { + return; + } + + var that = this, + i, jq, + aiColumns = []; + + for ( i=this.s.iTableColumns-this.s.iRightColumns ; ithead', oClone.header); + jqCloneThead.empty(); + + /* Add the created cloned TR elements to the table */ + for ( i=0, iLen=aoCloneLayout.length ; ithead', oClone.header)[0] ); + + for ( i=0, iLen=aoCloneLayout.length ; itbody>tr', that.dom.body).css('height', 'auto'); + } + + if ( oClone.body !== null ) + { + oClone.body.parentNode.removeChild( oClone.body ); + oClone.body = null; + } + + oClone.body = $(this.dom.body).clone(true)[0]; + oClone.body.className += " DTFC_Cloned"; + oClone.body.style.paddingBottom = dt.oScroll.iBarWidth+"px"; + oClone.body.style.marginBottom = (dt.oScroll.iBarWidth*2)+"px"; /* For IE */ + if ( oClone.body.getAttribute('id') !== null ) + { + oClone.body.removeAttribute('id'); + } + + $('>thead>tr', oClone.body).empty(); + $('>tfoot', oClone.body).remove(); + + var nBody = $('tbody', oClone.body)[0]; + $(nBody).empty(); + if ( dt.aiDisplay.length > 0 ) + { + /* Copy the DataTables' header elements to force the column width in exactly the + * same way that DataTables does it - have the header element, apply the width and + * colapse it down + */ + var nInnerThead = $('>thead>tr', oClone.body)[0]; + for ( iIndex=0 ; iIndextbody>tr', that.dom.body).each( function (z) { + var n = this.cloneNode(false); + n.removeAttribute('id'); + var i = that.s.dt.oFeatures.bServerSide===false ? + that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z; + var aTds = that.s.dt.aoData[ i ].anCells || $(this).children('td, th'); + + for ( iIndex=0 ; iIndex 0 ) + { + nClone = $( aTds[iColumn] ).clone(true, true)[0]; + n.appendChild( nClone ); + } + } + nBody.appendChild( n ); + } ); + } + else + { + $('>tbody>tr', that.dom.body).each( function (z) { + nClone = this.cloneNode(true); + nClone.className += ' DTFC_NoData'; + $('td', nClone).html(''); + nBody.appendChild( nClone ); + } ); + } + + oClone.body.style.width = "100%"; + oClone.body.style.margin = "0"; + oClone.body.style.padding = "0"; + + // Interop with Scroller - need to use a height forcing element in the + // scrolling area in the same way that Scroller does in the body scroll. + if ( dt.oScroller !== undefined ) + { + var scrollerForcer = dt.oScroller.dom.force; + + if ( ! oGrid.forcer ) { + oGrid.forcer = scrollerForcer.cloneNode( true ); + oGrid.liner.appendChild( oGrid.forcer ); + } + else { + oGrid.forcer.style.height = scrollerForcer.style.height; + } + } + + oGrid.liner.appendChild( oClone.body ); + + this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body ); + + /* + * Footer + */ + if ( dt.nTFoot !== null ) + { + if ( bAll ) + { + if ( oClone.footer !== null ) + { + oClone.footer.parentNode.removeChild( oClone.footer ); + } + oClone.footer = $(this.dom.footer).clone(true, true)[0]; + oClone.footer.className += " DTFC_Cloned"; + oClone.footer.style.width = "100%"; + oGrid.foot.appendChild( oClone.footer ); + + /* Copy the footer just like we do for the header */ + aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns ); + var jqCloneTfoot = $('>tfoot', oClone.footer); + jqCloneTfoot.empty(); + + for ( i=0, iLen=aoCloneLayout.length ; itfoot', oClone.footer)[0] ); + + for ( i=0, iLen=aoCloneLayout.length ; ithead', oClone.header)[0] ); + $(anUnique).each( function (i) { + iColumn = aiColumns[i]; + this.style.width = that.s.aiInnerWidths[iColumn]+"px"; + } ); + + if ( that.s.dt.nTFoot !== null ) + { + anUnique = dt.oApi._fnGetUniqueThs( dt, $('>tfoot', oClone.footer)[0] ); + $(anUnique).each( function (i) { + iColumn = aiColumns[i]; + this.style.width = that.s.aiInnerWidths[iColumn]+"px"; + } ); + } + }, + + + /** + * From a given table node (THEAD etc), get a list of TR direct child elements + * @param {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element) + * @returns {Array} List of TR elements found + * @private + */ + "_fnGetTrNodes": function ( nIn ) + { + var aOut = []; + for ( var i=0, iLen=nIn.childNodes.length ; i'+nodeName+'>tr:eq(0)', original).children(':first'), + iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(), + anOriginal = this._fnGetTrNodes( rootOriginal ), + anClone = this._fnGetTrNodes( rootClone ), + heights = []; + + for ( i=0, iLen=anClone.length ; i iHeightOriginal ? iHeightClone : iHeightOriginal; + + if ( this.s.sHeightMatch == 'semiauto' ) + { + anOriginal[i]._DTTC_iHeight = iHeight; + } + + heights.push( iHeight ); + } + + for ( i=0, iLen=anClone.length ; i
')[0], +g=c.childNodes[0],f=c.childNodes[1];this.dom.grid.dt.parentNode.insertBefore(c,this.dom.grid.dt);c.appendChild(this.dom.grid.dt);this.dom.grid.wrapper=c;0b.clientWidth&&(c.x=!0);a.offsetHeight>b.clientHeight&&(c.y=!0);return c},_fnDraw:function(a){this._fnGridLayout();this._fnCloneLeft(a);this._fnCloneRight(a);null!==this.s.fnDrawCallback&&this.s.fnDrawCallback.call(this,this.dom.clone.left,this.dom.clone.right);d(this).trigger("draw.dtfc",{leftClone:this.dom.clone.left,rightClone:this.dom.clone.right})},_fnCloneRight:function(a){if(!(0>= +this.s.iRightColumns)){var b,c=[];for(b=this.s.iTableColumns-this.s.iRightColumns;b=this.s.iLeftColumns)){var b,c=[];for(b=0;bthead",a.header);k.empty();e=0;for(h=n.length;ethead",a.header)[0]);e=0;for(h=n.length;etbody>tr",f.dom.body).css("height","auto");null!==a.body&&(a.body.parentNode.removeChild(a.body),a.body=null);a.body=d(this.dom.body).clone(!0)[0];a.body.className+=" DTFC_Cloned";a.body.style.paddingBottom=l.oScroll.iBarWidth+"px";a.body.style.marginBottom=2*l.oScroll.iBarWidth+"px";null!==a.body.getAttribute("id")&&a.body.removeAttribute("id");d(">thead>tr",a.body).empty();d(">tfoot", +a.body).remove();var p=d("tbody",a.body)[0];d(p).empty();if(0thead>tr",a.body)[0];for(o=0;otbody>tr",f.dom.body).each(function(a){var b=this.cloneNode(false);b.removeAttribute("id");a=f.s.dt.aoData[f.s.dt.oFeatures.bServerSide===false?f.s.dt.aiDisplay[f.s.dt._iDisplayStart+ +a]:a].anCells||d(this).children("td, th");for(o=0;o0){m=d(a[j]).clone(true,true)[0];b.appendChild(m)}}p.appendChild(b)})}else d(">tbody>tr",f.dom.body).each(function(){m=this.cloneNode(true);m.className=m.className+" DTFC_NoData";d("td",m).html("");p.appendChild(m)});a.body.style.width="100%";a.body.style.margin="0";a.body.style.padding="0";l.oScroller!==t&&(h=l.oScroller.dom.force,b.forcer?b.forcer.style.height=h.style.height:(b.forcer=h.cloneNode(!0),b.liner.appendChild(b.forcer))); +b.liner.appendChild(a.body);this._fnEqualiseHeights("tbody",f.dom.body,a.body);if(null!==l.nTFoot){if(g){null!==a.footer&&a.footer.parentNode.removeChild(a.footer);a.footer=d(this.dom.footer).clone(!0,!0)[0];a.footer.className+=" DTFC_Cloned";a.footer.style.width="100%";b.foot.appendChild(a.footer);n=this._fnCopyLayout(l.aoFooter,c);b=d(">tfoot",a.footer);b.empty();e=0;for(h=n.length;etfoot",a.footer)[0]);e=0;for(h=n.length;ethead",a.header)[0]);d(b).each(function(a){j=c[a];this.style.width=f.s.aiInnerWidths[j]+"px"});null!==f.s.dt.nTFoot&&(b=l.oApi._fnGetUniqueThs(l,d(">tfoot",a.footer)[0]),d(b).each(function(a){j=c[a];this.style.width=f.s.aiInnerWidths[j]+"px"}))},_fnGetTrNodes:function(a){for(var b= +[],c=0,d=a.childNodes.length;c"+a+">tr:eq(0)",b).children(":first");a.outerHeight();a.height();for(var e=this._fnGetTrNodes(e),b=this._fnGetTrNodes(c),h=[],c=0,a=b.length;cg?f:g,"semiauto"==this.s.sHeightMatch&& +(e[c]._DTTC_iHeight=g),h.push(g);c=0;for(a=b.length;ctfoot', s.nTable).length > 0) ? true : false; + + /* Add the 'sides' that are fixed */ + if ( s.oSides.top ) + { + s.aoCache.push( that._fnCloneTable( "fixedHeader", "FixedHeader_Header", that._fnCloneThead ) ); + } + if ( s.oSides.bottom ) + { + s.aoCache.push( that._fnCloneTable( "fixedFooter", "FixedHeader_Footer", that._fnCloneTfoot ) ); + } + if ( s.oSides.left ) + { + s.aoCache.push( that._fnCloneTable( "fixedLeft", "FixedHeader_Left", that._fnCloneTLeft, s.oSides.left ) ); + } + if ( s.oSides.right ) + { + s.aoCache.push( that._fnCloneTable( "fixedRight", "FixedHeader_Right", that._fnCloneTRight, s.oSides.right ) ); + } + + /* Event listeners for window movement */ + FixedHeader.afnScroll.push( function () { + that._fnUpdatePositions.call(that); + } ); + + $(window).resize( function () { + FixedHeader.fnMeasure(); + that._fnUpdateClones.call(that); + that._fnUpdatePositions.call(that); + } ); + + $(s.nTable) + .on('column-reorder.dt', function () { + FixedHeader.fnMeasure(); + that._fnUpdateClones( true ); + that._fnUpdatePositions(); + } ) + .on('column-visibility.dt', function () { + FixedHeader.fnMeasure(); + that._fnUpdateClones( true ); + that._fnUpdatePositions(); + } ); + + /* Get things right to start with */ + FixedHeader.fnMeasure(); + that._fnUpdateClones(); + that._fnUpdatePositions(); + + s.bInitComplete = true; + }, + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Support functions + */ + + /* + * Function: fnInitSettings + * Purpose: Take the user's settings and copy them to our local store + * Returns: - + * Inputs: object:s - the local settings object + * object:oInit - the user's settings object + */ + fnInitSettings: function ( s, oInit ) + { + if ( oInit !== undefined ) + { + if ( oInit.top !== undefined ) { + s.oSides.top = oInit.top; + } + if ( oInit.bottom !== undefined ) { + s.oSides.bottom = oInit.bottom; + } + if ( typeof oInit.left == 'boolean' ) { + s.oSides.left = oInit.left ? 1 : 0; + } + else if ( oInit.left !== undefined ) { + s.oSides.left = oInit.left; + } + if ( typeof oInit.right == 'boolean' ) { + s.oSides.right = oInit.right ? 1 : 0; + } + else if ( oInit.right !== undefined ) { + s.oSides.right = oInit.right; + } + + if ( oInit.zTop !== undefined ) { + s.oZIndexes.top = oInit.zTop; + } + if ( oInit.zBottom !== undefined ) { + s.oZIndexes.bottom = oInit.zBottom; + } + if ( oInit.zLeft !== undefined ) { + s.oZIndexes.left = oInit.zLeft; + } + if ( oInit.zRight !== undefined ) { + s.oZIndexes.right = oInit.zRight; + } + + if ( oInit.offsetTop !== undefined ) { + s.oOffset.top = oInit.offsetTop; + } + if ( oInit.alwaysCloneTop !== undefined ) { + s.oCloneOnDraw.top = oInit.alwaysCloneTop; + } + if ( oInit.alwaysCloneBottom !== undefined ) { + s.oCloneOnDraw.bottom = oInit.alwaysCloneBottom; + } + if ( oInit.alwaysCloneLeft !== undefined ) { + s.oCloneOnDraw.left = oInit.alwaysCloneLeft; + } + if ( oInit.alwaysCloneRight !== undefined ) { + s.oCloneOnDraw.right = oInit.alwaysCloneRight; + } + } + }, + + /* + * Function: _fnCloneTable + * Purpose: Clone the table node and do basic initialisation + * Returns: - + * Inputs: - + */ + _fnCloneTable: function ( sType, sClass, fnClone, iCells ) + { + var s = this.fnGetSettings(); + var nCTable; + + /* We know that the table _MUST_ has a DIV wrapped around it, because this is simply how + * DataTables works. Therefore, we can set this to be relatively position (if it is not + * alreadu absolute, and use this as the base point for the cloned header + */ + if ( $(s.nTable.parentNode).css('position') != "absolute" ) + { + s.nTable.parentNode.style.position = "relative"; + } + + /* Just a shallow clone will do - we only want the table node */ + nCTable = s.nTable.cloneNode( false ); + nCTable.removeAttribute( 'id' ); + + var nDiv = document.createElement( 'div' ); + nDiv.style.position = "absolute"; + nDiv.style.top = "0px"; + nDiv.style.left = "0px"; + nDiv.className += " FixedHeader_Cloned "+sType+" "+sClass; + + /* Set the zIndexes */ + if ( sType == "fixedHeader" ) + { + nDiv.style.zIndex = s.oZIndexes.top; + } + if ( sType == "fixedFooter" ) + { + nDiv.style.zIndex = s.oZIndexes.bottom; + } + if ( sType == "fixedLeft" ) + { + nDiv.style.zIndex = s.oZIndexes.left; + } + else if ( sType == "fixedRight" ) + { + nDiv.style.zIndex = s.oZIndexes.right; + } + + /* remove margins since we are going to position it absolutely */ + nCTable.style.margin = "0"; + + /* Insert the newly cloned table into the DOM, on top of the "real" header */ + nDiv.appendChild( nCTable ); + document.body.appendChild( nDiv ); + + return { + "nNode": nCTable, + "nWrapper": nDiv, + "sType": sType, + "sPosition": "", + "sTop": "", + "sLeft": "", + "fnClone": fnClone, + "iCells": iCells + }; + }, + + /* + * Function: _fnMeasure + * Purpose: Get the current positioning of the table in the DOM + * Returns: - + * Inputs: - + */ + _fnMeasure: function () + { + var + s = this.fnGetSettings(), + m = s.oMes, + jqTable = $(s.nTable), + oOffset = jqTable.offset(), + iParentScrollTop = this._fnSumScroll( s.nTable.parentNode, 'scrollTop' ), + iParentScrollLeft = this._fnSumScroll( s.nTable.parentNode, 'scrollLeft' ); + + m.iTableWidth = jqTable.outerWidth(); + m.iTableHeight = jqTable.outerHeight(); + m.iTableLeft = oOffset.left + s.nTable.parentNode.scrollLeft; + m.iTableTop = oOffset.top + iParentScrollTop; + m.iTableRight = m.iTableLeft + m.iTableWidth; + m.iTableRight = FixedHeader.oDoc.iWidth - m.iTableLeft - m.iTableWidth; + m.iTableBottom = FixedHeader.oDoc.iHeight - m.iTableTop - m.iTableHeight; + }, + + /* + * Function: _fnSumScroll + * Purpose: Sum node parameters all the way to the top + * Returns: int: sum + * Inputs: node:n - node to consider + * string:side - scrollTop or scrollLeft + */ + _fnSumScroll: function ( n, side ) + { + var i = n[side]; + while ( n = n.parentNode ) + { + if ( n.nodeName == 'HTML' || n.nodeName == 'BODY' ) + { + break; + } + i = n[side]; + } + return i; + }, + + /* + * Function: _fnUpdatePositions + * Purpose: Loop over the fixed elements for this table and update their positions + * Returns: - + * Inputs: - + */ + _fnUpdatePositions: function () + { + var s = this.fnGetSettings(); + this._fnMeasure(); + + for ( var i=0, iLen=s.aoCache.length ; i oWin.iScrollTop + s.oOffset.top ) + { + /* Above the table */ + this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style ); + this._fnUpdateCache( oCache, 'sTop', oMes.iTableTop+"px", 'top', nTable.style ); + this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style ); + } + else if ( oWin.iScrollTop + s.oOffset.top > oMes.iTableTop+iTbodyHeight ) + { + /* At the bottom of the table */ + this._fnUpdateCache( oCache, 'sPosition', "absolute", 'position', nTable.style ); + this._fnUpdateCache( oCache, 'sTop', (oMes.iTableTop+iTbodyHeight)+"px", 'top', nTable.style ); + this._fnUpdateCache( oCache, 'sLeft', oMes.iTableLeft+"px", 'left', nTable.style ); + } + else + { + /* In the middle of the table */ + this._fnUpdateCache( oCache, 'sPosition', 'fixed', 'position', nTable.style ); + this._fnUpdateCache( oCache, 'sTop', s.oOffset.top+"px", 'top', nTable.style ); + this._fnUpdateCache( oCache, 'sLeft', (oMes.iTableLeft-oWin.iScrollLeft)+"px", 'left', nTable.style ); + } + }, + + /* + * Function: _fnUpdateCache + * Purpose: Check the cache and update cache and value if needed + * Returns: - + * Inputs: object:oCache - local cache object + * string:sCache - cache property + * string:sSet - value to set + * string:sProperty - object property to set + * object:oObj - object to update + */ + _fnUpdateCache: function ( oCache, sCache, sSet, sProperty, oObj ) + { + if ( oCache[sCache] != sSet ) + { + oObj[sProperty] = sSet; + oCache[sCache] = sSet; + } + }, + + + + /** + * Copy the classes of all child nodes from one element to another. This implies + * that the two have identical structure - no error checking is performed to that + * fact. + * @param {element} source Node to copy classes from + * @param {element} dest Node to copy classes too + */ + _fnClassUpdate: function ( source, dest ) + { + var that = this; + + if ( source.nodeName.toUpperCase() === "TR" || source.nodeName.toUpperCase() === "TH" || + source.nodeName.toUpperCase() === "TD" || source.nodeName.toUpperCase() === "SPAN" ) + { + dest.className = source.className; + } + + $(source).children().each( function (i) { + that._fnClassUpdate( $(source).children()[i], $(dest).children()[i] ); + } ); + }, + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Cloning functions + */ + + /* + * Function: _fnCloneThead + * Purpose: Clone the thead element + * Returns: - + * Inputs: object:oCache - the cached values for this fixed element + */ + _fnCloneThead: function ( oCache ) + { + var s = this.fnGetSettings(); + var nTable = oCache.nNode; + + if ( s.bInitComplete && !s.oCloneOnDraw.top ) + { + this._fnClassUpdate( $('thead', s.nTable)[0], $('thead', nTable)[0] ); + return; + } + + /* Set the wrapper width to match that of the cloned table */ + var iDtWidth = $(s.nTable).outerWidth(); + oCache.nWrapper.style.width = iDtWidth+"px"; + nTable.style.width = iDtWidth+"px"; + + /* Remove any children the cloned table has */ + while ( nTable.childNodes.length > 0 ) + { + $('thead th', nTable).unbind( 'click' ); + nTable.removeChild( nTable.childNodes[0] ); + } + + /* Clone the DataTables header */ + var nThead = $('thead', s.nTable).clone(true)[0]; + nTable.appendChild( nThead ); + + /* Copy the widths across - apparently a clone isn't good enough for this */ + var a = []; + var b = []; + + $("thead>tr th", s.nTable).each( function (i) { + a.push( $(this).width() ); + } ); + + $("thead>tr td", s.nTable).each( function (i) { + b.push( $(this).width() ); + } ); + + $("thead>tr th", s.nTable).each( function (i) { + $("thead>tr th:eq("+i+")", nTable).width( a[i] ); + $(this).width( a[i] ); + } ); + + $("thead>tr td", s.nTable).each( function (i) { + $("thead>tr td:eq("+i+")", nTable).width( b[i] ); + $(this).width( b[i] ); + } ); + + // Stop DataTables 1.9 from putting a focus ring on the headers when + // clicked to sort + $('th.sorting, th.sorting_desc, th.sorting_asc', nTable).bind( 'click', function () { + this.blur(); + } ); + }, + + /* + * Function: _fnCloneTfoot + * Purpose: Clone the tfoot element + * Returns: - + * Inputs: object:oCache - the cached values for this fixed element + */ + _fnCloneTfoot: function ( oCache ) + { + var s = this.fnGetSettings(); + var nTable = oCache.nNode; + + /* Set the wrapper width to match that of the cloned table */ + oCache.nWrapper.style.width = $(s.nTable).outerWidth()+"px"; + + /* Remove any children the cloned table has */ + while ( nTable.childNodes.length > 0 ) + { + nTable.removeChild( nTable.childNodes[0] ); + } + + /* Clone the DataTables footer */ + var nTfoot = $('tfoot', s.nTable).clone(true)[0]; + nTable.appendChild( nTfoot ); + + /* Copy the widths across - apparently a clone isn't good enough for this */ + $("tfoot:eq(0)>tr th", s.nTable).each( function (i) { + $("tfoot:eq(0)>tr th:eq("+i+")", nTable).width( $(this).width() ); + } ); + + $("tfoot:eq(0)>tr td", s.nTable).each( function (i) { + $("tfoot:eq(0)>tr td:eq("+i+")", nTable).width( $(this).width() ); + } ); + }, + + /* + * Function: _fnCloneTLeft + * Purpose: Clone the left column(s) + * Returns: - + * Inputs: object:oCache - the cached values for this fixed element + */ + _fnCloneTLeft: function ( oCache ) + { + var s = this.fnGetSettings(); + var nTable = oCache.nNode; + var nBody = $('tbody', s.nTable)[0]; + + /* Remove any children the cloned table has */ + while ( nTable.childNodes.length > 0 ) + { + nTable.removeChild( nTable.childNodes[0] ); + } + + /* Is this the most efficient way to do this - it looks horrible... */ + nTable.appendChild( $("thead", s.nTable).clone(true)[0] ); + nTable.appendChild( $("tbody", s.nTable).clone(true)[0] ); + if ( s.bFooter ) + { + nTable.appendChild( $("tfoot", s.nTable).clone(true)[0] ); + } + + /* Remove unneeded cells */ + var sSelector = 'gt(' + (oCache.iCells - 1) + ')'; + $('thead tr', nTable).each( function (k) { + $('th:' + sSelector, this).remove(); + } ); + + $('tfoot tr', nTable).each( function (k) { + $('th:' + sSelector, this).remove(); + } ); + + $('tbody tr', nTable).each( function (k) { + $('td:' + sSelector, this).remove(); + } ); + + this.fnEqualiseHeights( 'thead', nBody.parentNode, nTable ); + this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable ); + this.fnEqualiseHeights( 'tfoot', nBody.parentNode, nTable ); + + var iWidth = 0; + for (var i = 0; i < oCache.iCells; i++) { + iWidth += $('thead tr th:eq(' + i + ')', s.nTable).outerWidth(); + } + nTable.style.width = iWidth+"px"; + oCache.nWrapper.style.width = iWidth+"px"; + }, + + /* + * Function: _fnCloneTRight + * Purpose: Clone the right most column(s) + * Returns: - + * Inputs: object:oCache - the cached values for this fixed element + */ + _fnCloneTRight: function ( oCache ) + { + var s = this.fnGetSettings(); + var nBody = $('tbody', s.nTable)[0]; + var nTable = oCache.nNode; + var iCols = $('tbody tr:eq(0) td', s.nTable).length; + + /* Remove any children the cloned table has */ + while ( nTable.childNodes.length > 0 ) + { + nTable.removeChild( nTable.childNodes[0] ); + } + + /* Is this the most efficient way to do this - it looks horrible... */ + nTable.appendChild( $("thead", s.nTable).clone(true)[0] ); + nTable.appendChild( $("tbody", s.nTable).clone(true)[0] ); + if ( s.bFooter ) + { + nTable.appendChild( $("tfoot", s.nTable).clone(true)[0] ); + } + $('thead tr th:lt('+(iCols-oCache.iCells)+')', nTable).remove(); + $('tfoot tr th:lt('+(iCols-oCache.iCells)+')', nTable).remove(); + + /* Remove unneeded cells */ + $('tbody tr', nTable).each( function (k) { + $('td:lt('+(iCols-oCache.iCells)+')', this).remove(); + } ); + + this.fnEqualiseHeights( 'thead', nBody.parentNode, nTable ); + this.fnEqualiseHeights( 'tbody', nBody.parentNode, nTable ); + this.fnEqualiseHeights( 'tfoot', nBody.parentNode, nTable ); + + var iWidth = 0; + for (var i = 0; i < oCache.iCells; i++) { + iWidth += $('thead tr th:eq('+(iCols-1-i)+')', s.nTable).outerWidth(); + } + nTable.style.width = iWidth+"px"; + oCache.nWrapper.style.width = iWidth+"px"; + }, + + + /** + * Equalise the heights of the rows in a given table node in a cross browser way. Note that this + * is more or less lifted as is from FixedColumns + * @method fnEqualiseHeights + * @returns void + * @param {string} parent Node type - thead, tbody or tfoot + * @param {element} original Original node to take the heights from + * @param {element} clone Copy the heights to + * @private + */ + "fnEqualiseHeights": function ( parent, original, clone ) + { + var that = this; + var originals = $(parent +' tr', original); + var height; + + $(parent+' tr', clone).each( function (k) { + height = originals.eq( k ).css('height'); + + // This is nasty :-(. IE has a sub-pixel error even when setting + // the height below (the Firefox fix) which causes the fixed column + // to go out of alignment. Need to add a pixel before the assignment + // Can this be feature detected? Not sure how... + if ( navigator.appName == 'Microsoft Internet Explorer' ) { + height = parseInt( height, 10 ) + 1; + } + + $(this).css( 'height', height ); + + // For Firefox to work, we need to also set the height of the + // original row, to the value that we read from it! Otherwise there + // is a sub-pixel rounding error + originals.eq( k ).css( 'height', height ); + } ); + } +}; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Static properties and methods + * We use these for speed! This information is common to all instances of FixedHeader, so no + * point if having them calculated and stored for each different instance. + */ + +/* + * Variable: oWin + * Purpose: Store information about the window positioning + * Scope: FixedHeader + */ +FixedHeader.oWin = { + "iScrollTop": 0, + "iScrollRight": 0, + "iScrollBottom": 0, + "iScrollLeft": 0, + "iHeight": 0, + "iWidth": 0 +}; + +/* + * Variable: oDoc + * Purpose: Store information about the document size + * Scope: FixedHeader + */ +FixedHeader.oDoc = { + "iHeight": 0, + "iWidth": 0 +}; + +/* + * Variable: afnScroll + * Purpose: Array of functions that are to be used for the scrolling components + * Scope: FixedHeader + */ +FixedHeader.afnScroll = []; + +/* + * Function: fnMeasure + * Purpose: Update the measurements for the window and document + * Returns: - + * Inputs: - + */ +FixedHeader.fnMeasure = function () +{ + var + jqWin = $(window), + jqDoc = $(document), + oWin = FixedHeader.oWin, + oDoc = FixedHeader.oDoc; + + oDoc.iHeight = jqDoc.height(); + oDoc.iWidth = jqDoc.width(); + + oWin.iHeight = jqWin.height(); + oWin.iWidth = jqWin.width(); + oWin.iScrollTop = jqWin.scrollTop(); + oWin.iScrollLeft = jqWin.scrollLeft(); + oWin.iScrollRight = oDoc.iWidth - oWin.iScrollLeft - oWin.iWidth; + oWin.iScrollBottom = oDoc.iHeight - oWin.iScrollTop - oWin.iHeight; +}; + + +FixedHeader.version = "2.1.2"; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Global processing + */ + +/* + * Just one 'scroll' event handler in FixedHeader, which calls the required components. This is + * done as an optimisation, to reduce calculation and proagation time + */ +$(window).scroll( function () { + FixedHeader.fnMeasure(); + + for ( var i=0, iLen=FixedHeader.afnScroll.length ; itfoot",c.nTable).length?!0:!1,c.oSides.top&&c.aoCache.push(d._fnCloneTable("fixedHeader","FixedHeader_Header",d._fnCloneThead)),c.oSides.bottom&&c.aoCache.push(d._fnCloneTable("fixedFooter","FixedHeader_Footer",d._fnCloneTfoot)),c.oSides.left&&c.aoCache.push(d._fnCloneTable("fixedLeft","FixedHeader_Left",d._fnCloneTLeft,c.oSides.left)), +c.oSides.right&&c.aoCache.push(d._fnCloneTable("fixedRight","FixedHeader_Right",d._fnCloneTRight,c.oSides.right)),FixedHeader.afnScroll.push(function(){d._fnUpdatePositions.call(d)}),e(j).resize(function(){FixedHeader.fnMeasure();d._fnUpdateClones.call(d);d._fnUpdatePositions.call(d)}),e(c.nTable).on("column-reorder.dt",function(){FixedHeader.fnMeasure();d._fnUpdateClones(!0);d._fnUpdatePositions()}).on("column-visibility.dt",function(){FixedHeader.fnMeasure();d._fnUpdateClones(!0);d._fnUpdatePositions()}), +FixedHeader.fnMeasure(),d._fnUpdateClones(),d._fnUpdatePositions(),c.bInitComplete=!0)},fnInitSettings:function(a,b){if(b!==h&&(b.top!==h&&(a.oSides.top=b.top),b.bottom!==h&&(a.oSides.bottom=b.bottom),"boolean"==typeof b.left?a.oSides.left=b.left?1:0:b.left!==h&&(a.oSides.left=b.left),"boolean"==typeof b.right?a.oSides.right=b.right?1:0:b.right!==h&&(a.oSides.right=b.right),b.zTop!==h&&(a.oZIndexes.top=b.zTop),b.zBottom!==h&&(a.oZIndexes.bottom=b.zBottom),b.zLeft!==h&&(a.oZIndexes.left=b.zLeft),b.zRight!== +h&&(a.oZIndexes.right=b.zRight),b.offsetTop!==h&&(a.oOffset.top=b.offsetTop),b.alwaysCloneTop!==h&&(a.oCloneOnDraw.top=b.alwaysCloneTop),b.alwaysCloneBottom!==h&&(a.oCloneOnDraw.bottom=b.alwaysCloneBottom),b.alwaysCloneLeft!==h&&(a.oCloneOnDraw.left=b.alwaysCloneLeft),b.alwaysCloneRight!==h))a.oCloneOnDraw.right=b.alwaysCloneRight},_fnCloneTable:function(a,b,c,d){var f=this.fnGetSettings(),g;"absolute"!=e(f.nTable.parentNode).css("position")&&(f.nTable.parentNode.style.position="relative");g=f.nTable.cloneNode(!1); +g.removeAttribute("id");var i=k.createElement("div");i.style.position="absolute";i.style.top="0px";i.style.left="0px";i.className+=" FixedHeader_Cloned "+a+" "+b;"fixedHeader"==a&&(i.style.zIndex=f.oZIndexes.top);"fixedFooter"==a&&(i.style.zIndex=f.oZIndexes.bottom);"fixedLeft"==a?i.style.zIndex=f.oZIndexes.left:"fixedRight"==a&&(i.style.zIndex=f.oZIndexes.right);g.style.margin="0";i.appendChild(g);k.body.appendChild(i);return{nNode:g,nWrapper:i,sType:a,sPosition:"",sTop:"",sLeft:"",fnClone:c,iCells:d}}, +_fnMeasure:function(){var a=this.fnGetSettings(),b=a.oMes,c=e(a.nTable),d=c.offset(),f=this._fnSumScroll(a.nTable.parentNode,"scrollTop");this._fnSumScroll(a.nTable.parentNode,"scrollLeft");b.iTableWidth=c.outerWidth();b.iTableHeight=c.outerHeight();b.iTableLeft=d.left+a.nTable.parentNode.scrollLeft;b.iTableTop=d.top+f;b.iTableRight=b.iTableLeft+b.iTableWidth;b.iTableRight=FixedHeader.oDoc.iWidth-b.iTableLeft-b.iTableWidth;b.iTableBottom=FixedHeader.oDoc.iHeight-b.iTableTop-b.iTableHeight},_fnSumScroll:function(a, +b){for(var c=a[b];(a=a.parentNode)&&!("HTML"==a.nodeName||"BODY"==a.nodeName);)c=a[b];return c},_fnUpdatePositions:function(){var a=this.fnGetSettings();this._fnMeasure();for(var b=0,c=a.aoCache.length;bd.iScrollTop+b.oOffset.top?(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",c.iTableTop+"px","top",e.style),this._fnUpdateCache(a,"sLeft",c.iTableLeft+"px","left",e.style)):d.iScrollTop+b.oOffset.top>c.iTableTop+ +g?(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",c.iTableTop+g+"px","top",e.style),this._fnUpdateCache(a,"sLeft",c.iTableLeft+"px","left",e.style)):(this._fnUpdateCache(a,"sPosition","fixed","position",e.style),this._fnUpdateCache(a,"sTop",b.oOffset.top+"px","top",e.style),this._fnUpdateCache(a,"sLeft",c.iTableLeft-d.iScrollLeft+"px","left",e.style))},_fnUpdateCache:function(a,b,c,d,e){a[b]!=c&&(e[d]=c,a[b]=c)},_fnClassUpdate:function(a,b){var c=this; +if("TR"===a.nodeName.toUpperCase()||"TH"===a.nodeName.toUpperCase()||"TD"===a.nodeName.toUpperCase()||"SPAN"===a.nodeName.toUpperCase())b.className=a.className;e(a).children().each(function(d){c._fnClassUpdate(e(a).children()[d],e(b).children()[d])})},_fnCloneThead:function(a){var b=this.fnGetSettings(),c=a.nNode;if(b.bInitComplete&&!b.oCloneOnDraw.top)this._fnClassUpdate(e("thead",b.nTable)[0],e("thead",c)[0]);else{var d=e(b.nTable).outerWidth();a.nWrapper.style.width=d+"px";for(c.style.width=d+ +"px";0tr th",b.nTable).each(function(){f.push(e(this).width())});e("thead>tr td",b.nTable).each(function(){g.push(e(this).width())});e("thead>tr th",b.nTable).each(function(a){e("thead>tr th:eq("+a+")",c).width(f[a]);e(this).width(f[a])});e("thead>tr td",b.nTable).each(function(a){e("thead>tr td:eq("+a+")",c).width(g[a]);e(this).width(g[a])}); +e("th.sorting, th.sorting_desc, th.sorting_asc",c).bind("click",function(){this.blur()})}},_fnCloneTfoot:function(a){var b=this.fnGetSettings(),c=a.nNode;for(a.nWrapper.style.width=e(b.nTable).outerWidth()+"px";0tr th",b.nTable).each(function(a){e("tfoot:eq(0)>tr th:eq("+a+")",c).width(e(this).width())});e("tfoot:eq(0)>tr td",b.nTable).each(function(a){e("tfoot:eq(0)>tr td:eq("+ +a+")",c).width(e(this).width())})},_fnCloneTLeft:function(a){for(var b=this.fnGetSettings(),c=a.nNode,d=e("tbody",b.nTable)[0];0= oSettings.fnDisplayEnd() ) + { + if ( oSettings._iDisplayLength >= 0 ) + { + /* Make sure we are not over running the display array */ + if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() ) + { + oSettings._iDisplayStart += oSettings._iDisplayLength; + } + } + else + { + oSettings._iDisplayStart = 0; + } + _oDatatable.oApi._fnCalculateEnd( oSettings ); + } + + /* Page backwards */ + while ( iRow < oSettings._iDisplayStart ) + { + oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ? + oSettings._iDisplayStart - oSettings._iDisplayLength : + 0; + + if ( oSettings._iDisplayStart < 0 ) + { + oSettings._iDisplayStart = 0; + } + _oDatatable.oApi._fnCalculateEnd( oSettings ); + } + + /* Re-draw the table */ + _oDatatable.oApi._fnDraw( oSettings ); + + /* Restore the key capture */ + _bKeyCapture = bKeyCaptureCache; + } + + /* Cache the information that we are interested in */ + var aNewPos = _fnCoordsFromCell( nTarget ); + _nOldFocus = nTarget; + _iOldX = aNewPos[0]; + _iOldY = aNewPos[1]; + + var iViewportHeight, iViewportWidth, iScrollTop, iScrollLeft, iHeight, iWidth, aiPos; + if ( bAutoScroll ) + { + /* Scroll the viewport such that the new cell is fully visible in the rendered window */ + iViewportHeight = $(window).height(); + iViewportWidth = $(window).width(); + iScrollTop = $(document).scrollTop(); + iScrollLeft = $(document).scrollLeft(); + iHeight = nTarget.offsetHeight; + iWidth = nTarget.offsetWidth; + aiPos = _fnGetPos( nTarget ); + + /* Take account of scrolling in DataTables 1.7 - remove scrolling since that would add to + * the positioning calculation + */ + if ( _oDatatable && typeof oSettings.oScroll != 'undefined' && + (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) + { + aiPos[1] -= $(oSettings.nTable.parentNode).scrollTop(); + aiPos[0] -= $(oSettings.nTable.parentNode).scrollLeft(); + } + + /* Correct viewport positioning for vertical scrolling */ + if ( aiPos[1]+iHeight > iScrollTop+iViewportHeight ) + { + /* Displayed element if off the bottom of the viewport */ + _fnSetScrollTop( aiPos[1]+iHeight - iViewportHeight ); + } + else if ( aiPos[1] < iScrollTop ) + { + /* Displayed element if off the top of the viewport */ + _fnSetScrollTop( aiPos[1] ); + } + + /* Correct viewport positioning for horizontal scrolling */ + if ( aiPos[0]+iWidth > iScrollLeft+iViewportWidth ) + { + /* Displayed element is off the bottom of the viewport */ + _fnSetScrollLeft( aiPos[0]+iWidth - iViewportWidth ); + } + else if ( aiPos[0] < iScrollLeft ) + { + /* Displayed element if off the Left of the viewport */ + _fnSetScrollLeft( aiPos[0] ); + } + } + + /* Take account of scrolling in DataTables 1.7 */ + if ( _oDatatable && typeof oSettings.oScroll != 'undefined' && + (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) + { + var dtScrollBody = oSettings.nTable.parentNode; + iViewportHeight = dtScrollBody.clientHeight; + iViewportWidth = dtScrollBody.clientWidth; + iScrollTop = dtScrollBody.scrollTop; + iScrollLeft = dtScrollBody.scrollLeft; + iHeight = nTarget.offsetHeight; + iWidth = nTarget.offsetWidth; + + /* Correct for vertical scrolling */ + if ( nTarget.offsetTop + iHeight > iViewportHeight+iScrollTop ) + { + dtScrollBody.scrollTop = (nTarget.offsetTop + iHeight) - iViewportHeight; + } + else if ( nTarget.offsetTop < iScrollTop ) + { + dtScrollBody.scrollTop = nTarget.offsetTop; + } + + /* Correct for horizontal scrolling */ + if ( nTarget.offsetLeft + iWidth > iViewportWidth+iScrollLeft ) + { + dtScrollBody.scrollLeft = (nTarget.offsetLeft + iWidth) - iViewportWidth; + } + else if ( nTarget.offsetLeft < iScrollLeft ) + { + dtScrollBody.scrollLeft = nTarget.offsetLeft; + } + } + + /* Focused - so we want to capture the keys */ + _fnCaptureKeys(); + + /* Fire of the focus event if there is one */ + _fnEventFire( "focus", _iOldX, _iOldY ); + } + + + /* + * Function: _fnBlur + * Purpose: Blur focus from the whole table + * Returns: - + * Inputs: - + */ + function _fnBlur() + { + _fnRemoveFocus( _nOldFocus ); + _iOldX = null; + _iOldY = null; + _nOldFocus = null; + _fnReleaseKeys(); + } + + + /* + * Function: _fnRemoveFocus + * Purpose: Remove focus from a cell and fire any blur events which are attached + * Returns: - + * Inputs: node:nTarget - cell of interest + */ + function _fnRemoveFocus( nTarget ) + { + $(nTarget).removeClass( _sFocusClass ); + $(nTarget).parent().removeClass( _sFocusClass ); + _fnEventFire( "blur", _iOldX, _iOldY ); + } + + + /* + * Function: _fnClick + * Purpose: Focus on the element that has been clicked on by the user + * Returns: - + * Inputs: event:e - click event + */ + function _fnClick ( e ) + { + var nTarget = this; + while ( nTarget.nodeName != "TD" ) + { + nTarget = nTarget.parentNode; + } + + _fnSetFocus( nTarget ); + _fnCaptureKeys(); + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Key events + */ + + /* + * Function: _fnKey + * Purpose: Deal with a key events, be it moving the focus or return etc. + * Returns: bool: - allow browser default action + * Inputs: event:e - key event + */ + function _fnKey ( e ) + { + /* If user or system has blocked KeyTable from doing anything, just ignore this event */ + if ( _that.block || !_bKeyCapture ) + { + return true; + } + + /* If a modifier key is pressed (exapct shift), ignore the event */ + if ( e.metaKey || e.altKey || e.ctrlKey ) + { + return true; + } + var + x, y, + iTableWidth = _nBody.getElementsByTagName('tr')[0].getElementsByTagName('td').length, + iTableHeight; + + /* Get table height and width - done here so as to be dynamic (if table is updated) */ + if ( _oDatatable ) + { + /* + * Locate the current node in the DataTable overriding the old positions - the reason for + * is is that there might have been some DataTables interaction between the last focus and + * now + */ + iTableHeight = _oDatatable.aiDisplay.length; + + var aDtPos = _fnFindDtCell( _nOldFocus ); + if ( aDtPos === null ) + { + /* If the table has been updated such that the focused cell can't be seen - do nothing */ + return; + } + _iOldX = aDtPos[ 0 ]; + _iOldY = aDtPos[ 1 ]; + } + else + { + iTableHeight = _nBody.getElementsByTagName('tr').length; + } + + /* Capture shift+tab to match the left arrow key */ + var iKey = (e.keyCode == 9 && e.shiftKey) ? -1 : e.keyCode; + + switch( iKey ) + { + case 13: /* return */ + e.preventDefault(); + e.stopPropagation(); + _fnEventFire( "action", _iOldX, _iOldY ); + return true; + + case 27: /* esc */ + if ( !_fnEventFire( "esc", _iOldX, _iOldY ) ) + { + /* Only lose focus if there isn't an escape handler on the cell */ + _fnBlur(); + return; + } + x = _iOldX; + y = _iOldY; + break; + + case -1: + case 37: /* left arrow */ + if ( _iOldX > 0 ) { + x = _iOldX - 1; + y = _iOldY; + } else if ( _iOldY > 0 ) { + x = iTableWidth-1; + y = _iOldY - 1; + } else { + /* at start of table */ + if ( iKey == -1 && _bForm ) + { + /* If we are in a form, return focus to the 'input' element such that tabbing will + * follow correctly in the browser + */ + _bInputFocused = true; + _nInput.focus(); + + /* This timeout is a little nasty - but IE appears to have some asyhnc behaviour for + * focus + */ + setTimeout( function(){ _bInputFocused = false; }, 0 ); + _bKeyCapture = false; + _fnBlur(); + return true; + } + else + { + return false; + } + } + break; + + case 38: /* up arrow */ + if ( _iOldY > 0 ) { + x = _iOldX; + y = _iOldY - 1; + } else { + return false; + } + break; + + case 36: /* home */ + x = _iOldX; + y = 0; + break; + + case 33: /* page up */ + x = _iOldX; + y = _iOldY - 10; + if (y < 0) { + y = 0; + } + break; + + case 9: /* tab */ + case 39: /* right arrow */ + if ( _iOldX < iTableWidth-1 ) { + x = _iOldX + 1; + y = _iOldY; + } else if ( _iOldY < iTableHeight-1 ) { + x = 0; + y = _iOldY + 1; + } else { + /* at end of table */ + if ( iKey == 9 && _bForm ) + { + /* If we are in a form, return focus to the 'input' element such that tabbing will + * follow correctly in the browser + */ + _bInputFocused = true; + _nInput.focus(); + + /* This timeout is a little nasty - but IE appears to have some asyhnc behaviour for + * focus + */ + setTimeout( function(){ _bInputFocused = false; }, 0 ); + _bKeyCapture = false; + _fnBlur(); + return true; + } + else + { + return false; + } + } + break; + + case 40: /* down arrow */ + if ( _iOldY < iTableHeight-1 ) { + x = _iOldX; + y = _iOldY + 1; + } else { + return false; + } + break; + + case 35: /* end */ + x = _iOldX; + y = iTableHeight-1; + break; + + case 34: /* page down */ + x = _iOldX; + y = _iOldY+10; + if (y > iTableHeight-1) { + y = iTableHeight-1; + } + break; + + default: /* Nothing we are interested in */ + return true; + } + + _fnSetFocus( _fnCellFromCoords(x, y) ); + return false; + } + + + /* + * Function: _fnCaptureKeys + * Purpose: Start capturing key events for this table + * Returns: - + * Inputs: - + */ + function _fnCaptureKeys( ) + { + if ( !_bKeyCapture ) + { + _bKeyCapture = true; + } + } + + + /* + * Function: _fnReleaseKeys + * Purpose: Stop capturing key events for this table + * Returns: - + * Inputs: - + */ + function _fnReleaseKeys( ) + { + _bKeyCapture = false; + } + + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Support functions + */ + + /* + * Function: _fnCellFromCoords + * Purpose: Calulate the target TD cell from x and y coordinates + * Returns: node: - TD target + * Inputs: int:x - x coordinate + * int:y - y coordinate + */ + function _fnCellFromCoords( x, y ) + { + if ( _oDatatable ) + { + if ( typeof _oDatatable.aoData[ _oDatatable.aiDisplay[ y ] ] != 'undefined' ) + { + return _oDatatable.aoData[ _oDatatable.aiDisplay[ y ] ].nTr.getElementsByTagName('td')[x]; + } + else + { + return null; + } + } + else + { + return $('tr:eq('+y+')>td:eq('+x+')', _nBody )[0]; + } + } + + + /* + * Function: _fnCoordsFromCell + * Purpose: Calculate the x and y position in a table from a TD cell + * Returns: array[2] int: [x, y] + * Inputs: node:n - TD cell of interest + * Notes: Not actually interested in this for DataTables since it might go out of date + */ + function _fnCoordsFromCell( n ) + { + if ( _oDatatable ) + { + return [ + $('td', n.parentNode).index(n), + $('tr', n.parentNode.parentNode).index(n.parentNode) + _oDatatable._iDisplayStart + ]; + } + else + { + return [ + $('td', n.parentNode).index(n), + $('tr', n.parentNode.parentNode).index(n.parentNode) + ]; + } + } + + + /* + * Function: _fnSetScrollTop + * Purpose: Set the vertical scrolling position + * Returns: - + * Inputs: int:iPos - scrolltop + * Notes: This is so nasty, but without browser detection you can't tell which you should set + * So on browsers that support both, the scroll top will be set twice. I can live with + * that :-) + */ + function _fnSetScrollTop( iPos ) + { + document.documentElement.scrollTop = iPos; + document.body.scrollTop = iPos; + } + + + /* + * Function: _fnSetScrollLeft + * Purpose: Set the horizontal scrolling position + * Returns: - + * Inputs: int:iPos - scrollleft + */ + function _fnSetScrollLeft( iPos ) + { + document.documentElement.scrollLeft = iPos; + document.body.scrollLeft = iPos; + } + + + /* + * Function: _fnGetPos + * Purpose: Get the position of an object on the rendered page + * Returns: array[2] int: [left, right] + * Inputs: node:obj - element of interest + */ + function _fnGetPos ( obj ) + { + var iLeft = 0; + var iTop = 0; + + if (obj.offsetParent) + { + iLeft = obj.offsetLeft; + iTop = obj.offsetTop; + obj = obj.offsetParent; + while (obj) + { + iLeft += obj.offsetLeft; + iTop += obj.offsetTop; + obj = obj.offsetParent; + } + } + return [iLeft,iTop]; + } + + + /* + * Function: _fnFindDtCell + * Purpose: Get the coords. of a cell from the DataTables internal information + * Returns: array[2] int: [x, y] coords. or null if not found + * Inputs: node:nTarget - the node of interest + */ + function _fnFindDtCell( nTarget ) + { + for ( var i=0, iLen=_oDatatable.aiDisplay.length ; i