diff --git a/core/modules/responsive_preview/config/responsive-preview.devices.yml b/core/modules/responsive_preview/config/responsive-preview.devices.yml index da246a4..2d6ad1d 100644 --- a/core/modules/responsive_preview/config/responsive-preview.devices.yml +++ b/core/modules/responsive_preview/config/responsive-preview.devices.yml @@ -15,7 +15,7 @@ devices: width: 768 height: 1024 desktop: - label: desktop + label: Standard desktop dimensions: width: 1366 height: 768 diff --git a/core/modules/responsive_preview/css/responsive-preview.base-rtl.css b/core/modules/responsive_preview/css/responsive-preview.base-rtl.css index d969346..497a528 100644 --- a/core/modules/responsive_preview/css/responsive-preview.base-rtl.css +++ b/core/modules/responsive_preview/css/responsive-preview.base-rtl.css @@ -3,20 +3,35 @@ * RTL base styling for responsive preview. */ +/** + * Toolbar tab. + */ +/* At narrow screen widths, float the tab to the left so it falls in line with + * the rest of the toolbar tabs. */ .js .toolbar .bar .responsive-preview-toolbar-tab.tab { - float: right; + float: right; /* LTR */ } - +/* At wide widths, float the tab to the left. */ @media only screen and (min-width: 36em) { .js .toolbar .bar .responsive-preview-toolbar-tab.tab { - float: left; + float: left; /* LTR */ } } +.responsive-preview-toolbar-tab .responsive-preview-options { + left: 0.3em; + right: auto; +} +/** + * Preview container. + * + * The container is kept offscreen after it is built and has been disabled. + */ #responsive-preview-container { - left: 0; + left: auto; right: -200%; } #responsive-preview-container.active { + left: auto; right: 0; } diff --git a/core/modules/responsive_preview/css/responsive-preview.base.css b/core/modules/responsive_preview/css/responsive-preview.base.css index 2fc1a40..49a9cef 100644 --- a/core/modules/responsive_preview/css/responsive-preview.base.css +++ b/core/modules/responsive_preview/css/responsive-preview.base.css @@ -22,6 +22,7 @@ .js .toolbar .bar .responsive-preview-toolbar-tab.tab { display: block; float: left; /* LTR */ + position: relative; } /* At wide widths, float the tab to the right. */ @media only screen and (min-width: 36em) { @@ -31,10 +32,14 @@ } .responsive-preview-toolbar-tab .responsive-preview-options { display: none; + right: 0.3em; /* LTR */ } .responsive-preview-toolbar-tab.open .responsive-preview-options { display: block; } +.js .responsive-preview-toolbar-tab.tab .responsive-preview-options li { + float: none; +} /** * Preview container. @@ -42,10 +47,12 @@ * The container is kept offscreen after it is built and has been disabled. */ #responsive-preview-container { + bottom: 0; display: none; height: 100%; left: -200%; /* LTR */ - position: absolute; + position: fixed; + top: 0; width: 100%; z-index: 1050; } @@ -81,10 +88,16 @@ /** * Override Toolbar styling in the preview iframe. */ -.responsive-preview-frame #toolbar-administration { - display: none !important; -} body.toolbar-tray-open.responsive-preview-frame { margin-left: 0 !important; margin-right: 0 !important; } +.responsive-preview-frame { + overflow-x: hidden !important; +} +.responsive-preview-frame #toolbar-administration { + display: none !important; +} +.responsive-preview-frame .contextual { + display: none !important; +} diff --git a/core/modules/responsive_preview/css/responsive-preview.theme-rtl.css b/core/modules/responsive_preview/css/responsive-preview.theme-rtl.css index f5e1339..47a60aa 100644 --- a/core/modules/responsive_preview/css/responsive-preview.theme-rtl.css +++ b/core/modules/responsive_preview/css/responsive-preview.theme-rtl.css @@ -3,12 +3,30 @@ * RTL styling for responsive preview. */ +/** + * Toolbar tab. + */ + +/* Toolbar icon. */ .toolbar .bar .responsive-preview-toolbar-tab .icon-responsive-preview:before { - left: 0; + left: auto; right: 1em; } +/* Toolbar tab triangle toggle. */ .responsive-preview-toolbar-tab .trigger:after { - right: 0; - left: 1.2em; + left: 1em; + right: auto; +} +.responsive-preview-toolbar-tab.open:before { + left: 0.3em; + right: auto; +} + +/** + * Preview container. + */ +#responsive-preview-close { + margin-left: 0; + margin-right: 10px; } diff --git a/core/modules/responsive_preview/css/responsive-preview.theme.css b/core/modules/responsive_preview/css/responsive-preview.theme.css index f2a648c..72ffecd 100644 --- a/core/modules/responsive_preview/css/responsive-preview.theme.css +++ b/core/modules/responsive_preview/css/responsive-preview.theme.css @@ -15,7 +15,6 @@ margin-right: 0; padding-left: 0; padding-right: 0; - text-indent: -9999px; width: 5em; } .icon-responsive-preview:before { @@ -43,6 +42,9 @@ background-color: white; border-top: 1px solid #cfcfcf; } +.responsive-preview-toolbar-tab .trigger { + height: 3em; +} .responsive-preview-toolbar-tab .trigger, .responsive-preview-toolbar-tab .responsive-preview-options a { padding-bottom: 1em; @@ -66,16 +68,29 @@ display: block; height: 0; line-height: 0; + overflow: hidden; position: absolute; - right: 1.2em; /* LTR */ + right: 1em; /* LTR */ top: 50%; margin-top: -0.1666em; width: 0; - overflow: hidden; + z-index: 1 +} +.responsive-preview-toolbar-tab.open:before { + background-color: white; + bottom: -1px; + content: ' '; + display: block; + position: absolute; + right: 0.3em; /* LTR */ + top: 0; + width: 2em; + z-index: 1; } .responsive-preview-toolbar-tab.open .trigger:after { border-bottom: 0.4545em solid; border-top-color: transparent; + color: black; top: 1.25em; } @@ -98,9 +113,8 @@ cursor: pointer; font-size: 1em; height: 2.333em; - margin-left: 10px; + margin-left: 10px; /* LTR */ margin-top: 9px; - text-indent: -9999px; width: 2.333em; } #responsive-preview-close:hover { diff --git a/core/modules/responsive_preview/js/responsive-preview.js b/core/modules/responsive_preview/js/responsive-preview.js index a8fd1f3..244d314 100644 --- a/core/modules/responsive_preview/js/responsive-preview.js +++ b/core/modules/responsive_preview/js/responsive-preview.js @@ -15,13 +15,16 @@ var $frame; // The iframe that contains the previewed page. var iframeDocument; // The document of the iframe that contains the preview. var size; // The width of the iframe container. - var leftOffset; // The left value of the iframe container. + var offset; // The left value of the iframe container. var device = { width: null, // The width of the device to preview. height: null // The height of the device to preview. }; var edgeTolerance = 60; + /** + * Attaches behaviors to the toolbar tab and preview containers. + */ Drupal.behaviors.responsivePreview = { attach: function (context, settings) { var $body = $(window.top.document.body).once('responsive-preview'); @@ -35,8 +38,7 @@ .on('click.responsivePreview', '.responsive-preview-device', loadDevicePreview); // Register a handler on window resize to reposition the tab dropdown. $(window.top) - .on('resize.responsivePreview.tab', handleWindowToolbarResize) - .trigger('resize.responsivePreview.tab'); + .on('resize.responsivePreview.tab', handleWindowToolbarResize); } // Remove administrative elements in the document inside the iframe. if (window.top !== window.self) { @@ -57,13 +59,12 @@ function toggleConfigurationOptions (event) { event.preventDefault(); var open = (event.data && typeof event.data.open === 'boolean') ? event.data.open : undefined; - $(event.delegateTarget) + var $list = $(event.delegateTarget) // Set an open class on the tab wrapper. .toggleClass('open', open) - .find('.responsive-preview-options') - // The list of options will most likely render outside the window. Correct - // this. - .drupalLayout('correctEdgeCollisions'); + .find('.responsive-preview-options'); + // The list of options will might render outside the window. + correctEdgeCollisions.call($list); } /** @@ -83,8 +84,6 @@ // Build the preview if it doesn't exist. if (!$container) { buildpreview(); - // Size is the width of the iframe. - updateDimensions({width: (size || window.top.document.documentElement.clientWidth)}); } $toolbarTab .find('> button') @@ -131,8 +130,7 @@ .trigger('resize.responsivePreview'); // Load the current page URI into the preview iframe. - // @todo, are there any security implications to loading a page like this? - iframeDocument.location.href = Drupal.settings.basePath + Drupal.settings.currentPath; + iframeDocument.location.href = Drupal.encodePath(Drupal.settings.currentPath); }); } @@ -159,7 +157,7 @@ // page width. width = (max - (left * 2) < width) ? max - (left * 2) : width; // Set the dimension variables in the closure. - leftOffset = left; + offset = left; size = width; // Trigger a dimension change. $container.trigger('sizeUpdate.responsivePreview'); @@ -172,17 +170,15 @@ * jQuery Event object. */ function handleWindowToolbarResize (event) { - var options = $toolbarTab - .find('.responsive-preview-options') - // Move the list back onto the screen. - .drupalLayout('correctEdgeCollisions') - .find('.responsive-preview-device') - // Hide layout options that are wider than the current screen - .drupalLayout('prunePreviewChoices', edgeTolerance) - // The
  • s will be toggled. Assign them to options. - .parent('li'); - - $toolbarTab.toggle(options.not('.element-hidden').length > 0); + var $list = $toolbarTab.find('.responsive-preview-options'); + // Move the list back onto the screen. + correctEdgeCollisions.call($list); + // Update the display of the option items. + var $options = $list.find('.responsive-preview-device'); + // Hide layout options that are wider than the current screen + prunePreviewChoices.call($options, edgeTolerance); + // Hide the tab if no options are visible. Show the tab if at least one is. + $toolbarTab.toggle($options.parent('li').not('.element-hidden').length > 0); } /** @@ -208,17 +204,19 @@ * A jQuery event object. */ function refreshPreviewSizing (event) { + // Take RTL text direction into account. + var dir = document.getElementsByTagName('html')[0].getAttribute('dir'); + var edge = (dir === 'rtl') ? 'right' : 'left'; + var options = { + width: size + } + options[edge] = offset; $frame .stop(true, true) - .animate({ - left: leftOffset, - width: size - }, 'fast'); + .animate(options, 'fast'); // Reposition the close button. $('#responsive-preview-close') - .css({ - 'left': (leftOffset + size) - }); + .css(edge, (offset + size)); } /** @@ -240,120 +238,106 @@ } /** - * A jQuery plugin that contains element manipulation utilities. + * Corrects element window edge collisions. * - * @return {Function} - * The method to invoke this plugin. + * Elements are moved back into the window if part of the element is + * rendered outside the visible window. + * + * This should be invoked against a jQuery set like this: + * correctEdgeCollisions.call($('div')); */ - $.fn.drupalLayout = (function () { - - /** - * Corrects element window edge collisions. - * - * Elements are moved back into the window if part of the element is - * rendered outside the visible window. - */ - function correct () { - // Clear any previous corrections. - clear.apply(this); - // Go through each element and correct edge collisions. - return this.each(function (index, element) { - var $this = $(this); - var width = $this.width(); - var height = $this.height(); - var clientW = document.documentElement.clientWidth; - var clientH = document.documentElement.clientHeight; - var collisions = { - 'top': null, - 'right': null, - 'bottom': null, - 'left': null - }; - // Determine if the element is too big for the document. Resize to fit. - if (width > clientW) { - $this.width(clientW); - // If the element is too wide, it will collide on both left and right. - collisions.left = true; - collisions.right = true; - } - if (height > clientH) { - $this.height(clientH); - // If the element is too high, it will collide on both top and bottom. - collisions.top = true; - collisions.bottom = true; - } - // Check each edge for a collision. - if (!collisions.top && $this.offset().top < 0) { - collisions.top = true; - } - if (!collisions.right && (($this.offset().left + width) > clientW)) { - collisions.right = true; - } - if (!collisions.bottom && (($this.offset().top + height) > clientH)) { - collisions.bottom = true; - } - if (!collisions.left && $this.offset().left < 0) { - collisions.left = true; - } - // Set the offset to zero for any collision on an edge. - for (var edge in collisions) { - if (collisions.hasOwnProperty(edge)) { - if (collisions[edge]) { - $this.css(edge, 0); - } + function correctEdgeCollisions () { + // Clear any previous corrections. + clearStyling.call(this, ['top', 'right', 'bottom', 'left']); + // Go through each element and correct edge collisions. + return this.each(function (index, element) { + var $this = $(this); + var width = $this.width(); + var height = $this.height(); + var clientW = document.documentElement.clientWidth; + var clientH = document.documentElement.clientHeight; + var collisions = { + 'top': null, + 'right': null, + 'bottom': null, + 'left': null + }; + // Determine if the element is too big for the document. Resize to fit. + if (width > clientW) { + $this.width(clientW); + // If the element is too wide, it will collide on both left and right. + collisions.left = true; + collisions.right = true; + } + if (height > clientH) { + $this.height(clientH); + // If the element is too high, it will collide on both top and bottom. + collisions.top = true; + collisions.bottom = true; + } + // Check each edge for a collision. + if (!collisions.top && $this.position().top < 0) { + collisions.top = true; + } + if (!collisions.right && (($this.offset().left + width) > clientW)) { + collisions.right = true; + } + if (!collisions.bottom && (($this.position().top + height) > clientH)) { + collisions.bottom = true; + } + if (!collisions.left && $this.offset().left < 0) { + collisions.left = true; + } + // Set the offset to zero for any collision on an edge. + for (var edge in collisions) { + if (collisions.hasOwnProperty(edge)) { + if (collisions[edge]) { + $this.css(edge, 0); } } - }); - } + } + }); + } - /** - * Clears any previous edge correction styling. - */ - function clear () { - var edges = ['top', 'right', 'bottom', 'left']; - return this.each(function (index, element) { - for (var i = 0; i < edges.length; i++) { - this.style[edges[i]] = ""; + /** + * Clears any previous styling. + * + * This should be invoked against a jQuery set like this: + * clearStyling.call($('div'), ['left','right']); + * + * @param {Array} styles + * - An array of strings where each string is the name of a CSS property. + */ + function clearStyling (styles) { + return this.each(function (index, element) { + for (var i = 0; i < styles.length; i++) { + if ('style' in this && styles[i] in this.style) { + this.style[styles[i]] = ""; } - }); - } - - /** - * Hides device preview options that are too wide for the current window. - * - * @param {Number} tolerance - * - The distance from the edge of the window that a device cannot exceed - * or it will be pruned from the list. - */ - function prune (tolerance) { - var docWidth = document.documentElement.clientWidth; - tolerance = (typeof tolerance === 'number' && tolerance > 0) ? tolerance : 0; - return this.each(function () { - var $this = $(this); - var width = parseInt($this.data('responsive-preview-width'), 10); - var fits = ((width + (tolerance * 2)) < docWidth); - $this.parent('li').toggleClass('element-hidden', !fits); - }); - } - - /** - * Methods that this plugin exposes. - */ - var methods = { - 'correctEdgeCollisions': correct, - 'prunePreviewChoices': prune - }; - - return function (method) { - if (methods[method]) { - return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } - else { - $.error(Drupal.t('Method @method does not exist in this plugin.', {'@method': method})); - } - }; + }); + } - }()); + /** + * Hides device preview options that are too wide for the current window. + * + * This should be invoked against a jQuery set like this: + * prunePreviewChoices.call($('div'), 20); + * + * @param {Number} tolerance + * - The distance from the edge of the window that a device cannot exceed + * or it will be pruned from the list. + */ + function prunePreviewChoices (tolerance) { + var docWidth = document.documentElement.clientWidth; + tolerance = (typeof tolerance === 'number' && tolerance > 0) ? tolerance : 0; + return this.each(function () { + var $this = $(this); + var width = parseInt($this.data('responsive-preview-width'), 10); + var fits = ((width + (tolerance * 2)) < docWidth); + $this.parent('li').toggleClass('element-hidden', !fits); + }); + } /** * Registers theme templates with Drupal.theme(). @@ -376,7 +360,7 @@ * The corresponding HTML. */ layoutClose: function () { - return ''; + return ''; }, /** diff --git a/core/modules/responsive_preview/responsive_preview.module b/core/modules/responsive_preview/responsive_preview.module index ac67170..59bd5b6 100644 --- a/core/modules/responsive_preview/responsive_preview.module +++ b/core/modules/responsive_preview/responsive_preview.module @@ -51,6 +51,8 @@ function responsive_preview_toolbar() { '#theme' => 'html_tag', '#tag' => 'button', '#value' => t('Layout preview'), + '#value_prefix' => '', + '#value_suffix' => '', '#attributes' => array( 'id' => 'responsive-preview', 'title' => "Preview page layout",