Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
K
kityminder-core
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
吴志俊
kityminder-core
Commits
99f37306
Commit
99f37306
authored
Feb 24, 2014
by
Akikonata
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev' of
https://github.com/kitygraph/kityminder
into dev
parents
f33d0046
89fa2d72
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
157 additions
and
190 deletions
+157
-190
.jshintrc
.jshintrc
+2
-1
configure.js
configure.js
+1
-1
kity
kity
+1
-1
zh-cn.js
lang/zh-cn/zh-cn.js
+1
-1
layout.js
src/adapter/layout.js
+13
-6
saveto.js
src/adapter/saveto.js
+2
-1
dragtree.js
src/module/dragtree.js
+21
-4
editor.js
src/module/editor.js
+26
-6
editor.selection.js
src/module/editor.selection.js
+12
-6
hand.js
src/module/hand.js
+61
-149
layout.js
src/module/layout.js
+2
-1
combobox.js
src/ui/combobox.js
+15
-13
No files found.
.jshintrc
View file @
99f37306
...
...
@@ -23,6 +23,7 @@
"utils",
"$",
"KM",
"keymap"
"keymap",
"baidu"
]
}
\ No newline at end of file
configure.js
View file @
99f37306
...
...
@@ -63,7 +63,7 @@
'KITYMINDER_HOME_URL'
:
getKMBasePath
(),
//定义工具栏
toolbars
:
[
'hand zoom-in zoom-out | undo redo | bold italic | fontfamily fontsize forecolor | saveto | markers | node | layout'
'hand zoom-in zoom-out | undo redo | bold italic | fontfamily fontsize forecolor | saveto | markers | node |
switch
layout'
]
//设置主题
...
...
kity
@
a9630e63
Subproject commit
df7b361ae5098710eca0fec1080dce4bc822bf24
Subproject commit
a9630e638b29633f62cdc2df239bc702118b231a
lang/zh-cn/zh-cn.js
View file @
99f37306
...
...
@@ -16,7 +16,7 @@ KityMinder.LANG[ 'zh-cn' ] = {
'zoom-in'
:
'放大'
,
'zoom-out'
:
'缩小'
,
'markers'
:
'添加标签'
,
'layout'
:
'切换主题'
'
switch
layout'
:
'切换主题'
},
'popupcolor'
:
{
'clearColor'
:
'清空颜色'
,
...
...
src/adapter/layout.js
View file @
99f37306
KM
.
registerToolbarUI
(
'layout'
,
function
(
name
)
{
KM
.
registerToolbarUI
(
'switchlayout'
,
function
(
name
)
{
var
me
=
this
,
label
=
me
.
getLang
(
'tooltips.'
+
name
),
options
=
{
...
...
@@ -8,7 +9,8 @@ KM.registerToolbarUI( 'layout', function ( name ) {
items
:
me
.
getLayoutStyleItems
()
||
[],
itemStyles
:
[],
value
:
me
.
getLayoutStyleItems
(),
autowidthitem
:
[]
autowidthitem
:
[],
enabledRecord
:
false
},
$combox
=
null
;
if
(
options
.
items
.
length
==
0
)
{
...
...
@@ -20,20 +22,25 @@ KM.registerToolbarUI( 'layout', function ( name ) {
comboboxWidget
=
$combox
.
kmui
();
comboboxWidget
.
on
(
'comboboxselect'
,
function
(
evt
,
res
)
{
me
.
execCommand
(
"switchlayout"
,
res
.
value
);
me
.
execCommand
(
name
,
res
.
value
);
}
).
on
(
"beforeshow"
,
function
()
{
if
(
$combox
.
parent
().
length
===
0
)
{
$combox
.
appendTo
(
me
.
$container
.
find
(
'.kmui-dialog-container'
)
);
}
}
);
//状态反射
me
.
on
(
'interactchange'
,
function
()
{
var
value
=
this
.
queryCommandValue
(
"switchlayout"
);
var
state
=
this
.
queryCommandState
(
name
),
value
=
this
.
queryCommandValue
(
name
);
//设置按钮状态
comboboxWidget
.
button
().
kmui
().
disabled
(
state
==
-
1
).
active
(
state
==
1
);
if
(
value
)
{
//设置label
value
=
value
.
replace
(
/
[
'"
]
/g
,
''
).
toLowerCase
().
split
(
/
[
'|"
]?\s
*,
\s
*
[\1]?
/
);
comboboxWidget
.
selectItemByLabel
(
value
);
}
}
);
return
comboboxWidget
.
button
().
addClass
(
'kmui-combobox'
);
}
);
\ No newline at end of file
src/adapter/saveto.js
View file @
99f37306
...
...
@@ -9,7 +9,8 @@ KM.registerToolbarUI( 'saveto', function ( name ) {
items
:
[],
itemStyles
:
[],
value
:
[],
autowidthitem
:
[]
autowidthitem
:
[],
enabledRecord
:
false
},
$combox
=
null
,
comboboxWidget
=
null
;
...
...
src/module/dragtree.js
View file @
99f37306
...
...
@@ -79,6 +79,12 @@ var DragBox = kity.createClass( "DragBox", {
ancestors
=
[],
judge
;
// 根节点不参与计算
var
rootIndex
=
nodes
.
indexOf
(
this
.
_minder
.
getRoot
()
);
if
(
~
rootIndex
)
{
nodes
.
splice
(
rootIndex
,
1
);
}
// 判断 nodes 列表中是否存在 judge 的祖先
function
hasAncestor
(
nodes
,
judge
)
{
for
(
var
i
=
nodes
.
length
-
1
;
i
>=
0
;
--
i
)
{
...
...
@@ -137,10 +143,15 @@ var DragBox = kity.createClass( "DragBox", {
// 4. 标记已启动
_enterDragMode
:
function
()
{
this
.
_calcDragSources
();
if
(
!
this
.
_dragSources
.
length
)
{
this
.
_startPosition
=
null
;
return
false
;
}
this
.
_calcDropTargets
();
this
.
_drawForDragMode
();
this
.
_shrink
();
this
.
_dragMode
=
true
;
return
true
;
},
_leaveDragMode
:
function
()
{
this
.
remove
();
...
...
@@ -150,7 +161,7 @@ var DragBox = kity.createClass( "DragBox", {
_drawForDragMode
:
function
()
{
this
.
_text
.
setContent
(
this
.
_dragSources
.
length
+
' items'
);
this
.
_text
.
setPosition
(
this
.
_startPosition
.
x
,
this
.
_startPosition
.
y
+
5
);
this
.
_minder
.
get
RenderContain
er
().
addShape
(
this
);
this
.
_minder
.
get
Pap
er
().
addShape
(
this
);
},
_shrink
:
function
()
{
// 合并所有拖放源图形的矩形即可
...
...
@@ -227,16 +238,21 @@ var DragBox = kity.createClass( "DragBox", {
},
dragMove
:
function
(
position
)
{
// 启动拖放模式需要最小的移动距离
var
DRAG_MOVE_THRESHOLD
=
10
;
if
(
!
this
.
_startPosition
)
return
;
this
.
_dragPosition
=
position
;
if
(
!
this
.
_dragMode
)
{
// 判断拖放模式是否该启动
if
(
GM
.
getDistance
(
this
.
_dragPosition
,
this
.
_startPosition
)
<
10
)
{
if
(
GM
.
getDistance
(
this
.
_dragPosition
,
this
.
_startPosition
)
<
DRAG_MOVE_THRESHOLD
)
{
return
;
}
if
(
!
this
.
_enterDragMode
()
)
{
return
;
}
this
.
_enterDragMode
();
}
var
movement
=
kity
.
Vector
.
fromPoints
(
this
.
_startPosition
,
this
.
_dragPosition
);
...
...
@@ -269,7 +285,8 @@ KityMinder.registerModule( "DragTree", function () {
},
events
:
{
mousedown
:
function
(
e
)
{
if
(
e
.
getTargetNode
()
)
{
// 单选中根节点也不触发拖拽
if
(
e
.
getTargetNode
()
&&
e
.
getTargetNode
()
!=
this
.
getRoot
()
)
{
this
.
_dragBox
.
dragStart
(
e
.
getPosition
()
);
}
},
...
...
src/module/editor.js
View file @
99f37306
...
...
@@ -12,9 +12,13 @@ KityMinder.registerModule( "TextEditModule", function () {
var
lastEvtPosition
,
dir
=
1
;
km
.
isTextEditStatus
=
function
(){
return
km
.
receiver
.
isTextEditStatus
();
}
};
var
selectionByClick
=
false
;
return
{
//插入光标
...
...
@@ -25,12 +29,19 @@ KityMinder.registerModule( "TextEditModule", function () {
'beforemousedown'
:
function
(
e
){
sel
.
setHide
();
var
node
=
e
.
getTargetNode
();
if
(
!
node
){
var
selectionShape
=
e
.
kityEvent
.
targetShape
;
if
(
selectionShape
&&
selectionShape
.
getType
()
==
'Selection'
){
selectionByClick
=
true
;
node
=
selectionShape
.
getData
(
'relatedNode'
);
e
.
stopPropagationImmediately
();
}
}
if
(
node
){
var
textShape
=
node
.
getTextShape
();
textShape
.
setStyle
(
'cursor'
,
'default'
);
if
(
this
.
isSingleSelect
()
&&
node
.
isSelected
())
{
// && e.kityEvent.targetShape.getType().toLowerCase()== 'text'
sel
.
collapse
();
node
.
getTextShape
().
setStyle
(
'cursor'
,
'text'
);
receiver
.
setTextEditStatus
(
true
)
...
...
@@ -44,15 +55,24 @@ KityMinder.registerModule( "TextEditModule", function () {
.
setCurrentIndex
(
e
.
getPosition
())
.
updateSelection
()
.
setRange
(
range
);
sel
.
setData
(
'relatedNode'
,
node
);
mouseDownStatus
=
true
;
lastEvtPosition
=
e
.
getPosition
();
if
(
selectionByClick
){
sel
.
setShow
();
selectionByClick
=
false
;
}
}
}
},
'mouseup'
:
function
(
e
){
if
(
!
sel
.
collapsed
&&
mouseDownStatus
){
receiver
.
updateRange
(
range
)
if
(
mouseDownStatus
){
if
(
!
sel
.
collapsed
){
receiver
.
updateRange
(
range
)
}
else
sel
.
setShow
()
}
mouseDownStatus
=
false
;
oneTime
=
0
;
},
...
...
@@ -62,7 +82,7 @@ KityMinder.registerModule( "TextEditModule", function () {
var
offset
=
e
.
getPosition
();
if
(
Math
.
abs
(
offset
.
y
-
lastEvtPosition
.
y
)
>
2
){
if
(
Math
.
abs
(
offset
.
y
-
lastEvtPosition
.
y
)
>
2
&&
Math
.
abs
(
lastEvtPosition
.
x
-
offset
.
x
)
<
1
){
sel
.
setHide
();
mouseDownStatus
=
false
;
return
;
...
...
@@ -70,7 +90,7 @@ KityMinder.registerModule( "TextEditModule", function () {
dir
=
offset
.
x
>
lastEvtPosition
.
x
?
1
:
(
offset
.
x
<
lastEvtPosition
.
x
?
-
1
:
dir
);
receiver
.
updateSelectionByMousePosition
(
offset
,
dir
)
.
updateSelectionShow
(
dir
);
sel
.
stroke
(
'none'
,
0
);
lastEvtPosition
=
e
.
getPosition
();
}
...
...
src/module/editor.selection.js
View file @
99f37306
...
...
@@ -5,9 +5,9 @@ Minder.Selection = kity.createClass( 'Selection', {
this
.
callBase
();
this
.
height
=
height
||
20
;
this
.
stroke
(
color
||
'
blue
'
,
width
||
1
);
this
.
width
=
1
;
this
.
fill
(
'
#99C8FF
'
);
this
.
stroke
(
color
||
'
rgb(27,171,255)
'
,
width
||
1
);
this
.
width
=
0
;
this
.
fill
(
'
rgb(27,171,255)
'
);
this
.
setHide
();
this
.
timer
=
null
;
this
.
collapsed
=
true
;
...
...
@@ -17,7 +17,8 @@ Minder.Selection = kity.createClass( 'Selection', {
},
collapse
:
function
(
toEnd
){
this
.
stroke
(
'blue'
,
1
);
this
.
stroke
(
'rgb(27,171,255)'
,
1
);
this
.
setOpacity
(
1
);
this
.
width
=
1
;
this
.
collapsed
=
true
;
if
(
toEnd
){
...
...
@@ -38,7 +39,8 @@ Minder.Selection = kity.createClass( 'Selection', {
return
this
;
}
this
.
collapsed
=
false
;
this
.
stroke
(
'none'
);
this
.
stroke
(
'none'
,
0
);
this
.
setOpacity
(
0.5
);
return
this
;
},
setEndOffset
:
function
(
offset
){
...
...
@@ -52,10 +54,14 @@ Minder.Selection = kity.createClass( 'Selection', {
return
this
;
}
this
.
collapsed
=
false
;
this
.
stroke
(
'none'
);
this
.
stroke
(
'none'
,
0
);
this
.
setOpacity
(
0.5
);
return
this
;
},
updateShow
:
function
(
offset
,
width
){
if
(
width
){
this
.
setShowHold
();
}
this
.
setPosition
(
offset
).
setWidth
(
width
);
return
this
;
},
...
...
src/module/hand.js
View file @
99f37306
kity
.
Draggable
=
(
function
()
{
var
Paper
=
kity
.
Paper
;
var
touchable
=
window
.
ontouchstart
!==
undefined
;
var
DRAG_START_EVENT
=
touchable
?
'touchstart'
:
'mousedown'
,
DRAG_MOVE_EVENT
=
touchable
?
'touchmove'
:
'mousemove'
,
DRAG_END_EVENT
=
touchable
?
'touchend'
:
'mouseup'
;
return
kity
.
createClass
(
{
drag
:
function
(
opt
)
{
if
(
this
.
dragEnabled
)
{
return
;
var
ViewDragger
=
kity
.
createClass
(
"ViewDragger"
,
{
constructor
:
function
(
minder
)
{
this
.
_minder
=
minder
;
this
.
_enabled
=
false
;
this
.
_offset
=
{
x
:
0
,
y
:
0
};
this
.
_bind
();
},
isEnabled
:
function
()
{
return
this
.
_enabled
;
},
setEnabled
:
function
(
value
)
{
var
paper
=
this
.
_minder
.
getPaper
();
paper
.
setStyle
(
'cursor'
,
value
?
'pointer'
:
'default'
);
paper
.
setStyle
(
'cursor'
,
value
?
'-webkit-grab'
:
'default'
);
this
.
_enabled
=
value
;
},
_bind
:
function
()
{
var
dragger
=
this
,
isRootDrag
=
false
,
lastPosition
=
null
,
currentPosition
=
null
;
this
.
_minder
.
on
(
'beforemousedown'
,
function
(
e
)
{
// 已经被用户打开拖放模式
if
(
dragger
.
isEnabled
()
)
{
lastPosition
=
e
.
getPosition
();
e
.
stopPropagation
();
}
// 点击未选中的根节点临时开启
else
if
(
e
.
getTargetNode
()
==
this
.
getRoot
()
&&
(
!
this
.
getRoot
().
isSelected
()
||
!
this
.
isSingleSelect
()))
{
lastPosition
=
e
.
getPosition
();
dragger
.
setEnabled
(
true
);
isRootDrag
=
true
;
}
var
dragStart
=
opt
&&
opt
.
start
||
this
.
dragStart
,
dragMove
=
opt
&&
opt
.
move
||
this
.
dragMove
,
dragEnd
=
opt
&&
opt
.
end
||
this
.
dragEnd
,
dragTarget
=
opt
&&
opt
.
target
||
this
.
dragTarget
||
this
,
me
=
this
;
this
.
dragEnabled
=
true
;
this
.
dragTarget
=
dragTarget
;
function
bindEvents
(
paper
)
{
var
startPosition
,
lastPosition
,
dragging
=
false
;
var
dragFn
=
function
(
e
)
{
if
(
!
dragging
)
{
paper
.
off
(
DRAG_MOVE_EVENT
,
dragFn
);
}
if
(
e
.
originEvent
.
touches
&&
e
.
originEvent
.
touches
.
length
!==
1
)
return
;
var
currentPosition
=
e
.
getPosition
();
var
movement
=
{
x
:
currentPosition
.
x
-
startPosition
.
x
,
y
:
currentPosition
.
y
-
startPosition
.
y
};
var
delta
=
{
x
:
currentPosition
.
x
-
lastPosition
.
x
,
y
:
currentPosition
.
y
-
lastPosition
.
y
};
var
dragInfo
=
{
position
:
currentPosition
,
movement
:
movement
,
delta
:
delta
};
lastPosition
=
currentPosition
;
if
(
dragMove
)
{
dragMove
.
call
(
me
,
dragInfo
);
}
else
if
(
me
instanceof
Paper
)
{
// treate paper drag different
var
view
=
me
.
getViewPort
();
view
.
center
.
x
-=
movement
.
x
;
view
.
center
.
y
-=
movement
.
y
;
me
.
setViewPort
(
view
);
}
else
{
me
.
translate
(
delta
.
x
,
delta
.
y
);
}
dragTarget
.
trigger
(
'dragmove'
,
dragInfo
);
e
.
stopPropagation
();
e
.
preventDefault
();
};
dragTarget
.
on
(
DRAG_START_EVENT
,
dragTarget
.
_dragStartHandler
=
function
(
e
)
{
if
(
e
.
originEvent
.
button
)
{
return
;
}
dragging
=
true
;
var
dragInfo
=
{
position
:
lastPosition
=
startPosition
=
e
.
getPosition
()
};
if
(
dragStart
)
{
var
cancel
=
dragStart
.
call
(
me
,
dragInfo
)
===
false
;
if
(
cancel
)
{
return
;
}
}
paper
.
on
(
DRAG_MOVE_EVENT
,
dragFn
);
dragTarget
.
trigger
(
'dragstart'
,
dragInfo
);
e
.
stopPropagation
();
e
.
preventDefault
();
}
);
}
)
paper
.
on
(
DRAG_END_EVENT
,
dragTarget
.
_dragEndHandler
=
function
(
e
)
{
if
(
dragging
)
{
dragging
=
false
;
if
(
dragEnd
)
{
dragEnd
.
call
(
me
);
}
.
on
(
'beforemousemove'
,
function
(
e
)
{
if
(
lastPosition
)
{
currentPosition
=
e
.
getPosition
();
paper
.
off
(
DRAG_MOVE_EVENT
,
dragFn
);
dragTarget
.
trigger
(
'dragend'
);
// 当前偏移加上历史偏移
var
offset
=
kity
.
Vector
.
fromPoints
(
lastPosition
,
currentPosition
);
if
(
e
)
{
e
.
stopPropagation
();
e
.
preventDefault
();
}
}
}
);
this
.
getRenderContainer
().
translate
(
offset
.
x
,
offset
.
y
);
e
.
stopPropagation
();
lastPosition
=
currentPosition
;
}
}
)
if
(
me
instanceof
Paper
)
{
bindEvents
(
me
);
}
else
if
(
me
.
getPaper
()
)
{
bindEvents
(
me
.
getPaper
()
);
}
else
{
var
listener
=
function
(
e
)
{
if
(
e
.
targetShape
.
getPaper
()
)
{
bindEvents
(
e
.
targetShape
.
getPaper
()
);
me
.
off
(
'add'
,
listener
);
me
.
off
(
'treeadd'
,
listener
);
}
};
me
.
on
(
'add treeadd'
,
listener
);
}
return
this
;
},
// end of drag
.
on
(
'mouseup'
,
function
(
e
)
{
lastPosition
=
null
;
undrag
:
function
()
{
var
target
=
this
.
dragTarget
;
target
.
off
(
DRAG_START_EVENT
,
target
.
_dragStartHandler
);
target
.
_dragEndHandler
();
target
.
getPaper
().
off
(
DRAG_END_EVENT
,
target
.
_dragEndHandler
);
delete
target
.
_dragStartHandler
;
delete
target
.
_dragEndHandler
;
this
.
dragEnabled
=
false
;
return
this
;
}
}
);
}
)();
// 临时拖动需要还原状态
if
(
isRootDrag
)
{
dragger
.
setEnabled
(
false
);
isRootDrag
=
false
;
}
}
);
}
}
);
KityMinder
.
registerModule
(
'Hand'
,
function
()
{
var
ToggleHandCommand
=
kity
.
createClass
(
"ToggleHandCommand"
,
{
base
:
Command
,
execute
:
function
(
minder
)
{
var
drag
=
minder
.
_onDragMode
=
!
minder
.
_onDragMode
;
minder
.
getPaper
().
setStyle
(
'cursor'
,
drag
?
'pointer'
:
'default'
);
minder
.
getPaper
().
setStyle
(
'cursor'
,
drag
?
'-webkit-grab'
:
'default'
);
if
(
drag
)
{
minder
.
getPaper
().
drag
();
}
else
{
minder
.
getPaper
().
undrag
();
}
minder
.
_viewDragger
.
setEnabled
(
!
minder
.
_viewDragger
.
isEnabled
()
);
},
queryState
:
function
(
minder
)
{
return
minder
.
_
onDragMode
?
1
:
0
;
return
minder
.
_
viewDragger
.
isEnabled
()
?
1
:
0
;
}
}
);
return
{
init
:
function
()
{
this
.
_onDragMode
=
false
;
kity
.
extendClass
(
kity
.
Paper
,
kity
.
Draggable
);
this
.
_viewDragger
=
new
ViewDragger
(
this
);
},
commands
:
{
'hand'
:
ToggleHandCommand
...
...
@@ -172,11 +89,6 @@ KityMinder.registerModule( 'Hand', function () {
this
.
execCommand
(
'hand'
);
e
.
preventDefault
();
}
},
beforemousemove
:
function
(
e
)
{
if
(
this
.
_onDragMode
)
{
e
.
stopPropagation
();
}
}
}
};
...
...
src/module/layout.js
View file @
99f37306
...
...
@@ -29,8 +29,9 @@ KityMinder.registerModule( "LayoutModule", function () {
},
initStyle
:
function
()
{
var
curStyle
=
this
.
getCurrentStyle
();
var
lastTransform
=
this
.
_rc
.
getTransform
();
this
.
_rc
.
remove
();
this
.
_rc
=
new
kity
.
Group
();
this
.
_rc
=
new
kity
.
Group
()
.
setTransform
(
lastTransform
)
;
this
.
_paper
.
addShape
(
this
.
_rc
);
var
_root
=
this
.
getRoot
();
...
...
src/ui/combobox.js
View file @
99f37306
...
...
@@ -134,9 +134,7 @@
*/
select
:
function
(
index
){
if
(
!
this
.
data
(
'options'
).
enabledRecord
){
return
this
;
}
var
options
=
this
.
data
(
'options'
),
itemCount
=
options
.
itemCount
,
items
=
options
.
autowidthitem
;
...
...
@@ -166,6 +164,7 @@
this
.
trigger
(
'changebefore'
,
items
[
index
]
);
this
.
_update
(
index
);
this
.
trigger
(
'changeafter'
,
items
[
index
]
);
...
...
@@ -334,22 +333,25 @@
var
options
=
this
.
data
(
"options"
),
newStack
=
[];
$
.
each
(
options
.
recordStack
,
function
(
i
,
item
){
if
(
this
.
data
(
'options'
).
enabledRecord
){
$
.
each
(
options
.
recordStack
,
function
(
i
,
item
){
if
(
item
!=
index
)
{
newStack
.
push
(
item
);
}
if
(
item
!=
index
)
{
newStack
.
push
(
item
);
}
}
);
}
);
//压入最新的记录
newStack
.
unshift
(
index
);
//压入最新的记录
newStack
.
unshift
(
index
);
if
(
newStack
.
length
>
options
.
recordCount
)
{
newStack
.
length
=
options
.
recordCount
;
}
if
(
newStack
.
length
>
options
.
recordCount
)
{
newStack
.
length
=
options
.
recordCount
;
options
.
recordStack
=
newStack
;
}
options
.
recordStack
=
newStack
;
options
.
selected
=
index
;
this
.
_repaint
();
...
...
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