end
@projects = scope.to_a
}
+ format.js {
+ if params[:q].present?
+ @projects = Project.visible.like(params[:q]).to_a
+ else
+ @projects = User.current.projects.to_a
+ end
+ }
format.api {
@offset, @limit = api_offset_and_limit
@project_count = scope.count
content_tag 'p', l(:label_no_data), :class => "nodata"
end
end
+
+ # Returns an array of projects that are displayed in the quick-jump box
+ def projects_for_jump_box(user=User.current)
+ if user.logged?
+ user.projects.active.select(:id, :name, :identifier, :lft, :rgt).to_a
+ else
+ []
+ end
+ end
+
+ def render_projects_for_jump_box(projects, selected=nil)
+ s = ''.html_safe
+ project_tree(projects) do |project, level|
+ padding = level * 16
+ text = content_tag('span', project.name, :style => "padding-left:#{padding}px;")
+ s << link_to(text, project_path(project, :jump => current_menu_item), :title => project.name, :class => (project == selected ? 'selected' : nil))
+ end
+ s
+ end
# Renders the project quick-jump box
def render_project_jump_box
- return unless User.current.logged?
- projects = User.current.projects.active.select(:id, :name, :identifier, :lft, :rgt).to_a
+ projects = projects_for_jump_box(User.current)
if projects.any?
- options =
- ("<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
- '<option value="" disabled="disabled">---</option>').html_safe
-
- options << project_tree_options_for_select(projects, :selected => @project) do |p|
- { :value => project_path(:id => p, :jump => current_menu_item) }
- end
-
- content_tag( :span, nil, :class => 'jump-box-arrow') +
- select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }')
+ text = @project.try(:name) || l(:label_jump_to_a_project)
+ trigger = content_tag('span', text, :class => 'drdn-trigger')
+ q = text_field_tag('q', '', :id => 'projects-quick-search', :class => 'autocomplete', :data => {:automcomplete_url => projects_path(:format => 'js')})
+ content = content_tag('div',
+ content_tag('div', q, :class => 'quick-search') +
+ content_tag('div', render_projects_for_jump_box(projects, @project), :class => 'drdn-items selection'),
+ :class => 'drdn-content'
+ )
+
+ content_tag('span', trigger + content, :id => "project-jump", :class => "drdn")
end
end
--- /dev/null
+<% s = @projects.any? ? render_projects_for_jump_box(@projects) : content_tag('span', l(:label_no_data)) %>
+$('#project-jump .drdn-items').html('<%= escape_javascript s %>');
<div class="contextual">
-<% if User.current.allowed_to?(:edit_wiki_pages, @project) %>
-<%= link_to l(:label_wiki_page_new), new_project_wiki_page_path(@project), :remote => true, :class => 'icon icon-add' %>
-<% end %>
-<% if @editable %>
-<% if @content.current_version? %>
+ <% if @editable && @content.current_version? %>
<%= link_to_if_authorized(l(:button_edit), {:action => 'edit', :id => @page.title}, :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
+ <% end %>
<%= watcher_link(@page, User.current) %>
+<span class="drdn">
+ <span class="drdn-trigger">Hello</span>
+ <div class="drdn-content drdn-items">
+<% if @editable %>
+<% if @content.current_version? %>
<%= link_to_if_authorized(l(:button_lock), {:action => 'protect', :id => @page.title, :protected => 1}, :method => :post, :class => 'icon icon-lock') if !@page.protected? %>
<%= link_to_if_authorized(l(:button_unlock), {:action => 'protect', :id => @page.title, :protected => 0}, :method => :post, :class => 'icon icon-unlock') if @page.protected? %>
<%= link_to_if_authorized(l(:button_rename), {:action => 'rename', :id => @page.title}, :class => 'icon icon-move') %>
<% end %>
<% end %>
<%= link_to_if_authorized(l(:label_history), {:action => 'history', :id => @page.title}, :class => 'icon icon-history') %>
+<% if User.current.allowed_to?(:edit_wiki_pages, @project) %>
+<%= link_to l(:label_wiki_page_new), new_project_wiki_page_path(@project), :remote => true, :class => 'icon icon-add' %>
+<% end %>
+ </div>
+</span>
</div>
<%= wiki_page_breadcrumb(@page) %>
});
}
+$(document).ready(function(){
+ $(".drdn .autocomplete").val('');
+
+ $(".drdn-trigger").click(function(e){
+ var drdn = $(this).closest(".drdn");
+ if (drdn.hasClass("expanded")) {
+ drdn.removeClass("expanded");
+ } else {
+ $(".drdn").removeClass("expanded");
+ drdn.addClass("expanded");
+ if (!isMobile()) {
+ drdn.find(".autocomplete").focus();
+ }
+ e.stopPropagation();
+ }
+ });
+ $(document).click(function(e){
+ if ($(e.target).closest(".drdn").length < 1) {
+ $(".drdn.expanded").removeClass("expanded");
+ }
+ });
+
+ observeSearchfield('projects-quick-search', null, $('#projects-quick-search').data('automcomplete-url'));
+
+ $(".drdn-content").keydown(function(event){
+ var items = $(this).find(".drdn-items");
+ var focused = items.find("a:focus");
+ switch (event.which) {
+ case 40: //down
+ if (focused.length > 0) {
+ focused.nextAll("a").first().focus();;
+ } else {
+ items.find("a").first().focus();;
+ }
+ event.preventDefault();
+ break;
+ case 38: //up
+ if (focused.length > 0) {
+ var prev = focused.prevAll("a");
+ if (prev.length > 0) {
+ prev.first().focus();
+ } else {
+ $(this).find(".autocomplete").focus();
+ }
+ event.preventDefault();
+ }
+ break;
+ case 35: //end
+ if (focused.length > 0) {
+ focused.nextAll("a").last().focus();
+ event.preventDefault();
+ }
+ break;
+ case 36: //home
+ if (focused.length > 0) {
+ focused.prevAll("a").last().focus();
+ event.preventDefault();
+ }
+ break;
+ }
+ });
+});
+
function beforeShowDatePicker(input, inst) {
var default_date = null;
switch ($(input).attr("id")) {
#header a {color:#f8f8f8;}
#header h1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
#header h1 .breadcrumbs { display:block; font-size: .5em; font-weight: normal; }
+
#quick-search {float:right;}
+#quick-search #q {width:130px; height:24px; box-sizing:border-box; vertical-align:middle; border:1px solid #ccc; border-radius:3px;}
#main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px; width: 100%;}
#main-menu ul {margin: 0; padding: 0; width: 100%; white-space: nowrap;}
a.toggle-checkboxes { margin-left: 5px; padding-left: 12px; background: url(../images/toggle_check.png) no-repeat 0% 50%; }
+/***** Dropdown *****/
+.drdn {position:relative;}
+.drdn-trigger {
+ width:100%;
+ height:24px;
+ box-sizing:border-box;
+ overflow:hidden;
+ text-overflow:ellipsis;
+ white-space:nowrap;
+ padding:3px 18px 3px 6px;
+ background:#fff url(../images/sort_desc.png) no-repeat 97% 50%;
+ cursor:pointer;
+ user-select:none;
+ -moz-user-select:none;
+ -webkit-user-select:none;
+}
+.drdn-content {
+ display:none;
+ position:absolute;
+ right:0px;
+ top:25px;
+ min-width:100px;
+ background-color:#fff;
+ border:1px solid #ccc;
+ border-radius:4px;
+ color:#555;
+ z-index:99;
+}
+.drdn.expanded .drdn-trigger {background-image:url(../images/sort_asc.png);}
+.drdn.expanded .drdn-content {display:block;}
+
+.drdn-content .quick-search {margin:8px;}
+.drdn-content .autocomplete {box-sizing: border-box; width:100% !important; height:28px;}
+.drdn-content .autocomplete:focus {border-color:#5ad;}
+.drdn-items {max-height:400px; overflow:auto;}
+.quick-search + .drdn-items {border-top:1px solid #ccc;}
+.drdn-items>* {
+ display:block;
+ border:1px solid #fff;
+ color:#555 !important;
+ overflow:hidden;
+ text-overflow: ellipsis;
+ white-space:nowrap;
+ padding:4px 8px;
+}
+.drdn-items>a:hover {text-decoration:none; background-color:#759FCF; color:#fff !important;}
+.drdn-items>*:focus {border:1px dotted #bbb;}
+
+.drdn-items.selection>*:before {
+ content:' ';
+ display:inline-block;
+ line-height:1em;
+ width:1em;
+ height:1em;
+ margin-right:4px;
+ font-weight:bold;
+}
+.drdn-items.selection>*.selected:before {
+ content:"\2713 ";
+}
+.drdn-items>span {color:#999;}
+
+#project-jump.drdn {width:200px;display:inline-block;}
+#project-jump .drdn-trigger {
+ display:inline-block;
+ border-radius:3px;
+ border:1px solid #ccc;
+ margin:0 !important;
+ vertical-align:middle;
+ color:#555;
+}
+#project-jump .drdn-content {width:280px;}
+
/***** Tables *****/
table.list, .table-list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
table.list th, .table-list-header { background-color:#EEEEEE; padding: 4px; white-space:nowrap; font-weight:bold; }
background: inherit;
}
- /* this represents the dropdown arrow to left of the mobile project menu */
- #header .jump-box-arrow:before {
+ /* styles for combobox within quick-search (#project_quick_jump_box) */
+ #project-jump.drdn {
+ position: absolute;
+ top: 0px;
+ left: 0;
+
+ width: 100%;
+ max-width: 100%;
+ height: 2em;
+ height: 64px;
+ padding: 5px;
+ padding-right: 72px;
+ padding-left: 20px;
+ }
+ #project-jump .drdn-trigger {
+ font-size:1.5em;
+ font-weight:bold;
+ display:block;
+ width:100%;
+ color:#fff;
+ padding-left:24px;
+ background:transparent;
+ height:50px;
+ line-height:40px;
+ border:0;
+ }
+ #project-jump .drdn-trigger: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;
+ font-size: 1.5em;
position: absolute;
left: 0;
-
- width: 2em;
- padding: 0 .5em;
+ padding: 0 8px;
/* achieve dropdwon arrow by scaling a caret character */
content: '^';
-webkit-transform: scale(1,-.8);
opacity: .6;
}
+ #project-jump.expanded .drdn-trigger:before {
+ -webkit-transform: scale(1,.8);
+ -ms-transform: scale(1,.8);
+ transform: scale(1,.8);
+ padding-top:8px;
+ }
- /* 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;
+ #project-jump .drdn-content {
+ position:absolute;
+ left:0px;
+ top:64px;
+ width:100%;
+ font-size:15px;
+ font-weight:normal;
+ }
+ #project-jump .drdn-content .autocomplete {
+ height:40px;
+ font-size:20px;
+ }
+ #project-jump .drdn-content a {
+ padding:8px;
}
#header #quick-search form {
assert_select 'feed>entry', :count => Project.visible(User.current).count
end
+ def test_index_js
+ xhr :get, :index, :format => 'js', :q => 'coo'
+ assert_response :success
+ assert_equal 'text/javascript', response.content_type
+ end
+
test "#index by non-admin user with view_time_entries permission should show overall spent time link" do
@request.session[:user_id] = 3
get :index
@request.session[:user_id] = 2
get :index
- assert_select "#header select" do
- assert_select "option", :text => 'Foo & Bar'
+ assert_select "#header #project-jump" do
+ assert_select "a", :text => 'Foo & Bar'
end
end