Added the Search API Synonym module to deal specifically with licence and license...
[yaffs-website] / web / core / modules / toolbar / js / toolbar.es6.js
1 /**
2  * @file
3  * Defines the behavior of the Drupal administration toolbar.
4  */
5
6 (function($, Drupal, drupalSettings) {
7   // Merge run-time settings with the defaults.
8   const options = $.extend(
9     {
10       breakpoints: {
11         'toolbar.narrow': '',
12         'toolbar.standard': '',
13         'toolbar.wide': '',
14       },
15     },
16     drupalSettings.toolbar,
17     // Merge strings on top of drupalSettings so that they are not mutable.
18     {
19       strings: {
20         horizontal: Drupal.t('Horizontal orientation'),
21         vertical: Drupal.t('Vertical orientation'),
22       },
23     },
24   );
25
26   /**
27    * Registers tabs with the toolbar.
28    *
29    * The Drupal toolbar allows modules to register top-level tabs. These may
30    * point directly to a resource or toggle the visibility of a tray.
31    *
32    * Modules register tabs with hook_toolbar().
33    *
34    * @type {Drupal~behavior}
35    *
36    * @prop {Drupal~behaviorAttach} attach
37    *   Attaches the toolbar rendering functionality to the toolbar element.
38    */
39   Drupal.behaviors.toolbar = {
40     attach(context) {
41       // Verify that the user agent understands media queries. Complex admin
42       // toolbar layouts require media query support.
43       if (!window.matchMedia('only screen').matches) {
44         return;
45       }
46       // Process the administrative toolbar.
47       $(context)
48         .find('#toolbar-administration')
49         .once('toolbar')
50         .each(function() {
51           // Establish the toolbar models and views.
52           const model = new Drupal.toolbar.ToolbarModel({
53             locked: JSON.parse(
54               localStorage.getItem('Drupal.toolbar.trayVerticalLocked'),
55             ),
56             activeTab: document.getElementById(
57               JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID')),
58             ),
59             height: $('#toolbar-administration').outerHeight(),
60           });
61
62           Drupal.toolbar.models.toolbarModel = model;
63
64           // Attach a listener to the configured media query breakpoints.
65           // Executes it before Drupal.toolbar.views to avoid extra rendering.
66           Object.keys(options.breakpoints).forEach(label => {
67             const mq = options.breakpoints[label];
68             const mql = window.matchMedia(mq);
69             Drupal.toolbar.mql[label] = mql;
70             // Curry the model and the label of the media query breakpoint to
71             // the mediaQueryChangeHandler function.
72             mql.addListener(
73               Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label),
74             );
75             // Fire the mediaQueryChangeHandler for each configured breakpoint
76             // so that they process once.
77             Drupal.toolbar.mediaQueryChangeHandler.call(
78               null,
79               model,
80               label,
81               mql,
82             );
83           });
84
85           Drupal.toolbar.views.toolbarVisualView = new Drupal.toolbar.ToolbarVisualView(
86             {
87               el: this,
88               model,
89               strings: options.strings,
90             },
91           );
92           Drupal.toolbar.views.toolbarAuralView = new Drupal.toolbar.ToolbarAuralView(
93             {
94               el: this,
95               model,
96               strings: options.strings,
97             },
98           );
99           Drupal.toolbar.views.bodyVisualView = new Drupal.toolbar.BodyVisualView(
100             {
101               el: this,
102               model,
103             },
104           );
105
106           // Force layout render to fix mobile view. Only needed on load, not
107           // for every media query match.
108           model.trigger('change:isFixed', model, model.get('isFixed'));
109           model.trigger('change:activeTray', model, model.get('activeTray'));
110
111           // Render collapsible menus.
112           const menuModel = new Drupal.toolbar.MenuModel();
113           Drupal.toolbar.models.menuModel = menuModel;
114           Drupal.toolbar.views.menuVisualView = new Drupal.toolbar.MenuVisualView(
115             {
116               el: $(this)
117                 .find('.toolbar-menu-administration')
118                 .get(0),
119               model: menuModel,
120               strings: options.strings,
121             },
122           );
123
124           // Handle the resolution of Drupal.toolbar.setSubtrees.
125           // This is handled with a deferred so that the function may be invoked
126           // asynchronously.
127           Drupal.toolbar.setSubtrees.done(subtrees => {
128             menuModel.set('subtrees', subtrees);
129             const theme = drupalSettings.ajaxPageState.theme;
130             localStorage.setItem(
131               `Drupal.toolbar.subtrees.${theme}`,
132               JSON.stringify(subtrees),
133             );
134             // Indicate on the toolbarModel that subtrees are now loaded.
135             model.set('areSubtreesLoaded', true);
136           });
137
138           // Trigger an initial attempt to load menu subitems. This first attempt
139           // is made after the media query handlers have had an opportunity to
140           // process. The toolbar starts in the vertical orientation by default,
141           // unless the viewport is wide enough to accommodate a horizontal
142           // orientation. Thus we give the Toolbar a chance to determine if it
143           // should be set to horizontal orientation before attempting to load
144           // menu subtrees.
145           Drupal.toolbar.views.toolbarVisualView.loadSubtrees();
146
147           $(document)
148             // Update the model when the viewport offset changes.
149             .on('drupalViewportOffsetChange.toolbar', (event, offsets) => {
150               model.set('offsets', offsets);
151             });
152
153           // Broadcast model changes to other modules.
154           model
155             .on('change:orientation', (model, orientation) => {
156               $(document).trigger(
157                 'drupalToolbarOrientationChange',
158                 orientation,
159               );
160             })
161             .on('change:activeTab', (model, tab) => {
162               $(document).trigger('drupalToolbarTabChange', tab);
163             })
164             .on('change:activeTray', (model, tray) => {
165               $(document).trigger('drupalToolbarTrayChange', tray);
166             });
167
168           // If the toolbar's orientation is horizontal and no active tab is
169           // defined then show the tray of the first toolbar tab by default (but
170           // not the first 'Home' toolbar tab).
171           if (
172             Drupal.toolbar.models.toolbarModel.get('orientation') ===
173               'horizontal' &&
174             Drupal.toolbar.models.toolbarModel.get('activeTab') === null
175           ) {
176             Drupal.toolbar.models.toolbarModel.set({
177               activeTab: $(
178                 '.toolbar-bar .toolbar-tab:not(.home-toolbar-tab) a',
179               ).get(0),
180             });
181           }
182
183           $(window).on({
184             'dialog:aftercreate': (event, dialog, $element, settings) => {
185               const $toolbar = $('#toolbar-bar');
186               $toolbar.css('margin-top', '0');
187
188               // When off-canvas is positioned in top, toolbar has to be moved down.
189               if (settings.drupalOffCanvasPosition === 'top') {
190                 const height = Drupal.offCanvas
191                   .getContainer($element)
192                   .outerHeight();
193                 $toolbar.css('margin-top', `${height}px`);
194
195                 $element.on('dialogContentResize.off-canvas', () => {
196                   const newHeight = Drupal.offCanvas
197                     .getContainer($element)
198                     .outerHeight();
199                   $toolbar.css('margin-top', `${newHeight}px`);
200                 });
201               }
202             },
203             'dialog:beforeclose': () => {
204               $('#toolbar-bar').css('margin-top', '0');
205             },
206           });
207         });
208     },
209   };
210
211   /**
212    * Toolbar methods of Backbone objects.
213    *
214    * @namespace
215    */
216   Drupal.toolbar = {
217     /**
218      * A hash of View instances.
219      *
220      * @type {object.<string, Backbone.View>}
221      */
222     views: {},
223
224     /**
225      * A hash of Model instances.
226      *
227      * @type {object.<string, Backbone.Model>}
228      */
229     models: {},
230
231     /**
232      * A hash of MediaQueryList objects tracked by the toolbar.
233      *
234      * @type {object.<string, object>}
235      */
236     mql: {},
237
238     /**
239      * Accepts a list of subtree menu elements.
240      *
241      * A deferred object that is resolved by an inlined JavaScript callback.
242      *
243      * @type {jQuery.Deferred}
244      *
245      * @see toolbar_subtrees_jsonp().
246      */
247     setSubtrees: new $.Deferred(),
248
249     /**
250      * Respond to configured narrow media query changes.
251      *
252      * @param {Drupal.toolbar.ToolbarModel} model
253      *   A toolbar model
254      * @param {string} label
255      *   Media query label.
256      * @param {object} mql
257      *   A MediaQueryList object.
258      */
259     mediaQueryChangeHandler(model, label, mql) {
260       switch (label) {
261         case 'toolbar.narrow':
262           model.set({
263             isOriented: mql.matches,
264             isTrayToggleVisible: false,
265           });
266           // If the toolbar doesn't have an explicit orientation yet, or if the
267           // narrow media query doesn't match then set the orientation to
268           // vertical.
269           if (!mql.matches || !model.get('orientation')) {
270             model.set({ orientation: 'vertical' }, { validate: true });
271           }
272           break;
273
274         case 'toolbar.standard':
275           model.set({
276             isFixed: mql.matches,
277           });
278           break;
279
280         case 'toolbar.wide':
281           model.set(
282             {
283               orientation:
284                 mql.matches && !model.get('locked') ? 'horizontal' : 'vertical',
285             },
286             { validate: true },
287           );
288           // The tray orientation toggle visibility does not need to be
289           // validated.
290           model.set({
291             isTrayToggleVisible: mql.matches,
292           });
293           break;
294
295         default:
296           break;
297       }
298     },
299   };
300
301   /**
302    * A toggle is an interactive element often bound to a click handler.
303    *
304    * @return {string}
305    *   A string representing a DOM fragment.
306    */
307   Drupal.theme.toolbarOrientationToggle = function() {
308     return (
309       '<div class="toolbar-toggle-orientation"><div class="toolbar-lining">' +
310       '<button class="toolbar-icon" type="button"></button>' +
311       '</div></div>'
312     );
313   };
314
315   /**
316    * Ajax command to set the toolbar subtrees.
317    *
318    * @param {Drupal.Ajax} ajax
319    *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
320    * @param {object} response
321    *   JSON response from the Ajax request.
322    * @param {number} [status]
323    *   XMLHttpRequest status.
324    */
325   Drupal.AjaxCommands.prototype.setToolbarSubtrees = function(
326     ajax,
327     response,
328     status,
329   ) {
330     Drupal.toolbar.setSubtrees.resolve(response.subtrees);
331   };
332 })(jQuery, Drupal, drupalSettings);