diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index f96cf66fb..ab3d7b120 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -23,6 +23,7 @@ class AdminController < ApplicationController before_filter :require_admin helper :sort + helper :Users include SortHelper def index @@ -39,6 +40,121 @@ class AdminController < ApplicationController render :action => "projects", :layout => false if request.xhr? end + def users + @project_type = params[:project_type] + role = params[:role] + + sort_init 'login', 'asc' + sort_update %w(login firstname lastname mail admin created_on last_login_on) + + case params[:format] + when 'xml', 'json' + @offset, @limit = api_offset_and_limit({:limit => 15}) + else + @limit = 15#per_page_option + end + + @status = params[:status] || 1 + has = { + "show_changesets" => true + } + # @count = Redmine::Activity::Fetcher.new(User.current, :author => @user).scope_select {|t| !has["show_#{t}"].nil?}.events(nil, nil).count + + scope = UserStatus.visible + case role + when 'teacher' + scope = UserStatus.teacher + when 'student' + scope = UserStatus.student + else + + end + + scope = scope.in_group(params[:group_id]) if params[:group_id].present? + # scope.each do |user| + # UserStatus.create(:changesets_count => user.changesets.count, :watchers_count => user.watcher_users.count, :user_id => user.id) + # end + @user_count = scope.count + @user_pages = Paginator.new @user_count, @limit, params['page'] + #@offset ||= @user_pages.offset + #@users = scope.order(sort_clause).limit(@limit).offset(@offset).all + @user_base_tag = params[:id] ? 'base_users':'base' + if params[:user_sort_type].present? + case params[:user_sort_type] + when '0' + @offset ||= @user_pages.reverse_offset + unless @offset == 0 + @users_statuses = scope.offset(@offset).limit(@limit).all.reverse + else + limit = @user_count % @limit + if limit == 0 + limit = @limit + end + @users_statuses = scope.offset(@offset).limit(limit).all.reverse + end + @s_type = 0 + # @projects = @projects.sort {|x,y| y.created_on <=> x.created_on } + # @projects = @projects[@offset, @limit] + when '1' + @offset ||= @user_pages.reverse_offset + unless @offset == 0 + @users_statuses = scope.reorder('grade').offset(@offset).limit(@limit).all.reverse + else + limit = @user_count % @limit + if limit == 0 + limit = @limit + end + @users_statuses = scope.reorder('grade').offset(@offset).limit(limit).all.reverse + end + @s_type = 1 + #sort {|x,y| y.user_status.changesets_count <=> x.user_status.changesets_count} + #@users = @users[@offset, @limit] + when '2' + @offset ||= @user_pages.reverse_offset + unless @offset == 0 + @users_statuses = scope.reorder('watchers_count').offset(@offset).limit(@limit).all.reverse + else + limit = @user_count % @limit + if limit == 0 + limit = @limit + end + @users_statuses = scope.reorder('watchers_count').offset(@offset).limit(limit).all.reverse + end + @s_type = 2 + #@users = @users[@offset, @limit] + end + + else + @offset ||= @user_pages.reverse_offset + unless @offset == 0 + @users_statuses = scope.reorder('grade').offset(@offset).limit(@limit).all.reverse + else + limit = @user_count % @limit + if limit == 0 + limit = @limit + end + @users_statuses = scope.reorder('grade').offset(@offset).limit(limit).all.reverse + end + @s_type = 1 + # @projects = @projects.sort {|x,y| y.created_on <=> x.created_on } + # @projects = @projects[@offset, @limit] + end + + @users = [] + @users_statuses.each do |obj| + @users << User.find_by_id("#{obj.user_id}") + end + + + respond_to do |format| + format.html { + @groups = Group.all.sort + render :layout => @user_base_tag + } + format.api + end + end + def plugins @plugins = Redmine::Plugin.all end diff --git a/app/controllers/open_source_projects_controller.rb b/app/controllers/open_source_projects_controller.rb index 1ca7ddcef..7b9333647 100644 --- a/app/controllers/open_source_projects_controller.rb +++ b/app/controllers/open_source_projects_controller.rb @@ -1,8 +1,8 @@ class OpenSourceProjectsController < ApplicationController - + before_filter :find_osp, :only => [:master_apply, :accept_master_apply, :refuse_master_apply] before_filter :require_master, :only => [:master_apply, :accept_master_apply, :refuse_master_apply] - + helper :sort include SortHelper helper :apply_project_masters @@ -22,7 +22,7 @@ class OpenSourceProjectsController < ApplicationController @os_project_count = @open_source_projects.count @os_project_pages = Paginator.new @os_project_count, per_page_option, params['page'] - + @open_source_projects = @open_source_projects.offset(@os_project_pages.offset).limit(@os_project_pages.per_page) # @open_source_projects = OpenSourceProject.all @@ -32,11 +32,11 @@ class OpenSourceProjectsController < ApplicationController format.json { render json: @open_source_projects } end end - + def master_apply @apply = @open_source_project.apply_tips @applicants = @open_source_project.applicants - + respond_to do |format| format.html { render :layout => "base_opensource_p" @@ -49,7 +49,7 @@ class OpenSourceProjectsController < ApplicationController # GET /open_source_projects/1.json def show @open_source_project = OpenSourceProject.find(params[:id]) - + sort_init 'updated_at', 'desc' sort_update 'created_at' => "#{RelativeMemo.table_name}.created_at", 'replies' => "#{RelativeMemo.table_name}.replies_count", @@ -63,9 +63,9 @@ class OpenSourceProjectsController < ApplicationController includes(:last_reply). limit(@topic_pages.per_page). offset(@topic_pages.offset). - order(sort_clause). + order(sort_clause). all - + @bugs = @open_source_project.bugs.limit(6) respond_to do |format| @@ -76,25 +76,50 @@ class OpenSourceProjectsController < ApplicationController end end + def search -def search - end + def showbug + @open_source_project = OpenSourceProject.find(params[:id]) -# added by yiang 暴力添加,请绕道 -def showmemo - @open_source_project = OpenSourceProject.find(params[:id]) + sort_init 'updated_at', 'desc' + sort_update 'created_at' => "#{RelativeMemo.table_name}.created_at", + 'replies' => "#{RelativeMemo.table_name}.replies_count", + 'updated_at' => "COALESCE (last_replies_relative_memos.created_at, #{RelativeMemo.table_name}.created_at)" - sort_init 'updated_at', 'desc' - sort_update 'created_at' => "#{RelativeMemo.table_name}.created_at", + @memo = RelativeMemo.new(:open_source_project => @open_source_project) + @topic_count = @open_source_project.bugs.count + @topic_pages = Paginator.new @topic_count, per_page_option, params['page'] + @memos = @open_source_project.bugs. + reorder("#{RelativeMemo.table_name}.sticky DESC"). + includes(:last_reply). + limit(@topic_pages.per_page). + offset(@topic_pages.offset). + order(sort_clause). + all + + respond_to do |format| + format.html { + render :layout => "base_opensource_p" + } + format.json { render json: @open_source_project } + end + end + + # added by yiang 暴力添加,请绕道 + def showmemo + @open_source_project = OpenSourceProject.find(params[:id]) + + sort_init 'updated_at', 'desc' + sort_update 'created_at' => "#{RelativeMemo.table_name}.created_at", 'replies' => "#{RelativeMemo.table_name}.replies_count", 'updated_at' => "COALESCE (last_replies_relative_memos.created_at, #{RelativeMemo.table_name}.created_at)" - @memo = RelativeMemo.new(:open_source_project => @open_source_project) - @topic_count = @open_source_project.topics.count - @topic_pages = Paginator.new @topic_count, per_page_option, params['page'] - @memos = @open_source_project.topics. + @memo = RelativeMemo.new(:open_source_project => @open_source_project) + @topic_count = @open_source_project.topics.count + @topic_pages = Paginator.new @topic_count, per_page_option, params['page'] + @memos = @open_source_project.topics. reorder("#{RelativeMemo.table_name}.sticky DESC"). includes(:last_reply). limit(@topic_pages.per_page). @@ -102,13 +127,14 @@ def showmemo order(sort_clause). all - respond_to do |format| - format.html { - render :layout => "base_opensource_p" - } - format.json { render json: @open_source_project } + respond_to do |format| + format.html { + render :layout => "base_opensource_p" + } + format.json { render json: @open_source_project } + end end -end + # GET /open_source_projects/new # GET /open_source_projects/new.json def new @@ -168,52 +194,51 @@ end format.json { head :no_content } end end - + def remove_condition @app_dir = params[:app_dir] @language = params[:language] @created_at = params[:created_at] redirect_to open_source_projects_path(:app_dir => @app_dir, :language => @language, :created_at => @created_at, :name => params[:name]) end - + def search # per_page_option = 10 -# + # # @open_source_projects = OpenSourceProject.filter(@app_dir, @language, @created_at) # @open_source_projects = @open_source_projects.like(params[:name]) if params[:name].present? -# + # # @os_project_count = @open_source_projects.count # @os_project_pages = Paginator.new @os_project_count, per_page_option, params['page'] -# + # # @open_source_projects = @open_source_projects.offset(@os_project_pages.offset).limit(@os_project_pages.per_page) redirect_to open_source_projects_path(:name => params[:name]) end - + def refuse_master_apply @apply = ApplyProjectMaster.where("user_id = ? and apply_id = ? and apply_type = 'OpenSourceProject'", params[:user_id], @open_source_project.id) @apply.first.destory - + redirect_to master_apply_open_source_project_path end - + def accept_master_apply @apply = ApplyProjectMaster.where("user_id = ? and apply_id = ? and apply_type = 'OpenSourceProject'", params[:user_id], @open_source_project.id) if @apply.count == 1 - @apply.first.update_attributes(:status => 2) + @apply.first.update_attributes(:status => 2) end - + redirect_to master_apply_open_source_project_path end - - + private - + def require_master render_403 unless @open_source_project.admin?(User.current) end - + def find_osp @open_source_project = OpenSourceProject.find(params[:id]) render_404 unless @open_source_project.present? diff --git a/app/controllers/test_controller.rb b/app/controllers/test_controller.rb index ddb47a8b7..ec3e5cbdb 100644 --- a/app/controllers/test_controller.rb +++ b/app/controllers/test_controller.rb @@ -3,6 +3,8 @@ class TestController < ApplicationController helper :UserScore layout 'bootstrap_base' + def bootstrap; end + def zip homeworks_attach_path = [] homework_id = params[:homework_id] diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 6174e2124..19c84cc33 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -67,7 +67,7 @@ class UsersController < ApplicationController if User.current.admin? @memberships = @user.memberships.all else - cond = Project.visible_condition(User.current) + "AND projects.project_type <> 1" + cond = Project.visible_condition(User.current) + " AND projects.project_type <> 1" @memberships = @user.memberships.all(:conditions => cond) end events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 20) @@ -565,7 +565,7 @@ class UsersController < ApplicationController end def watch_projects - @watch_projects = Project.joins(:watchers).where("project_type <>? and watchable_type = ? and user_id = ?", '1','Project', @user.id) + @watch_projects = Project.joins(:watchers).where("project_type <>? and watchable_type = ? and watchers.user_id = ?", '1','Project', @user.id) @state = 1 respond_to do |format| format.html { @@ -782,7 +782,7 @@ class UsersController < ApplicationController end def setting_layout(default_base='base_users') - User.current.admin? ? 'base_admin' : default_base + User.current.admin? ? default_base : default_base end # 必填自己的工作单位,其实就是学校 diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e0d52e89d..76da3b684 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1342,7 +1342,18 @@ module ApplicationHelper def bootstrap_head tags = stylesheet_link_tag('bootstrap/bootstrap.min', 'bootstrap/bootstrap-theme.min') - tags << javascript_include_tag('bootstrap/bootstrap.min', 'bootstrap/jquery.transition.min') + tags << javascript_include_tag('bootstrap/affix') + tags << javascript_include_tag('bootstrap/alert') + tags << javascript_include_tag('bootstrap/button') + tags << javascript_include_tag('bootstrap/carousel') + tags << javascript_include_tag('bootstrap/collapse') + tags << javascript_include_tag('bootstrap/dropdown') + tags << javascript_include_tag('bootstrap/modal') + tags << javascript_include_tag('bootstrap/popover') + tags << javascript_include_tag('bootstrap/scrollspy') + tags << javascript_include_tag('bootstrap/tab') + tags << javascript_include_tag('bootstrap/tooltip') + tags << javascript_include_tag('bootstrap/transition') tags end @@ -1674,5 +1685,20 @@ module ApplicationHelper # end # end # end + + def footer_logo(ul_class=nil, li_class=nil) + logos = [] + logos.push(link_to image_tag('/images/footer_logo/nudt.png',:alt=>"nudt"),"http://www.nudt.edu.cn/special.asp?classid=12" ) + logos.push(link_to image_tag('/images/footer_logo/peking_eecs.png', :alt=>"peking_eecs"), "http://eecs.pku.edu.cn" ) + logos.push(link_to image_tag('/images/footer_logo/buaa_scse.png', :alt=>"buaa_scse"), "http://scse.buaa.edu.cn/" ) + logos.push(link_to image_tag('/images/footer_logo/iscas.png', :alt=>"iscas"), "http://www.iscas.ac.cn" ) + logos.push(link_to image_tag('/images/footer_logo/inforbus.png', :alt=>"inforbus"), "http://www.inforbus.com" ) + + logos.collect! { |logo| + content_tag(:li, logo.html_safe, :class => li_class.to_s) + } + + content_tag(:ul, logos.join("").html_safe, :class => ul_class.to_s).html_safe + end end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 23351d919..5e7f7c18c 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -59,12 +59,11 @@ module IssuesHelper def issue_heading(issue) #h("#{issue.tracker} ##{issue.id}") - #h("#{issue.tracker} #{issue.source_from}") + # h("#{issue.tracker} #{issue.source_from}") s = '' - s << ">>" - s << link_to(@issue.project.name+l(:issue_list), project_issues_path(@issue.project)) - s << " >" - s << @issue.source_from + s << link_to(@issue.project.name, project_issues_path(@issue.project)) + s << " > #" + s << @issue.project_index s.html_safe end diff --git a/app/models/issue.rb b/app/models/issue.rb index d2332350a..7e899eafc 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1156,7 +1156,11 @@ class Issue < ActiveRecord::Base # back string obj which is belong to project. def source_from "" << self.project.name.to_s << - "#" << (self.project.issues.index(self).to_i + 1).to_s + "#" << project_index + end + + def project_index + (self.project.issues.index(self).to_i + 1).to_s end private diff --git a/app/models/relative_memo.rb b/app/models/relative_memo.rb index 073d1ca12..f087fce2b 100644 --- a/app/models/relative_memo.rb +++ b/app/models/relative_memo.rb @@ -70,6 +70,11 @@ class RelativeMemo < ActiveRecord::Base def cannot_reply_to_locked_topic errors.add :base, l(:label_memo_locked) if root.locked? && self != root end + + def short_content(length = 25) + str = "^(.{,#{length}})[^\n\r]*.*$" + content.gsub(Regexp.new(str), '\1...').strip if content + end # def update_memos_forum # if forum_id_changed? diff --git a/app/views/admin/users.html.erb b/app/views/admin/users.html.erb new file mode 100644 index 000000000..779316469 --- /dev/null +++ b/app/views/admin/users.html.erb @@ -0,0 +1,69 @@ +<% if User.current.admin? %> +
+ <%= link_to l(:label_user_new), new_user_path, :class => 'icon icon-add' %> +
+ +

<%= l(:label_user_plural)%>

+ + <%= form_tag(:controller => 'users', :action => 'search', :method => :get) do %> +
+ + <%= l(:label_filter_plural) %> + + + <%= select_tag 'status', users_status_options_for_select(@status), :class => "small", :onchange => "this.form.submit(); return false;" %> + + <% if @groups.present? %> + + <%= select_tag 'group_id', content_tag('option') + options_from_collection_for_select(@groups, :id, :name, params[:group_id].to_i), :onchange => "this.form.submit(); return false;" %> + <% end %> + + + <%= text_field_tag 'name', params[:name], :size => 30 %> + <%= submit_tag l(:label_search), :class => "small", :name => nil %> +
+ <% end %> +   + +
+ + + + <%= sort_header_tag('login', :caption => l(:field_login)) %> + <%= sort_header_tag('firstname', :caption => l(:field_firstname)) %> + <%= sort_header_tag('lastname', :caption => l(:field_lastname)) %> + <%= sort_header_tag('mail', :caption => l(:field_mail)) %> + + <%= sort_header_tag('admin', :caption => l(:field_admin), :default_order => 'desc') %> + <%= sort_header_tag('created_on', :caption => l(:field_created_on), :default_order => 'desc') %> + <%= sort_header_tag('last_login_on', :caption => l(:field_last_login_on), :default_order => 'desc') %> + + + + + <% for user in @users -%> + "> + + + + + + + + + + <% end -%> + +
<%= avatar(user, :size => "14") %><%= link_to h(user.login), edit_user_path(user) %><%= h(user.firstname) %><%= h(user.lastname) %><%= checked_image user.admin? %><%= format_time(user.created_on) %> <%= change_status_link(user) %> + <%= delete_link user_path(user, :back_url => users_path(params)) unless User.current == user %>
+
+ + + <% html_title(l(:label_user_plural)) -%> +<%else %> + +<% end%> \ No newline at end of file diff --git a/app/views/layouts/_bootstrap_base_footer.html.erb b/app/views/layouts/_bootstrap_base_footer.html.erb new file mode 100644 index 000000000..2bda28bdd --- /dev/null +++ b/app/views/layouts/_bootstrap_base_footer.html.erb @@ -0,0 +1,20 @@ +
+ \ No newline at end of file diff --git a/app/views/layouts/_bootstrap_base_header.html.erb b/app/views/layouts/_bootstrap_base_header.html.erb index 2c55ff2cc..8c2d26c54 100644 --- a/app/views/layouts/_bootstrap_base_header.html.erb +++ b/app/views/layouts/_bootstrap_base_header.html.erb @@ -1,51 +1,49 @@ <% - request.headers['REQUEST_URI'] = "" if request.headers['REQUEST_URI'].nil? - realUrl = request.original_url - if (realUrl.match(/forge\.trustie\.net\/*/)) - @nav_dispaly_project_label = 1 - @nav_dispaly_forum_label = 1 - elsif (realUrl.match(/course\.trustie\.net\/*/)) - @nav_dispaly_course_all_label = 1 - @nav_dispaly_forum_label = 1 - @nav_dispaly_course_label = nil - @nav_dispaly_store_all_label = 1 - elsif (realUrl.match(/user\.trustie\.net\/*/)) - @nav_dispaly_home_path_label = 1 - @nav_dispaly_main_course_label = 1 - @nav_dispaly_main_project_label = 1 - @nav_dispaly_main_contest_label = 1 - elsif (realUrl.match(/contest\.trustie\.net\/*/)) - @nav_dispaly_contest_label = 1 - @nav_dispaly_store_all_label = 1 - else - @nav_dispaly_project_all_label = 1 - @nav_dispaly_course_all_label = 1 - @nav_dispaly_forum_label = 1 - @nav_dispaly_bid_label = 1 - @nav_dispaly_contest_label = 1 - @nav_dispaly_store_all_label = 1 - @nav_dispaly_user_label = 1 - end +request.headers['REQUEST_URI'] = "" if request.headers['REQUEST_URI'].nil? +realUrl = request.original_url +if (realUrl.match(/forge\.trustie\.net\/*/)) + @nav_dispaly_project_label = 1 + @nav_dispaly_forum_label = 1 +elsif (realUrl.match(/course\.trustie\.net\/*/)) + @nav_dispaly_course_all_label = 1 + @nav_dispaly_forum_label = 1 + @nav_dispaly_course_label = nil + @nav_dispaly_store_all_label = 1 +elsif (realUrl.match(/user\.trustie\.net\/*/)) + @nav_dispaly_home_path_label = 1 + @nav_dispaly_main_course_label = 1 + @nav_dispaly_main_project_label = 1 + @nav_dispaly_main_contest_label = 1 +elsif (realUrl.match(/contest\.trustie\.net\/*/)) + @nav_dispaly_contest_label = 1 + @nav_dispaly_store_all_label = 1 +else + @nav_dispaly_project_all_label = 1 + @nav_dispaly_course_all_label = 1 + @nav_dispaly_forum_label = 1 + @nav_dispaly_bid_label = 1 + @nav_dispaly_contest_label = 1 + @nav_dispaly_store_all_label = 1 +end %> - + diff --git a/app/views/layouts/bootstrap_base.html.erb b/app/views/layouts/bootstrap_base.html.erb index f485d5c4a..040857df4 100644 --- a/app/views/layouts/bootstrap_base.html.erb +++ b/app/views/layouts/bootstrap_base.html.erb @@ -5,43 +5,30 @@ <%= h html_title %> + <%= csrf_meta_tag %> <%= favicon %> - <%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', 'application', 'nyan', :media => 'all' %> - <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %> + <%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', 'bootstrap_custom.css', :media => 'all' %> <%= javascript_heads %> - <%= javascript_include_tag "jquery.leanModal.min" %> - <%= javascript_include_tag 'seems_rateable/jRating', 'seems_rateable/rateable' %> - <%= heads_for_theme %> + <%#= javascript_include_tag "jquery.leanModal.min" %> <%= bootstrap_head %> - <%= call_hook :view_layouts_base_html_head %> - + <%= javascript_include_tag 'gas' %> <%= yield :header_tags -%> - + + <%= render :partial => 'layouts/bootstrap_base_header' %> +
+ <%= render_flash_messages %> + <%= yield %> +
+ -
-
-
- <%= render :partial => 'layouts/bootstrap_base_header' %> -
-
- <%= render_flash_messages %> - <%= yield %> - <%= call_hook :view_layouts_base_content %> -
- <%= render :partial => 'layouts/base_footer' %> -
-
-
- - -
-
-<%= call_hook :view_layouts_base_body_bottom %> + + + <%= render :partial => 'layouts/bootstrap_base_footer' %> diff --git a/app/views/open_source_projects/_show_bug.html.erb b/app/views/open_source_projects/_show_bug.html.erb new file mode 100644 index 000000000..97dae6829 --- /dev/null +++ b/app/views/open_source_projects/_show_bug.html.erb @@ -0,0 +1,80 @@ + + + +
+ 共有 <%= link_to @topic_count %> 个贴子 +
+ <% if memos.any? %> + <% memos.each do |topic| %> + + + + + + +
<%= link_to image_tag(url_to_avatar(topic.author), :class => "avatar"), user_path(topic.author) if topic.author%> + <%= image_tag('../images/avatars/User/0', :class => "avatar") unless topic.author%> + + + + + + + + + + + + + + + + + + + +
+ <% if topic.url.nil? || topic.url == '' %> + <%= link_to h(topic.subject), open_source_project_relative_memo_path(open_source_project, topic) %> + <% else %> + <%= link_to h(topic.subject), topic.url %> + <% end %> + + + + + + + +
<%= link_to (topic.replies_count), topic.url, :target => '_blank' %>
回帖
+ + + + + + + +
<%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), topic.url, :target => '_blank' %>
关注
+ + + + + + + +
<%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), topic.url, :target => '_blank' %>
浏览
<%= topic.short_content(70) %>
<%= user_url_and_time topic.username, topic.userhomeurl, topic.created_at %> +
+
帖子来源:<%=link_to topic.topic_resource, topic.url %> + <%= no_use_link(topic, User.current) %>
+ + <% end %> + + <% else %> +

+ <%= l(:label_no_data) %> +

+ <% end %> +
\ No newline at end of file diff --git a/app/views/open_source_projects/_show_memo.html.erb b/app/views/open_source_projects/_show_memo.html.erb index 63de42132..7d723dca9 100644 --- a/app/views/open_source_projects/_show_memo.html.erb +++ b/app/views/open_source_projects/_show_memo.html.erb @@ -2,7 +2,7 @@
- 共有 <%= link_to memos.count %> 个贴子 共有 <%= link_to @topic_count %> 个贴子
<% if memos.any? %> @@ -23,7 +23,7 @@ - + @@ -33,7 +33,7 @@
<%= link_to (topic.replies_count), open_source_project_relative_memo_path(open_source_project, topic) %><%= link_to (topic.replies_count), topic.url, :target => '_blank' %>
回帖 - + @@ -43,7 +43,7 @@ - + - - - + diff --git a/app/views/open_source_projects/_show_topics.html.erb b/app/views/open_source_projects/_show_topics.html.erb index 3ba463944..dacc9b013 100644 --- a/app/views/open_source_projects/_show_topics.html.erb +++ b/app/views/open_source_projects/_show_topics.html.erb @@ -20,7 +20,7 @@
-

项目安全态势

<%= link_to "更多 >>", :controller => "open_source_projects",:action => "showmemo", :id => @open_source_project.id %>
+

项目安全态势

<%= link_to "更多 >>", :controller => "open_source_projects",:action => "showbug", :id => @open_source_project.id %>
    @@ -435,11 +435,11 @@
<%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), open_source_project_relative_memo_path(open_source_project, topic) %><%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), topic.url, :target => '_blank' %>
关注 - + @@ -51,17 +51,17 @@
<%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), open_source_project_relative_memo_path(open_source_project, topic) %><%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), topic.url, :target => '_blank' %>
浏览
<%= user_url_and_time topic.username, topic.userhomeurl, topic.created_at %> + <%= user_url_and_time topic.username, topic.userhomeurl, topic.created_at %>
帖子来源:<%=link_to 'OSChina', topic.url %> + 帖子来源:<%=link_to 'OSChina', topic.url %> <%= no_use_link(topic, User.current) %> <%= no_use_link(topic, User.current) %>
- +
<%= link_to h(topic.subject), topic.url %><%= link_to h(topic.subject), topic.url, :target => '_blank' %> - + @@ -449,7 +449,7 @@ diff --git a/app/views/open_source_projects/show.html.erb b/app/views/open_source_projects/show.html.erb index d1751dec8..ec9d521d5 100644 --- a/app/views/open_source_projects/show.html.erb +++ b/app/views/open_source_projects/show.html.erb @@ -32,7 +32,7 @@ <% #= link_to '发布帖子', new_forum_memo_path(@forum), :class => 'icon icon-add' %> - <%= link_to l(:label_memo_new_from_forum), new_open_source_project_relative_memo_path(@open_source_project), :class => 'icon icon-add', + <%#= link_to l(:label_memo_new_from_forum), new_open_source_project_relative_memo_path(@open_source_project), :class => 'icon icon-add', :onclick => 'showAndScrollTo("add-memo", "memo_subject"); return false;' if User.current.logged? %> diff --git a/app/views/open_source_projects/showbug.html.erb b/app/views/open_source_projects/showbug.html.erb new file mode 100644 index 000000000..86e8a226b --- /dev/null +++ b/app/views/open_source_projects/showbug.html.erb @@ -0,0 +1,55 @@ + + + +<% #= link_to '发布帖子', new_forum_memo_path(@forum), :class => 'icon icon-add' %> + + <%= link_to l(:label_memo_new_from_forum), new_open_source_project_relative_memo_path(@open_source_project), :class => 'icon icon-add', + :onclick => 'showAndScrollTo("add-memo", "memo_subject"); return false;' if User.current.logged? %> + + +
+ <%#= link_to( + image_tag('edit.png')+l(:label_forum_edit), + {:action => 'edit', :id => @forum}, + :method => 'get', + :title => l(:button_edit) + ) if @forum.editable_by?(User.current) %> + <%#= link_to( + image_tag('delete.png')+'删除讨论区', + {:action => 'destroy', :id => @forum}, + :method => :delete, + :data => {:confirm => l(:text_are_you_sure)}, + :title => l(:button_delete) + ) if @forum.destroyable_by?(User.current) %> +
+<%= render :partial => 'open_source_projects/show_bug', :locals => {:memos => @memos, :open_source_project => @open_source_project} %> + \ No newline at end of file diff --git a/app/views/test/bootstrap.html.erb b/app/views/test/bootstrap.html.erb new file mode 100644 index 000000000..56c1d11bf --- /dev/null +++ b/app/views/test/bootstrap.html.erb @@ -0,0 +1 @@ +<%= image_tag "http://image227.poco.cn/mypoco/myphoto/20140516/17/5527437020140516173219035.png", class: "img-responsive" %> \ No newline at end of file diff --git a/app/views/test/courselist.html.erb b/app/views/test/courselist.html.erb index 959d6a4be..e11c16fad 100644 --- a/app/views/test/courselist.html.erb +++ b/app/views/test/courselist.html.erb @@ -1,42 +1,54 @@ - -<% @courses.each do |course| %> -
- <%= course.name %> +
+ Well done! You successfully read this important alert message. +
+
+ Heads up! This alert needs your attention, but it's not super important. +
+
+ Warning! Best check yo self, you're not looking too good. +
+
+ Oh snap! Change a few things up and try submitting again. +
+ +
<%= link_to (topic.replies_count), open_source_project_relative_memo_path(open_source_project, topic) %><%= link_to (topic.replies_count), topic.url, :target => '_blank' %>
回帖 - + @@ -459,7 +459,7 @@ - + - + - - +
<%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), open_source_project_relative_memo_path(open_source_project, topic) %><%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), topic.url, :target => '_blank' %>
关注 - + @@ -467,17 +467,17 @@
<%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), open_source_project_relative_memo_path(open_source_project, topic) %><%= link_to (topic.viewed_count_crawl+topic.viewed_count_local), topic.url, :target => '_blank' %>
浏览
帖子来源:<%=link_to 'OSChina', topic.url %> + 帖子来源:<%=link_to 'OSChina', topic.url %> <%= no_use_link(topic, User.current) %> <%= no_use_link(topic, User.current) %>
- - - <%= sort_header_tag('login', :caption => l(:field_login)) %> - <%= sort_header_tag('firstname', :caption => l(:field_firstname)) %> - <%= sort_header_tag('lastname', :caption => l(:field_lastname)) %> - <%= sort_header_tag('mail', :caption => l(:field_mail)) %> - - <%= sort_header_tag('admin', :caption => l(:field_admin), :default_order => 'desc') %> - <%= sort_header_tag('created_on', :caption => l(:field_created_on), :default_order => 'desc') %> - <%= sort_header_tag('last_login_on', :caption => l(:field_last_login_on), :default_order => 'desc') %> - - - - - <% for user in @users -%> - "> - - - - - - - - - - <% end -%> - -
<%= avatar(user, :size => "14") %><%= link_to h(user.login), edit_user_path(user) %><%= h(user.firstname) %><%= h(user.lastname) %><%= checked_image user.admin? %><%= format_time(user.created_on) %> <%= change_status_link(user) %> - <%= delete_link user_path(user, :back_url => users_path(params)) unless User.current == user %>
- - - -<% html_title(l(:label_user_plural)) -%> - -<% else %>
<%= form_tag(:controller => 'users', :action => 'search', :method => :get) do %> @@ -115,4 +47,3 @@
<% html_title(l(:label_user_plural)) -%> -<% end -%> diff --git a/app/views/welcome/contest.html.erb b/app/views/welcome/contest.html.erb index 3e0fecceb..d8d8a2d8a 100644 --- a/app/views/welcome/contest.html.erb +++ b/app/views/welcome/contest.html.erb @@ -189,7 +189,7 @@ <% find_new_forum_topics(11).each do |topic|%>
  • -       +       <%= link_to '['+topic.forum.name + ']',forum_path(topic.forum),:class => 'memo_Bar_title' %><%= link_to topic.subject.truncate(30, omission: '...'), topic.event_url, :class => "gray" , :style => "font-size: 10pt !important;" %>
    diff --git a/app/views/welcome/course.html.erb b/app/views/welcome/course.html.erb index 5e02730c0..88fd3bf4d 100644 --- a/app/views/welcome/course.html.erb +++ b/app/views/welcome/course.html.erb @@ -234,7 +234,7 @@ <% find_new_forum_topics(10).each do |topic|%>
  • -       +       <%= link_to '['+topic.forum.name + ']',forum_path(topic.forum),:class => 'memo_Bar_title' %><%= link_to topic.subject.truncate(30, omission: '...'), topic.event_url, :class => "gray" , :style => "font-size: 10pt !important;" %>
    diff --git a/app/views/welcome/index.html.erb b/app/views/welcome/index.html.erb index fcdce62e8..6702f520c 100644 --- a/app/views/welcome/index.html.erb +++ b/app/views/welcome/index.html.erb @@ -112,7 +112,7 @@
    <% find_new_forum_topics(7).each do |topic|%>
  • -
    +
    <%= link_to '['+topic.forum.name + ']',forum_path(topic.forum),:class => 'memo_Bar_title' %><%= link_to topic.subject.truncate(30, omission: '...'), topic.event_url,title: topic.subject %>
    diff --git a/config/routes.rb b/config/routes.rb index dbbc544ac..5206dfd53 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,11 +18,10 @@ RedmineApp::Application.routes.draw do resources :homework_users - resources :no_uses delete 'no_uses', :to => 'no_uses#delete' - resources :apply_project_masters + resources :apply_project_masters delete 'apply_project_masters', :to => 'apply_project_masters#delete' resources :homework_attach do @@ -42,11 +41,12 @@ RedmineApp::Application.routes.draw do match 'master_apply', via: [:get, :post] match 'accept_master_apply', via: [:get, :post] match 'refuse_master_apply', via: [:get, :post] + match 'showmemo', via: [:get, :post] + match 'showbug', via: [:get, :post] end end mount SeemsRateable::Engine => '/rateable', :as => :rateable - namespace :zipdown do match 'assort' end @@ -56,11 +56,11 @@ RedmineApp::Application.routes.draw do end ##new added by linchun #以发布应用的形式参与竞赛 resources :softapplications do - + collection do match 'new_message', via: :get end - member do + member do match 'create_message' , via: :post end end @@ -89,9 +89,9 @@ RedmineApp::Application.routes.draw do match 'add_softapplication' , via: [:get, :post] match 'create' , via: :post match 'settings' , via: [:get, :post] - end - end - + end + end + resources :stores do collection do match 'search', via: [:get, :post] @@ -321,7 +321,7 @@ RedmineApp::Application.routes.draw do resources :files, :only => [:index, :new, :create] do collection do match "getattachtype" , via: [:get, :post] - #match 'getattachtype/:attachtype', :to => 'files#getattachtype', via: [:get, :post] + #match 'getattachtype/:attachtype', :to => 'files#getattachtype', via: [:get, :post] end end @@ -480,8 +480,8 @@ RedmineApp::Application.routes.draw do collection do match "updateType" , via: [:get, :post] match "renderTag" , via: [:get, :post] - end - end + end + end resources :groups do member do @@ -520,6 +520,7 @@ RedmineApp::Application.routes.draw do match 'admin', :controller => 'admin', :action => 'index', :via => :get match 'admin/projects', :controller => 'admin', :action => 'projects', :via => :get + match 'admin/users', :controller => 'admin', :action => 'users', :via => :get match 'admin/plugins', :controller => 'admin', :action => 'plugins', :via => :get match 'admin/info', :controller => 'admin', :action => 'info', :via => :get match 'admin/test_email', :controller => 'admin', :action => 'test_email', :via => :get @@ -619,24 +620,27 @@ RedmineApp::Application.routes.draw do match 'calls/:id', :controller => 'bids', :action => 'show', :as => 'respond' match 'contest', :controller => 'bids', :action => 'contests', :as => 'contest' #modified @20140403 - ######################## ##added by wen########## #######confusing######## + get 'welcome/search', to: 'welcome#search' + get 'school/index', to: 'school#index' + get 'course/:school_id', to: 'welcome#course' + get 'course/:school_id', to: 'welcome#course' post 'school/get_options/:province', :to => 'school#get_options' get 'school/get_options/:province', :to => 'school#get_options' post 'school/get_province', :to => 'school#get_province' get 'school/get_province', :to => 'school#get_province' - + post 'school/get_schoollist/:province', :to => 'school#get_schoollist' get 'school/get_schoollist/:province', :to => 'school#get_schoollist' - + post 'school/search_school/', :to => 'school#search_school' get 'school/search_school/', :to => 'school#search_school' - + post 'school/upload', :to => 'school#upload' - + ######added by nie match 'tags/show_projects_tags',:to => 'tags#show_projects_tags' ########### added by liuping @@ -661,5 +665,4 @@ RedmineApp::Application.routes.draw do end end end - get ':controller(/:action(/:id))' end diff --git a/lib/redmine.rb b/lib/redmine.rb index 904be7c25..dd0973902 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -305,7 +305,7 @@ end ########end Redmine::MenuManager.map :admin_menu do |menu| menu.push :projects, {:controller => 'admin', :action => 'projects'}, :caption => :label_project_plural - menu.push :users, {:controller => 'users'}, :caption => :label_user_plural + menu.push :users, {:controller => 'admin', :action => 'users'}, :caption => :label_user_plural menu.push :groups, {:controller => 'groups'}, :caption => :label_group_plural menu.push :roles, {:controller => 'roles'}, :caption => :label_role_and_permissions menu.push :trackers, {:controller => 'trackers'}, :caption => :label_tracker_plural diff --git a/public/javascripts/bootstrap/affix.js b/public/javascripts/bootstrap/affix.js new file mode 100644 index 000000000..552bffa3f --- /dev/null +++ b/public/javascripts/bootstrap/affix.js @@ -0,0 +1,126 @@ +/* ======================================================================== + * Bootstrap: affix.js v3.0.3 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // AFFIX CLASS DEFINITION + // ====================== + + var Affix = function (element, options) { + this.options = $.extend({}, Affix.DEFAULTS, options) + this.$window = $(window) + .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) + .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) + + this.$element = $(element) + this.affixed = + this.unpin = null + + this.checkPosition() + } + + Affix.RESET = 'affix affix-top affix-bottom' + + Affix.DEFAULTS = { + offset: 0 + } + + Affix.prototype.checkPositionWithEventLoop = function () { + setTimeout($.proxy(this.checkPosition, this), 1) + } + + Affix.prototype.checkPosition = function () { + if (!this.$element.is(':visible')) return + + var scrollHeight = $(document).height() + var scrollTop = this.$window.scrollTop() + var position = this.$element.offset() + var offset = this.options.offset + var offsetTop = offset.top + var offsetBottom = offset.bottom + + if (typeof offset != 'object') offsetBottom = offsetTop = offset + if (typeof offsetTop == 'function') offsetTop = offset.top() + if (typeof offsetBottom == 'function') offsetBottom = offset.bottom() + + var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false : + offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' : + offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false + + if (this.affixed === affix) return + if (this.unpin) this.$element.css('top', '') + + this.affixed = affix + this.unpin = affix == 'bottom' ? position.top - scrollTop : null + + this.$element.removeClass(Affix.RESET).addClass('affix' + (affix ? '-' + affix : '')) + + if (affix == 'bottom') { + this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() }) + } + } + + + // AFFIX PLUGIN DEFINITION + // ======================= + + var old = $.fn.affix + + $.fn.affix = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.affix') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.affix', (data = new Affix(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.affix.Constructor = Affix + + + // AFFIX NO CONFLICT + // ================= + + $.fn.affix.noConflict = function () { + $.fn.affix = old + return this + } + + + // AFFIX DATA-API + // ============== + + $(window).on('load', function () { + $('[data-spy="affix"]').each(function () { + var $spy = $(this) + var data = $spy.data() + + data.offset = data.offset || {} + + if (data.offsetBottom) data.offset.bottom = data.offsetBottom + if (data.offsetTop) data.offset.top = data.offsetTop + + $spy.affix(data) + }) + }) + +}(jQuery); diff --git a/public/javascripts/bootstrap/alert.js b/public/javascripts/bootstrap/alert.js new file mode 100644 index 000000000..695ad74d0 --- /dev/null +++ b/public/javascripts/bootstrap/alert.js @@ -0,0 +1,98 @@ +/* ======================================================================== + * Bootstrap: alert.js v3.0.3 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.hasClass('alert') ? $this : $this.parent() + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + $parent.trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one($.support.transition.end, removeElement) + .emulateTransitionEnd(150) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + var old = $.fn.alert + + $.fn.alert = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); diff --git a/public/javascripts/bootstrap/button.js b/public/javascripts/bootstrap/button.js new file mode 100644 index 000000000..c9fdde5e4 --- /dev/null +++ b/public/javascripts/bootstrap/button.js @@ -0,0 +1,115 @@ +/* ======================================================================== + * Bootstrap: button.js v3.0.3 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + } + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (!data.resetText) $el.data('resetText', $el[val]()) + + $el[val](data[state] || this.options[state]) + + // push to event loop to allow forms to submit + setTimeout(function () { + state == 'loadingText' ? + $el.addClass(d).attr(d, d) : + $el.removeClass(d).removeAttr(d); + }, 0) + } + + Button.prototype.toggle = function () { + var $parent = this.$element.closest('[data-toggle="buttons"]') + var changed = true + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') === 'radio') { + // see if clicking on current one + if ($input.prop('checked') && this.$element.hasClass('active')) + changed = false + else + $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + var old = $.fn.button + + $.fn.button = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + $btn.button('toggle') + e.preventDefault() + }) + +}(jQuery); diff --git a/public/javascripts/bootstrap/carousel.js b/public/javascripts/bootstrap/carousel.js new file mode 100644 index 000000000..6391a36df --- /dev/null +++ b/public/javascripts/bootstrap/carousel.js @@ -0,0 +1,217 @@ +/* ======================================================================== + * Bootstrap: carousel.js v3.0.3 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.pause == 'hover' && this.$element + .on('mouseenter', $.proxy(this.pause, this)) + .on('mouseleave', $.proxy(this.cycle, this)) + } + + Carousel.DEFAULTS = { + interval: 5000 + , pause: 'hover' + , wrap: true + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getActiveIndex = function () { + this.$active = this.$element.find('.item.active') + this.$items = this.$active.parent().children() + + return this.$items.index(this.$active) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getActiveIndex() + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition.end) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || $active[type]() + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var fallback = type == 'next' ? 'first' : 'last' + var that = this + + if (!$next.length) { + if (!this.options.wrap) return + $next = this.$element.find('.item')[fallback]() + } + + this.sliding = true + + isCycling && this.pause() + + var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction }) + + if ($next.hasClass('active')) return + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + this.$element.one('slid.bs.carousel', function () { + var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) + $nextIndicator && $nextIndicator.addClass('active') + }) + } + + if ($.support.transition && this.$element.hasClass('slide')) { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one($.support.transition.end, function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0) + }) + .emulateTransitionEnd(600) + } else { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger('slid.bs.carousel') + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + var old = $.fn.carousel + + $.fn.carousel = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var $this = $(this), href + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + $target.carousel(options) + + if (slideIndex = $this.attr('data-slide-to')) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + }) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + $carousel.carousel($carousel.data()) + }) + }) + +}(jQuery); diff --git a/public/javascripts/bootstrap/collapse.js b/public/javascripts/bootstrap/collapse.js new file mode 100644 index 000000000..1a079938e --- /dev/null +++ b/public/javascripts/bootstrap/collapse.js @@ -0,0 +1,179 @@ +/* ======================================================================== + * Bootstrap: collapse.js v3.0.3 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.transitioning = null + + if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.toggle) this.toggle() + } + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var actives = this.$parent && this.$parent.find('> .panel > .in') + + if (actives && actives.length) { + var hasData = actives.data('bs.collapse') + if (hasData && hasData.transitioning) return + actives.collapse('hide') + hasData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing') + [dimension](0) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('in') + [dimension]('auto') + this.transitioning = 0 + this.$element.trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one($.support.transition.end, $.proxy(complete, this)) + .emulateTransitionEnd(350) + [dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element + [dimension](this.$element[dimension]()) + [0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse') + .removeClass('in') + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .trigger('hidden.bs.collapse') + .removeClass('collapsing') + .addClass('collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one($.support.transition.end, $.proxy(complete, this)) + .emulateTransitionEnd(350) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + var old = $.fn.collapse + + $.fn.collapse = function (option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) { + var $this = $(this), href + var target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + var $target = $(target) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + var parent = $this.attr('data-parent') + var $parent = parent && $(parent) + + if (!data || !data.transitioning) { + if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed') + $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + } + + $target.collapse(option) + }) + +}(jQuery); diff --git a/public/javascripts/bootstrap/dropdown.js b/public/javascripts/bootstrap/dropdown.js new file mode 100644 index 000000000..13352ef7c --- /dev/null +++ b/public/javascripts/bootstrap/dropdown.js @@ -0,0 +1,154 @@ +/* ======================================================================== + * Bootstrap: dropdown.js v3.0.3 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2013 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== */ + + ++function ($) { "use strict"; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle=dropdown]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('