Commit 35d0de8f367e949c3bab50506916ca87eeb5d5ab
Exists in
master
and in
4 other branches
merging upstream changes
Showing
49 changed files
with
511 additions
and
168 deletions
Show diff stats
app/assets/javascripts/application.js
app/assets/javascripts/merge_requests.js
... | ... | @@ -11,7 +11,7 @@ var MergeRequest = { |
11 | 11 | |
12 | 12 | $(".tabs a.merge-notes-tab").live("click", function(e) { |
13 | 13 | $(".merge-request-diffs").hide(); |
14 | - $(".merge-request-notes").show(); | |
14 | + $(".merge_request_notes").show(); | |
15 | 15 | e.preventDefault(); |
16 | 16 | }); |
17 | 17 | |
... | ... | @@ -19,7 +19,7 @@ var MergeRequest = { |
19 | 19 | if(!MergeRequest.diffs_loaded) { |
20 | 20 | MergeRequest.loadDiff(); |
21 | 21 | } |
22 | - $(".merge-request-notes").hide(); | |
22 | + $(".merge_request_notes").hide(); | |
23 | 23 | $(".merge-request-diffs").show(); |
24 | 24 | e.preventDefault(); |
25 | 25 | }); |
... | ... | @@ -33,7 +33,7 @@ var MergeRequest = { |
33 | 33 | url: $(".merge-diffs-tab").attr("data-url"), |
34 | 34 | complete: function(){ |
35 | 35 | MergeRequest.diffs_loaded = true; |
36 | - $(".merge-request-notes").hide(); | |
36 | + $(".merge_request_notes").hide(); | |
37 | 37 | $(".dashboard-loader").hide()}, |
38 | 38 | dataType: "script"}); |
39 | 39 | } | ... | ... |
app/assets/stylesheets/common.scss
... | ... | @@ -3,7 +3,7 @@ a { |
3 | 3 | color: $link_color; |
4 | 4 | &:hover { |
5 | 5 | text-decoration:none; |
6 | - color: $style_color; | |
6 | + color: $blue_link; | |
7 | 7 | } |
8 | 8 | |
9 | 9 | &.btn { |
... | ... | @@ -11,6 +11,18 @@ a { |
11 | 11 | } |
12 | 12 | } |
13 | 13 | |
14 | +.btn { | |
15 | + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f1f1f1), color-stop(25%, #f1f1f1), to(#e6e6e6)); | |
16 | + background-image: -webkit-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6); | |
17 | + background-image: -moz-linear-gradient(top, #f1f1f1, #f1f1f1 25%, #e6e6e6); | |
18 | + background-image: -ms-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6); | |
19 | + background-image: -o-linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6); | |
20 | + background-image: linear-gradient(#f1f1f1, #f1f1f1 25%, #e6e6e6); | |
21 | + | |
22 | + &:hover { | |
23 | + } | |
24 | +} | |
25 | + | |
14 | 26 | a:focus { |
15 | 27 | outline: none; |
16 | 28 | } |
... | ... | @@ -29,6 +41,29 @@ a:focus { |
29 | 41 | |
30 | 42 | .label { |
31 | 43 | background-color: #474D57; |
44 | + | |
45 | + &.pushed { | |
46 | + background-color: $link_color; | |
47 | + } | |
48 | +} | |
49 | + | |
50 | +.pretty_label { | |
51 | + @include round-borders-all(4px); | |
52 | + padding:2px 4px; | |
53 | + background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8)); | |
54 | + background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8); | |
55 | + background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8); | |
56 | + background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8); | |
57 | + color: #777; | |
58 | + border: 1px solid #DEDFE1; | |
59 | + | |
60 | + &.branch { | |
61 | + border:none; | |
62 | + font-size:13px; | |
63 | + background: #474D57; | |
64 | + color:#fff; | |
65 | + font-family: monospace; | |
66 | + } | |
32 | 67 | } |
33 | 68 | |
34 | 69 | .tabs > li > a, .pills > li > a { |
... | ... | @@ -807,12 +842,19 @@ p.time { |
807 | 842 | width:840px; |
808 | 843 | margin:auto; |
809 | 844 | |
810 | - .wll { | |
811 | - padding:5px; | |
812 | - margin-top:5px; | |
845 | + .dash_project_item { | |
846 | + margin-bottom:10px; | |
813 | 847 | border:none; |
814 | 848 | &:hover { |
815 | 849 | background:none; |
850 | + | |
851 | + h4 { | |
852 | + color:#2FA0BB; | |
853 | + .arrow { | |
854 | + background:#2FA0BB; | |
855 | + color:#fff; | |
856 | + } | |
857 | + } | |
816 | 858 | } |
817 | 859 | |
818 | 860 | h4 { |
... | ... | @@ -887,7 +929,7 @@ p.time { |
887 | 929 | } |
888 | 930 | a:last-child h4 { border:none; } |
889 | 931 | |
890 | - a.active { | |
932 | + a:hover { | |
891 | 933 | h4 { |
892 | 934 | color:#111; |
893 | 935 | border-right:4px solid $styled_border_color; |
... | ... | @@ -974,3 +1016,32 @@ p.time { |
974 | 1016 | } |
975 | 1017 | } |
976 | 1018 | } |
1019 | + | |
1020 | +.highlight_word { | |
1021 | + background:#EEDC94; | |
1022 | +} | |
1023 | + | |
1024 | +.status_info { | |
1025 | + font-size:14px; | |
1026 | + padding:5px 15px; | |
1027 | + line-height:24px; | |
1028 | + width:60px; | |
1029 | + text-align:center; | |
1030 | + float:left; | |
1031 | + margin-right:20px; | |
1032 | +} | |
1033 | + | |
1034 | +.merge_request_status_holder { | |
1035 | + margin-bottom:20px; | |
1036 | +} | |
1037 | + | |
1038 | +.arrow{ | |
1039 | + float: right; | |
1040 | + background: #E3E5EA; | |
1041 | + padding: 10px; | |
1042 | + border-radius: 5px; | |
1043 | + text-shadow: none; | |
1044 | + color: #999; | |
1045 | + line-height: 16px; | |
1046 | + font-weight:bold; | |
1047 | +} | ... | ... |
app/assets/stylesheets/main.scss
app/assets/stylesheets/ui_basic.scss
app/controllers/dashboard_controller.rb
... | ... | @@ -18,7 +18,7 @@ class DashboardController < ApplicationController |
18 | 18 | # Get authored or assigned open merge requests |
19 | 19 | def merge_requests |
20 | 20 | @projects = current_user.projects.all |
21 | - @merge_requests = MergeRequest.where("author_id = :id or assignee_id = :id", :id => current_user.id).opened.order("created_at DESC").limit(40) | |
21 | + @merge_requests = current_user.cared_merge_requests.order("created_at DESC").limit(40) | |
22 | 22 | end |
23 | 23 | |
24 | 24 | # Get only assigned issues | ... | ... |
app/controllers/merge_requests_controller.rb
... | ... | @@ -41,13 +41,9 @@ class MergeRequestsController < ApplicationController |
41 | 41 | |
42 | 42 | @note = @project.notes.new(:noteable => @merge_request) |
43 | 43 | |
44 | - @commits = @project.repo. | |
45 | - commits_between(@merge_request.target_branch, @merge_request.source_branch). | |
46 | - map {|c| Commit.new(c)}. | |
47 | - sort_by(&:created_at). | |
48 | - reverse | |
49 | - | |
50 | - render_full_content | |
44 | + # Get commits from repository | |
45 | + # or from cache if already merged | |
46 | + @commits = @merge_request.commits | |
51 | 47 | |
52 | 48 | respond_to do |format| |
53 | 49 | format.html |
... | ... | @@ -76,6 +72,7 @@ class MergeRequestsController < ApplicationController |
76 | 72 | |
77 | 73 | respond_to do |format| |
78 | 74 | if @merge_request.save |
75 | + @merge_request.reload_code | |
79 | 76 | format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully created.' } |
80 | 77 | format.json { render json: @merge_request, status: :created, location: @merge_request } |
81 | 78 | else |
... | ... | @@ -88,6 +85,7 @@ class MergeRequestsController < ApplicationController |
88 | 85 | def update |
89 | 86 | respond_to do |format| |
90 | 87 | if @merge_request.update_attributes(params[:merge_request].merge(:author_id_of_changes => current_user.id)) |
88 | + @merge_request.reload_code | |
91 | 89 | format.html { redirect_to [@project, @merge_request], notice: 'Merge request was successfully updated.' } |
92 | 90 | format.json { head :ok } |
93 | 91 | else | ... | ... |
app/controllers/projects_controller.rb
... | ... | @@ -13,6 +13,7 @@ class ProjectsController < ApplicationController |
13 | 13 | def index |
14 | 14 | @projects = current_user.projects |
15 | 15 | @projects = @projects.select(&:last_activity_date).sort_by(&:last_activity_date).reverse |
16 | + @events = Event.where(:project_id => @projects.map(&:id)).recent.limit(40) | |
16 | 17 | end |
17 | 18 | |
18 | 19 | def new |
... | ... | @@ -78,7 +79,6 @@ class ProjectsController < ApplicationController |
78 | 79 | render "projects/empty" |
79 | 80 | end |
80 | 81 | end |
81 | - format.js | |
82 | 82 | end |
83 | 83 | end |
84 | 84 | ... | ... |
... | ... | @@ -0,0 +1,12 @@ |
1 | +class SearchController < ApplicationController | |
2 | + def show | |
3 | + query = params[:search] | |
4 | + if query.blank? | |
5 | + @projects = [] | |
6 | + @merge_requests = [] | |
7 | + else | |
8 | + @projects = Project.search(query).limit(10) | |
9 | + @merge_requests = MergeRequest.search(query).limit(10) | |
10 | + end | |
11 | + end | |
12 | +end | ... | ... |
app/models/event.rb
app/models/merge_request.rb
1 | +require File.join(Rails.root, "app/models/commit") | |
2 | + | |
1 | 3 | class MergeRequest < ActiveRecord::Base |
2 | 4 | belongs_to :project |
3 | 5 | belongs_to :author, :class_name => "User" |
4 | 6 | belongs_to :assignee, :class_name => "User" |
5 | 7 | has_many :notes, :as => :noteable, :dependent => :destroy |
6 | 8 | |
9 | + serialize :st_commits | |
10 | + serialize :st_diffs | |
11 | + | |
7 | 12 | attr_protected :author, :author_id, :project, :project_id |
8 | 13 | attr_accessor :author_id_of_changes |
9 | 14 | |
... | ... | @@ -32,6 +37,13 @@ class MergeRequest < ActiveRecord::Base |
32 | 37 | scope :closed, where(:closed => true) |
33 | 38 | scope :assigned, lambda { |u| where(:assignee_id => u.id)} |
34 | 39 | |
40 | + def self.search query | |
41 | + where("title like :query", :query => "%#{query}%") | |
42 | + end | |
43 | + | |
44 | + def self.find_all_by_branch(branch_name) | |
45 | + where("source_branch like :branch or target_branch like :branch", :branch => branch_name) | |
46 | + end | |
35 | 47 | |
36 | 48 | def validate_branches |
37 | 49 | if target_branch == source_branch |
... | ... | @@ -39,23 +51,99 @@ class MergeRequest < ActiveRecord::Base |
39 | 51 | end |
40 | 52 | end |
41 | 53 | |
54 | + def reload_code | |
55 | + self.reloaded_commits | |
56 | + self.reloaded_diffs | |
57 | + end | |
58 | + | |
42 | 59 | def new? |
43 | 60 | today? && created_at == updated_at |
44 | 61 | end |
45 | 62 | |
46 | 63 | def diffs |
64 | + st_diffs || [] | |
65 | + end | |
66 | + | |
67 | + def reloaded_diffs | |
68 | + if open? && unmerged_diffs.any? | |
69 | + self.st_diffs = unmerged_diffs | |
70 | + save | |
71 | + end | |
72 | + diffs | |
73 | + end | |
74 | + | |
75 | + def unmerged_diffs | |
47 | 76 | commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)} |
48 | 77 | diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue [] |
49 | 78 | end |
50 | 79 | |
51 | 80 | def last_commit |
52 | - project.commit(source_branch) | |
81 | + commits.first | |
82 | + end | |
83 | + | |
84 | + def merged? | |
85 | + merged && merge_event | |
86 | + end | |
87 | + | |
88 | + def merge_event | |
89 | + self.project.events.where(:target_id => self.id, :target_type => "MergeRequest", :action => Event::Merged).last | |
90 | + end | |
91 | + | |
92 | + def closed_event | |
93 | + self.project.events.where(:target_id => self.id, :target_type => "MergeRequest", :action => Event::Closed).last | |
53 | 94 | end |
54 | 95 | |
96 | + | |
55 | 97 | # Return the number of +1 comments (upvotes) |
56 | 98 | def upvotes |
57 | 99 | notes.select(&:upvote?).size |
58 | 100 | end |
101 | + | |
102 | + def commits | |
103 | + st_commits || [] | |
104 | + end | |
105 | + | |
106 | + def probably_merged? | |
107 | + unmerged_commits.empty? && | |
108 | + commits.any? && open? | |
109 | + end | |
110 | + | |
111 | + def open? | |
112 | + !closed | |
113 | + end | |
114 | + | |
115 | + def mark_as_merged! | |
116 | + self.merged = true | |
117 | + self.closed = true | |
118 | + save | |
119 | + end | |
120 | + | |
121 | + def reloaded_commits | |
122 | + if open? && unmerged_commits.any? | |
123 | + self.st_commits = unmerged_commits | |
124 | + save | |
125 | + end | |
126 | + commits | |
127 | + end | |
128 | + | |
129 | + def unmerged_commits | |
130 | + self.project.repo. | |
131 | + commits_between(self.target_branch, self.source_branch). | |
132 | + map {|c| Commit.new(c)}. | |
133 | + sort_by(&:created_at). | |
134 | + reverse | |
135 | + end | |
136 | + | |
137 | + def merge!(user_id) | |
138 | + self.mark_as_merged! | |
139 | + Event.create( | |
140 | + :project => self.project, | |
141 | + :action => Event::Merged, | |
142 | + :target_id => self.id, | |
143 | + :target_type => "MergeRequest", | |
144 | + :author_id => user_id | |
145 | + ) | |
146 | + end | |
59 | 147 | end |
60 | 148 | # == Schema Information |
61 | 149 | # | ... | ... |
app/models/project.rb
... | ... | @@ -54,6 +54,10 @@ class Project < ActiveRecord::Base |
54 | 54 | UsersProject.access_roles |
55 | 55 | end |
56 | 56 | |
57 | + def self.search query | |
58 | + where("name like :query or code like :query or path like :query", :query => "%#{query}%") | |
59 | + end | |
60 | + | |
57 | 61 | def to_param |
58 | 62 | code |
59 | 63 | end |
... | ... | @@ -73,6 +77,24 @@ class Project < ActiveRecord::Base |
73 | 77 | ) |
74 | 78 | end |
75 | 79 | |
80 | + def update_merge_requests(oldrev, newrev, ref, author_key_id) | |
81 | + return true unless ref =~ /heads/ | |
82 | + branch_name = ref.gsub("refs/heads/", "") | |
83 | + user = Key.find_by_identifier(author_key_id).user | |
84 | + c_ids = self.commits_between(oldrev, newrev).map(&:id) | |
85 | + | |
86 | + # Update code for merge requests | |
87 | + mrs = self.merge_requests.opened.find_all_by_branch(branch_name).all | |
88 | + mrs.each { |merge_request| merge_request.reload_code } | |
89 | + | |
90 | + # Close merge requests | |
91 | + mrs = self.merge_requests.opened.where(:target_branch => branch_name).all | |
92 | + mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } | |
93 | + mrs.each { |merge_request| merge_request.merge!(user.id) } | |
94 | + | |
95 | + true | |
96 | + end | |
97 | + | |
76 | 98 | def execute_web_hooks(oldrev, newrev, ref, author_key_id) |
77 | 99 | ref_parts = ref.split('/') |
78 | 100 | ... | ... |
app/models/user.rb
app/views/commits/show.html.haml
... | ... | @@ -12,8 +12,8 @@ |
12 | 12 | = @commit.committer_name |
13 | 13 | %small= @commit.committed_date.stamp("Aug 21, 2011 9:23pm") |
14 | 14 | |
15 | -%hr | |
16 | -%pre.commit_message | |
15 | +%br | |
16 | +%pre.commit_message.prettyprint | |
17 | 17 | = commit_msg_with_link_to_issues(@project, @commit.safe_message) |
18 | 18 | .clear |
19 | 19 | %br | ... | ... |
app/views/dashboard/_events_feed.html.haml
app/views/dashboard/_issues_feed.html.haml
... | ... | @@ -3,8 +3,10 @@ |
3 | 3 | = link_to [issue.project, issue] do |
4 | 4 | %p |
5 | 5 | %strong |
6 | - %span.label= issue.project.name | |
6 | + %span.pretty_label= issue.project.name | |
7 | 7 | – |
8 | 8 | Issue # |
9 | 9 | = issue.id |
10 | 10 | = truncate issue.title, :length => 50 |
11 | + %span.right.cgray | |
12 | + = issue.updated_at.stamp("Aug 21, 2011") | ... | ... |
app/views/dashboard/_merge_requests_feed.html.haml
... | ... | @@ -3,8 +3,9 @@ |
3 | 3 | = link_to [merge_request.project, merge_request] do |
4 | 4 | %p |
5 | 5 | %strong |
6 | - %span.label= merge_request.project.name | |
6 | + %span.pretty_label= merge_request.project.name | |
7 | 7 | – |
8 | - Merge Request # | |
9 | - = merge_request.id | |
8 | + Merge Request ##{merge_request.id} | |
10 | 9 | = truncate merge_request.title, :length => 50 |
10 | + %span.right.cgray | |
11 | + = merge_request.updated_at.stamp("Aug 21, 2011") | ... | ... |
app/views/dashboard/_projects_feed.html.haml
1 | -- @active_projects.first(5).each do |project| | |
2 | - .wll | |
1 | +- projects.first(5).each do |project| | |
2 | + %div.dash_project_item | |
3 | 3 | = link_to project do |
4 | 4 | %h4 |
5 | 5 | %span.ico.project |
... | ... | @@ -7,3 +7,5 @@ |
7 | 7 | %small |
8 | 8 | last activity at |
9 | 9 | = project.last_activity_date.stamp("Aug 25, 2011") |
10 | + %span.right.arrow | |
11 | + → | ... | ... |
app/views/dashboard/index.html.haml
... | ... | @@ -20,7 +20,7 @@ |
20 | 20 | .row |
21 | 21 | .dashboard_block |
22 | 22 | .row |
23 | - .span10= render "dashboard/projects_feed" | |
23 | + .span10= render "dashboard/projects_feed", :projects => @active_projects | |
24 | 24 | .span4.right |
25 | 25 | - if current_user.can_create_project? |
26 | 26 | .alert-message.block-message.warning |
... | ... | @@ -65,4 +65,4 @@ |
65 | 65 | |
66 | 66 | %hr |
67 | 67 | .row |
68 | - .dashboard_block= render "dashboard/events_feed" | |
68 | + .dashboard_block= render @events | ... | ... |
app/views/events/_event_changed_issue.html.haml
1 | 1 | = image_tag gravatar_icon(event.author_email), :class => "avatar" |
2 | 2 | %strong #{event.author_name} |
3 | -- if event.closed? | |
4 | - closed | |
5 | -- else | |
6 | - reopened | |
7 | -issue | |
3 | +%span.label.important | |
4 | + - if event.closed? | |
5 | + closed | |
6 | + - else | |
7 | + reopened | |
8 | + issue | |
8 | 9 | = link_to project_issue_path(event.project, event.issue) do |
9 | 10 | %strong= truncate event.issue_title |
10 | 11 | at | ... | ... |
app/views/events/_event_changed_merge_request.html.haml
1 | 1 | = image_tag gravatar_icon(event.author_email), :class => "avatar" |
2 | 2 | %strong #{event.author_name} |
3 | -- if event.closed? | |
4 | - closed | |
5 | -- else | |
6 | - reopened | |
7 | -merge request | |
3 | +%span.label.important | |
4 | + - if event.closed? | |
5 | + closed | |
6 | + - else | |
7 | + reopened | |
8 | + merge request | |
8 | 9 | = link_to project_merge_request_path(event.project, event.merge_request) do |
9 | 10 | %strong= truncate event.merge_request_title |
10 | 11 | at |
... | ... | @@ -12,7 +13,6 @@ at |
12 | 13 | %span.cgray |
13 | 14 | = time_ago_in_words(event.created_at) |
14 | 15 | ago. |
15 | -%br | |
16 | 16 | %span.label= event.merge_request.source_branch |
17 | 17 | → |
18 | 18 | %span.label= event.merge_request.target_branch | ... | ... |
app/views/events/_event_new_issue.html.haml
app/views/events/_event_new_merge_request.html.haml
1 | 1 | = image_tag gravatar_icon(event.author_email), :class => "avatar" |
2 | 2 | %strong #{event.author_name} |
3 | -requested merge | |
3 | +%span.label.success requested | |
4 | + merge | |
4 | 5 | = link_to project_merge_request_path(event.project, event.merge_request) do |
5 | 6 | %strong= truncate event.merge_request_title |
6 | 7 | at |
... | ... | @@ -8,7 +9,6 @@ at |
8 | 9 | %span.cgray |
9 | 10 | = time_ago_in_words(event.created_at) |
10 | 11 | ago. |
11 | -%br | |
12 | 12 | %span.label= event.merge_request.source_branch |
13 | 13 | → |
14 | 14 | %span.label= event.merge_request.target_branch | ... | ... |
app/views/events/_event_push.html.haml
1 | 1 | - if event.new_branch? || event.new_tag? |
2 | 2 | = image_tag gravatar_icon(event.author_email), :class => "avatar" |
3 | 3 | %strong #{event.author_name} |
4 | - pushed new | |
4 | + %span.label.pushed pushed | |
5 | + new | |
5 | 6 | - if event.new_tag? |
6 | 7 | tag |
7 | 8 | = link_to project_commits_path(event.project, :ref => event.tag_name) do |
... | ... | @@ -18,7 +19,8 @@ |
18 | 19 | - else |
19 | 20 | = image_tag gravatar_icon(event.author_email), :class => "avatar" |
20 | 21 | %strong #{event.author_name} |
21 | - pushed to | |
22 | + %span.label.pushed pushed | |
23 | + to | |
22 | 24 | = link_to project_commits_path(event.project, :ref => event.branch_name) do |
23 | 25 | %strong= event.branch_name |
24 | 26 | at |
... | ... | @@ -30,10 +32,10 @@ |
30 | 32 | = link_to compare_project_commits_path(event.project, :from => event.commits.first.prev_commit_id, :to => event.commits.last.id) do |
31 | 33 | Compare #{event.commits.first.commit.id[0..8]}...#{event.commits.last.id[0..8]} |
32 | 34 | - @project = event.project |
33 | - %ul.unstyled | |
34 | - - if event.commits.size > 4 | |
35 | - = render event.commits[0..2] | |
36 | - %li ... and #{event.commits.size - 3} more commits | |
35 | + %ul.unstyled.event_commits | |
36 | + - if event.commits.size > 3 | |
37 | + = render event.commits[0...2] | |
38 | + %li ... and #{event.commits.size - 2} more commits | |
37 | 39 | - else |
38 | 40 | = render event.commits |
39 | 41 | ... | ... |
app/views/layouts/_app_menu.html.haml
1 | 1 | %nav.main_menu |
2 | 2 | = render "layouts/const_menu_links" |
3 | 3 | = link_to "Projects", projects_path, :class => "#{"current" if current_page?(projects_path)}" |
4 | - = link_to "Issues", dashboard_issues_path, :class => "#{"current" if current_page?(dashboard_issues_path)}", :id => "issues_slide" | |
5 | - = link_to "Requests", dashboard_merge_requests_path, :class => "#{"current" if current_page?(dashboard_merge_requests_path)}", :id => "merge_requests_slide" | |
4 | + = link_to dashboard_issues_path, :class => "#{"current" if current_page?(dashboard_issues_path)}", :id => "issues_slide" do | |
5 | + Issues | |
6 | + %span.count= current_user.assigned_issues.opened.count | |
7 | + = link_to dashboard_merge_requests_path, :class => "#{"current" if current_page?(dashboard_merge_requests_path)}", :id => "merge_requests_slide" do | |
8 | + Requests | |
9 | + %span.count= current_user.cared_merge_requests.count | |
10 | + = link_to "Search", search_path, :class => "#{"current" if current_page?(search_path)}" | |
6 | 11 | = link_to "Help", help_path, :class => "#{"current" if controller.controller_name == "help"}" | ... | ... |
app/views/layouts/_head_panel.html.haml
... | ... | @@ -7,7 +7,9 @@ |
7 | 7 | %h1 |
8 | 8 | GITLAB |
9 | 9 | %h1.project_name= title |
10 | - .search= text_field_tag "search", nil, :placeholder => "Search", :class => "search-input" | |
10 | + .search | |
11 | + = form_tag search_path, :method => :get do |f| | |
12 | + = text_field_tag "search", nil, :placeholder => "Search", :class => "search-input" | |
11 | 13 | - if current_user.is_admin? |
12 | 14 | = link_to admin_projects_path, :class => "admin_link", :title => "Admin area" do |
13 | 15 | = image_tag "admin.PNG", :width => 16 | ... | ... |
app/views/merge_requests/_commits.html.haml
app/views/merge_requests/_how_to_merge.html.haml
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | :javascript |
16 | 16 | $(function(){ |
17 | 17 | var modal = $('#modal_merge_info').modal({modal: true}); |
18 | - $('.info_link').bind("click", function(){ | |
18 | + $('.how_to_merge_link').bind("click", function(){ | |
19 | 19 | modal.show(); |
20 | 20 | }); |
21 | 21 | $('.modal-header .close').bind("click", function(){ | ... | ... |
app/views/merge_requests/_merge_request.html.haml
... | ... | @@ -6,7 +6,7 @@ |
6 | 6 | = time_ago_in_words(merge_request.created_at) |
7 | 7 | ago |
8 | 8 | - if merge_request.notes.any? |
9 | - %span.label= pluralize merge_request.notes.count, 'note' | |
9 | + %span.pretty_label= pluralize merge_request.notes.count, 'note' | |
10 | 10 | - if merge_request.upvotes > 0 |
11 | 11 | %span.label.success= "+#{merge_request.upvotes}" |
12 | 12 | .right | ... | ... |
app/views/merge_requests/show.html.haml
1 | 1 | %h3 |
2 | 2 | = "Merge Request ##{@merge_request.id}:" |
3 | 3 | |
4 | - %span.label= @merge_request.source_branch | |
4 | + %span.pretty_label.branch= @merge_request.source_branch | |
5 | 5 | → |
6 | - %span.label= @merge_request.target_branch | |
6 | + %span.pretty_label.branch= @merge_request.target_branch | |
7 | 7 | |
8 | 8 | %small |
9 | 9 | created at |
... | ... | @@ -11,12 +11,10 @@ |
11 | 11 | |
12 | 12 | %span.right |
13 | 13 | - if can?(current_user, :modify_merge_request, @merge_request) |
14 | - - if @merge_request.closed | |
15 | - = link_to 'Reopen', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => false }, :status_only => true), :method => :put, :class => "btn" | |
16 | - - else | |
14 | + - if @merge_request.open? | |
17 | 15 | = link_to 'Close', project_merge_request_path(@project, @merge_request, :merge_request => {:closed => true }, :status_only => true), :method => :put, :class => "btn", :title => "Close merge request" |
18 | - = link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn" do | |
19 | - Edit | |
16 | + = link_to edit_project_merge_request_path(@project, @merge_request), :class => "btn" do | |
17 | + Edit | |
20 | 18 | |
21 | 19 | %br |
22 | 20 | - if @merge_request.upvotes > 0 |
... | ... | @@ -28,17 +26,27 @@ |
28 | 26 | |
29 | 27 | |
30 | 28 | %hr |
31 | -- if @merge_request.closed | |
32 | - .alert-message.error Closed | |
33 | -- else | |
34 | - .alert-message.success | |
35 | - = link_to "#", :class => "info_link", :title => "How To Merge" do | |
36 | - = image_tag "Info-UI.PNG" | |
37 | - Open | |
29 | +.merge_request_status_holder | |
30 | + - if @merge_request.closed | |
31 | + %h5 | |
32 | + .alert-message.error.status_info Closed | |
33 | + - if @merge_request.merged? | |
34 | + %span | |
35 | + Merged by #{@merge_request.merge_event.author_name} | |
36 | + %small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago. | |
37 | + - elsif @merge_request.closed_event | |
38 | + %span | |
39 | + Closed by #{@merge_request.closed_event.author_name} | |
40 | + %small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago. | |
41 | + %br | |
42 | + - else | |
43 | + %h5 | |
44 | + .alert-message.success.status_info Open | |
45 | + = link_to "How to merge", "#", :class => "vlink how_to_merge_link", :title => "How To Merge" | |
38 | 46 | |
39 | 47 | = render "merge_requests/how_to_merge" |
40 | 48 | |
41 | -%div.well | |
49 | +%div.well.prettyprint | |
42 | 50 | %div |
43 | 51 | %cite.cgray Created by |
44 | 52 | = image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av" | ... | ... |
app/views/profile/password.html.haml
1 | -%h3 Password | |
2 | -%hr | |
3 | -= form_for @user, :url => profile_password_path, :method => :put do |f| | |
4 | - .data | |
5 | - .alert-message.block-message.warning | |
6 | - %p After successfull password update you will be redirected to login page where you should login with new password | |
7 | - -if @user.errors.any? | |
8 | - .alert-message.block-message.error | |
9 | - %ul | |
10 | - - @user.errors.full_messages.each do |msg| | |
11 | - %li= msg | |
1 | +.row | |
2 | + .span8 | |
3 | + %h3 Password | |
4 | + %hr | |
5 | + = form_for @user, :url => profile_password_path, :method => :put do |f| | |
6 | + .data | |
7 | + .alert-message.block-message.warning | |
8 | + %p After successfull password update you will be redirected to login page where you should login with new password | |
9 | + -if @user.errors.any? | |
10 | + .alert-message.block-message.error | |
11 | + %ul | |
12 | + - @user.errors.full_messages.each do |msg| | |
13 | + %li= msg | |
14 | + | |
15 | + .clearfix | |
16 | + = f.label :password | |
17 | + .input= f.password_field :password | |
18 | + .clearfix | |
19 | + = f.label :password_confirmation | |
20 | + .input= f.password_field :password_confirmation | |
21 | + .actions | |
22 | + = f.submit 'Save', :class => "btn" | |
12 | 23 | |
13 | - .clearfix | |
14 | - = f.label :password | |
15 | - .input= f.password_field :password | |
16 | - .clearfix | |
17 | - = f.label :password_confirmation | |
18 | - .input= f.password_field :password_confirmation | |
19 | - .actions | |
20 | - = f.submit 'Save', :class => "btn" | |
21 | - | |
22 | -%h3 | |
23 | - Private token | |
24 | - %span.cred.right | |
25 | - keep it in secret! | |
26 | -%hr | |
27 | -= form_for @user, :url => profile_reset_private_token_path, :method => :put do |f| | |
28 | - .data | |
29 | - %p Private token used to access application resources without authentication. | |
30 | - %p For example its required to access commits feed. | |
24 | + .span7.right | |
25 | + %h3 | |
26 | + Private token | |
27 | + %span.cred.right | |
28 | + keep it in secret! | |
31 | 29 | %hr |
32 | - %p.cgray | |
33 | - - if current_user.private_token | |
34 | - = text_field_tag "token", current_user.private_token | |
35 | - - else | |
36 | - You don`t have one yet. Click generate to fix it. | |
37 | - .actions | |
38 | - - if current_user.private_token | |
39 | - = f.submit 'Reset', :confirm => "Are you sure?", :class => "btn" | |
40 | - - else | |
41 | - = f.submit 'Generate', :class => "btn" | |
30 | + = form_for @user, :url => profile_reset_private_token_path, :method => :put do |f| | |
31 | + .data | |
32 | + .alert-message.block-message.warning | |
33 | + %p Private token used to access application resources without authentication. | |
34 | + %hr | |
35 | + %p * required for rss feed | |
36 | + %p.cgray | |
37 | + - if current_user.private_token | |
38 | + = text_field_tag "token", current_user.private_token | |
39 | + - else | |
40 | + You don`t have one yet. Click generate to fix it. | |
41 | + .actions | |
42 | + - if current_user.private_token | |
43 | + = f.submit 'Reset', :confirm => "Are you sure?", :class => "btn" | |
44 | + - else | |
45 | + = f.submit 'Generate', :class => "btn" | |
42 | 46 | ... | ... |
app/views/projects/_show.html.haml
app/views/projects/empty.html.haml
... | ... | @@ -6,7 +6,7 @@ |
6 | 6 | %li Visit profile → keys and add public key of every machine you want to use for work with gitlabhq. |
7 | 7 | |
8 | 8 | .alert-message.block-message.error |
9 | - %ul.alert_holder | |
9 | + %ul.unstyled.alert_holder | |
10 | 10 | %li You should push repository to proceed. |
11 | 11 | %li After push you will be able to browse code, commits etc. |
12 | 12 | ... | ... |
app/views/projects/index.html.haml
1 | 1 | - if @projects.any? |
2 | 2 | .row |
3 | - .span4 | |
3 | + .span11 | |
4 | + = render @events | |
5 | + .span5.right | |
4 | 6 | %div.leftbar.ui-box |
5 | 7 | %h5 |
6 | 8 | Projects |
9 | + %small | |
10 | + (#{@projects.count}) | |
7 | 11 | - if current_user.can_create_project? |
8 | 12 | %span.right |
9 | 13 | = link_to new_project_path, :class => "btn very_small info" do |
10 | 14 | New Project |
11 | 15 | .content_list |
12 | 16 | - @projects.each do |project| |
13 | - = link_to project_path(project), :remote => true, :class => dom_class(project) do | |
17 | + = link_to project_path(project), :class => dom_class(project) do | |
14 | 18 | %h4 |
15 | 19 | %span.ico.project |
16 | - = truncate(project.name, :length => 22) | |
17 | - .span12.right | |
18 | - .show_holder.ui-box.padded | |
19 | - .loading | |
20 | + = truncate(project.name, :length => 25) | |
21 | + %span.right | |
22 | + → | |
20 | 23 | |
21 | 24 | - else |
22 | 25 | %h3 Nothing here |
... | ... | @@ -31,20 +34,3 @@ |
31 | 34 | New Project » |
32 | 35 | - else |
33 | 36 | If you will be added to project - it will be displayed here |
34 | - | |
35 | - | |
36 | -:javascript | |
37 | - $(function(){ | |
38 | - $("a.project").live("ajax:before", function() { | |
39 | - $(".show_holder").html("<div class='loading'>"); | |
40 | - $('a.project').removeClass("active"); | |
41 | - $(this).addClass("active"); | |
42 | - }); | |
43 | - $('a.project:first-child').trigger("click"); | |
44 | - }); | |
45 | - | |
46 | -- if @projects.count == @limit | |
47 | - :javascript | |
48 | - $(function(){ | |
49 | - Pager.init(#{@limit}); | |
50 | - }); | ... | ... |
app/views/projects/show.html.haml
... | ... | @@ -21,9 +21,13 @@ |
21 | 21 | = text_field_tag :project_clone, @project.url_to_repo, :class => "xlarge one_click_select git_clone_url" |
22 | 22 | |
23 | 23 | - if @project.description.present? |
24 | - = markdown @project.description | |
24 | + .prettyprint= markdown @project.description | |
25 | 25 | - unless @events.blank? |
26 | - %h5.cgray Recent Activity | |
26 | + %br | |
27 | + %h5.cgray | |
28 | + %span.ico.activities | |
29 | + Recent Activity | |
30 | + %hr | |
27 | 31 | .content_list= render @events |
28 | 32 | |
29 | 33 | ... | ... |
app/views/projects/show.js.haml
No preview for this file type
... | ... | @@ -0,0 +1,41 @@ |
1 | += form_tag search_path, :method => :get do |f| | |
2 | + .padded | |
3 | + = label_tag :search, "Looking for" | |
4 | + .input | |
5 | + = text_field_tag :search, params[:search],:placeholder => "issue 143", :class => "xxlarge" | |
6 | + = submit_tag 'Search', :class => "btn primary" | |
7 | +- if params[:search].present? | |
8 | + %br | |
9 | + %h3 Search results | |
10 | + %hr | |
11 | + .search_results | |
12 | + - if @projects.empty? && @merge_requests.empty? | |
13 | + %h3 | |
14 | + %small Nothing here | |
15 | + - else | |
16 | + - if @projects.any? | |
17 | + - @projects.each do |project| | |
18 | + = link_to project do | |
19 | + %h4 | |
20 | + %span.ico.project | |
21 | + = project.name | |
22 | + %small | |
23 | + last activity at | |
24 | + = project.last_activity_date.stamp("Aug 25, 2011") | |
25 | + - if @merge_requests.any? | |
26 | + - @merge_requests.each do |merge_request| | |
27 | + = link_to [merge_request.project, merge_request] do | |
28 | + %h5 | |
29 | + Merge Request # | |
30 | + = merge_request.id | |
31 | + – | |
32 | + = truncate merge_request.title, :length => 50 | |
33 | + %small | |
34 | + updated at | |
35 | + = merge_request.updated_at.stamp("Aug 25, 2011") | |
36 | + %strong | |
37 | + %span.label= merge_request.project.name | |
38 | + :javascript | |
39 | + $(function() { | |
40 | + $(".search_results").highlight("#{params[:search]}"); | |
41 | + }) | ... | ... |
app/workers/post_receive.rb
... | ... | @@ -8,7 +8,13 @@ class PostReceive |
8 | 8 | # Ignore push from non-gitlab users |
9 | 9 | return false unless Key.find_by_identifier(author_key_id) |
10 | 10 | |
11 | + # Create push event | |
11 | 12 | project.observe_push(oldrev, newrev, ref, author_key_id) |
13 | + | |
14 | + # Close merged MR | |
15 | + project.update_merge_requests(oldrev, newrev, ref, author_key_id) | |
16 | + | |
17 | + # Execute web hooks | |
12 | 18 | project.execute_web_hooks(oldrev, newrev, ref, author_key_id) |
13 | 19 | end |
14 | 20 | end | ... | ... |
config/routes.rb
1 | 1 | Gitlab::Application.routes.draw do |
2 | - | |
2 | + get 'search' => "search#show" | |
3 | 3 | |
4 | 4 | # Optionally, enable Resque here |
5 | 5 | require 'resque/server' |
... | ... | @@ -40,6 +40,7 @@ Gitlab::Application.routes.draw do |
40 | 40 | get "dashboard", :to => "dashboard#index" |
41 | 41 | get "dashboard/issues", :to => "dashboard#issues" |
42 | 42 | get "dashboard/merge_requests", :to => "dashboard#merge_requests" |
43 | + get "dashboard/activities", :to => "dashboard#activities" | |
43 | 44 | |
44 | 45 | #get "profile/:id", :to => "profile#show" |
45 | 46 | ... | ... |
db/migrate/20120315111711_add_commits_diff_store_to_merge_request.rb
0 → 100644
db/migrate/20120315132931_add_merged_to_merge_request.rb
0 → 100644
db/schema.rb
... | ... | @@ -11,7 +11,7 @@ |
11 | 11 | # |
12 | 12 | # It's strongly recommended to check this file into your version control system. |
13 | 13 | |
14 | -ActiveRecord::Schema.define(:version => 20120307095918) do | |
14 | +ActiveRecord::Schema.define(:version => 20120315132931) do | |
15 | 15 | |
16 | 16 | create_table "events", :force => true do |t| |
17 | 17 | t.string "target_type" |
... | ... | @@ -61,6 +61,9 @@ ActiveRecord::Schema.define(:version => 20120307095918) do |
61 | 61 | t.boolean "closed", :default => false, :null => false |
62 | 62 | t.datetime "created_at", :null => false |
63 | 63 | t.datetime "updated_at", :null => false |
64 | + t.text "st_commits" | |
65 | + t.text "st_diffs" | |
66 | + t.boolean "merged", :default => false, :null => false | |
64 | 67 | end |
65 | 68 | |
66 | 69 | add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" | ... | ... |
doc/installation.md
... | ... | @@ -209,7 +209,7 @@ Application can be started with next command: |
209 | 209 | |
210 | 210 | cd /home/gitlab/gitlab |
211 | 211 | sudo -u gitlab cp config/unicorn.rb.orig config/unicorn.rb |
212 | - sudo -u gitlab unicorn_rails -c config/unicorn.rb -E production -D | |
212 | + sudo -u gitlab bundle exec unicorn_rails -c config/unicorn.rb -E production -D | |
213 | 213 | |
214 | 214 | Edit /etc/nginx/nginx.conf. Add next code to **http** section: |
215 | 215 | |
... | ... | @@ -256,33 +256,38 @@ Create init script in /etc/init.d/gitlab: |
256 | 256 | NAME=unicorn |
257 | 257 | DESC="Gitlab service" |
258 | 258 | PID=/home/gitlab/gitlab/tmp/pids/unicorn.pid |
259 | + RESQUE_PID=/home/gitlab/gitlab/tmp/pids/resque_worker.pid | |
259 | 260 | |
260 | 261 | case "$1" in |
261 | 262 | start) |
262 | 263 | CD_TO_APP_DIR="cd /home/gitlab/gitlab" |
263 | 264 | START_DAEMON_PROCESS="bundle exec unicorn_rails $DAEMON_OPTS" |
265 | + START_RESQUE_PROCESS="./resque.sh" | |
264 | 266 | |
265 | 267 | echo -n "Starting $DESC: " |
266 | 268 | if [ `whoami` = root ]; then |
267 | - sudo -u gitlab sh -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS" | |
269 | + sudo -u gitlab sh -c "$CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS" | |
268 | 270 | else |
269 | - $CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS | |
271 | + $CD_TO_APP_DIR > /dev/null 2>&1 && $START_DAEMON_PROCESS && $START_RESQUE_PROCESS | |
270 | 272 | fi |
271 | 273 | echo "$NAME." |
272 | 274 | ;; |
273 | 275 | stop) |
274 | 276 | echo -n "Stopping $DESC: " |
275 | 277 | kill -QUIT `cat $PID` |
278 | + kill -QUIT `cat $RESQUE_PID` | |
276 | 279 | echo "$NAME." |
277 | 280 | ;; |
278 | 281 | restart) |
279 | 282 | echo -n "Restarting $DESC: " |
280 | 283 | kill -USR2 `cat $PID` |
284 | + kill -USR2 `cat $RESQUE_PID` | |
281 | 285 | echo "$NAME." |
282 | 286 | ;; |
283 | 287 | reload) |
284 | 288 | echo -n "Reloading $DESC configuration: " |
285 | 289 | kill -HUP `cat $PID` |
290 | + kill -HUP `cat $RESQUE_PID` | |
286 | 291 | echo "$NAME." |
287 | 292 | ;; |
288 | 293 | *) | ... | ... |
resque.sh
1 | 1 | mkdir tmp/pids |
2 | -nohup bundle exec rake environment resque:work QUEUE=* RAILS_ENV=production PIDFILE=tmp/pids/resque_worker_QUEUE.pid & >> log/resque_worker_QUEUE.log 2>&1 | |
2 | +nohup bundle exec rake environment resque:work QUEUE=* RAILS_ENV=production PIDFILE=tmp/pids/resque_worker.pid & >> log/resque_worker.log 2>&1 | ... | ... |
spec/models/project_spec.rb
... | ... | @@ -160,6 +160,34 @@ describe Project do |
160 | 160 | end |
161 | 161 | end |
162 | 162 | end |
163 | + | |
164 | + describe :update_merge_requests do | |
165 | + let(:project) { Factory :project } | |
166 | + | |
167 | + before do | |
168 | + @merge_request = Factory :merge_request, | |
169 | + :project => project, | |
170 | + :merged => false, | |
171 | + :closed => false | |
172 | + @key = Factory :key, :user_id => project.owner.id | |
173 | + end | |
174 | + | |
175 | + it "should close merge request if last commit from source branch was pushed to target branch" do | |
176 | + @merge_request.reloaded_commits | |
177 | + @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" | |
178 | + project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/stable", @key.identifier) | |
179 | + @merge_request.reload | |
180 | + @merge_request.merged.should be_true | |
181 | + @merge_request.closed.should be_true | |
182 | + end | |
183 | + | |
184 | + it "should update merge request commits with new one if pushed to source branch" do | |
185 | + @merge_request.last_commit.should == nil | |
186 | + project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/master", @key.identifier) | |
187 | + @merge_request.reload | |
188 | + @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" | |
189 | + end | |
190 | + end | |
163 | 191 | end |
164 | 192 | # == Schema Information |
165 | 193 | # | ... | ... |
spec/requests/dashboard_spec.rb
1 | 1 | require 'spec_helper' |
2 | -__END__ | |
3 | -# Disabled for now | |
4 | 2 | describe "Dashboard" do |
5 | 3 | before do |
6 | 4 | @project = Factory :project |
... | ... | @@ -22,19 +20,7 @@ describe "Dashboard" do |
22 | 20 | end |
23 | 21 | |
24 | 22 | it "should have projects panel" do |
25 | - within ".project-list" do | |
26 | - page.should have_content(@project.name) | |
27 | - end | |
23 | + page.should have_content(@project.name) | |
28 | 24 | end |
29 | - | |
30 | - # Temporary disabled cause of travis | |
31 | - # TODO: fix or rewrite | |
32 | - #it "should have news feed" do | |
33 | - #within "#news-feed" do | |
34 | - #page.should have_content("commit") | |
35 | - #page.should have_content(@project.commit.author.name) | |
36 | - #page.should have_content(@project.commit.safe_message) | |
37 | - #end | |
38 | - #end | |
39 | 25 | end |
40 | 26 | end | ... | ... |
spec/requests/merge_requests_spec.rb
... | ... | @@ -42,7 +42,7 @@ describe "MergeRequests" do |
42 | 42 | |
43 | 43 | it { should have_content(@merge_request.title[0..10]) } |
44 | 44 | it "Show page should inform user that merge request closed" do |
45 | - page.should have_content "Reopen" | |
45 | + page.should have_content "Closed" | |
46 | 46 | end |
47 | 47 | end |
48 | 48 | end | ... | ... |
... | ... | @@ -0,0 +1,53 @@ |
1 | +/* | |
2 | + | |
3 | +highlight v3 | |
4 | + | |
5 | +Highlights arbitrary terms. | |
6 | + | |
7 | +<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html> | |
8 | + | |
9 | +MIT license. | |
10 | + | |
11 | +Johann Burkard | |
12 | +<http://johannburkard.de> | |
13 | +<mailto:jb@eaio.com> | |
14 | + | |
15 | +*/ | |
16 | + | |
17 | +jQuery.fn.highlight = function(pat) { | |
18 | + function innerHighlight(node, pat) { | |
19 | + var skip = 0; | |
20 | + if (node.nodeType == 3) { | |
21 | + var pos = node.data.toUpperCase().indexOf(pat); | |
22 | + if (pos >= 0) { | |
23 | + var spannode = document.createElement('span'); | |
24 | + spannode.className = 'highlight_word'; | |
25 | + var middlebit = node.splitText(pos); | |
26 | + var endbit = middlebit.splitText(pat.length); | |
27 | + var middleclone = middlebit.cloneNode(true); | |
28 | + spannode.appendChild(middleclone); | |
29 | + middlebit.parentNode.replaceChild(spannode, middlebit); | |
30 | + skip = 1; | |
31 | + } | |
32 | + } | |
33 | + else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) { | |
34 | + for (var i = 0; i < node.childNodes.length; ++i) { | |
35 | + i += innerHighlight(node.childNodes[i], pat); | |
36 | + } | |
37 | + } | |
38 | + return skip; | |
39 | + } | |
40 | + return this.each(function() { | |
41 | + innerHighlight(this, pat.toUpperCase()); | |
42 | + }); | |
43 | +}; | |
44 | + | |
45 | +jQuery.fn.removeHighlight = function() { | |
46 | + return this.find("span.highlight").each(function() { | |
47 | + this.parentNode.firstChild.nodeName; | |
48 | + with (this.parentNode) { | |
49 | + replaceChild(this.firstChild, this); | |
50 | + normalize(); | |
51 | + } | |
52 | + }).end(); | |
53 | +}; | ... | ... |