<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title><%= html_title %></title>
+<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="<%= Redmine::Info.app_name %>" />
<meta name="keywords" content="issue,bug,tracker" />
<%= csrf_meta_tag %>
<%= favicon %>
-<%= stylesheet_link_tag 'jquery/jquery-ui-1.11.0', 'application', :media => 'all' %>
+<%= stylesheet_link_tag 'jquery/jquery-ui-1.11.0', 'application', 'responsive', :media => 'all' %>
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
<%= javascript_heads %>
<%= heads_for_theme %>
</head>
<body class="<%= body_css_classes %>">
<div id="wrapper">
+
+<div class="flyout-menu js-flyout-menu">
+
+
+ <% if User.current.logged? || !Setting.login_required? %>
+ <div class="flyout-menu__search">
+ <%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
+ <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
+ <%= label_tag 'flyout-search', '⚲'.html_safe, :class => 'search-magnifier search-magnifier--flyout' %>
+ <%= text_field_tag 'q', @question, :id => 'flyout-search', :class => 'small js-search-input', :placeholder => l(:label_search) %>
+ <% end %>
+ </div>
+ <% end %>
+
+ <% if User.current.logged? %>
+ <div class="flyout-menu__avatar <% if !Setting.gravatar_enabled? %>flyout-menu__avatar--no-avatar<% end %>">
+ <% if Setting.gravatar_enabled? %>
+ <%= link_to(avatar(User.current, :size => "80"), user_path(User.current)) %>
+ <% end %>
+ <%= link_to_user(User.current, :format => :username) %>
+ </div>
+ <% end %>
+
+ <% if display_main_menu?(@project) %>
+ <h3><%= l(:label_project) %></h3>
+ <span class="js-project-menu"></span>
+ <% end %>
+
+ <h3><%= l(:label_general) %></h3>
+ <span class="js-general-menu"></span>
+
+ <span class="js-sidebar flyout-menu__sidebar"></span>
+
+ <h3><%= l(:label_profile) %></h3>
+ <span class="js-profile-menu"></span>
+
+</div>
+
<div id="wrapper2">
<div id="wrapper3">
<div id="top-menu">
</div>
<div id="header">
+
+ <a href="#" class="mobile-toggle-button js-flyout-menu-toggle-button"></a>
+
<% if User.current.logged? || !Setting.login_required? %>
<div id="quick-search">
<%= form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
--- /dev/null
+// generic layout specific responsive stuff goes here
+
+function openFlyout() {
+ $('html').addClass('flyout-is-active');
+ $('#wrapper2').on('click', function(e){
+ e.preventDefault();
+ e.stopPropagation();
+ closeFlyout();
+ });
+}
+
+function closeFlyout() {
+ $('html').removeClass('flyout-is-active');
+ $('#wrapper2').off('click');
+}
+
+
+function isMobile() {
+ return $('.js-flyout-menu-toggle-button').is(":visible");
+}
+
+function setupFlyout() {
+ var mobileInit = false,
+ desktopInit = false;
+
+ /* click handler for mobile menu toggle */
+ $('.js-flyout-menu-toggle-button').on('click', function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ if($('html').hasClass('flyout-is-active')) {
+ closeFlyout();
+ } else {
+ openFlyout();
+ }
+ });
+
+ /* bind resize handler */
+ $(window).resize(function() {
+ initMenu();
+ })
+
+ /* menu init function for dom detaching and appending on mobile / desktop view */
+ function initMenu() {
+
+ var _initMobileMenu = function() {
+ /* only init mobile menu, if it hasn't been done yet */
+ if(!mobileInit) {
+
+ $('#main-menu > ul').detach().appendTo('.js-project-menu');
+ $('#top-menu > ul').detach().appendTo('.js-general-menu');
+ $('#sidebar > *').detach().appendTo('.js-sidebar');
+ $('#account ul').detach().appendTo('.js-profile-menu');
+
+ mobileInit = true;
+ desktopInit = false;
+ }
+ }
+
+ var _initDesktopMenu = function() {
+ if(!desktopInit) {
+
+ $('.js-project-menu > ul').detach().appendTo('#main-menu');
+ $('.js-general-menu ul').detach().appendTo('#top-menu');
+ $('.js-sidebar > *').detach().appendTo('#sidebar');
+ $('.js-profile-menu ul').detach().appendTo('#account');
+
+ desktopInit = true;
+ mobileInit = false;
+ }
+ }
+
+ if(isMobile()) {
+ _initMobileMenu();
+ } else {
+ _initDesktopMenu();
+ }
+ }
+
+ // init menu on page load
+ initMenu();
+}
+
+$(document).ready(setupFlyout);
--- /dev/null
+/*----------------------------------------*\
+ RESPONSIVE CSS
+\*----------------------------------------*/
+
+
+/*
+
+ CONTENTS
+
+ A) BASIC MOBILE RESETS
+ B) HEADER & TOP MENUS
+ C) MAIN CONTENT & SIDEBAR
+ D) TOGGLE BUTTON & FLYOUT MENU
+
+*/
+
+
+/* Hide new elements (toggle button and flyout menu) above 900px */
+.mobile-toggle-button,
+.flyout-menu
+{
+ display: none;
+}
+
+/*
+ redmine's body is set to min-width: 900px
+ add first breakpoint here and start adding responsiveness
+*/
+
+@media all and (max-width: 899px)
+{
+ /*----------------------------------------*\
+ A) BASIC MOBILE RESETS
+ \*----------------------------------------*/
+
+ /*
+ apply natural border box, see: http://www.paulirish.com/2012/box-sizing-border-box-ftw/
+ this helps us to better deal with percentages and padding / margin
+ */
+ *,
+ *:before,
+ *:after
+ {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+
+ body,
+ html
+ {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ }
+
+ html
+ {
+ overflow-y: auto; /* avoid 2nd scrollbar on desktop */
+ }
+
+ body
+ {
+ overflow-x: hidden; /* hide horizontal overflow */
+
+ min-width: 0; /* reset the min-width of 900px */
+ }
+
+
+ body,
+ input,
+ select,
+ textarea,
+ button
+ {
+ font-size: 14px; /* Set font-size for standard elements to 14px */
+ }
+
+
+ select
+ {
+ max-width: 100%; /* prevent long names within select menues from breaking content */
+ }
+
+
+ #wrapper
+ {
+ position: relative;
+
+ max-width: 100%;
+ }
+
+ #wrapper,
+ #wrapper2
+ {
+ margin: 0;
+ }
+
+ /*----------------------------------------*\
+ B) HEADER & TOP MENUS
+ \*----------------------------------------*/
+
+ #header
+ {
+ width: 100%;
+ height: 64px; /* the height of our header on mobile */
+ min-height: 0;
+ margin: 0;
+ padding: 0;
+
+ border: none;
+ background-color: #628db6;
+ }
+
+ /* Hide project name on mobile (project name is still visible in select menu) */
+ #header h1
+ {
+ display: none !important;
+ }
+
+ /* reset #header a color for mobile toggle button */
+ #header a.mobile-toggle-button
+ {
+ color: #f8f8f8;
+ }
+
+
+ /* Hide top-menu and main-menu on mobile, because it's placed in our flyout menu */
+ #top-menu,
+ #header #main-menu
+ {
+ display: none;
+ }
+
+ /* the quick search within header holding search form and #project_quick_jump_box box*/
+ #header #quick-search
+ {
+ float: none;
+ clear: none; /* there are themes which set clear property, this resets it */
+
+ max-width: 100%; /* reset max-width */
+ margin: 0;
+
+ background: inherit;
+ }
+
+ /* this represents the dropdown arrow to left of the mobile project menu */
+ #header .jump-box-arrow:before
+ {
+ /* set a font-size in order to achive same result in different themes */
+ font-family: Verdana, sans-serif;
+ font-size: 2em;
+ line-height: 64px;
+
+ position: absolute;
+ left: 0;
+
+ width: 2em;
+ padding: 0 .5em;
+ /* achieve dropdwon arrow by scaling a caret character */
+
+ content: '^';
+ -webkit-transform: scale(1,-.8);
+ -ms-transform: scale(1,-.8);
+ transform: scale(1,-.8);
+ text-align: right;
+ pointer-events: none;
+
+ opacity: .6;
+ }
+
+ /* styles for combobox within quick-search (#project_quick_jump_box) */
+ #header #quick-search select
+ {
+ font-size: 1.5em;
+ font-weight: bold;
+ line-height: 1.2;
+
+ position: absolute;
+ top: 15px;
+ left: 0;
+
+ float: left;
+
+ width: 100%;
+ max-width: 100%;
+ height: 2em;
+ height: 35px;
+ padding: 5px;
+ padding-right: 72px;
+ padding-left: 50px;
+
+ text-indent: .01px;
+
+ color: inherit;
+ border: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+ background: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ /* hide default browser arrow */
+
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ }
+
+ #header #quick-search form
+ {
+ display: none;
+ }
+
+ /*----------------------------------------*\
+ C) MAIN CONTENT & SIDEBAR
+ \*----------------------------------------*/
+
+ #main
+ {
+ padding: 0;
+ }
+
+ #main.nosidebar #content,
+ div#content
+ {
+ width: 100%;
+ min-height: 0; /* reset min-height of #content */
+ margin: 0;
+ }
+
+
+ /* hide sidebar and sidebar switch panel, since it's placed in mobile flyout menu */
+ #sidebar,
+ #sidebar-switch-panel
+ {
+ display: none;
+ }
+
+ .splitcontentleft
+ {
+ width: 100%; /* use full width */
+ }
+
+ .splitcontentright
+ {
+ width: 100%; /* use full width */
+ }
+
+ /*----------------------------------------*\
+ D) TOGGLE BUTTON & FLYOUT MENU
+ \*----------------------------------------*/
+
+ /* Mobile toggle button */
+
+ .mobile-toggle-button
+ {
+ font-size: 42px;
+ line-height: 64px;
+
+ position: relative;
+ z-index: 10;
+
+ display: block; /* remove display: none; of non-mobile version */
+ float: right;
+
+ width: 60px;
+ height: 64px;
+ margin-top: 0;
+
+ text-align: center;
+
+ border-left: 1px solid #ddd;
+ }
+
+ .mobile-toggle-button:hover,
+ .mobile-toggle-button:active
+ {
+ text-decoration: none;
+ }
+
+ .mobile-toggle-button:after
+ {
+ font-family: Verdana, sans-serif;
+
+ display: block;
+
+ margin-top: -3px;
+
+ content: '\2261';
+ }
+
+ /* search magnifier icon */
+ .search-magnifier
+ {
+ font-family: Verdana;
+
+ cursor: pointer;
+ -webkit-transform: rotate(-45deg);
+ -moz-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+
+ color: #bbb;
+ }
+
+ .search-magnifier--flyout
+ {
+ font-size: 25px;
+ line-height: 54px;
+
+ position: absolute;
+ z-index: 1;
+ left: 12px;
+ }
+
+
+ /* Flyout Menu */
+
+ .flyout-menu
+ {
+ position: absolute;
+ right: -250px;
+
+ display: block; /* remove display: none; of non-mobile version */
+ overflow-x: hidden;
+
+ width: 250px;
+ height: 100%;
+ margin: 0; /* reset margin for themes that define it */
+ padding: 0; /* reset padding for themes that define it */
+
+ color: white;
+ background-color: #3e5b76;
+ }
+
+
+ /* avoid zoom on search input focus for ios devices */
+ .flyout-menu input[type='text']
+ {
+ font-size: 16px;
+ }
+
+ .flyout-menu h3
+ {
+ font-size: 11px;
+ line-height: 19px;
+
+ height: 20px;
+ margin: 0;
+ padding: 0;
+
+ letter-spacing: .1em;
+ text-transform: uppercase;
+
+ color: white;
+ border-top: 1px solid #506a83;
+ border-bottom: 1px solid #506a83;
+ background-color: #628db6;
+ }
+
+ .flyout-menu h4
+ {
+ color: white;
+ }
+
+ .flyout-menu h3,
+ .flyout-menu h4,
+ .flyout-menu > p,
+ .flyout-menu > a,
+ .flyout-menu ul li a,
+ .flyout-menu__search,
+ .flyout-menu__sidebar > div,
+ .flyout-menu__sidebar > p,
+ .flyout-menu__sidebar > a,
+ .flyout-menu__sidebar > form,
+ .flyout-menu > div,
+ .flyout-menu > form
+ {
+ padding-left: 8px;
+ }
+
+ .flyout-menu .flyout-menu__avatar
+ {
+ margin-top: -1px; /* move avatar up 1px */
+ padding-left: 0;
+ }
+
+ .flyout-menu__sidebar > form
+ {
+ display: block;
+ }
+
+ .flyout-menu__sidebar > form h3
+ {
+ margin-left: -8px;
+ }
+
+ .flyout-menu__sidebar > form label
+ {
+ display: inline-block;
+
+ margin: 8px 0;
+ }
+
+ .flyout-menu__sidebar > form br br
+ {
+ display: none;
+ }
+
+ .flyout-menu ul
+ {
+ margin: 0;
+ padding: 0;
+
+ list-style: none;
+ }
+
+ .flyout-menu ul li a
+ {
+ line-height: 40px;
+
+ display: block;
+ overflow: hidden;
+
+ height: 40px;
+
+ white-space: nowrap;
+ text-overflow: ellipsis;
+
+ border-top: 1px solid rgba(255,255,255,.1);
+ }
+
+ .flyout-menu ul li:first-child a
+ {
+ line-height: 39px;
+
+ height: 39px;
+
+ border-top: none;
+ }
+
+ .flyout-menu a
+ {
+ color: white;
+ }
+
+ .flyout-menu ul li a:hover
+ {
+ text-decoration: none;
+ }
+
+ .flyout-menu ul li a.new-object,
+ .new-object ~ .menu-children
+ {
+ display: none;
+ }
+
+ /* Left flyout search container */
+ .flyout-menu__search
+ {
+ line-height: 54px;
+
+ height: 64px;
+ padding-top: 3px;
+ padding-right: 8px;
+ }
+
+ .flyout-menu__search input[type='text']
+ {
+ line-height: 2;
+
+ width: 100%;
+ height: 38px;
+ padding-left: 27px;
+
+ vertical-align: middle;
+
+ border: none;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ background-color: #fff;
+ }
+
+ .flyout-menu__avatar
+ {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+
+ width: 100%;
+
+ border-top: 1px solid rgba(255,255,255,.1);
+ }
+
+
+ .flyout-menu__avatar img.gravatar
+ {
+ width: 40px;
+ height: 40px;
+ padding: 0;
+
+ vertical-align: top;
+
+ border-width: 0;
+ }
+
+ .flyout-menu__avatar a
+ {
+ line-height: 40px;
+
+ height: auto;
+ height: 40px;
+
+ text-decoration: none;
+
+ color: white;
+ }
+
+ /* avatar */
+ .flyout-menu__avatar a:first-child
+ {
+ line-height: 0;
+
+ width: 40px;
+ padding: 0;
+ }
+
+ .flyout-menu__avatar .user
+ {
+ padding-left: 15px;
+ }
+
+ /* user link when no avatar is present */
+ .flyout-menu__avatar--no-avatar a.user
+ {
+ line-height: 40px;
+
+ padding-left: 8px;
+ }
+
+
+ .flyout-is-active body
+ {
+ overflow: hidden; /* for body not to have scrollbars when left flyout menu is active */
+ }
+
+ html.flyout-is-active
+ {
+ overflow: hidden;
+ }
+
+
+ .flyout-is-active #wrapper
+ {
+ right: 250px; /* when left flyout is active, move body to the right (same amount like flyout-menu's width) */
+
+ height: 100%;
+ }
+
+ .flyout-is-active .mobile-toggle-button:after
+ {
+ content: '\00D7'; /* close glyph */
+ }
+
+ .flyout-is-active #wrapper2
+ {
+
+ /*
+ * only relevant for devices with cursor when flyout it active, in order to show,
+ * that whole wrapper content is clickable and closes flyout menu
+ */
+ cursor: pointer;
+ }
+
+
+ #admin-menu
+ {
+ padding-left: 0;
+ }
+
+ #admin-menu li
+ {
+ padding-bottom: 0;
+ }
+
+ #admin-menu a,
+ #admin-menu a.selected
+ {
+ line-height: 40px;
+
+ padding: 0;
+ padding-left: 32px !important;
+
+ background-position: 8px 50%;
+ }
+}