diff --git a/core/modules/layout/css/layout.preview.base.css b/core/modules/layout/css/layout.preview.base.css index e2aaed5..0d91502 100644 --- a/core/modules/layout/css/layout.preview.base.css +++ b/core/modules/layout/css/layout.preview.base.css @@ -1,3 +1,6 @@ +/** + * Constrain the window height to the client height when the preview is active. + */ .layout-preview-active { height: 100%; overflow: hidden; @@ -9,62 +12,47 @@ .layout-preview-toolbar-tab { display: none; } +/* 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 .layout-preview-toolbar-tab.tab { display: block; - float: left; + float: left; /* LTR */ } +/* At wide widths, float the tab to the right. */ @media only screen and (min-width: 36em) { .js .toolbar .bar .layout-preview-toolbar-tab.tab { float: right; /* LTR */ } } -.layout-preview-toolbar-tab .layout-previewer-options { +.layout-preview-toolbar-tab .layout-preview-options { display: none; } -.layout-preview-toolbar-tab.open .layout-previewer-options { +.layout-preview-toolbar-tab.open .layout-preview-options { display: block; } -#layout-previewer-container { - box-shadow: 0 0 10px 0 black; +/** + * Preview container. + * + * The container is kept offscreen after it is built and has been disabled. + */ +#layout-preview-container { display: none; height: 100%; - left: -200%; + left: -200%; /* LTR */ position: absolute; width: 100%; z-index: 1050; } -#layout-previewer-container.active { +#layout-preview-container.active { display: block; - left: 0; + left: 0; /* LTR */ } #layout-preview-close { - background-attachment: scroll; - background-color: #a0a0a0; - background-image: url("../images/close.png"); - background-image: url("../images/close.png"), -webkit-linear-gradient(transparent, #787878 150%); - background-image: url("../images/close.png"), linear-gradient(transparent, #787878 150%); - background-position: center center; - background-repeat: no-repeat; - border: none; - border-radius: 3px; - cursor: pointer; - font-size: 1em; - height: 2.333em; - left: 0; - margin-left: 10px; - margin-top: 9px; position: absolute; - text-indent: -9999em; /* LTR */ - width: 2.333em; z-index: 75; } -#layout-preview-close:hover { - background-image: url("../images/close.png"); -} .layout-modal-background { - background-color: black; - background-color: rgba(0,0,0,0.92); bottom: 0; height: 100%; left: 0; @@ -74,17 +62,25 @@ width: 100%; z-index: 1; } + +/** + * Developer ruler + */ .layout-slider-background { - background-color: white; - border: 1px solid white; height: 2em; position: absolute; top: -1px; width: 100%; z-index: 2; } +#frame-slider { + z-index: 50; +} + +/** + * Breakpoints + */ #layout-bp-view { - background-color: rgba(150,150,150,0.6667); height: 2em; position: absolute; top: 2px; @@ -95,6 +91,7 @@ height: 2em; position: absolute; left: auto; + right: auto; top: 0; width: 100%; } @@ -105,104 +102,53 @@ width: 100%; } .layout-bp-min { - background-color: rgba(255,255,255,0.75); - border-color: white; - border-style: solid; - border-width: 0 1px; position: relative; } .layout-bp .label { box-sizing: border-box; display: block; - font-size: 0.7em; - height: 1em; - left: -100%; - line-height: 1; - padding-right: 6px; + left: -100%; /* LTR */ position: absolute; - text-align: right; top: 0; white-space: nowrap; width: 100%; } -#frame-slider { - z-index: 50; -} -#frame-slider.ui-slider-horizontal .ui-slider-handle { - border: 0; - border-left: 1px solid #b0b0b0; - border-radius: 0; - height: 100%; - margin-left: 0; - top: 0; -} -#frame-slider.ui-slider-horizontal .ui-slider-handle + .ui-slider-handle { - border-left: 0 none; - border-right: 1px solid #b0b0b0; - margin-left: -1.2em; -} -#layout-previewer-container iframe { + +/** + * Preview iframe. + */ +#layout-preview-container iframe { height: 100%; position: relative; width: 100%; z-index: 100; } -#layout-previewer-controls { + +/** + * Developer controls. + */ +#layout-preview-controls { display: none; position: relative; z-index: 25; } -#layout-previewer-controls.active { +#layout-preview-controls.active { display: block; } #layout-size-input { - font-size: 0.75em; position: absolute; top: 1em; z-index: 50; } #layout-size-input * { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; box-sizing: border-box; display: inline-block; - float: left; -} -#layout-size-input .label { -} -#layout-size-input input { - padding: 0; - width: 10em; -} -#layout-size-input .unit { -} -#layout-devices-list { - display: block; - float: left; -} -/* Toolbar */ -.js .layout-preview-toolbar-tab .dropbutton-wrapper { - min-width: 6em; -} -.js .layout-preview-toolbar-tab .dropbutton-widget { - right: 0; -} -.js .layout-preview-toolbar-tab .dropbutton-widget .trigger { - text-align: right; -} -.js .layout-preview-toolbar-tab .dropbutton-widget .trigger a { - display: inline-block; - text-indent: 9999px; /* LTR */ -} -.layout-preview-toolbar-tab .dropbutton-toggle { - bottom: auto; - height: 3em; -} -.js .layout-preview-toolbar-tab .dropbutton-widget .dropbutton { - overflow: visible; + float: left; /* LTR */ } -/* Override Toolbar styling */ +/** + * Override Toolbar styling in the preview iframe. + */ .layout-preview-frame #toolbar-administration { display: none !important; } diff --git a/core/modules/layout/css/layout.preview.theme.css b/core/modules/layout/css/layout.preview.theme.css index 9b900ee..66072cb 100644 --- a/core/modules/layout/css/layout.preview.theme.css +++ b/core/modules/layout/css/layout.preview.theme.css @@ -1,50 +1,23 @@ /** * Toolbar tab. */ -.layout-preview-toolbar-tab .layout-previewer-options { +.layout-preview-toolbar-tab .layout-preview-options { background-color: #0f0f0f; } - -#frame-slider { - background-attachment: scroll; - background-color: transparent; - background-image: url("../images/ruler.png"); - background-position: center bottom; - background-repeat: repeat-x; - border-color: #ccc; - border-style: solid; - border-radius: 0; - border-width: 1px 0 0; - height: 2em; -} -#frame-slider .ui-slider-range { - background: none; - background-color: rgba(90,90,90,0.2); -} -#frame-slider.ui-slider-horizontal .ui-slider-handle { - background-attachment: scroll; - background-color: transparent; - background-image: url("../images/handle.png"); - background-position: center 1px; - background-repeat: no-repeat; -} - -/** - * Toolbar. - */ +/* Toolbar icon. */ .toolbar .bar .icon.icon-layout { margin-left: 0; margin-right: 0; padding-left: 0; padding-right: 0; - text-indent: -9999px; + text-indent: -9999px; /* LTR */ width: 5em; } .icon-layout:before { background-image: url("../images/icon-layout.png"); } .toolbar .bar .layout-preview-toolbar-tab .icon-layout:before { - left: 1em; + left: 1em; /* LTR */ } .layout-preview-toolbar-tab .open .icon-layout:before, .layout-preview-toolbar-tab .icon-layout.active:before { @@ -55,23 +28,25 @@ width: 20px; } } -.layout-preview-toolbar-tab .layout-previewer-options { - border-right: none; +/* Device preview options. */ +.layout-preview-toolbar-tab .layout-preview-options { + border-right: none; /* LTR */ box-shadow: 0 0 2em 0 rgba(0, 0, 0, 0.75); position: absolute; } -.layout-preview-toolbar-tab .layout-previewer-options li { +.layout-preview-toolbar-tab .layout-preview-options li { background-color: white; border-top: 1px solid #cfcfcf; } .layout-preview-toolbar-tab .trigger, -.layout-preview-toolbar-tab .layout-previewer-options a { +.layout-preview-toolbar-tab .layout-preview-options a { padding-bottom: 1em; padding-top: 1em; } -.toolbar .layout-preview-toolbar-tab.tab .layout-previewer-options a { +.toolbar .layout-preview-toolbar-tab.tab .layout-preview-options a { color: black; } +/* Toolbar tab triangle toggle. */ .layout-preview-toolbar-tab .trigger:after { border-bottom-color: transparent; border-left-color: transparent; @@ -84,7 +59,7 @@ height: 0; line-height: 0; position: absolute; - right: 1.2em; + right: 1.2em; /* LTR */ top: 50%; margin-top: -0.1666em; width: 0; @@ -95,3 +70,109 @@ border-top-color: transparent; top: 1.25em; } + +/** + * Preview container. + */ +#layout-preview-container { + box-shadow: 0 0 10px 0 black; +} +#layout-preview-close { + background-attachment: scroll; + background-color: #a0a0a0; + background-image: url("../images/close.png"); + background-image: url("../images/close.png"), -webkit-linear-gradient(transparent, #787878 150%); + background-image: url("../images/close.png"), linear-gradient(transparent, #787878 150%); + background-position: center center; + background-repeat: no-repeat; + border: none; + border-radius: 3px; + cursor: pointer; + font-size: 1em; + height: 2.333em; + left: 0; /* LTR */ + margin-left: 10px; + margin-top: 9px; + text-indent: -9999em; /* LTR */ + width: 2.333em; +} +#layout-preview-close:hover { + background-image: url("../images/close.png"); +} +.layout-modal-background { + background-color: black; + background-color: rgba(0,0,0,0.92); +} + +/** + * Developer ruler. + */ +.layout-slider-background { + background-color: white; + border: 1px solid white; +} +#frame-slider { + background-attachment: scroll; + background-color: transparent; + background-image: url("../images/ruler.png"); + background-position: center bottom; + background-repeat: repeat-x; + border-color: #ccc; + border-style: solid; + border-radius: 0; + border-width: 1px 0 0; + height: 2em; +} +#frame-slider .ui-slider-range { + background: none; + background-color: rgba(90,90,90,0.2); +} +#frame-slider.ui-slider-horizontal .ui-slider-handle { + background-attachment: scroll; + background-color: transparent; + background-image: url("../images/handle.png"); + background-position: center 1px; + background-repeat: no-repeat; + border: 0; + border-left: 1px solid #b0b0b0; /* LTR */ + border-radius: 0; + height: 100%; + margin-left: 0; /* LTR */ + top: 0; +} +#frame-slider.ui-slider-horizontal .ui-slider-handle + .ui-slider-handle { + border-left: 0 none; /* LTR */ + border-right: 1px solid #b0b0b0; /* LTR */ + margin-left: -1.2em; /* LTR */ +} + +/** + * Breakpoints. + */ +#layout-bp-view { + background-color: rgba(150,150,150,0.6667); +} +.layout-bp-min { + background-color: rgba(255,255,255,0.75); + border-color: white; + border-style: solid; + border-width: 0 1px; +} +.layout-bp .label { + font-size: 0.7em; + height: 1em; + line-height: 1; + padding-right: 6px; /* LTR */ + text-align: right; +} + +/** + * Developer controls. + */ +#layout-size-input { + font-size: 0.75em; +} +#layout-size-input input { + padding: 0; + width: 10em; +} diff --git a/core/modules/layout/js/layout.preview.js b/core/modules/layout/js/layout.preview.js index 9ce0c92..a560239 100644 --- a/core/modules/layout/js/layout.preview.js +++ b/core/modules/layout/js/layout.preview.js @@ -1,3 +1,8 @@ +/** + * @file layout,preview.js + * + * Builds a component that previews the a page in various device dimensions. + */ (function (Drupal, $) { Drupal.layout = Drupal.layout || {}; @@ -10,7 +15,7 @@ var iframeDocument; // The document of the iframe that contains the preview. var $slider; // The jQuery UI Slider widget that adjusts the iframed preview. var $container; // The container of the page preview component. - var $controls; + var $controls; // Developer controls container. var $sizeInput; // The input element that display the width of the preview. var breakpoints = {}; // A list of breakpoints, keyed by configuration string. var $breakpointView; // The container of the breakpoint views. @@ -23,9 +28,9 @@ if ($body.length) { // Append the selector to the preview container. $toolbarTab = $('.layout-preview-toolbar-tab') - .on('click.layout', '#layout-previewer', toggleConfigurationOptions) - .on('mouseleave.layout', '.layout-previewer-options', {open: false}, toggleConfigurationOptions) - .on('click.layout', '.layout-previewer-options .layout-preview-device', {open: false}, toggleConfigurationOptions) + .on('click.layout', '#layout-preview', toggleConfigurationOptions) + .on('mouseleave.layout', '.layout-preview-options', {open: false}, toggleConfigurationOptions) + .on('click.layout', '.layout-preview-options .layout-preview-device', {open: false}, toggleConfigurationOptions) .on('click.layout', '.layout-preview-device', loadDevicePreview) .on('click.layout', '#layout-preview-ruler-trigger', toggleControls); // Register a handler on window resize to reposition the tab dropdown. @@ -44,29 +49,40 @@ }; /** + * Toggles the list of devices available to preview from the toolbar tab. * + * @param {Object} event + * - jQuery Event object. */ var toggleConfigurationOptions = function (event) { event.preventDefault(); var open = (event.data && typeof event.data.open === 'boolean') ? event.data.open : undefined; $(event.delegateTarget) + // Set an open class on the tab wrapper. .toggleClass('open', open) - .find('.layout-previewer-options') + .find('.layout-preview-options') + // The list of options will most likely render outside the window. Correct + // this. .drupalLayout('correctEdgeCollisions'); }; /** + * Toggles the developer controls above the preview iframe. * + * @param {Object} event + * - jQuery Event object. */ var toggleControls = function (event) { event.preventDefault(); var $this = $(this); - $controls.toggleClass('active'); - if ($controls.hasClass('active')) { - $this.text(Drupal.t('Hide developer ruler')); - } - else { - $this.text(Drupal.t('Show developer ruler')); + if ($controls) { + $controls.toggleClass('active'); + if ($controls.hasClass('active')) { + $this.text(Drupal.t('Hide developer ruler')); + } + else { + $this.text(Drupal.t('Show developer ruler')); + } } } @@ -74,18 +90,21 @@ * Toggles the layout preview component on or off. * * When first toggled on, the layout preview component is built. All - * subsequent toggles hide or show the build component. + * subsequent toggles hide or show the built component. * - * @param Object Event + * @param {Object} event * - jQuery Event object. + * + * @param {Boolean} activate + * - A boolean that forces the preview to show (true) or to hide (false). */ var toggleLayoutPreview = function (event, activate) { event.preventDefault(); - // Build the previewer if it doesn't exist. + // Build the preview if it doesn't exist. if (!$container) { // Initialize the handle positions. handles = (handles.length) ? handles : [0, document.documentElement.clientWidth]; - buildPreviewer(); + buildpreview(); // Size is the width of the iframe. updateDimensions({width: (size || window.top.document.documentElement.clientWidth)}); } @@ -96,14 +115,14 @@ }; /** - * Assembles a layout previewer. + * Assembles a layout preview. */ - var buildPreviewer = function () { + var buildpreview = function () { $(window.top.document.body).once('layout-preview-container', function (index, element) { $container = $(Drupal.theme('layoutContainer')); - // Build the previewer controls. + // Build the preview controls. $controls = $(Drupal.theme('layoutControls')); - // Slider. + // Theme and render a jQuery UI slider. $slider = $(Drupal.theme('layoutSlider')) .filter('#frame-slider') .slider({ @@ -121,7 +140,7 @@ $.ajax(Drupal.settings.layout.routes.breakpoints) .success(breakpointsCallback); } - // Width label. + // Displays and allows a user to edit the width of the preview. $sizeInput = $(Drupal.theme('layoutSizeInput')) .appendTo($controls); @@ -132,7 +151,7 @@ $container .append(Drupal.theme('layoutClose')); - // Attach the frame that will hold the preview. + // Attach the iframe that will hold the preview. $frame = $(Drupal.theme('layoutFrame')) .css({ 'width': size @@ -170,10 +189,10 @@ /** * Responds to a jQuery UI Slider slide event. * - * @param Object Event + * @param {Object} event * - jQuery Event object. * - * @param Object ui + * @param {Object} ui * - jQuery Slider widget state information resulting from a slide event. */ var handleSlide = function (event, ui) { @@ -210,7 +229,7 @@ /** * Responds to keypress events from the frame size input. * - * @param Object Event + * @param {Object} event * - jQuery Event object. */ var handleSizeInputChange = function (event) { @@ -237,17 +256,15 @@ }; /** - * Updates the dimension variables of the previewer components. - * - * @param Array values - * - An array that contains the position values of the handles of the jQuery - * UI slider. + * Updates the dimension variables of the preview components. * - * @param max - * - The maximum width of the previewer. Often this is just the width of the - * client. + * @param {Object} dimensions + * - An object with the following properties: + * - {Number} width: The width the preview should be set to. + * - {Number} height (optional): The height the preview should be set to. + * Currently this is not used. * - * @param speed + * @param {Number} speed * - A number representing time in milliseconds or a jQuery speed keyword. * Determines the speed at which animations between changes dimension values * should take place. Defaults to zero. @@ -277,16 +294,19 @@ }; /** + * Handles refreshing the layout toolbar tab positioning. * + * @param {Object} event + * - jQuery Event object. */ var handleWindowToolbarResize = function (event) { var options = $toolbarTab - .find('.layout-previewer-options') + .find('.layout-preview-options') // Move the list back onto the screen. .drupalLayout('correctEdgeCollisions') .find('.layout-preview-device') // Hide layout options that are wider than the current screen - .drupalLayout('prunePreviewChoices', {tolerance: edgeTolerance}) + .drupalLayout('prunePreviewChoices', edgeTolerance) // The lis will be toggled. Assign them to options. .parent('li'); @@ -296,7 +316,7 @@ /** * Responds to window resize events. * - * @param Object Event + * @param {Object} event * - jQuery Event object. */ var handleWindowResize = function (event) { @@ -329,12 +349,18 @@ }; /** + * A jQuery plugin that contains element manipulation utilities. * + * @return {Function} + * - The method to invoke this plugin. */ $.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. @@ -390,7 +416,7 @@ } /** - * + * Clears any previous edge correction styling. */ function clear () { var edges = ['top', 'right', 'bottom', 'left']; @@ -402,11 +428,15 @@ } /** + * Hides device prevview 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 (options) { + function prune (tolerance) { var docWidth = document.documentElement.clientWidth; - var tolerance = (options && 'tolerance' in options && typeof options.tolerance === 'number' && options.tolerance > 0) ? options.tolerance : 0; + tolerance = (typeof tolerance === 'number' && tolerance > 0) ? tolerance : 0; return this.each(function () { var $this = $(this); var width = parseInt($this.data('layout-width')); @@ -416,7 +446,7 @@ } /** - * + * Methods that this plugin exposes. */ var methods = { 'correctEdgeCollisions': correct, @@ -424,7 +454,6 @@ }; return function (method) { - // Method calling logic if (methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } @@ -438,15 +467,15 @@ /** * Renders breakpoint configuration to an HTML view. * - * @param Object data + * @param {Object} data * - Breakpoint configuration data. The keys of the object correspond to the * keys of theme-configured breakpoints. The value of each key is a string * that represents a media query. * - * @param String textStatus + * @param {String} textStatus * - The status of the AJAX request. * - * @param Object jqXHR + * @param {Object} jqXHR * - A jQuery XMLHttpRequest object. */ var breakpointsCallback = function (data, textStatus, jqXHR) { @@ -466,23 +495,17 @@ } } if ($breakpointView.children().length) { - // @todo, These breakpoints should be sorted by minw eventually. For now - // we assume they are listed from small to largest. - $container.find('#layout-previewer-controls').append($breakpointView); + // @todo, These breakpoints should be sorted by min width eventually. + // For now we assume they are listed from small to largest. + $container.find('#layout-preview-controls').append($breakpointView); } }; /** + * Resizes the preview iframe to the configured dimensions of a device. * - */ - function loadDefaultPreview (event) { - event.preventDefault(); - toggleLayoutPreview(event); - updateDimensions({width: document.documentElement.clientWidth}, 250); - } - - /** - * + * @param {Object} event + * - A jQuery event object. */ function loadDevicePreview (event) { event.preventDefault(); @@ -495,10 +518,10 @@ /** * Redraws the layout preview component based on the stored dimensions. * - * @param Object event + * @param {Object} event * - A jQuery event object. * - * @param Number/String speed + * @param {Number/String} speed * - A number representing time in milliseconds or a jQuery speed keyword. * Determines the speed at which animations between changes dimension values * should take place. Defaults to zero. @@ -536,16 +559,19 @@ return displacement; }; + /** + * Registers theme templates with Drupal.theme(). + */ $.extend(Drupal.theme, { /** * Returns the preview container element. */ layoutContainer: function () { - return '
'; + return '
'; }, /** - * + * Returns the close button for the preview container. */ layoutClose: function () { return ''; @@ -555,7 +581,7 @@ * Returns an overlay iframe element. */ layoutFrame: function (url) { - return ''; + return ''; }, /** @@ -566,10 +592,10 @@ }, /** - * + * Returns a container for the preview developer controls. */ layoutControls: function () { - return '
'; + return '
'; }, /** @@ -610,15 +636,15 @@ * the event.key property, or, if the key is a control key or does not match * a supplied pattern, then null as the event.key value. * - * @param Regex event.data.pattern + * @param {Regex} event.data.pattern * - A regular expression that filters allowed key input. Only keys matching * the expression will be returned. All other keys return null. * - * @param Function event.data.callback + * @param {Function} event.data.callback * - A callback function to be invoked after a key is processed. Any * variadic parameters supplied to keyManager are passed through as well. * - * @param Array event.data.controls + * @param {Array} event.data.controls * - An Array of char codes that should be ignored as control keys. */ var keyManager = function (event) { @@ -654,14 +680,14 @@ /** * Maps keyCode to Strings, taking the shift key state into account. * - * @param Boolean isShifted + * @param {Boolean} isShifted * - A Boolean representing if the shift key was pressed (true) or not * (false) * - * @param Number keyCode + * @param {Number} keyCode * - The numeric code of the key that was pressed. * - * @return String + * @return {String} * - A single character corresponding to the keyCode or null if no * correspondence is found. */ @@ -727,7 +753,7 @@ /** * Parses a String representing a media query into usable values. * - * @param String mq + * @param {String} mq * - A String representing a media query e.g. 'all and (min-width: 800px)' */ var parseMediaQuery = function (mq) { diff --git a/core/modules/layout/layout.install b/core/modules/layout/layout.install index 78d86f2..af17f07 100644 --- a/core/modules/layout/layout.install +++ b/core/modules/layout/layout.install @@ -9,7 +9,7 @@ * */ function layout_enable() { - /* Set the previewer to active on bartik if the setting does not exist. */ + /* Set the preview to active on bartik if the setting does not exist. */ $theme_var = 'theme_bartik_settings'; $settings = variable_get($theme_var, array()); if (!isset($settings['layout_preview_tab'])) { diff --git a/core/modules/layout/layout.module b/core/modules/layout/layout.module index 50b2b1b..16b8611 100644 --- a/core/modules/layout/layout.module +++ b/core/modules/layout/layout.module @@ -217,7 +217,7 @@ function layout_toolbar() { '#tag' => 'button', '#value' => t('Layout preview'), '#attributes' => array( - 'id' => 'layout-previewer', + 'id' => 'layout-preview', 'title' => "Preview page layout", 'class' => array('icon', 'icon-layout', 'trigger'), ), @@ -226,7 +226,7 @@ function layout_toolbar() { '#theme' => 'links', '#links' => layout_preview_toolbar_controls(), '#attributes' => array( - 'class' => array('layout-previewer-options'), + 'class' => array('layout-preview-options'), ), ), ),