diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module index 76e39ba..5fd3786 100644 --- a/core/modules/contextual/contextual.module +++ b/core/modules/contextual/contextual.module @@ -14,7 +14,6 @@ function contextual_toolbar() { } $tab['contextual'] = array( - '#type' => 'toolbar_item', 'tab' => array( '#type' => 'html_tag', '#tag' => 'button', @@ -27,6 +26,7 @@ function contextual_toolbar() { // @todo remove this once http://drupal.org/node/1908906 lands. '#options' => array('attributes' => array()), ), + '#theme_wrappers' => array('toolbar_tab_wrapper'), '#wrapper_attributes' => array( 'class' => array('element-hidden', 'contextual-toolbar-tab'), ), diff --git a/core/modules/contextual/contextual.toolbar.js b/core/modules/contextual/contextual.toolbar.js index f1e6f9f..45f9757 100644 --- a/core/modules/contextual/contextual.toolbar.js +++ b/core/modules/contextual/contextual.toolbar.js @@ -34,8 +34,7 @@ Drupal.behaviors.contextualToolbar = { }) .on('drupalOverlayClose.contextualToolbar', function () { model.set('isVisible', true); - }) - .on('drupalEditModeChanged', Drupal.contextualToolbar.editModeChangedHandler); + }); // Update the model to show the edit tab if there's >=1 contextual link. if ($contextuals.length > 0) { @@ -57,13 +56,7 @@ Drupal.behaviors.contextualToolbar = { } }; -Drupal.contextualToolbar = Drupal.contextualToolbar || { - models: {}, - views: {}, - editModeChangedHandler: function (event, data) { - Drupal.toolbar.setActiveItem($('.js .toolbar .bar .contextual-toolbar-tab')); - } -}; +Drupal.contextualToolbar = Drupal.contextualToolbar || { models: {}, views: {}}; /** * Backbone Model for the edit toggle. diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module index 3a75378..c4990b7 100644 --- a/core/modules/shortcut/shortcut.module +++ b/core/modules/shortcut/shortcut.module @@ -580,7 +580,7 @@ function shortcut_toolbar() { '#options' => array( 'attributes' => array( 'title' => t('Shortcuts'), - 'class' => array('icon', 'icon-shortcut', 'toolbar-tab'), + 'class' => array('icon', 'icon-shortcut', 'toolbar-tab',), ), ), ), diff --git a/core/modules/toolbar/js/toolbar.js b/core/modules/toolbar/js/toolbar.js index 344c69e..7b21a2b 100644 --- a/core/modules/toolbar/js/toolbar.js +++ b/core/modules/toolbar/js/toolbar.js @@ -27,41 +27,42 @@ Drupal.behaviors.toolbar = { var model = new StateModel({ locked: JSON.parse(localStorage.getItem('Drupal.toolbar.trayVerticalLocked')) || false, activeTab: document.getElementById(JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID'))), - orientation: JSON.parse(localStorage.getItem('Drupal.toolbar.trayOrientation')) || 'vertical', mqMatches: mql.matches }); - // Respond to external events. - $(window) - .on('resize.toolbar', Drupal.debounce(function (event) { - model.set('dimensionsAreValid', false); - }, 150)); - // Update the model when matchMedia fires. mql.addListener(function (mql) { model.set('mqMatches', mql.matches); }); + // Respond to viewport offset dimension changes. + $(document) + .on('drupalViewportOffsetChange.toolbar', function (event, offsets) { + model.set('offsets', { + top: offsets.top, + right: offsets.right, + bottom: offsets.bottom, + left: offsets.left + }); + // Alter the padding on the top of the body element. + $('body').css('padding-top', offsets.top); + }); + // Respond to toolbar events. $(document) - .on('drupalViewportOffsetChange.toolbar', Drupal.toolbar.adjustPlacement); - .on('drupalToolbarDimensionsChanged.toolbar', dimensionsChangeHandler) - .on('drupalToolbarOrientationChanged.toolbar', orientationChangeHandler) - .on('drupalToolbarTrayChanged.toolbar', trayChangeHandler); + .on('drupalToolbarOrientationChange.toolbar', Drupal.toolbar.orientationChangeHandler) + .on('drupalToolbarTrayChange.toolbar', Drupal.toolbar.trayChangeHandler); // Broadcast model changes to other modules. model - .on('change:height', function (model, height) { - $(document).trigger('drupalToolbarDimensionsChanged', height); - }) .on('change:orientation', function (model, orientation) { $(document).trigger('drupalToolbarOrientationChange', orientation); }) .on('change:activeTab', function (model, tab) { - $(document).trigger('drupalToolbarTabChanged', tab); + $(document).trigger('drupalToolbarTabChange', tab); }) .on('change:activeTray', function (model, tray) { - $(document).trigger('drupalToolbarTrayChanged', tray); + $(document).trigger('drupalToolbarTrayChange', tray); }); // Build the toolbar view and assign it to the closure variable reference. @@ -101,40 +102,48 @@ Drupal.behaviors.toolbar = { }; // Expose a limited public API. - Drupal.toolbar = Drupal.toolbar || { - /** - * Accepts a list of subtree menu elements. - * - * A deferred object that is resolved by an inlined JavaScript callback. - * - * JSONP callback. - * @see toolbar_subtrees_jsonp(). - */ - setSubtrees: new $.Deferred(), - - /** - * - */ - setActiveItem: function (element) { - var item; - if (typeof element === 'object') { - // Check if the element is a DOM element. - if ('tagName' in element) { - item = element; - } - // Check if the element is a jQuery set. - if ('jquery' in element) { - item = element.get(0); - } - } - else if (!element) { - item = element; - } - if (this.model && item) { - this.model.set('activeTab', item); - } - } - }; +Drupal.toolbar = Drupal.toolbar || { + /** + * Accepts a list of subtree menu elements. + * + * A deferred object that is resolved by an inlined JavaScript callback. + * + * JSONP callback. + * @see toolbar_subtrees_jsonp(). + */ + setSubtrees: new $.Deferred(), + + /** + * Responds to drupalToolbarOrientationChange. + * + * Applies classes to the body element that reflect the current orientation + * of the active toolbar. + * + * @param jQuery.Event event + * @param string orientation + * The value can be either 'horizontal' or 'vertical'. + */ + orientationChangeHandler: function (event, orientation) { + $('body') + .toggleClass('toolbar-vertical', orientation === 'vertical') + .toggleClass('toolbar-horizontal', orientation === 'horizontal'); + }, + + /** + * Responds to drupalToolbarTrayChange. + * + * Toggles the toolbar-tray-open class on the body elment. The class is applied + * when a toolbar tray is active. + * + * @param jQuery.Event event + * @param DOM tray + * The currently active tray DOM element. + */ + trayChangeHandler: function (event, tray) { + $('body') + .toggleClass('toolbar-tray-open', !!tray); + } +}; /** * Backbone model for the toolbar. @@ -159,9 +168,15 @@ var StateModel = Backbone.Model.extend({ mqMatches: null, // The height of the toolbar. height: null, - // Whether the current dimensions are valid. Dimensions can be invalidated - // by a screen resize. - dimensionsAreValid: true + // The current viewport offsets determined by Drupal.displace(). The offsets + // suggest how a module might position is components relative to the + // viewport. + offsets: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } } }); @@ -183,7 +198,7 @@ var ToolbarView = Backbone.View.extend({ this.model.on('change:activeTab', this.render, this); this.model.on('change:orientation', this.render, this); this.model.on('change:mqMatches', this.onMediaQueryChange, this); - this.model.on('change:dimensionsAreValid', this.setHeight, this); + this.model.on('change:offsets', this.adjustPlacement, this); // Add the tray orientation toggles. this.$el.find('.tray') @@ -191,25 +206,29 @@ var ToolbarView = Backbone.View.extend({ .append(Drupal.theme('toolbarOrientationToggle')); // Update the tray orientation. - if (!this.model.get('locked')) { - var orientation = this._getTrayOrientation(this.model.get('mqMatches')); - // This model change is silent because this is the initialization phase. - this.model.set('orientation', orientation, { silent: true }); - } + var orientation = this._checkOrientationLock(this._getTrayOrientation(this.model.get('mqMatches'))); + // This model change is silent because it will be triggered below when + // change:activeTab is triggered. + this.model.set('orientation', orientation, {silent: true}); // Trigger an activeTab change so that listening scripts can respond on // page load. This will call render. this.model.trigger('change:activeTab'); + + // Invoke Drupal.displace() to get the current viewport offset values. + Drupal.displace(); }, /** * Implements Backbone.View.prototype.render(). + * + * @return Backbone.View */ - render: function (model, property) { + render: function () { // Update the display of the tabs. this.refreshTabs(); // Adjust the orientation of the active tray. - this.changeOrientation(); + this.setOrientation(); // Adjust the height of the toolbar. this.model.set('dimensionsAreValid', false); @@ -217,7 +236,9 @@ var ToolbarView = Backbone.View.extend({ }, /** - * Toolbar tab click event handler; sets activeTab. + * Responds to a toolbar tab click. + * + * @param jQuery.Event event */ onTabClick: function (event) { var tab = this.model.get('activeTab'); @@ -230,7 +251,9 @@ var ToolbarView = Backbone.View.extend({ }, /** - * Orientation toggle click event handler; toggles orientation. + * Toggles the orientation of a toolbar tray. + * + * @param jQuery.Event event */ onOrientationToggleClick: function (event) { var orientation = this.model.get('orientation'); @@ -255,19 +278,22 @@ var ToolbarView = Backbone.View.extend({ }, /** - * Model change handler; updates tray orientation. + * Responds to change:orientation event when window.matchMedia fires. + * + * @param Backbone.Model model + * @param boolean mqMatches + * The matches property of a MediaQueryList object. */ onMediaQueryChange: function (model, mqMatches) { - this.model.set('orientation', this._getTrayOrientation(mqMatches)); + this.model.set('orientation', this._checkOrientationLock(this._getTrayOrientation(mqMatches))); }, /** * Gets the tray orientation depending on whether the media query matches. * - * @param Boolean mqMatches - * Indicates whether the media query matches. - * - * @return String + * @param boolean mqMatches + * The matches property of a MediaQueryList object. + * @return string * The orientation, either 'horizontal' or 'vertical'. */ _getTrayOrientation: function (mqMatches) { @@ -276,6 +302,8 @@ var ToolbarView = Backbone.View.extend({ /** * Toggles a toolbar tab and the associated tray. + * + * @param jQuery.Event event */ refreshTabs: function (event) { var $tab = $(this.model.get('activeTab')); @@ -327,15 +355,11 @@ var ToolbarView = Backbone.View.extend({ /** * Change the orientation of the tray between vertical and horizontal. */ - changeOrientation: function () { - var orientation = this.model.get('orientation'); + setOrientation: function () { + var orientation = this._checkOrientationLock(this.model.get('orientation')); + // The antiOrientation is used to render the view of action buttons like + // the tray orientation toggle. var antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical'; - var locked = this.model.get('locked'); - // Set the orientation of the tray. - // If the tray is locked to vertical in localStorage, persist the vertical - // presentation. If the tray is not locked to vertical, let the media - // query application decide the orientation. - orientation = (locked) ? 'vertical' : orientation; // Update the orientation of the trays. var $trays = this.$el.find('.tray') .removeClass('horizontal vertical') @@ -350,6 +374,17 @@ var ToolbarView = Backbone.View.extend({ .removeClass(iconClass) .addClass(iconAntiClass); + var dir = document.documentElement.dir; + var edge = (dir === 'rtl') ? 'right' : 'left'; + // Remove data-offset attributes from the trays so they can be refreshed. + $trays.removeAttr('data-offset-left data-offset-right data-offset-top'); + // If an active vertical tray exists, mark it as an offset element. + $trays.filter('.vertical.active').attr('data-offset-' + edge, ''); + // If an active horizontal tray exists, mark it as an offset element. + $trays.filter('.horizontal.active').attr('data-offset-top', ''); + // Trigger a recalculation of viewport displacing elements. + Drupal.displace(); + // Append a message that the tray orientation has been changed. Drupal.announce(Drupal.t('Tray orientation changed to @orientation.', { '@orientation': orientation @@ -357,17 +392,29 @@ var ToolbarView = Backbone.View.extend({ }, /** - * The height of the toolbar offsets the top of the page content. + * Returns an orientation based on the orientation lock. * - * Page components can register with the offsettopchange event to know when - * the height of the toolbar changes. + * Orientation is locked to the vertical position when a user changes a + * horizontal tray to a vertical try using the tray orientation toggle button. + * + * @param string orientation + * The value can be either 'horizontal' or 'vertical'. + * @return string + * The orientation, either 'horizontal' or 'vertical'. */ - setHeight: function (model, dimensionsAreValid) { - // Don't reprocess unless the current dimensions have been invalidated. - if (dimensionsAreValid) { - return; - } - var originalHeight = this.model.get('height'); + _checkOrientationLock: function (orientation) { + var locked = this.model.get('locked'); + // Set the orientation of the tray. + // If the tray is locked to vertical in localStorage, persist the vertical + // presentation. If the tray is not locked to vertical, let the media + // query application decide the orientation. + return (locked) ? 'vertical' : orientation; + }, + + /** + * Sets the tops of the trays so that they align with the bottom of the bar. + */ + adjustPlacement: function () { // Set the top of the all the trays to the height of the bar. var barHeight = this.$el.find('.bar').outerHeight(); var height = barHeight; @@ -380,67 +427,10 @@ var ToolbarView = Backbone.View.extend({ tray.style.top = bhpx; } } - /** - * Get the height of the active tray and include it in the total - * height of the toolbar. - */ - height += $trays.filter('.active.horizontal').outerHeight() || 0; - // Indicate the height of the toolbar in the attribute data-offset-top. - var offset = parseInt(this.$el.attr('data-offset-top'), 10); - if (originalHeight !== height) { - this.$el.attr('data-offset-top', height); - this.model.set('height', height); - } - this.model.set('dimensionsAreValid', true); } }); /** - * Repositions trays and sets body padding according to the height of the bar. - * - * @param {Event} event - * - jQuery Event object. - * - * @param {Object} offsets - * - Contains for keys -- top, right, bottom and left -- that indicate the - * viewport offset distances calculated by Drupal.displace(). - */ -Drupal.toolbar.adjustPlacement = function (event, offsets) { - // Set the top of the all the trays to the height of the bar. - var barHeight = $toolbar.find('.bar').outerHeight(); - var height = barHeight; - var bhpx = barHeight + 'px'; - var tray; - for (var i = 0, il = $trays.length; i < il; i++) { - tray = $trays[i]; - if (!tray.style.top.length || (tray.style.top !== bhpx)) { - tray.style.top = bhpx; - } - } - // Alter the padding on the top of the body element. - $('body').css('padding-top', offsets.top); -}; - -/** - * Sets the width of a vertical tray in a data attribute. - * - * If the width of the tray changed, Drupal.displace is called so that elements - * can adjust to the placement of the tray. - */ -Drupal.toolbar.setTrayWidth = function () { - var dir = document.documentElement.dir; - var edge = (dir === 'rtl') ? 'right' : 'left'; - // Remove the left offset from the trays. - $toolbar.find('.tray').removeAttr('data-offset-' + edge + ' data-offset-top'); - // If an active vertical tray exists, mark it as an offset element. - $toolbar.find('.tray.vertical.active').attr('data-offset-' + edge, ''); - // If an active horizontal tray exists, mark it as an offset element. - $toolbar.find('.tray.horizontal.active').attr('data-offset-top', ''); - // Trigger a recalculation of viewport displacing elements. - Drupal.displace(); -}; - -/** * Backbone Model for collapsible menus. */ var MenuStateModel = Backbone.Model.extend({ @@ -464,7 +454,7 @@ var MenuView = Backbone.View.extend({ /** * Implements Backbone.View.prototype.render(). */ - render: function (model, property) { + render: function () { var subtrees = this.model.get('subtrees'); // Add subtrees. // @todo Optimize this to delay adding each subtree to the DOM until it is @@ -485,39 +475,8 @@ var MenuView = Backbone.View.extend({ .drupalToolbarMenu(); } } - }); - - -/** - * - */ -function dimensionsChangeHandler (event, height) { - // Alter the padding on the top of the body element. - $('body') - .css('padding-top', height); - $(document) - .trigger('offsettopchange', height); -} - -/** - * - */ -function orientationChangeHandler (event, orientation) { - $('body') - .toggleClass('toolbar-vertical', orientation === 'vertical') - .toggleClass('toolbar-horizontal', orientation === 'horizontal'); -} - -/** - * - */ -function trayChangeHandler (event, tray) { - $('body') - .toggleClass('toolbar-tray-open', !!tray); -} - /** * Theme function for the toolbar orientation toggle. * @@ -530,4 +489,4 @@ Drupal.theme.toolbarOrientationToggle = function () { ''; }; -}(jQuery, Backbone, Drupal, drupalSettings, document, window)); +}(jQuery, Backbone, Drupal, drupalSettings)); diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index 6afa5c2..6645147 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -91,8 +91,6 @@ function toolbar_element_info() { // The id cannot be simply "toolbar" or it will clash with the simpletest // tests listing which produces a checkbox with attribute id="toolbar" 'id' => 'toolbar-administration', - // The 'overlay-displace-top' class pushes the overlay down, so it appears - // below the toolbar. 'class' => array('toolbar',), 'role' => 'navigation', ), @@ -284,21 +282,25 @@ function theme_toolbar(&$variables) { */ function toolbar_pre_render_item($element) { + // Assign each item an unique ID. + $id = drupal_html_id('toolbar-item'); + + // Provide attributes for a toolbar item. + $attributes = array( + 'id' => $id, + ); + // If tray content is present, markup the tray and its associated trigger. if (!empty($element['tray'])) { - // Trays are associated with their trigger by a unique ID. - $id = drupal_html_id('toolbar-tray'); - $element['tab']['#tray_id'] = $id; - - // Provide attributes for a tray trigger. - $attributes = array( - 'id' => 'toolbar-tab-' . $id, - 'data-toolbar-tray' => $id, + // Provide attributes necessary for trays. + $attributes += array( + 'data-toolbar-tray' => $id . '-tray', 'aria-owns' => $id, 'role' => 'button', 'aria-pressed' => 'false', ); + // Merge in module-provided attributes. $element['tab'] += array('#attributes' => array()); $element['tab']['#attributes'] += $attributes; @@ -306,9 +308,9 @@ function toolbar_pre_render_item($element) { // Provide attributes for the tray theme wrapper. $attributes = array( - 'id' => $id, - 'data-toolbar-tray' => $id, - 'aria-owned-by' => 'toolbar-tab-' . $id, + 'id' => $id . '-tray', + 'data-toolbar-tray' => $id . '-tray', + 'aria-owned-by' => $id, ); // Merge in module-provided attributes. if (!isset($element['tray']['#wrapper_attributes'])) { @@ -485,7 +487,7 @@ function toolbar_toolbar() { '#options' => array( 'attributes' => array( 'title' => t('Admin menu'), - 'class' => array('icon', 'icon-menu', 'toolbar-tab'), + 'class' => array('icon', 'icon-menu', 'toolbar-tab',), ), ), ), diff --git a/core/modules/tour/js/tour.js b/core/modules/tour/js/tour.js index b6e8a58..d8885be 100644 --- a/core/modules/tour/js/tour.js +++ b/core/modules/tour/js/tour.js @@ -130,7 +130,6 @@ Drupal.tour.views.ToggleTourView = Backbone.View.extend({ */ onClick: function (event) { this.model.set('isActive', !this.model.get('isActive')); - Drupal.toolbar.setActiveItem(event.target); event.preventDefault(); event.stopPropagation(); }, @@ -202,4 +201,4 @@ Drupal.tour.views.ToggleTourView = Backbone.View.extend({ }); -})(jQuery, Backbone, Drupal, document); +})(jQuery, Backbone, Drupal, document); \ No newline at end of file diff --git a/core/modules/user/user.module b/core/modules/user/user.module index f092329..efd6f65 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -2716,7 +2716,7 @@ function user_toolbar() { '#options' => array( 'attributes' => array( 'title' => t('My account'), - 'class' => array('icon', 'icon-user', 'toolbar-tab'), + 'class' => array('icon', 'icon-user', 'toolbar-tab',), ), ), ),