From 35583627a207870732a5f1032ff028ab87d9c170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?"J.=20Rene=CC=81e=20Beach"?= Date: Wed, 2 Jan 2013 12:59:44 -0500 Subject: [PATCH] Issue #1741498 by jessebeach: Add a mobile preview bar to Drupal core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f61b3ec25c411c76ce6ea3747c0921a8ac52a7ba Author: J. Renée Beach Date: Wed Jan 2 12:58:25 2013 -0500 Refining the cleanup of the iframed document. Signed-off-by: J. Renée Beach commit ce0b8f87335e6bab5fadcee80ba8d3b2d8ff8d5e Author: J. Renée Beach Date: Wed Jan 2 12:51:33 2013 -0500 Created a preview trigger method Signed-off-by: J. Renée Beach commit 9650b6955a8f8a531c183b390702072a7e3c926d Author: J. Renée Beach Date: Wed Jan 2 10:55:56 2013 -0500 Added a launch trigger for the preview. Signed-off-by: J. Renée Beach commit d3b70bfb0ca95fffbbf8af4c64689754078ab773 Author: J. Renée Beach Date: Wed Jan 2 10:09:18 2013 -0500 Small change to how the ruler is positioned. Signed-off-by: J. Renée Beach commit 0bdefd20718486e3c3ce229db9f034270ef4a47c Author: J. Renée Beach Date: Tue Jan 1 19:06:12 2013 -0500 Styling the ruler. Signed-off-by: J. Renée Beach commit 4fd0182a17469f12d820d277075894516c918db8 Author: J. Renée Beach Date: Tue Jan 1 18:19:46 2013 -0500 Size input now reflects the size of the page. Signed-off-by: J. Renée Beach commit daa746da3b1828c9abb9c7a6b7613a23e9f2c61e Author: J. Renée Beach Date: Tue Jan 1 17:42:56 2013 -0500 Resizing the window now works well. Signed-off-by: J. Renée Beach commit 59ad7066c290aa40ec4dd7c73183a463a057a275 Author: J. Renée Beach Date: Tue Jan 1 17:24:05 2013 -0500 Cleaned up some of the code. Signed-off-by: J. Renée Beach commit 7528d39c3cefac0cb8287660e8aff970d1b203fa Author: J. Renée Beach Date: Tue Jan 1 17:18:41 2013 -0500 And the frame now responds to the slider. Signed-off-by: J. Renée Beach commit 6f9b1fe4592dcbeb7eb2140f62a6a6c307b46431 Author: J. Renée Beach Date: Tue Jan 1 17:08:38 2013 -0500 Handle resizing works. Signed-off-by: J. Renée Beach commit 8839a8f1feb7c7c105f41002c58061337388e350 Author: J. Renée Beach Date: Fri Dec 28 17:04:15 2012 -0500 Resizing works a little bit. Signed-off-by: J. Renée Beach commit 8d6164c6c1515acdcc40f52f8e788908f7354ed0 Author: J. Renée Beach Date: Fri Dec 28 13:56:29 2012 -0500 Getting the slider to stay put. Signed-off-by: J. Renée Beach commit e19a5e59975fa94a12826a53bf144d5ad6a6b30d Author: J. Renée Beach Date: Fri Dec 28 13:20:35 2012 -0500 Changing the width of the iframe based on the slider. Signed-off-by: J. Renée Beach commit c7d44c25bad45653810b83f8b2adf5b6b9b3f645 Author: J. Renée Beach Date: Fri Dec 28 13:12:55 2012 -0500 Added a jQuery UI slider. Signed-off-by: J. Renée Beach commit f768fa980f9eb263d80de67090315a517f6d1a73 Author: J. Renée Beach Date: Fri Dec 28 12:06:38 2012 -0500 Displacement is working. Signed-off-by: J. Renée Beach commit 699adbd188e5aa8ce72a0e30cd24e94f41f1a1f6 Author: J. Renée Beach Date: Fri Dec 28 11:15:16 2012 -0500 moar layout preview stuff Signed-off-by: J. Renée Beach commit 9549761df30abeeeb1fee407bce9a3227d3084ae Author: J. Renée Beach Date: Thu Dec 27 18:44:41 2012 -0500 sorting out the iframes Signed-off-by: J. Renée Beach commit caef75b0a948354fef40e873c0f19083e62cb4bc Author: J. Renée Beach Date: Wed Dec 26 12:14:51 2012 -0500 Previewer is at least loading. Signed-off-by: J. Renée Beach commit a9ead9f37ddcfc5fa3021b85ce6263eabcc1a68e Author: J. Renée Beach Date: Wed Dec 26 12:00:09 2012 -0500 Implemented hook_toolbar in the layout module Signed-off-by: J. Renée Beach commit b54cf4c9428937da8718fa04dbce2ed096c822b7 Author: J. Renée Beach Date: Wed Dec 26 11:39:26 2012 -0500 working commit Signed-off-by: J. Renée Beach Signed-off-by: J. Renée Beach --- core/modules/layout/css/layout.base.css | 65 ++++++++++ core/modules/layout/css/layout.theme.css | 23 ++++ core/modules/layout/images/handle.png | 4 + core/modules/layout/images/ruler.png | 7 ++ core/modules/layout/js/layout.js | 203 ++++++++++++++++++++++++++++++ core/modules/layout/layout.module | 63 ++++++++++ 6 files changed, 365 insertions(+) create mode 100644 core/modules/layout/css/layout.base.css create mode 100644 core/modules/layout/css/layout.theme.css create mode 100644 core/modules/layout/images/handle.png create mode 100644 core/modules/layout/images/ruler.png create mode 100644 core/modules/layout/js/layout.js diff --git a/core/modules/layout/css/layout.base.css b/core/modules/layout/css/layout.base.css new file mode 100644 index 0000000..316f804 --- /dev/null +++ b/core/modules/layout/css/layout.base.css @@ -0,0 +1,65 @@ +#layout-previewer-container { + box-shadow: 0 0 10px 0 black; + height: 100%; + left: -200%; + position: absolute; + width: 100%; + z-index: 1500; +} +#layout-previewer-container.active { + left: 0; +} +#layout-previewer-container .modal-background { + background-color: black; + bottom: 0; + height: 100%; + left: 0; + position: absolute; + right: 0; + top: 0; + width: 100%; + z-index: 1; +} +#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 { + height: 100%; + position: relative; + width: 100%; + z-index: 25; +} +#layout-size-input { + font-size: 0.75em; + position: absolute; + top: 0; + z-index: 100; +} +#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 { +} diff --git a/core/modules/layout/css/layout.theme.css b/core/modules/layout/css/layout.theme.css new file mode 100644 index 0000000..3b218d3 --- /dev/null +++ b/core/modules/layout/css/layout.theme.css @@ -0,0 +1,23 @@ +#frame-slider { + background-attachment: scroll; + background-color: #fcfcfc; + 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-widget-header { + background: none; + background-color: rgba(60,60,60,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; +} diff --git a/core/modules/layout/images/handle.png b/core/modules/layout/images/handle.png new file mode 100644 index 0000000..656b884 --- /dev/null +++ b/core/modules/layout/images/handle.png @@ -0,0 +1,4 @@ +PNG + + IHDR&(ۙtEXtSoftwareAdobe ImageReadyqe<IDATxڤ9Aa?@B!$8 82bZ$q_fke[\AS~˛/@z"hEI'ėXFov .1"2(=ܻ /9LX_USB.uXsA{VX`˅xc0Еd l6q8D"i<k^ )8뽿%&x5 # 0#LN'%r9jQy8G;>a.K1R9L:|Bp'v z" F-|Hv4ᕛTnQXr;C9VT.-# GܨT*r@Q^Fd9sR)lZV (ك nZ4>H=n^ KaPW)UFWCRD?wFȃS:(pjRkUV.JCb^+5Ul60ը&()n*{T/iZLcͩ GĮ:ő +e>:T-{*zVmи=l| !&JIJcTŭ^lTnRT. >]C`ci( et 2(M^L͕ɒ{R@ҤAcӎwG1:$ ?*M=bIENDB` \ No newline at end of file diff --git a/core/modules/layout/images/ruler.png b/core/modules/layout/images/ruler.png new file mode 100644 index 0000000..f094c63 --- /dev/null +++ b/core/modules/layout/images/ruler.png @@ -0,0 +1,7 @@ +PNG + + IHDRZ )#yMtEXtSoftwareAdobe ImageReadyqe<IDATxVQ %z=PK +bpvghBRO_cL0MC,}έ=MS,K+ rԮThV@s҈ot366Cʟi _ksx^sy R,i.[;QK؆8t8M6ťq}p=Ӛ^L9/@RcKѳ(5b] '†2G36vT@@} ON:!mD +tY= +܆r}> 1 +1]-PV)th7!Jh+NWN}οS¸8VW;5{ҡ͛y!h69Gṁ3`^n7чNZO!}d9>} +0}_tQIENDB` \ No newline at end of file diff --git a/core/modules/layout/js/layout.js b/core/modules/layout/js/layout.js new file mode 100644 index 0000000..300d7a6 --- /dev/null +++ b/core/modules/layout/js/layout.js @@ -0,0 +1,203 @@ +(function (Drupal, $) { + + Drupal.layout = Drupal.layout || {}; + + var size; + var handles = []; + var $frame; + var $slider; + var $container; + var $sizeInput; + var active = false; + + Drupal.behaviors.layout = { + attach: function (context, settings) { + var $body = $(window.top.document.body).once('layout-preview'); + + if ($body.length) { + // Set up the trigger link. + $('#toolbar-tab-layout_preview').on('click.layout', Drupal.layout.toggleLayoutPreview); + } + // Remove administrative elements in the document inside the iframe. + if (window.top !== window.self) { + $(context) + .find('body') + .removeClass('toolbar-tray-open') + .find('#toolbar-administration') + .remove(); + } + } + }; + + /** + * + */ + Drupal.layout.toggleLayoutPreview = function (event) { + // Build the previewer if it doesn't exist. + if (!$container) { + // Size is the width of the iframe. + size = size || window.top.document.documentElement.clientWidth; + // Initialize the handle positions. + handles = (handles.length) ? handles : [0, document.documentElement.clientWidth]; + Drupal.layout.buildPreviewer(); + } + $container.toggleClass('active'); + }; + + /** + * + */ + Drupal.layout.buildPreviewer = function (width) { + $(window.top.document.body).once('layout-preview-container', function (index, element) { + $container = $(Drupal.theme('layoutContainer')) + .appendTo(window.top.document.body); + $frame = $(Drupal.theme('layoutFrame')) + .css({ + 'width': size + }) + .appendTo($container); + }); + // Slider. + $slider = $('
') + .slider({ + 'range': true, + 'max': document.documentElement.clientWidth, + 'min': 0, + 'values': handles, + 'slide': Drupal.layout.handleSlide + }) + .prependTo($container); + // Width label. + $sizeInput = $(Drupal.theme('layoutSizeInput')) + .appendTo($container); + // Displace the top of the container. + $container + .css({ + top: Drupal.layout.getDisplacement('top'), + }) + .attr('data-offset-top', Drupal.overlay.getDisplacement('top')); + // The contentDocument property is not supported in IE until IE8. + var iframeDocument = $frame[0].contentDocument || $frame[0].contentWindow.document; + + $(window.top).on('resize.layout', Drupal.layout.handleWindowResize); + + // Trigger a resize to kick off some initial placements. + $(window.top).triggerHandler('resize.layout'); + + // Load the current page in the iframe. + // @todo, the location.href shouldn't be passed in naked like this. + iframeDocument.location.href = document.location.href; + }; + + /** + * + */ + Drupal.layout.handleSlide = function (event, ui) { + var delta = 0; + var vals = []; + var handle, split; + // Get the delta of the original value of the handles and the new value. + for (var i = ui.values.length - 1; i >= 0; i--) { + // Get the original handle value. + var handle = handles[i]; + // The new handle value. + var value = ui.values[i]; + // If the original and the new values are not equal, adjust the handles. + if (handle !== value) { + var max = $slider.slider('option', 'max'); + var otherHandle = (i === 0) ? 1 : 0; + vals[i] = value; + // The value of the other handle is the inverse of the percentage of the + // active handle. + vals[otherHandle] = max * (1 - (value / max)); + $slider.slider('option', 'values', [vals[0], vals[1]]); + // Store the new values of the handles. + handles = [vals[0], vals[1]]; + // Adjust the frame. + size = max * ((vals[1] - vals[0]) / max); + $frame.css({ + left: max * (vals[0] / max), + width: size + }); + // Update the size input value. + $sizeInput.find('input').val(size); + // Only one handle moves at a time, so if a handle-move was processed, + // then break; + break; + } + } + }; + + /** + * + */ + Drupal.layout.handleWindowResize = function (event) { + var doc = this.document.documentElement; + var docWidth = doc.clientWidth; + var framePercent = size / docWidth; + var gutterPercent = (1 - framePercent) / 2; + // Adjust the parameters of the slider. + $slider + // The new max of the slider is the width of the document. + .slider('option', 'max', docWidth) + // Update the position values of the slider handles. + .slider('values', [(gutterPercent * docWidth), ((gutterPercent + framePercent) * docWidth)]); + // If the window has been reduced below the width of the frame, reduce the + // width of the frame. + size = (docWidth < size) ? docWidth : size; + // Adjust the parameters of the frame. + $frame.css({ + 'left': gutterPercent * docWidth, + 'width': size + }); + // Adjust the position of the size input. + var inputPercent = $sizeInput.width() / docWidth; + gutterPercent = (1 - inputPercent) / 2; + $sizeInput.css({ + 'left': gutterPercent * docWidth + }); + // Update the size input value. + $sizeInput.find('input').val(size); + }; + + /** + * Get the total displacement of given region. + * + * @param region + * Region name. Either "top" or "bottom". + * + * @return + * The total displacement of given region in pixels. + */ + Drupal.layout.getDisplacement = function (region) { + var displacement = 0; + var lastDisplaced = $('[data-offset-' + region + ']'); + if (lastDisplaced.length) { + displacement = parseInt(lastDisplaced.attr('data-offset-' + region)); + } + return displacement; + }; + + $.extend(Drupal.theme, { + /** + * Theme function to create the overlay iframe element. + */ + layoutContainer: function () { + return '
'; + }, + + /** + * Theme function to create an overlay iframe element. + */ + layoutFrame: function (url) { + return ''; + }, + + /** + * + */ + layoutSizeInput: function () { + return '
' + Drupal.t('Width') + 'px
'; + } + }); +}(Drupal, jQuery)); diff --git a/core/modules/layout/layout.module b/core/modules/layout/layout.module index c6ed7ae..321face 100644 --- a/core/modules/layout/layout.module +++ b/core/modules/layout/layout.module @@ -80,3 +80,66 @@ function layout_theme($existing, $type, $theme, $path) { } return $items; } + +/** + * Implements hook_toolbar(). + */ +function layout_toolbar() { + $items = array(); + + $items['layout_preview'] = array( + 'tab' => array( + 'title' => t('Layout preview'), + 'href' => '', + 'html' => FALSE, + 'attributes' => array( + 'title' => t('Preview page layout'), + 'id' => 'layout-previewer', + ), + ), + 'tray' => array( + '#attached' => array( + 'library' => array( + array('layout', 'layout.previewer'), + ), + ), + ), + 'weight' => 200, + ); + + return $items; +} + +/** + * Implements hook_library(). + */ +function layout_library_info() { + $libraries = array(); + $path = drupal_get_path('module', 'layout'); + $options = array( + 'scope' => 'footer', + 'attributes' => array('defer' => TRUE), + ); + + $libraries['layout.previewer'] = array( + 'title' => 'Preview layouts', + 'website' => 'http://drupal.org/project/layout', + 'version' => VERSION, + 'css' => array( + $path . '/css/layout.base.css', + $path . '/css/layout.theme.css', + ), + 'js' => array( + // Core. + $path . '/js/layout.js' => $options, + ), + 'dependencies' => array( + array('system', 'jquery'), + array('system', 'drupal.ajax'), + array('system', 'drupalSettings'), + array('system', 'jquery.ui.slider'), + ), + ); + + return $libraries; +} -- 1.7.10.4