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
ddd8adc1
Commit
ddd8adc1
authored
Mar 18, 2014
by
campaign
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/dev' into dev
parents
da9717dd
ad50a2a5
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
244 additions
and
127 deletions
+244
-127
CHANGELOG.md
CHANGELOG.md
+35
-4
index.html
dev/index.html
+4
-7
index.html
dist/index.html
+4
-6
kityminder.all.js
dist/kityminder.all.js
+113
-78
kityminder.all.min.js
dist/kityminder.all.min.js
+5
-5
social.js
social/social.js
+83
-27
No files found.
CHANGELOG.md
View file @
ddd8adc1
# KityMinder Change L
ist
# KityMinder Change L
og
## 1.1.0
## v1.1.0
### 功能更新
1.
添加了粘贴剪贴复制功能
1.
添加了剪贴板操作的支持
2.
添加了本地自动保存草稿的功能
3.
发布版本上添加了 Github 地址
### 功能调整
1.
视野定位功能抽取为 camera 命令,并且创建新文件和打开文件会把视野定位到根节点
### 问题修复
1.
修正了bottom主题的位置问题
1.
修正了 bottom 主题的位置问题
2.
修正了子节点全部删除后展开/收缩标志依然存在画布上的问题
3.
修复了 bottom 主题的键盘导航出错的问题
4.
滚轮缩放的问题
## v1.0.1
### 问题修复
1.
修复 png 导出问题。
## v1.0.0
首个版本发布,包括基本的编辑功能。
dev/index.html
View file @
ddd8adc1
...
...
@@ -17,16 +17,13 @@
<link
href=
"../themes/default/css/import.css"
type=
"text/css"
rel=
"stylesheet"
/>
<link
href=
"../dialogs/dialogs.css"
rel=
"stylesheet"
type=
"text/css"
/>
<script>
$
(
function
()
{
// create km instance
window
.
km
=
KM
.
getKityMinder
(
'kityminder'
);
});
</script>
</head>
<body>
<div
id=
"kityminder"
onselectstart=
"return false"
></div>
</body>
<script>
// create km instance
window
.
km
=
KM
.
getKityMinder
(
'kityminder'
);
</script>
</html>
\ No newline at end of file
dist/index.html
View file @
ddd8adc1
...
...
@@ -19,12 +19,6 @@
<link
rel=
"stylesheet"
href=
"css/links.css"
/>
<script>
$
(
function
()
{
// create km instance
window
.
km
=
KM
.
getKityMinder
(
'kityminder'
);
});
</script>
</head>
<body>
...
...
@@ -39,4 +33,8 @@
</p>
</body>
<script>
// create km instance
window
.
km
=
KM
.
getKityMinder
(
'kityminder'
);
</script>
</html>
\ No newline at end of file
dist/kityminder.all.js
View file @
ddd8adc1
...
...
@@ -810,7 +810,7 @@ var Minder = KityMinder.Minder = kity.createClass( "KityMinder", {
this
.
_paper
=
new
kity
.
Paper
();
this
.
_paper
.
getNode
().
setAttribute
(
'contenteditable'
,
true
);
this
.
_paper
.
getNode
().
ondragstart
=
function
(
e
)
{
this
.
_paper
.
getNode
().
ondragstart
=
function
(
e
)
{
e
.
preventDefault
();
};
...
...
@@ -846,47 +846,48 @@ var Minder = KityMinder.Minder = kity.createClass( "KityMinder", {
this
.
_shortcutkeys
=
{};
this
.
_bindshortcutKeys
();
},
isTextEditStatus
:
function
()
{
isTextEditStatus
:
function
()
{
return
false
;
},
addShortcutKeys
:
function
(
cmd
,
keys
)
{
var
obj
=
{},
km
=
this
;
var
obj
=
{},
km
=
this
;
if
(
keys
)
{
obj
[
cmd
]
=
keys
}
else
{
obj
=
cmd
;
}
utils
.
each
(
obj
,
function
(
k
,
v
)
{
km
.
_shortcutkeys
[
k
.
toLowerCase
()
]
=
v
;
});
utils
.
each
(
obj
,
function
(
k
,
v
)
{
km
.
_shortcutkeys
[
k
.
toLowerCase
()
]
=
v
;
}
);
},
getShortcutKey
:
function
(
cmdName
)
{
return
this
.
_shortcutkeys
[
cmdName
]
getShortcutKey
:
function
(
cmdName
)
{
return
this
.
_shortcutkeys
[
cmdName
]
},
_bindshortcutKeys
:
function
()
{
var
me
=
this
,
shortcutkeys
=
this
.
_shortcutkeys
;
function
checkkey
(
key
,
keyCode
,
e
){
switch
(
key
){
function
checkkey
(
key
,
keyCode
,
e
)
{
switch
(
key
)
{
case
'ctrl'
:
case
'cmd'
:
if
(
e
.
ctrlKey
||
e
.
metaKey
)
{
if
(
e
.
ctrlKey
||
e
.
metaKey
)
{
return
true
;
}
break
;
case
'alt'
:
if
(
e
.
altKey
)
{
if
(
e
.
altKey
)
{
return
true
}
break
;
case
'shift'
:
if
(
e
.
shiftKey
)
{
if
(
e
.
shiftKey
)
{
return
true
;
}
}
if
(
keyCode
==
keymap
[
key
])
{
if
(
keyCode
==
keymap
[
key
]
)
{
return
true
;
}
return
false
...
...
@@ -896,15 +897,15 @@ var Minder = KityMinder.Minder = kity.createClass( "KityMinder", {
var
originEvent
=
e
.
originEvent
;
var
keyCode
=
originEvent
.
keyCode
||
originEvent
.
which
;
for
(
var
i
in
shortcutkeys
)
{
var
keys
=
shortcutkeys
[
i
].
toLowerCase
().
split
(
'+'
);
var
keys
=
shortcutkeys
[
i
].
toLowerCase
().
split
(
'+'
);
var
current
=
0
;
utils
.
each
(
keys
,
function
(
i
,
k
)
{
if
(
checkkey
(
k
,
keyCode
,
originEvent
))
{
utils
.
each
(
keys
,
function
(
i
,
k
)
{
if
(
checkkey
(
k
,
keyCode
,
originEvent
)
)
{
current
++
;
}
});
}
);
if
(
current
==
keys
.
length
)
{
if
(
current
==
keys
.
length
)
{
if
(
me
.
queryCommandState
(
i
)
!=
-
1
)
me
.
execCommand
(
i
);
originEvent
.
preventDefault
();
...
...
@@ -914,38 +915,38 @@ var Minder = KityMinder.Minder = kity.createClass( "KityMinder", {
}
}
);
},
_initContextmenu
:
function
()
{
_initContextmenu
:
function
()
{
this
.
contextmenus
=
[];
},
addContextmenu
:
function
(
item
)
{
if
(
utils
.
isArray
(
item
))
{
this
.
contextmenus
=
this
.
contextmenus
.
concat
(
item
);
}
else
{
this
.
contextmenus
.
push
(
item
);
addContextmenu
:
function
(
item
)
{
if
(
utils
.
isArray
(
item
)
)
{
this
.
contextmenus
=
this
.
contextmenus
.
concat
(
item
);
}
else
{
this
.
contextmenus
.
push
(
item
);
}
return
this
;
},
getContextmenu
:
function
()
{
getContextmenu
:
function
()
{
return
this
.
contextmenus
;
},
_initStatus
:
function
()
{
_initStatus
:
function
()
{
this
.
_status
=
"normal"
;
this
.
_rollbackStatus
=
"normal"
;
},
setStatus
:
function
(
status
)
{
if
(
status
)
{
setStatus
:
function
(
status
)
{
if
(
status
)
{
this
.
_rollbackStatus
=
this
.
_status
;
this
.
_status
=
status
;
}
else
{
}
else
{
this
.
_status
=
''
;
}
return
this
;
},
rollbackStatus
:
function
()
{
rollbackStatus
:
function
()
{
this
.
_status
=
this
.
_rollbackStatus
;
},
getStatus
:
function
()
{
getStatus
:
function
()
{
return
this
.
_status
;
}
}
);
...
...
@@ -1097,7 +1098,7 @@ kity.extendClass( Minder, {
_bindKeyboardEvents
:
function
()
{
if
(
(
navigator
.
userAgent
.
indexOf
(
'iPhone'
)
==
-
1
)
&&
(
navigator
.
userAgent
.
indexOf
(
'iPod'
)
==
-
1
)
&&
(
navigator
.
userAgent
.
indexOf
(
'iPad'
)
==
-
1
)
)
{
//只能在这里做,要不无法触发
Utils
.
listen
(
document
.
body
,
'keydown keyup keypress'
,
this
.
_firePharse
.
bind
(
this
)
);
Utils
.
listen
(
document
.
body
,
'keydown keyup keypress
paste
'
,
this
.
_firePharse
.
bind
(
this
)
);
}
},
_firePharse
:
function
(
e
)
{
...
...
@@ -1439,9 +1440,12 @@ var keymap = KityMinder.keymap = {
"b"
:
66
,
'i'
:
73
,
//回退
'z'
:
90
,
'y'
:
89
'y'
:
89
,
//粘贴
'v'
:
86
,
'x'
:
88
};
//添加多语言模块
...
...
@@ -2669,14 +2673,6 @@ KityMinder.registerModule( "LayoutDefault", function () {
node
.
setType
(
"main"
);
}
else
{
node
.
setType
(
"sub"
);
var
isExpand
=
parent
.
getData
(
"expand"
);
if
(
isExpand
===
undefined
)
{
isExpand
=
true
;
parent
.
setData
(
"expand"
,
isExpand
);
}
// if ( !isExpand ) {
// return false;
// }
}
//计算位置等流程
updateBg
(
node
);
...
...
@@ -4135,6 +4131,7 @@ KityMinder.registerModule( "Select", function () {
if
(
!
downNode
)
{
this
.
removeAllSelectedNodes
();
marqueeActivator
.
selectStart
(
e
);
this
.
setStatus
(
'normal'
)
}
...
...
@@ -4217,6 +4214,8 @@ KityMinder.registerModule( "TextEditModule", function () {
node
=
selectionShape
.
getData
(
'relatedNode'
);
e
.
stopPropagationImmediately
();
}
if
(
this
.
getStatus
()
==
'textedit'
)
this
.
fire
(
'contentchange'
);
km
.
setStatus
(
'normal'
)
}
if
(
node
){
...
...
@@ -4408,6 +4407,7 @@ Minder.Range = kity.createClass('Range',{
startOffset
:
range
.
startOffset
}
},
collapse
:
function
(
toStart
){
this
.
nativeRange
.
collapse
(
toStart
===
true
);
return
this
;
...
...
@@ -4415,6 +4415,11 @@ Minder.Range = kity.createClass('Range',{
insertNode
:
function
(
node
){
this
.
nativeRange
.
insertNode
(
node
);
return
this
;
},
updateNativeRange
:
function
(){
this
.
nativeRange
=
this
.
nativeSel
.
getRangeAt
(
0
);
return
this
;
}
});
...
...
@@ -4425,6 +4430,7 @@ Minder.Receiver = kity.createClass('Receiver',{
this
.
selection
&&
this
.
selection
.
setHide
();
this
.
range
&&
this
.
range
.
nativeSel
.
removeAllRanges
();
this
.
index
=
0
;
this
.
inputLength
=
0
;
return
this
;
},
setTextEditStatus
:
function
(
status
){
...
...
@@ -4442,7 +4448,7 @@ Minder.Receiver = kity.createClass('Receiver',{
_div
.
className
=
'km_receiver'
;
this
.
container
=
document
.
body
.
insertBefore
(
_div
,
document
.
body
.
firstChild
);
utils
.
addCssRule
(
'km_receiver_css'
,
' .km_receiver{position:absolute;padding:0;margin:0;word-wrap:break-word;clip:rect(1em 1em 1em 1em);}'
);
//
this
.
km
.
on
(
'textedit.beforekeyup textedit.keydown'
,
utils
.
proxy
(
this
.
keyboardEvents
,
this
));
this
.
km
.
on
(
'textedit.beforekeyup textedit.keydown
textedit.paste
'
,
utils
.
proxy
(
this
.
keyboardEvents
,
this
));
this
.
timer
=
null
;
this
.
index
=
0
;
},
...
...
@@ -4494,8 +4500,32 @@ Minder.Receiver = kity.createClass('Receiver',{
var
orgEvt
=
e
.
originEvent
;
var
keyCode
=
orgEvt
.
keyCode
;
var
keys
=
KityMinder
.
keymap
;
function
setTextToContainer
(){
var
text
=
me
.
container
.
textContent
.
replace
(
/
[\u
200b
\t\r\n]
/g
,
''
);
if
(
me
.
textShape
.
getOpacity
()
==
0
){
me
.
textShape
.
setOpacity
(
1
);
}
me
.
textShape
.
setContent
(
text
);
me
.
setContainerStyle
();
me
.
minderNode
.
setText
(
text
);
if
(
text
.
length
==
0
){
me
.
textShape
.
setContent
(
'a'
);
me
.
textShape
.
setOpacity
(
0
);
}
me
.
km
.
updateLayout
(
me
.
minderNode
);
me
.
setBaseOffset
();
me
.
updateTextData
();
me
.
updateIndex
();
me
.
updateSelection
();
me
.
timer
=
setTimeout
(
function
(){
me
.
selection
.
setShow
()
},
500
);
}
switch
(
e
.
type
){
case
'keydown'
:
switch
(
e
.
originEvent
.
keyCode
)
{
case
keys
.
Enter
:
...
...
@@ -4506,8 +4536,33 @@ Minder.Receiver = kity.createClass('Receiver',{
e
.
preventDefault
();
break
;
}
if
(
e
.
originEvent
.
ctrlKey
||
e
.
originEvent
.
metaKey
){
//粘贴
if
(
keyCode
==
keymap
.
v
){
setTimeout
(
function
(){
me
.
range
.
updateNativeRange
().
insertNode
(
$
(
'<span>$$_kityminder_bookmark_$$</span>'
)[
0
]);
me
.
container
.
innerHTML
=
me
.
container
.
textContent
.
replace
(
/
[\u
200b
\t\r\n]
/g
,
''
);
var
index
=
me
.
container
.
textContent
.
indexOf
(
'$$_kityminder_bookmark_$$'
);
me
.
container
.
textContent
=
me
.
container
.
textContent
.
replace
(
'$$_kityminder_bookmark_$$'
,
''
);
me
.
range
.
setStart
(
me
.
container
.
firstChild
,
index
).
collapse
(
true
).
select
();
setTextToContainer
()
},
100
);
}
//剪切
if
(
keyCode
==
keymap
.
x
){
setTimeout
(
function
(){
setTextToContainer
()
},
100
);
}
return
;
}
break
;
case
'beforekeyup'
:
switch
(
keyCode
){
case
keymap
.
Enter
:
case
keymap
.
Tab
:
...
...
@@ -4525,34 +4580,14 @@ Minder.Receiver = kity.createClass('Receiver',{
return
;
}
var
text
=
this
.
container
.
textContent
.
replace
(
/
\u
200b/g
,
''
);
if
(
this
.
textShape
.
getOpacity
()
==
0
){
this
.
textShape
.
setOpacity
(
1
);
}
this
.
textShape
.
setContent
(
text
);
this
.
setContainerStyle
();
this
.
minderNode
.
setText
(
text
);
if
(
text
.
length
==
0
){
this
.
textShape
.
setContent
(
'a'
);
this
.
textShape
.
setOpacity
(
0
);
}
this
.
km
.
updateLayout
(
this
.
minderNode
);
this
.
setBaseOffset
();
this
.
updateTextData
();
this
.
updateIndex
();
this
.
updateSelection
();
this
.
timer
=
setTimeout
(
function
(){
me
.
selection
.
setShow
()
},
500
);
setTextToContainer
();
return
true
;
}
},
updateIndex
:
function
(){
this
.
index
=
this
.
range
.
getStart
().
startOffset
;
},
...
...
dist/kityminder.all.min.js
View file @
ddd8adc1
This source diff could not be displayed because it is too large. You can
view the blob
instead.
social/social.js
View file @
ddd8adc1
...
...
@@ -10,10 +10,8 @@ $.extend( $.fn, {
},
loading
:
function
(
text
)
{
if
(
text
)
{
if
(
!
this
.
disabled
()
)
{
this
.
disabled
(
true
);
this
.
attr
(
'origin-text'
,
this
.
text
()
);
}
this
.
text
(
text
);
}
else
{
this
.
text
(
this
.
attr
(
'origin-text'
)
);
...
...
@@ -49,7 +47,7 @@ $( function () {
}
}
)();
$login_btn
=
$
(
'<button>登录</button>'
).
addClass
(
'login'
).
click
(
login
);
$login_btn
=
$
(
'<button>登录</button>'
).
addClass
(
'login'
).
click
(
login
)
.
appendTo
(
$panel
)
;
$user_btn
=
$
(
'<button><span class="text"></span></button>'
).
addClass
(
'user-file'
);
...
...
@@ -69,13 +67,16 @@ $( function () {
$user_menu
.
attachTo
(
$user_btn
);
$save_btn
=
$
(
'<button>保存</button>'
).
click
(
saveThisFile
).
addClass
(
'baidu-cloud'
);
$save_btn
=
$
(
'<button>保存</button>'
).
click
(
saveThisFile
)
.
addClass
(
'baidu-cloud'
).
appendTo
(
$panel
).
disabled
(
true
);
$share_btn
=
$
(
'<button>分享</button>'
).
click
(
shareThisFile
).
addClass
(
'share'
);
$share_btn
=
$
(
'<button>分享</button>'
).
click
(
shareThisFile
)
.
addClass
(
'share'
).
appendTo
(
$panel
).
disabled
(
true
);
var
AK
,
thisMapFilename
,
currentUser
,
share_id
=
uuid
(),
isShareLink
;
isShareLink
,
isFileSaved
=
true
,
draft
=
{};
AK
=
'wiE55BGOG8BkGnpPs6UNtPbb'
;
...
...
@@ -100,9 +101,9 @@ $( function () {
function
setCurrentUser
(
user
)
{
currentUser
=
user
;
$user_btn
.
text
(
user
.
getName
()
+
' 的脑图'
);
$user_btn
.
ap
pendTo
(
$panel
);
$save_btn
.
appendTo
(
$panel
);
$share_btn
.
appendTo
(
$panel
);
$user_btn
.
pre
pendTo
(
$panel
);
$save_btn
.
disabled
(
false
);
$share_btn
.
disabled
(
false
);
$login_btn
.
detach
();
loadRecent
();
loadAvator
();
...
...
@@ -129,11 +130,14 @@ $( function () {
by
:
'time'
,
success
:
function
(
result
)
{
if
(
result
.
list
.
length
)
{
if
(
!
isShareLink
&&
!
thisMapFilename
)
{
if
(
!
isShareLink
&&
!
thisMapFilename
&&
!
draft
.
data
)
{
loadPersonal
(
result
.
list
[
0
].
path
);
}
else
{
$user_btn
.
loading
(
false
);
}
if
(
draft
.
data
)
{
setFileSaved
(
false
);
}
addToRecentMenu
(
result
.
list
);
}
}
...
...
@@ -165,7 +169,6 @@ $( function () {
function
loadPersonal
(
path
)
{
var
sto
=
baidu
.
frontia
.
personalStorage
;
thisMapFilename
=
path
;
$user_btn
.
loading
(
'加载“'
+
getFileName
(
path
)
+
'”...'
);
sto
.
getFileUrl
(
path
,
{
success
:
function
(
url
)
{
...
...
@@ -174,8 +177,12 @@ $( function () {
url
:
url
,
dataType
:
'text'
,
success
:
function
(
result
)
{
thisMapFilename
=
path
;
window
.
km
.
importData
(
result
,
'json'
);
$user_btn
.
loading
(
false
).
text
(
getFileName
(
path
)
);
window
.
km
.
execCommand
(
'camera'
,
window
.
km
.
getRoot
()
);
$user_btn
.
loading
(
false
);
setFileSaved
(
true
);
clearDraft
();
}
}
);
}
...
...
@@ -197,17 +204,17 @@ $( function () {
var
data
=
window
.
km
.
exportData
(
'json'
);
save
(
data
,
thisMapFilename
||
getMapFileName
(),
function
(
success
,
info
)
{
if
(
success
)
{
$save_btn
.
text
(
'保存成功!'
);
setTimeout
(
function
()
{
$save_btn
.
loading
(
false
);
},
3000
);
if
(
!
thisMapFilename
)
{
thisMapFilename
=
info
.
path
;
$save_btn
.
text
(
'已保存!'
);
if
(
!
thisMapFilename
)
{
addToRecentMenu
(
[
info
]
);
$user_btn
.
text
(
getFileName
(
thisMapFilename
)
);
checkAutoSave
();
}
setFileSaved
(
true
);
}
else
{
$save_btn
.
loading
(
false
).
text
(
'保存失败!'
);
}
console
.
log
(
info
);
}
);
$save_btn
.
loading
(
'正在保存...'
);
}
...
...
@@ -217,13 +224,14 @@ $( function () {
var
options
=
{
ondup
:
thisMapFilename
?
sto
.
constant
.
ONDUP_OVERWRITE
:
sto
.
constant
.
ONDUP_NEWCOPY
,
success
:
function
(
result
)
{
callback
(
true
,
result
);
callback
(
!!
result
.
path
,
result
);
},
error
:
function
(
error
)
{
callback
(
false
,
error
);
}
};
sto
.
uploadTextFile
(
file
,
filename
,
options
);
clearDraft
();
}
function
uuid
()
{
...
...
@@ -315,19 +323,67 @@ $( function () {
var
pattern
=
/path=
(
.+
?)([
&#
]
|$
)
/
;
var
match
=
pattern
.
exec
(
window
.
location
)
||
pattern
.
exec
(
document
.
referrer
);
if
(
!
match
)
return
;
thisMapFilename
=
decodeURIComponent
(
match
[
1
]
);
var
path
=
decodeURIComponent
(
match
[
1
]
);
loadPersonal
(
path
);
}
function
setFileSaved
(
saved
)
{
$save_btn
.
disabled
(
!
currentUser
||
saved
);
if
(
saved
)
{
$user_btn
.
text
(
getFileName
(
thisMapFilename
)
);
clearDraft
();
}
else
{
$save_btn
.
text
(
'保存'
);
$user_btn
.
text
(
getFileName
(
thisMapFilename
||
draft
.
filename
||
getMapFileName
()
)
+
' *'
);
}
}
function
checkAutoSave
()
{
var
sto
=
window
.
localStorage
;
if
(
!
sto
)
return
;
draft
=
{
filename
:
thisMapFilename
||
getMapFileName
(),
data
:
window
.
km
.
exportData
(
'json'
)
};
sto
.
setItem
(
'draft_filename'
,
draft
.
filename
);
sto
.
setItem
(
'draft_data'
,
draft
.
data
);
setFileSaved
(
false
);
}
function
loadAutoSave
()
{
var
sto
=
window
.
localStorage
;
if
(
!
sto
)
return
;
draft
.
data
=
sto
.
getItem
(
'draft_data'
);
thisMapFilename
=
draft
.
filename
=
sto
.
getItem
(
'draft_filename'
);
if
(
draft
.
data
)
{
window
.
km
.
importData
(
draft
.
data
,
'json'
);
setFileSaved
(
false
);
}
}
function
clearDraft
()
{
var
sto
=
window
.
localStorage
;
if
(
!
sto
)
return
;
sto
.
removeItem
(
'draft_data'
);
sto
.
removeItem
(
'draft_filename'
);
}
loadShare
();
if
(
!
isShareLink
)
{
loadAutoSave
();
}
currentUser
=
baidu
.
frontia
.
getCurrentAccount
();
if
(
currentUser
)
{
setCurrentUser
(
currentUser
);
loadPath
();
if
(
thisMapFilename
)
{
loadPersonal
(
thisMapFilename
);
}
}
else
{
$login_btn
.
appendTo
(
$panel
);
}
window
.
km
.
on
(
'contentchange'
,
checkAutoSave
);
}
);
\ No newline at end of file
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