ginatra 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +1 -1
- data/README.md +24 -26
- data/Rakefile +1 -21
- data/bin/ginatra +10 -1
- data/bin/ginatra-setup +25 -0
- data/ginatra.gemspec +1 -1
- data/lib/ginatra/config.rb +6 -4
- data/lib/ginatra/graph_commit.rb +77 -0
- data/lib/ginatra/repo.rb +42 -6
- data/lib/ginatra.rb +29 -1
- data/public/src/branch-graph.js +170 -0
- data/public/src/colour.css +1 -0
- data/public/src/graph.css +9 -0
- data/public/src/raphael.js +7 -0
- data/spec/graph_commit_spec.rb +54 -0
- data/spec/repo_spec.rb +8 -8
- data/views/graph.erb +15 -0
- data/views/layout.erb +1 -1
- data/views/log.erb +2 -2
- metadata +69 -103
- data/features/pages.feature +0 -35
- data/features/step_definitions/app_steps.rb +0 -7
- data/features/step_definitions/page_steps.rb +0 -36
- data/features/support/env.rb +0 -12
data/.travis.yml
CHANGED
data/README.md
CHANGED
|
@@ -9,34 +9,30 @@ repositories out of a set of specified directories using an array of glob-based
|
|
|
9
9
|
paths. I have plans to make it function just as gitweb does, including leeching
|
|
10
10
|
config files and suchlike.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
Installation
|
|
13
|
+
------------
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
- (re)Move `~/.ginatra`
|
|
17
|
-
- Run the following to open an irb with Ginatra loaded: ` $ irb -r 'rubygems' -r 'rubygems'`
|
|
18
|
-
- In this irb session, run the following then close it: `>> Ginatra::Config.setup!`
|
|
15
|
+
**NEW: You should be using Ruby 1.9.2: because it's awesome!**
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
This ignores anything already there, so be careful. You have been warned.
|
|
17
|
+
To install ginatra:
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
$ gem install ginatra
|
|
20
|
+
...
|
|
21
|
+
$ ginatra setup
|
|
22
|
+
checked deps
|
|
23
|
+
installed config
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
If you get those two lines of output, you're sorted. anything else and something
|
|
26
|
+
has gone wrong.
|
|
27
|
+
|
|
28
|
+
### External Dependencies
|
|
27
29
|
|
|
28
30
|
You should be using Git 1.6.3 or later just to be sure that it all works:
|
|
29
31
|
|
|
30
32
|
$ git --version
|
|
31
33
|
git version 1.6.3
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
$ gem install ginatra
|
|
36
|
-
|
|
37
|
-
This pulls down most of the required dependencies too.
|
|
38
|
-
|
|
39
|
-
The last dependency you need is pygments, an awesome python syntax highlighter.
|
|
35
|
+
The other dependency you need is pygments, an awesome python syntax highlighter.
|
|
40
36
|
To check whether you have it, run:
|
|
41
37
|
|
|
42
38
|
$ which pygmentize
|
|
@@ -92,6 +88,7 @@ Attribution
|
|
|
92
88
|
|
|
93
89
|
- Samuel Elliott (lenary)
|
|
94
90
|
- Ryan Bigg (radar)
|
|
91
|
+
- Jan Topiński (simcha)
|
|
95
92
|
|
|
96
93
|
**Patches**
|
|
97
94
|
|
|
@@ -103,28 +100,29 @@ Attribution
|
|
|
103
100
|
|
|
104
101
|
In a new spirit of openness, all those who submit a patch that gets applied will gain commit access to the main (lenary/ginatra) repository.
|
|
105
102
|
|
|
106
|
-
**Thanks**
|
|
107
|
-
|
|
108
|
-
Too many to name. Thanks be to you all.
|
|
109
|
-
|
|
110
103
|
Screenshots
|
|
111
104
|
-----------
|
|
112
105
|
|
|
113
106
|
**Index**
|
|
114
107
|
|
|
115
|
-
.png "Ginatra Index")
|
|
116
109
|
|
|
117
110
|
**Log**
|
|
118
111
|
|
|
119
|
-
.png "Ginatra Log")
|
|
120
113
|
|
|
121
114
|
**Commit**
|
|
122
115
|
|
|
123
|
-
.png "Ginatra Commit")
|
|
124
117
|
|
|
125
118
|
**Tree**
|
|
126
119
|
|
|
127
|
-
.png "Ginatra Tree")
|
|
121
|
+
|
|
122
|
+
**Branch graph**
|
|
123
|
+
|
|
124
|
+

|
|
125
|
+
|
|
128
126
|
|
|
129
127
|
Licence
|
|
130
128
|
-------
|
data/Rakefile
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
require "bundler"
|
|
2
2
|
Bundler.setup(:default, :development)
|
|
3
|
-
require 'cucumber/rake/task'
|
|
4
3
|
require 'rspec/core/rake_task'
|
|
5
4
|
|
|
6
|
-
task :default => ['rake:spec'
|
|
7
|
-
|
|
5
|
+
task :default => ['rake:spec']
|
|
8
6
|
|
|
9
7
|
desc "Clones the Test Repository"
|
|
10
8
|
task :repo do |t|
|
|
@@ -13,27 +11,9 @@ task :repo do |t|
|
|
|
13
11
|
end
|
|
14
12
|
end
|
|
15
13
|
|
|
16
|
-
desc "Runs the Cucumber Feature Suite"
|
|
17
|
-
Cucumber::Rake::Task.new(:features) do |t|
|
|
18
|
-
t.cucumber_opts = ["--format pretty", "features"]
|
|
19
|
-
end
|
|
20
|
-
namespace :features do
|
|
21
|
-
desc "Runs the `@current` feature(s) or scenario(s)"
|
|
22
|
-
Cucumber::Rake::Task.new(:current) do |c|
|
|
23
|
-
c.cucumber_opts = ["--format pretty", "-t current", "features"]
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
14
|
desc "Runs the RSpec Test Suite"
|
|
28
15
|
RSpec::Core::RakeTask.new(:spec) do |r|
|
|
29
16
|
r.pattern = 'spec/*_spec.rb'
|
|
30
17
|
r.rspec_opts = ['--color']
|
|
31
18
|
end
|
|
32
|
-
namespace :spec do
|
|
33
|
-
desc "RSpec Test Suite with pretty output"
|
|
34
|
-
RSpec::Core::RakeTask.new(:long) do |r|
|
|
35
|
-
r.pattern = 'spec/*_spec.rb'
|
|
36
|
-
r.rspec_opts = ['--color', '--format documentation']
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
19
|
|
data/bin/ginatra
CHANGED
|
@@ -4,12 +4,14 @@ require "ginatra"
|
|
|
4
4
|
|
|
5
5
|
module Ginatra::Executable
|
|
6
6
|
HELP = <<HELP
|
|
7
|
-
Usage: ginatra [
|
|
7
|
+
Usage: ginatra [ setup |
|
|
8
|
+
version |
|
|
8
9
|
server <options> <command> |
|
|
9
10
|
daemon <command> |
|
|
10
11
|
directory <command> <globs> ]
|
|
11
12
|
|
|
12
13
|
Ginatra Commands:
|
|
14
|
+
setup - Checks Dependencies and Installs Configuration
|
|
13
15
|
version - Pretty Self explanatory. Print version number and exit
|
|
14
16
|
|
|
15
17
|
Ginatra Server Commands:
|
|
@@ -47,10 +49,17 @@ HELP
|
|
|
47
49
|
load("#{path}/ginatra-server")
|
|
48
50
|
end
|
|
49
51
|
|
|
52
|
+
def self.setup
|
|
53
|
+
path = File.expand_path(File.dirname(__FILE__))
|
|
54
|
+
load("#{path}/ginatra-setup")
|
|
55
|
+
end
|
|
56
|
+
|
|
50
57
|
def self.execute(command, args)
|
|
51
58
|
case command
|
|
52
59
|
when "version"
|
|
53
60
|
puts Ginatra::VERSION
|
|
61
|
+
when "setup"
|
|
62
|
+
setup
|
|
54
63
|
when "daemon"
|
|
55
64
|
daemon
|
|
56
65
|
when "directory"
|
data/bin/ginatra-setup
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "ginatra"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
|
|
6
|
+
if RbConfig::CONFIG["ruby_version"] < "1.9"
|
|
7
|
+
$stderr.puts "You need Ruby 1.9.2 to run ginatra"
|
|
8
|
+
exit(1)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
if `which pygmentize` !~ /pygmentize/
|
|
12
|
+
$stderr.puts "You need Pygmentize to run ginatra"
|
|
13
|
+
exit(1)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
if `git --version` < "git version 1.6.3"
|
|
17
|
+
$stderr.puts "You need at least git version 1.6.3 to run ginatra"
|
|
18
|
+
exit(1)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
puts "checked deps"
|
|
22
|
+
Ginatra::Config.safe_setup
|
|
23
|
+
puts "installed config"
|
|
24
|
+
|
|
25
|
+
exit(0)
|
data/ginatra.gemspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = "ginatra"
|
|
3
|
-
s.version = "
|
|
3
|
+
s.version = "3.0.0"
|
|
4
4
|
s.summary = "A Gitweb Clone in Sinatra and Grit"
|
|
5
5
|
s.description = "Host your own git repository browser through the power of Sinatra and Grit"
|
|
6
6
|
s.email = "sam@lenary.co.uk"
|
data/lib/ginatra/config.rb
CHANGED
|
@@ -57,10 +57,12 @@ module Ginatra
|
|
|
57
57
|
@logger
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
def self.safe_setup
|
|
61
|
+
unless File.exist?(CONFIG_PATH)
|
|
62
|
+
FileUtils.mkdir_p(File.dirname(CONFIG_PATH))
|
|
63
|
+
File.open(CONFIG_PATH, 'w') do |f|
|
|
64
|
+
YAML.dump(DEFAULT_CONFIG, f)
|
|
65
|
+
end
|
|
64
66
|
end
|
|
65
67
|
end
|
|
66
68
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require "grit"
|
|
2
|
+
|
|
3
|
+
module Ginatra
|
|
4
|
+
|
|
5
|
+
class GraphCommit
|
|
6
|
+
attr_accessor :time, :space
|
|
7
|
+
def initialize(commit)
|
|
8
|
+
@_commit = commit
|
|
9
|
+
@time = -1
|
|
10
|
+
@space = 0
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def method_missing(m, *args, &block)
|
|
14
|
+
@_commit.send(m, *args, &block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Method is adding time and space on the
|
|
18
|
+
# list of commits. As well as returns date list
|
|
19
|
+
# corelated with time set on commits.
|
|
20
|
+
#
|
|
21
|
+
# @param [Array<GraphCommit>] comits to index
|
|
22
|
+
#
|
|
23
|
+
# @return [Array<TimeDate>] list of commit dates corelated with time on commits
|
|
24
|
+
def self.index_commits(commits)
|
|
25
|
+
days, heads = [], []
|
|
26
|
+
map = {}
|
|
27
|
+
|
|
28
|
+
commits.reverse.each_with_index do |c,i|
|
|
29
|
+
c.time = i
|
|
30
|
+
days[i]=c.committed_date
|
|
31
|
+
map[c.id] = c
|
|
32
|
+
heads += c.refs unless c.refs.nil?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
heads.select!{|h|h.is_a? Grit::Head or h.is_a? Grit::Remote}
|
|
36
|
+
# sort heads so the master is top and current branches are closer
|
|
37
|
+
heads.sort! do |a,b|
|
|
38
|
+
if a.name == "master"
|
|
39
|
+
-1
|
|
40
|
+
elsif b.name == "master"
|
|
41
|
+
1
|
|
42
|
+
else
|
|
43
|
+
b.commit.committed_date <=> a.commit.committed_date
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
j=0
|
|
48
|
+
heads.each do |h|
|
|
49
|
+
if map.include? h.commit.id then
|
|
50
|
+
j = mark_chain(j+=1,map[h.commit.id], map)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
return days
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Add space mark on commit and its parents
|
|
57
|
+
#
|
|
58
|
+
# @param [Fixnum] space (row on the graph) to be set
|
|
59
|
+
# @param [GraphCommit] the commit object.
|
|
60
|
+
# @param [Hash<String,GraphCommit>] map of commits
|
|
61
|
+
#
|
|
62
|
+
# @return [Fixnum] max space used.
|
|
63
|
+
def self.mark_chain(mark, commit, map)
|
|
64
|
+
commit.space = mark if commit.space == 0
|
|
65
|
+
m1 = mark-1
|
|
66
|
+
marks = commit.parents.collect do |p|
|
|
67
|
+
if map.include? p.id and map[p.id].space == 0 then
|
|
68
|
+
mark_chain(m1+=1, map[p.id],map)
|
|
69
|
+
else
|
|
70
|
+
m1+1
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
marks << mark
|
|
74
|
+
return marks.compact.max
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/lib/ginatra/repo.rb
CHANGED
|
@@ -4,10 +4,15 @@ module Grit
|
|
|
4
4
|
class Commit
|
|
5
5
|
# this lets us add a link between commits and refs directly
|
|
6
6
|
attr_accessor :refs
|
|
7
|
+
|
|
8
|
+
def ==(other_commit)
|
|
9
|
+
id == other_commit.id
|
|
10
|
+
end
|
|
7
11
|
end
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
module Ginatra
|
|
15
|
+
|
|
11
16
|
# A thin wrapper to the Grit::repo class so that we can add a name and a url-sanitised-name
|
|
12
17
|
# to a repo, and also intercept and add refs to the commit objects.
|
|
13
18
|
class Repo
|
|
@@ -27,7 +32,11 @@ module Ginatra
|
|
|
27
32
|
@name = @param
|
|
28
33
|
@description = @repo.description
|
|
29
34
|
@description = "Please edit the #{@repo.path}/description file for this repository and set the description for it." if /^Unnamed repository;/.match(@description)
|
|
30
|
-
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ==(other_repo)
|
|
38
|
+
# uses method_missing
|
|
39
|
+
path == other_repo.path
|
|
31
40
|
end
|
|
32
41
|
|
|
33
42
|
# Return a commit corresponding to the commit to the repo,
|
|
@@ -42,7 +51,7 @@ module Ginatra
|
|
|
42
51
|
def commit(id)
|
|
43
52
|
@commit = @repo.commit(id)
|
|
44
53
|
raise(Ginatra::InvalidCommit.new(id)) if @commit.nil?
|
|
45
|
-
add_refs(@commit)
|
|
54
|
+
add_refs(@commit,{})
|
|
46
55
|
@commit
|
|
47
56
|
end
|
|
48
57
|
|
|
@@ -57,19 +66,42 @@ module Ginatra
|
|
|
57
66
|
# @return [Array<Grit::Commit>] the array of commits.
|
|
58
67
|
def commits(start = 'master', max_count = 10, skip = 0)
|
|
59
68
|
raise(Ginatra::Error.new("max_count cannot be less than 0")) if max_count < 0
|
|
69
|
+
refs_cache = {}
|
|
60
70
|
@repo.commits(start, max_count, skip).each do |commit|
|
|
61
|
-
add_refs(commit)
|
|
71
|
+
add_refs(commit,refs_cache)
|
|
62
72
|
end
|
|
63
73
|
end
|
|
64
74
|
|
|
75
|
+
# Return a list of commits like --all, including pagination options and all the refs.
|
|
76
|
+
#
|
|
77
|
+
# @param [Integer] max_count the maximum count of commits
|
|
78
|
+
# @param [Integer] skip the number of commits in the branch to skip before taking the count.
|
|
79
|
+
#
|
|
80
|
+
# @raise [Ginatra::Error] if max_count is less than 0. silly billy!
|
|
81
|
+
#
|
|
82
|
+
# @return [Array<GraphCommit>] the array of commits.
|
|
83
|
+
def all_commits(max_count = 10, skip = 0)
|
|
84
|
+
raise(Ginatra::Error.new("max_count cannot be less than 0")) if max_count < 0
|
|
85
|
+
commits = Grit::Commit.find_all(@repo, nil, {:max_count => max_count, :skip => skip})
|
|
86
|
+
ref_cache = {}
|
|
87
|
+
commits.collect do |commit|
|
|
88
|
+
add_refs(commit, ref_cache)
|
|
89
|
+
GraphCommit.new(commit)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
65
92
|
# Adds the refs corresponding to Grit::Commit objects to the respective Commit objects.
|
|
66
93
|
#
|
|
67
94
|
# @todo Perhaps move into commit class.
|
|
68
95
|
#
|
|
69
96
|
# @param [Grit::Commit] commit the commit you want refs added to
|
|
97
|
+
# @param [Hash] empty hash with scope out of loop to speed things up
|
|
70
98
|
# @return [Array] the array of refs added to the commit. they are also on the commit object.
|
|
71
|
-
def add_refs(commit)
|
|
72
|
-
|
|
99
|
+
def add_refs(commit, ref_cache)
|
|
100
|
+
if ref_cache.empty?
|
|
101
|
+
@repo.refs.each {|ref| ref_cache[ref.commit.id] ||= [];ref_cache[ref.commit.id] << ref}
|
|
102
|
+
end
|
|
103
|
+
commit.refs = ref_cache[commit.id] if ref_cache.include? commit.id
|
|
104
|
+
commit.refs ||= []
|
|
73
105
|
end
|
|
74
106
|
|
|
75
107
|
# Catch all
|
|
@@ -78,7 +110,11 @@ module Ginatra
|
|
|
78
110
|
#
|
|
79
111
|
# @todo update respond_to? method
|
|
80
112
|
def method_missing(sym, *args, &block)
|
|
81
|
-
@repo.
|
|
113
|
+
if @repo.respond_to?(sym)
|
|
114
|
+
@repo.send(sym, *args, &block)
|
|
115
|
+
else
|
|
116
|
+
super
|
|
117
|
+
end
|
|
82
118
|
end
|
|
83
119
|
|
|
84
120
|
# to correspond to the #method_missing definition
|
data/lib/ginatra.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
require 'sinatra/base'
|
|
3
3
|
require "sinatra/partials"
|
|
4
|
+
require 'json'
|
|
4
5
|
|
|
5
6
|
# Written myself. i know, what the hell?!
|
|
6
7
|
module Ginatra
|
|
@@ -9,6 +10,7 @@ module Ginatra
|
|
|
9
10
|
autoload :Helpers, "ginatra/helpers"
|
|
10
11
|
autoload :Repo, "ginatra/repo"
|
|
11
12
|
autoload :RepoList, "ginatra/repo_list"
|
|
13
|
+
autoload :GraphCommit, "ginatra/graph_commit"
|
|
12
14
|
|
|
13
15
|
# A standard error class for inheritance.
|
|
14
16
|
class Error < StandardError; end
|
|
@@ -28,7 +30,7 @@ module Ginatra
|
|
|
28
30
|
end
|
|
29
31
|
end
|
|
30
32
|
|
|
31
|
-
VERSION = "
|
|
33
|
+
VERSION = "3.0.0"
|
|
32
34
|
|
|
33
35
|
# The main application class.
|
|
34
36
|
#
|
|
@@ -95,6 +97,32 @@ module Ginatra
|
|
|
95
97
|
erb :log
|
|
96
98
|
end
|
|
97
99
|
|
|
100
|
+
get '/:repo/graph' do
|
|
101
|
+
@repo = RepoList.find(params[:repo])
|
|
102
|
+
max_count = 650
|
|
103
|
+
max_count = params[:max_count].to_i unless params[:max_count].nil?
|
|
104
|
+
commits = @repo.all_commits(max_count)
|
|
105
|
+
|
|
106
|
+
days = GraphCommit.index_commits(commits)
|
|
107
|
+
@days_json = days.compact.collect{|d|[d.day,d.strftime("%b")]}.to_json
|
|
108
|
+
@commits_json = commits.collect do |c|
|
|
109
|
+
h = {}
|
|
110
|
+
h[:parents] = c.parents.collect do |p|
|
|
111
|
+
[p.id,0,0]
|
|
112
|
+
end
|
|
113
|
+
h[:author] = c.author.name.force_encoding("UTF-8")
|
|
114
|
+
h[:time] = c.time
|
|
115
|
+
h[:space] = c.space
|
|
116
|
+
h[:refs] = c.refs.collect{|r|r.name}.join(" ") unless c.refs.nil?
|
|
117
|
+
h[:id] = c.sha
|
|
118
|
+
h[:date] = c.date
|
|
119
|
+
h[:message] = c.message.force_encoding("UTF-8")
|
|
120
|
+
h[:login] = c.author.email
|
|
121
|
+
h
|
|
122
|
+
end.to_json
|
|
123
|
+
erb :graph
|
|
124
|
+
end
|
|
125
|
+
|
|
98
126
|
# The atom feed of recent commits to a certain branch of a +repo+.
|
|
99
127
|
#
|
|
100
128
|
# @param [String] repo the repository url-sanitised-name
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
var commits = chunk1.commits,
|
|
2
|
+
comms = {},
|
|
3
|
+
pixelsX = [],
|
|
4
|
+
pixelsY = [],
|
|
5
|
+
mmax = Math.max,
|
|
6
|
+
mtime = 0,
|
|
7
|
+
mspace = 0,
|
|
8
|
+
parents = {};
|
|
9
|
+
for (var i = 0, ii = commits.length; i < ii; i++) {
|
|
10
|
+
for (var j = 0, jj = commits[i].parents.length; j < jj; j++) {
|
|
11
|
+
parents[commits[i].parents[j][0]] = true;
|
|
12
|
+
}
|
|
13
|
+
mtime = Math.max(mtime, commits[i].time);
|
|
14
|
+
mspace = Math.max(mspace, commits[i].space);
|
|
15
|
+
}
|
|
16
|
+
mtime = mtime + 4;
|
|
17
|
+
mspace = mspace + 10;
|
|
18
|
+
for (i = 0; i < ii; i++) {
|
|
19
|
+
if (commits[i].id in parents) {
|
|
20
|
+
commits[i].isParent = true;
|
|
21
|
+
}
|
|
22
|
+
comms[commits[i].id] = commits[i];
|
|
23
|
+
}
|
|
24
|
+
var colors = ["#000"];
|
|
25
|
+
for (var k = 0; k < mspace; k++) {
|
|
26
|
+
colors.push(Raphael.getColor());
|
|
27
|
+
}
|
|
28
|
+
function branchGraph(holder) {
|
|
29
|
+
var ch = mspace * 20 + 20, cw = mtime * 20 + 20,
|
|
30
|
+
r = Raphael("holder", cw, ch),
|
|
31
|
+
top = r.set();
|
|
32
|
+
var cuday = 0, cumonth = "";
|
|
33
|
+
r.rect(0,0,days.length*20+20,40).attr({fill: "#999"});
|
|
34
|
+
|
|
35
|
+
for (mm = 0; mm < days.length; mm++) {
|
|
36
|
+
if(days[mm] != null){
|
|
37
|
+
if(cuday != days[mm][0]){
|
|
38
|
+
r.text(10+mm*20,30,days[mm][0]).attr({font: "12px Fontin-Sans, Arial", fill: "#444"});
|
|
39
|
+
cuday = days[mm][0]
|
|
40
|
+
}
|
|
41
|
+
if(cumonth != days[mm][1]){
|
|
42
|
+
r.text(10+mm*20,10,days[mm][1]).attr({font: "12px Fontin-Sans, Arial", fill: "#444"});
|
|
43
|
+
cumonth = days[mm][1]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (i = 0; i < ii; i++) {
|
|
49
|
+
var x = 10 + 20 * commits[i].time,
|
|
50
|
+
y = 70 + 20 * commits[i].space;
|
|
51
|
+
r.circle(x, y, 3).attr({fill: colors[commits[i].space], stroke: "none"});
|
|
52
|
+
if (commits[i].refs != null && commits[i].refs != "") {
|
|
53
|
+
var longrefs = commits[i].refs
|
|
54
|
+
var shortrefs = commits[i].refs;
|
|
55
|
+
if (shortrefs.length > 15){
|
|
56
|
+
shortrefs = shortrefs.substr(0,13) + "...";
|
|
57
|
+
}
|
|
58
|
+
var t = r.text(x+5,y+5,shortrefs).attr({font: "12px Fontin-Sans, Arial", fill: "#666",
|
|
59
|
+
title: longrefs, cursor: "pointer", rotation: "90"});
|
|
60
|
+
|
|
61
|
+
var textbox = t.getBBox();
|
|
62
|
+
t.translate(textbox.height/-4,textbox.width/2);
|
|
63
|
+
}
|
|
64
|
+
for (var j = 0, jj = commits[i].parents.length; j < jj; j++) {
|
|
65
|
+
var c = comms[commits[i].parents[j][0]];
|
|
66
|
+
if (c) {
|
|
67
|
+
var cx = 10 + 20 * c.time,
|
|
68
|
+
cy = 70 + 20 * c.space;
|
|
69
|
+
if (c.space == commits[i].space) {
|
|
70
|
+
r.path("M" + (x - 5) + "," + (y + .0001) + "L" + (15 + 20 * c.time) + "," + (y + .0001))
|
|
71
|
+
.attr({stroke: colors[c.space], "stroke-width": 2});
|
|
72
|
+
|
|
73
|
+
} else if (c.space < commits[i].space) {
|
|
74
|
+
r.path(["M", x - 5, y + .0001, "l-5-2,0,4,5,-2C",x-5,y,x -17, y+2, x -20, y-10,"L", cx,y-10,cx , cy])
|
|
75
|
+
.attr({stroke: colors[commits[i].space], "stroke-width": 2});
|
|
76
|
+
} else {
|
|
77
|
+
r.path(["M", x-5, y, "l-5-2,0,4,5,-2C",x-5,y,x -17, y-2, x -20, y+10,"L", cx,y+10,cx , cy])
|
|
78
|
+
.attr({stroke: colors[commits[i].space], "stroke-width": 2});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
(function (c, x, y) {
|
|
83
|
+
top.push(r.circle(x, y, 10).attr({fill: "#000", opacity: 0, cursor: "pointer"})
|
|
84
|
+
.hover(function () {
|
|
85
|
+
var s = r.text(100, 100,c.author + "\n \n" +c.id + "\n \n" + c.message).attr({fill: "#fff"});
|
|
86
|
+
this.popup = r.popupit(x, y + 5, s, 0);
|
|
87
|
+
top.push(this.popup.insertBefore(this));
|
|
88
|
+
}, function () {
|
|
89
|
+
this.popup && this.popup.remove() && delete this.popup;
|
|
90
|
+
}));
|
|
91
|
+
}(commits[i], x, y));
|
|
92
|
+
}
|
|
93
|
+
top.toFront();
|
|
94
|
+
var hw = holder.offsetWidth,
|
|
95
|
+
hh = holder.offsetHeight,
|
|
96
|
+
v = r.rect(hw - 8, 0, 4, Math.pow(hh, 2) / ch, 2).attr({fill: "#000", opacity: 0}),
|
|
97
|
+
h = r.rect(0, hh - 8, Math.pow(hw, 2) / cw, 4, 2).attr({fill: "#000", opacity: 0}),
|
|
98
|
+
bars = r.set(v, h),
|
|
99
|
+
drag,
|
|
100
|
+
dragger = function (e) {
|
|
101
|
+
if (drag) {
|
|
102
|
+
e = e || window.event;
|
|
103
|
+
holder.scrollLeft = drag.sl - (e.clientX - drag.x);
|
|
104
|
+
holder.scrollTop = drag.st - (e.clientY - drag.y);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
holder.onmousedown = function (e) {
|
|
108
|
+
e = e || window.event;
|
|
109
|
+
drag = {x: e.clientX, y: e.clientY, st: holder.scrollTop, sl: holder.scrollLeft};
|
|
110
|
+
document.onmousemove = dragger;
|
|
111
|
+
bars.animate({opacity: .5}, 300);
|
|
112
|
+
};
|
|
113
|
+
document.onmouseup = function () {
|
|
114
|
+
drag = false;
|
|
115
|
+
document.onmousemove = null;
|
|
116
|
+
bars.animate({opacity: 0}, 300);
|
|
117
|
+
};
|
|
118
|
+
holder.scrollLeft = cw;
|
|
119
|
+
};
|
|
120
|
+
Raphael.fn.popupit = function (x, y, set, dir, size) {
|
|
121
|
+
dir = dir == null ? 2 : dir;
|
|
122
|
+
size = size || 5;
|
|
123
|
+
x = Math.round(x);
|
|
124
|
+
y = Math.round(y);
|
|
125
|
+
var bb = set.getBBox(),
|
|
126
|
+
w = Math.round(bb.width / 2),
|
|
127
|
+
h = Math.round(bb.height / 2),
|
|
128
|
+
dx = [0, w + size * 2, 0, -w - size * 2],
|
|
129
|
+
dy = [-h * 2 - size * 3, -h - size, 0, -h - size],
|
|
130
|
+
p = ["M", x - dx[dir], y - dy[dir], "l", -size, (dir == 2) * -size, -mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size,
|
|
131
|
+
"l", 0, -mmax(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -mmax(h - size, 0), "a", size, size, 0, 0, 1, size, -size,
|
|
132
|
+
"l", mmax(w - size, 0), 0, size, !dir * -size, size, !dir * size, mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size,
|
|
133
|
+
"l", 0, mmax(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, mmax(h - size, 0), "a", size, size, 0, 0, 1, -size, size,
|
|
134
|
+
"l", -mmax(w - size, 0), 0, "z"].join(","),
|
|
135
|
+
xy = [{x: x, y: y + size * 2 + h}, {x: x - size * 2 - w, y: y}, {x: x, y: y - size * 2 - h}, {x: x + size * 2 + w, y: y}][dir];
|
|
136
|
+
set.translate(xy.x - w - bb.x, xy.y - h - bb.y);
|
|
137
|
+
return this.set(this.path(p).attr({fill: "#234", stroke: "none"}).insertBefore(set.node ? set : set[0]), set);
|
|
138
|
+
};
|
|
139
|
+
Raphael.fn.popup = function (x, y, text, dir, size) {
|
|
140
|
+
dir = dir == null ? 2 : dir > 3 ? 3 : dir;
|
|
141
|
+
size = size || 5;
|
|
142
|
+
text = text || "$9.99";
|
|
143
|
+
var res = this.set(),
|
|
144
|
+
d = 3;
|
|
145
|
+
res.push(this.path().attr({fill: "#000", stroke: "#000"}));
|
|
146
|
+
res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff", "font-family": "Helvetica, Arial"}));
|
|
147
|
+
res.update = function (X, Y, withAnimation) {
|
|
148
|
+
X = X || x;
|
|
149
|
+
Y = Y || y;
|
|
150
|
+
var bb = this[1].getBBox(),
|
|
151
|
+
w = bb.width / 2,
|
|
152
|
+
h = bb.height / 2,
|
|
153
|
+
dx = [0, w + size * 2, 0, -w - size * 2],
|
|
154
|
+
dy = [-h * 2 - size * 3, -h - size, 0, -h - size],
|
|
155
|
+
p = ["M", X - dx[dir], Y - dy[dir], "l", -size, (dir == 2) * -size, -mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size,
|
|
156
|
+
"l", 0, -mmax(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -mmax(h - size, 0), "a", size, size, 0, 0, 1, size, -size,
|
|
157
|
+
"l", mmax(w - size, 0), 0, size, !dir * -size, size, !dir * size, mmax(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size,
|
|
158
|
+
"l", 0, mmax(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, mmax(h - size, 0), "a", size, size, 0, 0, 1, -size, size,
|
|
159
|
+
"l", -mmax(w - size, 0), 0, "z"].join(","),
|
|
160
|
+
xy = [{x: X, y: Y + size * 2 + h}, {x: X - size * 2 - w, y: Y}, {x: X, y: Y - size * 2 - h}, {x: X + size * 2 + w, y: Y}][dir];
|
|
161
|
+
xy.path = p;
|
|
162
|
+
if (withAnimation) {
|
|
163
|
+
this.animate(xy, 500, ">");
|
|
164
|
+
} else {
|
|
165
|
+
this.attr(xy);
|
|
166
|
+
}
|
|
167
|
+
return this;
|
|
168
|
+
};
|
|
169
|
+
return res.update(x, y);
|
|
170
|
+
};
|
data/public/src/colour.css
CHANGED