Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / misc / tableselect.es6.js
1 /**
2  * @file
3  * Table select functionality.
4  */
5
6 (function ($, Drupal) {
7   /**
8    * Initialize tableSelects.
9    *
10    * @type {Drupal~behavior}
11    *
12    * @prop {Drupal~behaviorAttach} attach
13    *   Attaches tableSelect functionality.
14    */
15   Drupal.behaviors.tableSelect = {
16     attach(context, settings) {
17       // Select the inner-most table in case of nested tables.
18       $(context)
19         .find('th.select-all')
20         .closest('table')
21         .once('table-select')
22         .each(Drupal.tableSelect);
23     },
24   };
25
26   /**
27    * Callback used in {@link Drupal.behaviors.tableSelect}.
28    */
29   Drupal.tableSelect = function () {
30     // Do not add a "Select all" checkbox if there are no rows with checkboxes
31     // in the table.
32     if ($(this).find('td input[type="checkbox"]').length === 0) {
33       return;
34     }
35
36     // Keep track of the table, which checkbox is checked and alias the
37     // settings.
38     const table = this;
39     let checkboxes;
40     let lastChecked;
41     const $table = $(table);
42     const strings = {
43       selectAll: Drupal.t('Select all rows in this table'),
44       selectNone: Drupal.t('Deselect all rows in this table'),
45     };
46     const updateSelectAll = function (state) {
47       // Update table's select-all checkbox (and sticky header's if available).
48       $table.prev('table.sticky-header').addBack().find('th.select-all input[type="checkbox"]').each(function () {
49         const $checkbox = $(this);
50         const stateChanged = $checkbox.prop('checked') !== state;
51
52         $checkbox.attr('title', state ? strings.selectNone : strings.selectAll);
53
54         /**
55          * @checkbox {HTMLElement}
56          */
57         if (stateChanged) {
58           $checkbox.prop('checked', state).trigger('change');
59         }
60       });
61     };
62
63     // Find all <th> with class select-all, and insert the check all checkbox.
64     $table.find('th.select-all').prepend($('<input type="checkbox" class="form-checkbox" />').attr('title', strings.selectAll)).on('click', (event) => {
65       if ($(event.target).is('input[type="checkbox"]')) {
66         // Loop through all checkboxes and set their state to the select all
67         // checkbox' state.
68         checkboxes.each(function () {
69           const $checkbox = $(this);
70           const stateChanged = $checkbox.prop('checked') !== event.target.checked;
71
72           /**
73            * @checkbox {HTMLElement}
74            */
75           if (stateChanged) {
76             $checkbox.prop('checked', event.target.checked).trigger('change');
77           }
78           // Either add or remove the selected class based on the state of the
79           // check all checkbox.
80
81           /**
82            * @checkbox {HTMLElement}
83            */
84           $checkbox.closest('tr').toggleClass('selected', this.checked);
85         });
86         // Update the title and the state of the check all box.
87         updateSelectAll(event.target.checked);
88       }
89     });
90
91     // For each of the checkboxes within the table that are not disabled.
92     checkboxes = $table.find('td input[type="checkbox"]:enabled').on('click', function (e) {
93       // Either add or remove the selected class based on the state of the
94       // check all checkbox.
95
96       /**
97        * @this {HTMLElement}
98        */
99       $(this).closest('tr').toggleClass('selected', this.checked);
100
101       // If this is a shift click, we need to highlight everything in the
102       // range. Also make sure that we are actually checking checkboxes
103       // over a range and that a checkbox has been checked or unchecked before.
104       if (e.shiftKey && lastChecked && lastChecked !== e.target) {
105         // We use the checkbox's parent <tr> to do our range searching.
106         Drupal.tableSelectRange($(e.target).closest('tr')[0], $(lastChecked).closest('tr')[0], e.target.checked);
107       }
108
109       // If all checkboxes are checked, make sure the select-all one is checked
110       // too, otherwise keep unchecked.
111       updateSelectAll((checkboxes.length === checkboxes.filter(':checked').length));
112
113       // Keep track of the last checked checkbox.
114       lastChecked = e.target;
115     });
116
117     // If all checkboxes are checked on page load, make sure the select-all one
118     // is checked too, otherwise keep unchecked.
119     updateSelectAll((checkboxes.length === checkboxes.filter(':checked').length));
120   };
121
122   /**
123    * @param {HTMLElement} from
124    *   The HTML element representing the "from" part of the range.
125    * @param {HTMLElement} to
126    *   The HTML element representing the "to" part of the range.
127    * @param {bool} state
128    *   The state to set on the range.
129    */
130   Drupal.tableSelectRange = function (from, to, state) {
131     // We determine the looping mode based on the order of from and to.
132     const mode = from.rowIndex > to.rowIndex ? 'previousSibling' : 'nextSibling';
133
134     // Traverse through the sibling nodes.
135     for (let i = from[mode]; i; i = i[mode]) {
136       const $i = $(i);
137       // Make sure that we're only dealing with elements.
138       if (i.nodeType !== 1) {
139         continue;
140       }
141       // Either add or remove the selected class based on the state of the
142       // target checkbox.
143       $i.toggleClass('selected', state);
144       $i.find('input[type="checkbox"]').prop('checked', state);
145
146       if (to.nodeType) {
147         // If we are at the end of the range, stop.
148         if (i === to) {
149           break;
150         }
151       }
152       // A faster alternative to doing $(i).filter(to).length.
153       else if ($.filter(to, [i]).r.length) {
154         break;
155       }
156     }
157   };
158 }(jQuery, Drupal));