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