Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
Motion
Overview
Overview
Details
Activity
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
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dmitriy Stepanets
Motion
Commits
5a76e020
Commit
5a76e020
authored
Dec 12, 2017
by
Daniel Dahan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
finished preprocessors
parent
b0e21e3b
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
160 additions
and
136 deletions
+160
-136
Motion.xcodeproj/project.pbxproj
+0
-0
Sources/Motion+Start.swift
+4
-5
Sources/Motion.swift
+1
-1
Sources/MotionContext.swift
+2
-2
Sources/MotionPlugin.swift
+1
-1
Sources/MotionTransitionState.swift
+3
-0
Sources/Preprocessors/CascadePreprocessor.swift
+3
-3
Sources/Preprocessors/ConditionalPreprocessor.swift
+112
-0
Sources/Preprocessors/DurationPreprocessor.swift
+0
-89
Sources/Preprocessors/IgnoreSubviewModifiersPreprocessor.swift
+3
-3
Sources/Preprocessors/MatchPreprocessor.swift
+28
-20
Sources/Preprocessors/MotionCorePreprocessor.swift
+1
-1
Sources/Preprocessors/SourcePreprocessor.swift
+1
-1
Sources/Preprocessors/TransitionPreprocessor.swift
+1
-10
No files found.
Motion.xcodeproj/project.pbxproj
View file @
5a76e020
This diff is collapsed.
Click to expand it.
Sources/Motion+Start.swift
View file @
5a76e020
...
@@ -102,13 +102,12 @@ fileprivate extension Motion {
...
@@ -102,13 +102,12 @@ fileprivate extension Motion {
/// Prepares the preprocessors.
/// Prepares the preprocessors.
func
preparePreprocessors
()
{
func
preparePreprocessors
()
{
for
x
in
[
for
x
in
[
IgnoreSubviewTransitionsPreprocessor
(),
IgnoreSubviewTransitionsPreprocessor
(),
ConditionalPreprocessor
(),
TransitionPreprocessor
(),
MatchPreprocessor
(),
MatchPreprocessor
(),
SourcePreprocessor
(),
SourcePreprocessor
(),
CascadePreprocessor
(),
CascadePreprocessor
()]
as
[
MotionPreprocessor
]
{
TransitionPreprocessor
(
motion
:
self
),
DurationPreprocessor
()]
as
[
MotionPreprocessor
]
{
preprocessors
.
append
(
x
)
preprocessors
.
append
(
x
)
}
}
...
...
Sources/Motion.swift
View file @
5a76e020
...
@@ -441,7 +441,7 @@ public extension Motion {
...
@@ -441,7 +441,7 @@ public extension Motion {
}
}
let
s
=
MotionTransitionState
(
transitions
:
transitions
)
let
s
=
MotionTransitionState
(
transitions
:
transitions
)
let
v
=
context
.
transitionP
airedView
(
for
:
view
)
??
view
let
v
=
context
.
p
airedView
(
for
:
view
)
??
view
for
a
in
animators
{
for
a
in
animators
{
a
.
apply
(
state
:
s
,
to
:
v
)
a
.
apply
(
state
:
s
,
to
:
v
)
...
...
Sources/MotionContext.swift
View file @
5a76e020
...
@@ -148,7 +148,7 @@ public extension MotionContext {
...
@@ -148,7 +148,7 @@ public extension MotionContext {
source and destination view controllers.
source and destination view controllers.
- Returns: An optional UIView.
- Returns: An optional UIView.
*/
*/
func
transitionP
airedView
(
for
view
:
UIView
)
->
UIView
?
{
func
p
airedView
(
for
view
:
UIView
)
->
UIView
?
{
if
let
i
=
view
.
motionIdentifier
{
if
let
i
=
view
.
motionIdentifier
{
if
view
==
sourceView
(
for
:
i
)
{
if
view
==
sourceView
(
for
:
i
)
{
return
destinationView
(
for
:
i
)
return
destinationView
(
for
:
i
)
...
@@ -316,7 +316,7 @@ public extension MotionContext {
...
@@ -316,7 +316,7 @@ public extension MotionContext {
hide
(
view
:
view
)
hide
(
view
:
view
)
}
}
if
let
pairedView
=
transitionP
airedView
(
for
:
view
),
let
pairedSnapshot
=
viewToSnapshot
[
pairedView
]
{
if
let
pairedView
=
p
airedView
(
for
:
view
),
let
pairedSnapshot
=
viewToSnapshot
[
pairedView
]
{
let
siblingViews
=
pairedView
.
superview
!.
subviews
let
siblingViews
=
pairedView
.
superview
!.
subviews
let
nextSiblings
=
siblingViews
[
siblingViews
.
index
(
of
:
pairedView
)
!
+
1
..<
siblingViews
.
count
]
let
nextSiblings
=
siblingViews
[
siblingViews
.
index
(
of
:
pairedView
)
!
+
1
..<
siblingViews
.
count
]
...
...
Sources/MotionPlugin.swift
View file @
5a76e020
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
import
UIKit
import
UIKit
class
MotionPlugin
:
BaseMotion
Preprocessor
,
MotionAnimator
{
class
MotionPlugin
:
MotionCore
Preprocessor
,
MotionAnimator
{
/**
/**
Determines whether or not to receive `seekTo` callback on every frame.
Determines whether or not to receive `seekTo` callback on every frame.
...
...
Sources/MotionTransitionState.swift
View file @
5a76e020
...
@@ -45,6 +45,9 @@ public struct MotionTransitionState {
...
@@ -45,6 +45,9 @@ public struct MotionTransitionState {
/// The initial state that the transition will start at.
/// The initial state that the transition will start at.
internal
var
beginState
:
MotionTransitionStateWrapper
?
internal
var
beginState
:
MotionTransitionStateWrapper
?
public
var
conditionalTransitions
:
[((
MotionConditionalContext
)
->
Bool
,
[
MotionTransition
])]?
/// The start state if there is a match in the desition view controller.
/// The start state if there is a match in the desition view controller.
public
var
beginStateIfMatched
:
[
MotionTransition
]?
public
var
beginStateIfMatched
:
[
MotionTransition
]?
...
...
Sources/Preprocessors/CascadePreprocessor.swift
View file @
5a76e020
...
@@ -60,7 +60,7 @@ public enum CascadeDirection {
...
@@ -60,7 +60,7 @@ public enum CascadeDirection {
}
}
}
}
class
CascadePreprocessor
:
BaseMotion
Preprocessor
{
class
CascadePreprocessor
:
MotionCore
Preprocessor
{
/**
/**
Processes the transitionary views.
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter fromViews: An Array of UIViews.
...
@@ -91,10 +91,10 @@ class CascadePreprocessor: BaseMotionPreprocessor {
...
@@ -91,10 +91,10 @@ class CascadePreprocessor: BaseMotionPreprocessor {
let
delay
=
TimeInterval
(
i
)
*
deltaTime
+
initialDelay
let
delay
=
TimeInterval
(
i
)
*
deltaTime
+
initialDelay
func
applyDelay
(
view
:
UIView
)
{
func
applyDelay
(
view
:
UIView
)
{
if
context
.
transitionP
airedView
(
for
:
view
)
==
nil
{
if
context
.
p
airedView
(
for
:
view
)
==
nil
{
context
[
view
]?
.
delay
=
delay
context
[
view
]?
.
delay
=
delay
}
else
if
delayMatchedViews
,
let
paired
=
context
.
transitionP
airedView
(
for
:
view
)
{
}
else
if
delayMatchedViews
,
let
paired
=
context
.
p
airedView
(
for
:
view
)
{
context
[
view
]?
.
delay
=
finalDelay
context
[
view
]?
.
delay
=
finalDelay
context
[
paired
]?
.
delay
=
finalDelay
context
[
paired
]?
.
delay
=
finalDelay
}
}
...
...
Sources/Preprocessors/ConditionalPreprocessor.swift
0 → 100644
View file @
5a76e020
/*
* The MIT License (MIT)
*
* Copyright (C) 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Original Inspiration & Author
* Copyright (c) 2016 Luke Zhao <me@lkzhao.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import
UIKit
public
struct
MotionConditionalContext
{
internal
weak
var
motion
:
Motion
!
public
weak
var
view
:
UIView
!
public
private(set)
var
isAppearing
:
Bool
public
var
isPresenting
:
Bool
{
return
motion
.
isPresenting
}
public
var
isTabBarController
:
Bool
{
return
motion
.
isTabBarController
}
public
var
isNavigationController
:
Bool
{
return
motion
.
isNavigationController
}
public
var
isMatched
:
Bool
{
return
matchedView
!=
nil
}
public
var
isAncestorViewMatched
:
Bool
{
return
matchedAncestorView
!=
nil
}
public
var
matchedView
:
UIView
?
{
return
motion
.
context
.
pairedView
(
for
:
view
)
}
public
var
matchedAncestorView
:
(
UIView
,
UIView
)?
{
var
current
=
view
.
superview
while
let
ancestor
=
current
,
ancestor
!=
motion
.
context
.
container
{
if
let
pairedView
=
motion
.
context
.
pairedView
(
for
:
ancestor
)
{
return
(
ancestor
,
pairedView
)
}
current
=
ancestor
.
superview
}
return
nil
}
public
var
fromViewController
:
UIViewController
{
return
motion
.
fromViewController
!
}
public
var
toViewController
:
UIViewController
{
return
motion
.
toViewController
!
}
public
var
currentViewController
:
UIViewController
{
return
isAppearing
?
toViewController
:
fromViewController
}
public
var
otherViewController
:
UIViewController
{
return
isAppearing
?
fromViewController
:
toViewController
}
}
class
ConditionalPreprocessor
:
MotionCorePreprocessor
{
override
func
process
(
fromViews
:
[
UIView
],
toViews
:
[
UIView
])
{
process
(
views
:
fromViews
,
isAppearing
:
false
)
process
(
views
:
toViews
,
isAppearing
:
true
)
}
func
process
(
views
:
[
UIView
],
isAppearing
:
Bool
)
{
for
v
in
views
{
guard
let
conditionalTransitions
=
context
[
v
]?
.
conditionalTransitions
else
{
continue
}
for
(
condition
,
transitions
)
in
conditionalTransitions
{
if
condition
(
MotionConditionalContext
(
motion
:
motion
,
view
:
v
,
isAppearing
:
isAppearing
))
{
context
[
v
]
!.
append
(
contentsOf
:
transitions
)
}
}
}
}
}
Sources/Preprocessors/DurationPreprocessor.swift
deleted
100644 → 0
View file @
b0e21e3b
/*
* The MIT License (MIT)
*
* Copyright (C) 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Original Inspiration & Author
* Copyright (c) 2016 Luke Zhao <me@lkzhao.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import
UIKit
class
DurationPreprocessor
:
BaseMotionPreprocessor
{
/**
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter toViews: An Array of UIViews.
*/
override
func
process
(
fromViews
:
[
UIView
],
toViews
:
[
UIView
])
{
var
maxDuration
:
TimeInterval
=
0
maxDuration
=
applyOptimizedDurationIfNoDuration
(
views
:
fromViews
)
maxDuration
=
max
(
maxDuration
,
applyOptimizedDurationIfNoDuration
(
views
:
toViews
))
setDurationForInfiniteDuration
(
views
:
fromViews
,
duration
:
maxDuration
)
setDurationForInfiniteDuration
(
views
:
toViews
,
duration
:
maxDuration
)
}
/**
Retrieves the optimized duration for a given UIView.
- Parameter for view: A UIView.
- Returns: A TimeInterval.
*/
func
optimizedDuration
(
for
view
:
UIView
)
->
TimeInterval
{
let
v
=
context
[
view
]
!
return
view
.
optimizedDuration
(
position
:
context
.
container
.
convert
(
view
.
layer
.
position
,
from
:
view
.
superview
),
size
:
v
.
size
,
transform
:
v
.
transform
)
}
/**
Applies the optimized duration for an Array of UIViews.
- Parameter views: An Array of UIViews.
- Returns: A TimeInterval.
*/
func
applyOptimizedDurationIfNoDuration
(
views
:
[
UIView
])
->
TimeInterval
{
var
d
:
TimeInterval
=
0
for
v
in
views
where
nil
!=
context
[
v
]
{
if
nil
==
context
[
v
]?
.
duration
{
context
[
v
]
!.
duration
=
optimizedDuration
(
for
:
v
)
}
d
=
.
infinity
==
context
[
v
]
!.
duration
!
?
max
(
d
,
optimizedDuration
(
for
:
v
))
:
max
(
d
,
context
[
v
]
!.
duration
!
)
}
return
d
}
/**
Sets the duration if the duration of a transition is set to `.infinity`.
- Parameter views: An Array of UIViews.
- Parameter duration: A TimeInterval.
*/
func
setDurationForInfiniteDuration
(
views
:
[
UIView
],
duration
:
TimeInterval
)
{
for
v
in
views
where
.
infinity
==
context
[
v
]?
.
duration
{
context
[
v
]
!.
duration
=
duration
}
}
}
Sources/Preprocessors/IgnoreSubviewModifiersPreprocessor.swift
View file @
5a76e020
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
import
UIKit
import
UIKit
class
IgnoreSubviewTransitionsPreprocessor
:
BaseMotion
Preprocessor
{
class
IgnoreSubviewTransitionsPreprocessor
:
MotionCore
Preprocessor
{
/**
/**
Processes the transitionary views.
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter fromViews: An Array of UIViews.
...
@@ -64,12 +64,12 @@ class IgnoreSubviewTransitionsPreprocessor: BaseMotionPreprocessor {
...
@@ -64,12 +64,12 @@ class IgnoreSubviewTransitionsPreprocessor: BaseMotionPreprocessor {
}
}
}
}
extension
IgnoreSubviewTransitionsPreprocessor
{
fileprivate
extension
IgnoreSubviewTransitionsPreprocessor
{
/**
/**
Clears the transition for a given view's subviews.
Clears the transition for a given view's subviews.
- Parameter for view: A UIView.
- Parameter for view: A UIView.
*/
*/
f
ileprivate
f
unc
cleanSubviewTransitions
(
for
view
:
UIView
)
{
func
cleanSubviewTransitions
(
for
view
:
UIView
)
{
for
v
in
view
.
subviews
{
for
v
in
view
.
subviews
{
context
[
v
]
=
nil
context
[
v
]
=
nil
cleanSubviewTransitions
(
for
:
v
)
cleanSubviewTransitions
(
for
:
v
)
...
...
Sources/Preprocessors/MatchPreprocessor.swift
View file @
5a76e020
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
import
UIKit
import
UIKit
class
MatchPreprocessor
:
BaseMotion
Preprocessor
{
class
MatchPreprocessor
:
MotionCore
Preprocessor
{
/**
/**
Processes the transitionary views.
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter fromViews: An Array of UIViews.
...
@@ -36,23 +36,17 @@ class MatchPreprocessor: BaseMotionPreprocessor {
...
@@ -36,23 +36,17 @@ class MatchPreprocessor: BaseMotionPreprocessor {
*/
*/
override
func
process
(
fromViews
:
[
UIView
],
toViews
:
[
UIView
])
{
override
func
process
(
fromViews
:
[
UIView
],
toViews
:
[
UIView
])
{
for
tv
in
toViews
{
for
tv
in
toViews
{
guard
let
i
=
tv
.
motionIdentifier
,
let
fv
=
context
.
sourceView
(
for
:
i
)
else
{
continue
}
guard
let
motionIdentifier
=
tv
.
motionIdentifier
,
let
fv
=
context
.
sourceView
(
for
:
motionIdentifier
)
else
{
continue
}
var
tvState
=
context
[
tv
]
??
MotionTransitionState
()
var
tvState
=
context
[
tv
]
??
MotionTransitionState
()
var
fvState
=
context
[
fv
]
??
MotionTransitionState
()
var
fvState
=
context
[
fv
]
??
MotionTransitionState
()
if
let
v
=
tvState
.
beginStateIfMatched
{
// match is just a two-way source effect
tvState
.
append
(
.
beginWith
(
transitions
:
v
))
tvState
.
motionIdentifier
=
motionIdentifier
}
fvState
.
motionIdentifier
=
motionIdentifier
if
let
v
=
fvState
.
beginStateIfMatched
{
fvState
.
append
(
.
beginWith
(
transitions
:
v
))
}
tvState
.
motionIdentifier
=
i
tvState
.
opacity
=
0
fvState
.
motionIdentifier
=
i
fvState
.
arc
=
tvState
.
arc
fvState
.
arc
=
tvState
.
arc
fvState
.
duration
=
tvState
.
duration
fvState
.
duration
=
tvState
.
duration
fvState
.
timingFunction
=
tvState
.
timingFunction
fvState
.
timingFunction
=
tvState
.
timingFunction
...
@@ -62,22 +56,36 @@ class MatchPreprocessor: BaseMotionPreprocessor {
...
@@ -62,22 +56,36 @@ class MatchPreprocessor: BaseMotionPreprocessor {
let
forceNonFade
=
tvState
.
nonFade
||
fvState
.
nonFade
let
forceNonFade
=
tvState
.
nonFade
||
fvState
.
nonFade
let
isNonOpaque
=
!
fv
.
isOpaque
||
fv
.
alpha
<
1
||
!
tv
.
isOpaque
||
tv
.
alpha
<
1
let
isNonOpaque
=
!
fv
.
isOpaque
||
fv
.
alpha
<
1
||
!
tv
.
isOpaque
||
tv
.
alpha
<
1
if
!
forceNonFade
&&
isNonOpaque
{
if
context
.
insertToViewFirst
{
// Cross fade if from/toViews are not opaque.
fvState
.
opacity
=
0
fvState
.
opacity
=
0
if
!
forceNonFade
&&
isNonOpaque
{
tvState
.
opacity
=
0
}
else
{
}
else
{
// No cross fade in this case, fromView is always displayed during the transition.
tvState
.
opacity
=
nil
if
!
tv
.
layer
.
masksToBounds
&&
tvState
.
displayShadow
{
fvState
.
displayShadow
=
false
}
}
}
else
{
tvState
.
opacity
=
0
if
!
forceNonFade
&&
isNonOpaque
{
// cross fade if from/toViews are not opaque
fvState
.
opacity
=
0
}
else
{
// no cross fade in this case, fromView is always displayed during the transition.
fvState
.
opacity
=
nil
fvState
.
opacity
=
nil
/**
// we dont want two shadows showing up. Therefore we disable toView's shadow when fromView is able to display its shadow
We dont want two shadows showing up. Therefore we disable toView's
shadow when fromView is able to display its shadow.
*/
if
!
fv
.
layer
.
masksToBounds
&&
fvState
.
displayShadow
{
if
!
fv
.
layer
.
masksToBounds
&&
fvState
.
displayShadow
{
tvState
.
displayShadow
=
false
tvState
.
displayShadow
=
false
}
}
}
}
}
context
[
tv
]
=
tvState
context
[
tv
]
=
tvState
context
[
fv
]
=
fvState
context
[
fv
]
=
fvState
...
...
Sources/Preprocessors/
BaseMotion
Preprocessor.swift
→
Sources/Preprocessors/
MotionCore
Preprocessor.swift
View file @
5a76e020
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
import
UIKit
import
UIKit
class
BaseMotion
Preprocessor
:
MotionPreprocessor
{
class
MotionCore
Preprocessor
:
MotionPreprocessor
{
weak
public
var
motion
:
Motion
!
weak
public
var
motion
:
Motion
!
/// A reference to the MotionContext.
/// A reference to the MotionContext.
...
...
Sources/Preprocessors/SourcePreprocessor.swift
View file @
5a76e020
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
import
UIKit
import
UIKit
class
SourcePreprocessor
:
BaseMotion
Preprocessor
{
class
SourcePreprocessor
:
MotionCore
Preprocessor
{
/**
/**
Processes the transitionary views.
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter fromViews: An Array of UIViews.
...
...
Sources/Preprocessors/TransitionPreprocessor.swift
View file @
5a76e020
...
@@ -172,16 +172,7 @@ public enum MotionTransitionType {
...
@@ -172,16 +172,7 @@ public enum MotionTransitionType {
}
}
}
}
class
TransitionPreprocessor
:
BaseMotionPreprocessor
{
class
TransitionPreprocessor
:
MotionCorePreprocessor
{
/**
An initializer that accepts a given Motion instance.
- Parameter motion: A Motion.
*/
init
(
motion
:
Motion
)
{
super
.
init
()
self
.
motion
=
motion
}
/**
/**
Processes the transitionary views.
Processes the transitionary views.
- Parameter fromViews: An Array of UIViews.
- Parameter fromViews: An Array of UIViews.
...
...
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