Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Tatuya Kamada
gitlab-ce
Commits
4b2ecbc4
Commit
4b2ecbc4
authored
Dec 07, 2012
by
Koen Punt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Updated branch-graph, abstracted some code in seperate functions
Removed unused Raphael.fn.popup
parent
e1282d50
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
149 additions
and
160 deletions
+149
-160
app/controllers/projects_controller.rb
app/controllers/projects_controller.rb
+7
-13
app/views/projects/graph.html.haml
app/views/projects/graph.html.haml
+4
-1
lib/gitlab/graph/json_builder.rb
lib/gitlab/graph/json_builder.rb
+2
-2
vendor/assets/javascripts/branch-graph.js
vendor/assets/javascripts/branch-graph.js
+136
-144
No files found.
app/controllers/projects_controller.rb
View file @
4b2ecbc4
...
...
@@ -83,19 +83,13 @@ class ProjectsController < ProjectResourceController
end
def
graph
respond_to
do
|
format
|
format
.
html
format
.
json
do
graph
=
Gitlab
::
Graph
::
JsonBuilder
.
new
(
project
)
#@days_json, @commits_json = graph.days_json, graph.commits_json
render
:text
=>
graph
.
to_json
render
:json
=>
graph
.
to_json
end
end
end
def
destroy
...
...
app/views/projects/graph.html.haml
View file @
4b2ecbc4
...
...
@@ -9,5 +9,8 @@
:javascript
var
branch_graph
;
$
(
function
(){
branch_graph
=
new
BranchGraph
(
$
(
"
#holder
"
),
'
#{
url_for
:controller
=>
'projects'
,
:action
=>
'graph'
}
'
);
branch_graph
=
new
BranchGraph
(
$
(
"
#holder
"
),
{
url
:
'
#{
url_for
controller:
'projects'
,
action:
'graph'
,
format: :json
}
'
,
commit_url
:
'
#{
url_for
controller:
'projects'
,
action:
'show'
}
/commits/%s
'
});
});
lib/gitlab/graph/json_builder.rb
View file @
4b2ecbc4
...
...
@@ -18,11 +18,11 @@ module Gitlab
@days
=
index_commits
end
def
to_json
def
to_json
(
*
args
)
{
days:
@days
.
compact
.
map
{
|
d
|
[
d
.
day
,
d
.
strftime
(
"%b"
)]
},
commits:
@commits
.
map
(
&
:to_graph_hash
)
}.
to_json
}.
to_json
(
*
args
)
end
protected
...
...
vendor/assets/javascripts/branch-graph.js
View file @
4b2ecbc4
!
function
(){
var
BranchGraph
=
function
(
element
,
url
){
var
BranchGraph
=
function
(
element
,
options
){
this
.
element
=
element
;
this
.
url
=
url
;
this
.
options
=
options
;
this
.
comm
s
=
{};
this
.
preparedCommit
s
=
{};
this
.
mtime
=
0
;
this
.
mspace
=
0
;
this
.
parents
=
{};
...
...
@@ -15,67 +15,83 @@
BranchGraph
.
prototype
.
load
=
function
(){
$
.
ajax
({
url
:
this
.
url
,
url
:
this
.
options
.
url
,
method
:
'
get
'
,
dataType
:
'
json
'
,
success
:
$
.
proxy
(
function
(
data
){
$
(
'
.loading
'
,
this
.
element
).
hide
();
this
.
prepareData
(
data
.
days
,
data
.
commits
);
this
.
buildGraph
(
this
.
element
.
get
(
0
)
);
this
.
buildGraph
();
},
this
)
});
}
,
}
;
BranchGraph
.
prototype
.
prepareData
=
function
(
days
,
commits
){
this
.
days
=
days
;
this
.
dayCount
=
days
.
length
;
this
.
commits
=
commits
;
ii
=
this
.
commits
.
length
;
for
(
var
i
=
0
;
i
<
ii
;
i
++
)
{
for
(
var
j
=
0
,
jj
=
this
.
commits
[
i
].
parents
.
length
;
j
<
jj
;
j
++
)
{
this
.
parents
[
this
.
commits
[
i
].
parents
[
j
][
0
]]
=
true
;
}
this
.
mtime
=
Math
.
max
(
this
.
mtime
,
this
.
commits
[
i
].
time
);
this
.
mspace
=
Math
.
max
(
this
.
mspace
,
this
.
commits
[
i
].
space
);
}
this
.
commitCount
=
commits
.
length
;
this
.
collectParents
();
this
.
mtime
+=
4
;
this
.
mspace
+=
10
;
for
(
i
=
0
;
i
<
ii
;
i
++
)
{
for
(
var
i
=
0
;
i
<
this
.
commitCount
;
i
++
)
{
if
(
this
.
commits
[
i
].
id
in
this
.
parents
)
{
this
.
commits
[
i
].
isParent
=
true
;
}
this
.
comm
s
[
this
.
commits
[
i
].
id
]
=
this
.
commits
[
i
];
this
.
preparedCommit
s
[
this
.
commits
[
i
].
id
]
=
this
.
commits
[
i
];
}
this
.
collectColors
();
};
BranchGraph
.
prototype
.
collectParents
=
function
(){
for
(
var
i
=
0
;
i
<
this
.
commitCount
;
i
++
)
{
for
(
var
j
=
0
,
jj
=
this
.
commits
[
i
].
parents
.
length
;
j
<
jj
;
j
++
)
{
this
.
parents
[
this
.
commits
[
i
].
parents
[
j
][
0
]]
=
true
;
}
this
.
mtime
=
Math
.
max
(
this
.
mtime
,
this
.
commits
[
i
].
time
);
this
.
mspace
=
Math
.
max
(
this
.
mspace
,
this
.
commits
[
i
].
space
);
}
};
BranchGraph
.
prototype
.
collectColors
=
function
(){
for
(
var
k
=
0
;
k
<
this
.
mspace
;
k
++
)
{
this
.
colors
.
push
(
Raphael
.
getColor
());
}
};
BranchGraph
.
prototype
.
buildGraph
=
function
(
holder
){
var
ch
=
this
.
mspace
*
20
+
20
,
cw
=
this
.
mtime
*
20
+
20
,
r
=
Raphael
(
holder
,
cw
,
ch
)
BranchGraph
.
prototype
.
buildGraph
=
function
(){
var
graphWidth
=
$
(
this
.
element
).
width
()
,
ch
=
this
.
mspace
*
20
+
20
,
cw
=
Math
.
max
(
graphWidth
,
this
.
mtime
*
20
+
20
)
,
r
=
Raphael
(
this
.
element
.
get
(
0
),
cw
,
ch
)
,
top
=
r
.
set
()
,
cuday
=
0
,
cumonth
=
""
,
r
;
,
offsetX
=
20
,
offsetY
=
60
,
barWidth
=
Math
.
max
(
graphWidth
,
this
.
dayCount
*
20
+
80
);
this
.
raphael
=
r
;
r
.
rect
(
0
,
0
,
this
.
days
.
length
*
20
+
80
,
3
0
).
attr
({
fill
:
"
#222
"
});
r
.
rect
(
0
,
30
,
this
.
days
.
length
*
20
+
80
,
20
).
attr
({
fill
:
"
#444
"
});
r
.
rect
(
0
,
0
,
barWidth
,
2
0
).
attr
({
fill
:
"
#222
"
});
r
.
rect
(
0
,
20
,
barWidth
,
20
).
attr
({
fill
:
"
#444
"
});
for
(
mm
=
0
;
mm
<
this
.
day
s
.
length
;
mm
++
)
{
for
(
mm
=
0
;
mm
<
this
.
day
Count
;
mm
++
)
{
if
(
this
.
days
[
mm
]
!=
null
){
if
(
cuday
!=
this
.
days
[
mm
][
0
]){
r
.
text
(
10
+
mm
*
20
,
40
,
this
.
days
[
mm
][
0
]).
attr
({
font
:
"
14px Fontin-Sans, Arial
"
,
// Dates
r
.
text
(
offsetX
+
mm
*
20
,
31
,
this
.
days
[
mm
][
0
]).
attr
({
font
:
"
12px Monaco, Arial
"
,
fill
:
"
#DDD
"
});
cuday
=
this
.
days
[
mm
][
0
];
}
if
(
cumonth
!=
this
.
days
[
mm
][
1
]){
r
.
text
(
10
+
mm
*
20
,
15
,
this
.
days
[
mm
][
1
]).
attr
({
font
:
"
14px Fontin-Sans, Arial
"
,
// Months
r
.
text
(
offsetX
+
mm
*
20
,
11
,
this
.
days
[
mm
][
1
]).
attr
({
font
:
"
12px Monaco, Arial
"
,
fill
:
"
#EEE
"
});
cumonth
=
this
.
days
[
mm
][
1
];
...
...
@@ -83,9 +99,9 @@
}
}
for
(
i
=
0
;
i
<
ii
;
i
++
)
{
var
x
=
10
+
20
*
this
.
commits
[
i
].
time
,
y
=
70
+
20
*
this
.
commits
[
i
].
space
;
for
(
i
=
0
;
i
<
this
.
commitCount
;
i
++
)
{
var
x
=
offsetX
+
20
*
this
.
commits
[
i
].
time
,
y
=
offsetY
+
20
*
this
.
commits
[
i
].
space
;
r
.
circle
(
x
,
y
,
3
).
attr
({
fill
:
this
.
colors
[
this
.
commits
[
i
].
space
],
stroke
:
"
none
"
...
...
@@ -96,22 +112,28 @@
if
(
shortrefs
.
length
>
15
){
shortrefs
=
shortrefs
.
substr
(
0
,
13
)
+
"
...
"
;
}
var
t
=
r
.
text
(
x
+
5
,
y
+
5
,
shortrefs
).
attr
({
font
:
"
12px Fontin-Sans, Arial
"
,
fill
:
"
#666
"
,
title
:
longrefs
,
cursor
:
"
pointer
"
,
rotation
:
"
90
"
var
t
=
r
.
text
(
x
+
5
,
y
+
8
,
shortrefs
).
attr
({
font
:
"
12px Monaco, Arial
"
,
fill
:
"
#666
"
,
title
:
longrefs
,
cursor
:
"
pointer
"
,
rotation
:
"
90
"
});
var
textbox
=
t
.
getBBox
();
t
.
translate
(
textbox
.
height
/-
4
,
textbox
.
width
/
2
);
}
var
c
;
for
(
var
j
=
0
,
jj
=
this
.
commits
[
i
].
parents
.
length
;
j
<
jj
;
j
++
)
{
var
c
=
this
.
comm
s
[
this
.
commits
[
i
].
parents
[
j
][
0
]];
c
=
this
.
preparedCommit
s
[
this
.
commits
[
i
].
parents
[
j
][
0
]];
if
(
c
)
{
var
cx
=
10
+
20
*
c
.
time
,
cy
=
70
+
20
*
c
.
space
;
var
cx
=
offsetX
+
20
*
c
.
time
,
cy
=
offsetY
+
20
*
c
.
space
;
if
(
c
.
space
==
this
.
commits
[
i
].
space
)
{
r
.
path
(
"
M
"
+
(
x
-
5
)
+
"
,
"
+
(
y
+
.
0001
)
+
"
L
"
+
(
15
+
20
*
c
.
time
)
+
"
,
"
+
(
y
+
.
0001
))
.
attr
({
r
.
path
([
"
M
"
,
x
,
y
,
"
L
"
,
x
-
20
*
(
c
.
time
+
1
),
y
]).
attr
({
stroke
:
this
.
colors
[
c
.
space
],
"
stroke-width
"
:
2
});
...
...
@@ -134,130 +156,100 @@
this
.
appendAnchor
(
top
,
this
.
commits
[
i
],
x
,
y
);
}
top
.
toFront
();
var
hw
=
holder
.
offsetWidth
,
hh
=
holder
.
offsetHeight
,
v
=
r
.
rect
(
hw
-
8
,
0
,
4
,
Math
.
pow
(
hh
,
2
)
/
ch
,
2
).
attr
({
fill
:
"
#000
"
,
opacity
:
0
})
,
h
=
r
.
rect
(
0
,
hh
-
8
,
Math
.
pow
(
hw
,
2
)
/
cw
,
4
,
2
).
attr
({
fill
:
"
#000
"
,
opacity
:
0
})
,
bars
=
r
.
set
(
v
,
h
)
,
drag
,
dragger
=
function
(
event
)
{
if
(
drag
)
{
event
=
event
||
window
.
event
;
holder
.
scrollLeft
=
drag
.
sl
-
(
event
.
clientX
-
drag
.
x
);
holder
.
scrollTop
=
drag
.
st
-
(
event
.
clientY
-
drag
.
y
);
}
this
.
element
.
scrollLeft
(
cw
);
this
.
bindEvents
();
};
BranchGraph
.
prototype
.
bindEvents
=
function
(){
var
drag
=
{}
,
element
=
this
.
element
;
var
dragger
=
function
(
event
){
element
.
scrollLeft
(
drag
.
sl
-
(
event
.
clientX
-
drag
.
x
));
element
.
scrollTop
(
drag
.
st
-
(
event
.
clientY
-
drag
.
y
));
};
holder
.
onmousedown
=
function
(
event
)
{
event
=
event
||
window
.
event
;
element
.
on
({
mousedown
:
function
(
event
)
{
drag
=
{
x
:
event
.
clientX
,
y
:
event
.
clientY
,
st
:
holder
.
scrollTop
,
sl
:
holder
.
scrollLeft
};
document
.
onmousemove
=
dragger
;
bars
.
animate
({
opacity
:
.
5
},
300
);
};
document
.
onmouseup
=
function
()
{
drag
=
false
;
document
.
onmousemove
=
null
;
bars
.
animate
({
opacity
:
0
},
300
);
st
:
element
.
scrollTop
(),
sl
:
element
.
scrollLeft
()
};
$
(
window
).
on
(
'
keydown
'
,
function
(
event
){
$
(
window
).
on
(
'
mousemove
'
,
dragger
);
}
});
$
(
window
).
on
({
mouseup
:
function
(){
//bars.animate({opacity: 0}, 300);
$
(
window
).
off
(
'
mousemove
'
,
dragger
);
},
keydown
:
function
(
event
){
if
(
event
.
keyCode
==
37
){
holder
.
scrollLeft
-=
50
;
// left
element
.
scrollLeft
(
element
.
scrollLeft
()
-
50
);
}
if
(
event
.
keyCode
==
38
){
// top
element
.
scrollTop
(
element
.
scrollTop
()
-
50
);
}
if
(
event
.
keyCode
==
39
){
// right
holder
.
scrollLeft
+=
50
;
element
.
scrollLeft
(
element
.
scrollLeft
()
+
50
);
}
if
(
event
.
keyCode
==
40
){
// bottom
element
.
scrollTop
(
element
.
scrollTop
()
+
50
);
}
}
});
holder
.
scrollLeft
=
cw
;
};
BranchGraph
.
prototype
.
appendAnchor
=
function
(
top
,
c
,
x
,
y
)
{
var
r
=
this
.
raphael
;
top
.
push
(
r
.
circle
(
x
,
y
,
10
).
attr
({
var
r
=
this
.
raphael
,
options
=
this
.
options
,
anchor
;
anchor
=
r
.
circle
(
x
,
y
,
10
).
attr
({
fill
:
"
#000
"
,
opacity
:
0
,
cursor
:
"
pointer
"
})
.
click
(
function
(){
location
.
href
=
location
.
href
.
replace
(
"
graph
"
,
"
commits/
"
+
c
.
id
);
window
.
location
=
options
.
commit_url
.
replace
(
'
%s
'
,
c
.
id
);
})
.
hover
(
function
()
{
// Create empty node to convert entities to character
var
s
=
r
.
text
(
100
,
100
,
c
.
author
+
"
\n
\n
"
+
c
.
id
+
"
\n
\n
"
+
c
.
message
).
attr
({
.
hover
(
function
(){
var
text
=
r
.
text
(
100
,
100
,
c
.
author
+
"
\n
\n
"
+
c
.
id
+
"
\n
\n
"
+
c
.
message
).
attr
({
fill
:
"
#fff
"
});
this
.
popup
=
r
.
popupit
(
x
,
y
+
5
,
s
,
0
);
this
.
popup
=
r
.
tooltip
(
x
,
y
+
5
,
text
,
0
);
top
.
push
(
this
.
popup
.
insertBefore
(
this
));
},
function
()
{
},
function
()
{
this
.
popup
&&
this
.
popup
.
remove
()
&&
delete
this
.
popup
;
}));
});
top
.
push
(
anchor
);
};
this
.
BranchGraph
=
BranchGraph
;
}(
this
);
Raphael
.
fn
.
popupit
=
function
(
x
,
y
,
set
,
dir
,
size
)
{
Raphael
.
fn
.
tooltip
=
function
(
x
,
y
,
set
,
dir
,
size
)
{
dir
=
dir
==
null
?
2
:
dir
;
size
=
size
||
5
;
x
=
Math
.
round
(
x
);
y
=
Math
.
round
(
y
);
var
mmax
=
Math
.
max
,
bb
=
set
.
getBBox
(),
w
=
Math
.
round
(
bb
.
width
/
2
),
h
=
Math
.
round
(
bb
.
height
/
2
),
dx
=
[
0
,
w
+
size
*
2
,
0
,
-
w
-
size
*
2
],
dy
=
[
-
h
*
2
-
size
*
3
,
-
h
-
size
,
0
,
-
h
-
size
],
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
,
var
mmax
=
Math
.
max
,
bb
=
set
.
getBBox
()
,
w
=
Math
.
round
(
bb
.
width
/
2
)
,
h
=
Math
.
round
(
bb
.
height
/
2
)
,
dx
=
[
0
,
w
+
size
*
2
,
0
,
-
w
-
size
*
2
]
,
dy
=
[
-
h
*
2
-
size
*
3
,
-
h
-
size
,
0
,
-
h
-
size
]
,
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
,
"
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
,
"
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
,
"
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
,
"
l
"
,
-
mmax
(
w
-
size
,
0
),
0
,
"
z
"
].
join
(
"
,
"
),
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
];
"
l
"
,
-
mmax
(
w
-
size
,
0
),
0
,
"
z
"
].
join
(
"
,
"
)
,
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
];
set
.
translate
(
xy
.
x
-
w
-
bb
.
x
,
xy
.
y
-
h
-
bb
.
y
);
return
this
.
set
(
this
.
path
(
p
).
attr
({
fill
:
"
#234
"
,
stroke
:
"
none
"
}).
insertBefore
(
set
.
node
?
set
:
set
[
0
]),
set
);
};
\ No newline at end of file
Raphael
.
fn
.
popup
=
function
(
x
,
y
,
text
,
dir
,
size
)
{
dir
=
dir
==
null
?
2
:
dir
>
3
?
3
:
dir
;
size
=
size
||
5
;
text
=
text
||
"
$9.99
"
;
var
res
=
this
.
set
(),
d
=
3
;
res
.
push
(
this
.
path
().
attr
({
fill
:
"
#000
"
,
stroke
:
"
#000
"
}));
res
.
push
(
this
.
text
(
x
,
y
,
text
).
attr
(
this
.
g
.
txtattr
).
attr
({
fill
:
"
#fff
"
,
"
font-family
"
:
"
Helvetica, Arial
"
}));
res
.
update
=
function
(
X
,
Y
,
withAnimation
)
{
X
=
X
||
x
;
Y
=
Y
||
y
;
var
bb
=
this
[
1
].
getBBox
(),
w
=
bb
.
width
/
2
,
h
=
bb
.
height
/
2
,
dx
=
[
0
,
w
+
size
*
2
,
0
,
-
w
-
size
*
2
],
dy
=
[
-
h
*
2
-
size
*
3
,
-
h
-
size
,
0
,
-
h
-
size
],
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
,
"
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
,
"
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
,
"
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
,
"
l
"
,
-
mmax
(
w
-
size
,
0
),
0
,
"
z
"
].
join
(
"
,
"
),
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
];
xy
.
path
=
p
;
if
(
withAnimation
)
{
this
.
animate
(
xy
,
500
,
"
>
"
);
}
else
{
this
.
attr
(
xy
);
}
return
this
;
};
return
res
.
update
(
x
,
y
);
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment