Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
subugoe
emo
TIDO
Commits
ce38ae6a
Commit
ce38ae6a
authored
Aug 02, 2021
by
dindigala
Browse files
feat: display nested motifs and refactors of current logic
parent
9ad7c3f3
Pipeline
#217539
passed with stages
in 2 minutes and 56 seconds
Changes
7
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
package-lock.json
View file @
ce38ae6a
This diff is collapsed.
Click to expand it.
src/App.vue
View file @
ce38ae6a
...
...
@@ -40,6 +40,7 @@
</
template
>
<
script
>
import
Annotation
from
'
@/mixins/annotation
'
;
import
{
colors
}
from
'
quasar
'
;
import
treestore
from
'
@/stores/treestore.js
'
;
import
Header
from
'
@/components/header.vue
'
;
...
...
@@ -50,7 +51,10 @@ export default {
components
:
{
Header
,
},
mixins
:
[
Panels
],
mixins
:
[
Annotation
,
Panels
,
],
data
()
{
return
{
annotations
:
[],
...
...
@@ -131,7 +135,9 @@ export default {
*/
async
request
(
url
,
responsetype
=
'
json
'
)
{
const
response
=
await
fetch
(
url
);
const
data
=
await
(
responsetype
===
'
text
'
?
response
.
text
()
:
response
.
json
());
const
data
=
await
(
responsetype
===
'
text
'
?
response
.
text
()
:
response
.
json
());
return
data
;
},
...
...
@@ -153,10 +159,12 @@ export default {
return
;
}
const
current
=
await
this
.
request
(
annotations
.
annotationCollection
.
first
);
const
current
=
await
this
.
request
(
annotations
.
annotationCollection
.
first
,
);
if
(
current
.
annotationPage
.
items
.
length
)
{
this
.
annotations
=
current
.
annotationPage
.
items
;
this
.
annotations
=
current
.
annotationPage
.
items
.
map
((
x
)
=>
({
...
x
,
targetId
:
this
.
stripTargetId
(
x
,
true
)
}))
;
}
else
{
this
.
annotations
=
[];
}
...
...
@@ -182,17 +190,15 @@ export default {
this
.
collection
=
data
;
this
.
collectiontitle
=
this
.
getLabel
(
data
);
this
.
tree
.
push
(
{
children
:
[],
handler
:
(
node
)
=>
{
this
.
$root
.
$emit
(
'
update-tree-knots
'
,
node
.
label
);
},
label
:
this
.
collectiontitle
,
'
label-key
'
:
this
.
collectiontitle
,
selectable
:
false
,
this
.
tree
.
push
({
children
:
[],
handler
:
(
node
)
=>
{
this
.
$root
.
$emit
(
'
update-tree-knots
'
,
node
.
label
);
},
);
label
:
this
.
collectiontitle
,
'
label-key
'
:
this
.
collectiontitle
,
selectable
:
false
,
});
if
(
Array
.
isArray
(
data
.
sequence
))
{
const
promises
=
[];
...
...
@@ -297,20 +303,20 @@ export default {
sequence
.
forEach
((
item
)
=>
{
const
itemLabel
=
this
.
getItemLabel
(
item
.
id
);
urls
.
push
(
{
label
:
item
.
id
,
'
label
-key
'
:
`
${
itemLabel
}
`
,
labelSheet
:
true
,
handler
:
(
node
)
=>
{
if
(
this
.
itemurl
===
node
.
label
)
{
return
;
}
this
.
loaded
=
false
;
this
.
$router
.
push
({
query
:
{
...
this
.
$route
.
query
,
itemurl
:
node
.
label
}
});
}
,
urls
.
push
(
{
label
:
item
.
id
,
'
label
-key
'
:
`
${
itemLabel
}
`
,
label
Sheet
:
true
,
handler
:
(
node
)
=>
{
if
(
this
.
itemurl
===
node
.
label
)
{
return
;
}
this
.
loaded
=
false
;
this
.
$router
.
push
({
query
:
{
...
this
.
$route
.
query
,
itemurl
:
node
.
label
}
,
}
);
},
);
}
);
});
return
urls
;
},
...
...
@@ -324,9 +330,13 @@ export default {
*/
getLabel
(
data
)
{
if
(
Object
.
keys
(
this
.
collection
).
length
)
{
return
data
.
title
&&
data
.
title
[
0
].
title
?
data
.
title
[
0
].
title
:
data
.
label
;
return
data
.
title
&&
data
.
title
[
0
].
title
?
data
.
title
[
0
].
title
:
data
.
label
;
}
return
data
.
label
?
data
.
label
:
'
Manifest <small>(No label available)</small>
'
;
return
data
.
label
?
data
.
label
:
'
Manifest <small>(No label available)</small>
'
;
},
/**
* get all the data provided on 'manifest level'
...
...
@@ -339,7 +349,11 @@ export default {
// if the entrypoint points to a single manifest, initialize the tree
if
(
this
.
isCollection
===
false
)
{
this
.
tree
.
push
({
label
:
''
,
'
label-key
'
:
this
.
config
.
labels
.
manifest
,
children
:
[]
});
this
.
tree
.
push
({
label
:
''
,
'
label-key
'
:
this
.
config
.
labels
.
manifest
,
children
:
[],
});
}
if
(
!
Array
.
isArray
(
data
.
sequence
))
{
...
...
@@ -351,17 +365,15 @@ export default {
}
this
.
manifests
.
push
(
data
);
this
.
tree
[
0
].
children
.
push
(
{
children
:
this
.
getItemUrls
(
data
.
sequence
,
data
.
label
),
label
:
data
.
label
,
'
label-key
'
:
data
.
label
,
handler
:
(
node
)
=>
{
this
.
$root
.
$emit
(
'
update-tree-knots
'
,
node
.
label
);
},
selectable
:
false
,
this
.
tree
[
0
].
children
.
push
({
children
:
this
.
getItemUrls
(
data
.
sequence
,
data
.
label
),
label
:
data
.
label
,
'
label-key
'
:
data
.
label
,
handler
:
(
node
)
=>
{
this
.
$root
.
$emit
(
'
update-tree-knots
'
,
node
.
label
);
},
);
selectable
:
false
,
});
},
/**
* caller: *getItemUrls()*
...
...
src/components/annotations/annotations.vue
View file @
ce38ae6a
...
...
@@ -13,27 +13,26 @@
:key=
"annotationTab.key"
:label=
"$t(annotationTab.collectionTitle)"
:name=
"annotationTab.key"
@
click=
"activeTab(annotationTab.key,annotationTab.type)"
@
click=
"activeTab(annotationTab.key,
annotationTab.type)"
/>
</q-tabs>
<AnnotationToggles
/>
<Loading
v-if=
"!isloading || isProcessing"
/>
<Loading
v-if=
"!isloading || isProcessing"
/>
<AnnotationList
v-else-if=
"
current
Annotations.length && isloading && !isProcessing"
v-else-if=
"
filtered
Annotations.length && isloading && !isProcessing"
class=
"custom-font"
:configured-annotations=
"currentAnnotations"
:active-annotation=
"activeAnnotation"
:configured-annotations=
"filteredAnnotations"
:content-ids=
"contentIds"
:get-icon=
"getIcon"
:status-check=
"statusCheck"
:toggle=
"toggle"
/>
<div
v-else-if=
"!
current
Annotations.length && isloading && !isProcessing"
v-else-if=
"!
filtered
Annotations.length && isloading && !isProcessing"
class=
"q-pa-sm"
>
<Notification
...
...
@@ -87,114 +86,163 @@ export default {
type
:
Object
,
default
:
()
=>
{},
},
panels
:
{
type
:
Array
,
default
:
()
=>
[],
},
},
data
()
{
return
{
configuredAnnotations
:
[],
activeAnnotation
:
{},
contentIds
:
{},
currentTab
:
''
,
ids
:
[],
isProcessing
:
false
,
messages
:
{
none
:
'
noAnnotationMessage
'
,
},
selectedAll
:
false
,
selectedNone
:
true
,
};
},
computed
:
{
annotationTabs
()
{
return
Object
.
entries
(
this
.
tabConfig
)
.
map
(([
key
,
type
])
=>
({
key
,
collectionTitle
:
key
,
type
,
}))
.
filter
((
el
)
=>
this
.
annotations
.
find
((
x
)
=>
el
.
type
.
includes
(
x
.
body
[
'
x-content-type
'
])));
},
annotationTabConfig
()
{
return
this
.
config
?.
annotations
?.
tabs
||
{};
},
configuredTypes
()
{
return
this
.
config
.
annotations
.
types
.
map
((
type
)
=>
type
.
contenttype
);
},
currentAnnotations
()
{
const
contentType
=
this
.
annotationTabs
.
find
((
collection
)
=>
collection
.
key
===
this
.
currentTab
);
const
contentType
=
this
.
annotationTabs
.
find
(
(
collection
)
=>
collection
.
key
===
this
.
currentTab
,
);
if
(
!
contentType
)
{
return
[];
}
const
annotationType
=
this
.
configuredAnnotations
.
filter
((
annotationCollection
)
=>
contentType
.
type
.
includes
(
annotationCollection
.
body
[
'
x-content-type
'
]));
const
annotationType
=
this
.
configuredAnnotations
.
filter
(
(
annotationCollection
)
=>
contentType
.
type
.
includes
(
annotationCollection
.
body
[
'
x-content-type
'
]),
);
return
this
.
sortAnnotation
(
annotationType
);
},
annotationTabConfig
()
{
return
this
.
config
?.
annotations
?.
tabs
||
{};
},
annotationTabs
()
{
const
contentTypes
=
{};
const
annotationTab
=
{};
Object
.
entries
(
this
.
annotationTabConfig
).
forEach
(([
key
,
types
])
=>
{
types
.
forEach
((
type
)
=>
{
contentTypes
[
type
]
=
key
;
});
filteredAnnotations
()
{
if
(
!
this
.
currentTab
)
{
return
[];
}
const
output
=
this
.
annotations
.
filter
(
(
x
)
=>
this
.
tabConfig
[
this
.
currentTab
].
includes
(
x
.
body
[
'
x-content-type
'
])
&&
this
.
contentIds
[
x
.
targetId
],
);
annotationTab
[
key
]
=
{
key
,
collectionTitle
:
key
,
type
:
[],
};
});
this
.
configuredAnnotations
.
forEach
((
curr
)
=>
{
const
contentType
=
curr
.
body
.[
'
x-content-type
'
];
if
(
contentTypes
[
contentType
])
{
annotationTab
[
contentTypes
[
contentType
]].
type
.
push
(
contentType
);
return
this
.
sortAnnotation
(
output
);
},
selectedAll
()
{
return
(
Object
.
keys
(
this
.
activeAnnotation
).
length
===
this
.
filteredAnnotations
.
length
);
},
selectedNone
()
{
return
!
Object
.
keys
(
this
.
activeAnnotation
).
length
;
},
tabConfig
()
{
return
this
.
config
.
annotations
.
tabs
;
},
},
watch
:
{
filteredAnnotations
:
{
handler
()
{
this
.
activeAnnotation
=
{};
},
immediate
:
true
,
},
panels
:
{
handler
(
curr
,
prev
)
{
const
currentState
=
curr
.
find
(
(
x
)
=>
x
.
panel_label
===
'
Annotations
'
&&
x
.
show
,
);
const
previousState
=
prev
.
find
(
(
x
)
=>
x
.
panel_label
===
'
Annotations
'
&&
x
.
show
,
);
if
(
currentState
!==
previousState
)
{
this
.
activeAnnotation
=
{};
if
(
currentState
)
{
[
...
document
.
querySelectorAll
(
'
[data-annotation-level]
'
),
].
forEach
((
el
)
=>
el
.
setAttribute
(
'
data-annotation-level
'
,
0
));
}
else
{
[
...
document
.
querySelectorAll
(
'
[data-annotation-level]
'
),
].
forEach
((
el
)
=>
el
.
setAttribute
(
'
data-annotation-level
'
,
-
1
));
[
...
document
.
querySelectorAll
(
'
[data-annotation-icon]
'
),
].
forEach
((
el
)
=>
el
.
remove
());
}
}
});
return
Object
.
values
(
annotationTab
).
filter
((
x
)
=>
x
.
type
.
length
);
},
},
},
mounted
()
{
this
.
$root
.
$on
(
'
update-annotations
'
,
(
content
,
isProcessing
)
=>
{
this
.
$root
.
$on
(
'
update-annotations
'
,
this
.
onContentUpdate
);
this
.
$root
.
$on
(
'
update-annotation-loading
'
,
(
isProcessing
)
=>
{
this
.
isProcessing
=
!!
isProcessing
;
const
parser
=
new
DOMParser
();
const
doc
=
parser
.
parseFromString
(
content
,
'
text/html
'
);
this
.
ids
=
[...
doc
.
body
.
querySelectorAll
(
'
[id]
'
)].
map
((
el
)
=>
el
.
getAttribute
(
'
id
'
));
this
.
configuredAnnotations
=
[];
const
interval
=
setInterval
(()
=>
{
if
(
this
.
isloading
)
{
this
.
configuredAnnotations
=
this
.
filterAnnotationTypes
();
const
firstTab
=
this
.
annotationTabs
.
find
((
x
)
=>
x
.
type
.
length
)?.
key
||
''
;
this
.
highlightActiveTabContent
(
this
.
annotationTabConfig
[
firstTab
]
||
[]);
this
.
currentTab
=
firstTab
;
clearInterval
(
interval
);
}
},
50
);
});
this
.
$root
.
$on
(
'
panels-position
'
,
(
newPanels
)
=>
{
const
annotationPanelHidden
=
newPanels
.
find
((
x
)
=>
x
.
panel_label
===
'
Annotations
'
&&
!
x
.
show
);
this
.
currentAnnotations
.
forEach
((
annotation
)
=>
{
const
id
=
this
.
stripAnnotationId
(
annotation
.
target
.
id
);
const
textElement
=
document
.
getElementById
(
id
);
if
(
annotationPanelHidden
)
{
textElement
.
classList
.
remove
(
'
annotation
'
);
}
else
{
textElement
.
classList
.
add
(
'
annotation
'
);
textElement
.
classList
.
add
(
'
annotation-disabled
'
);
}
});
});
},
methods
:
{
activeTab
(
key
,
types
)
{
activeTab
(
key
)
{
if
(
this
.
currentTab
===
key
)
{
return
;
}
this
.
filteredAnnotations
.
forEach
((
x
)
=>
this
.
removeAnnotation
(
x
,
-
1
));
this
.
currentTab
=
key
;
this
.
selectedAll
=
false
;
this
.
selectedNone
=
true
;
this
.
highlightActiveTabContent
(
types
);
this
.
highlightActiveContent
(
this
.
filteredAnnotations
);
},
addAnnotation
(
annotation
)
{
const
updated
=
{
...
this
.
activeAnnotation
};
let
selector
=
this
.
stripTargetId
(
annotation
,
false
);
if
(
selector
.
startsWith
(
'
.
'
))
{
selector
=
selector
.
replace
(
/
\.
/g
,
''
);
}
this
.
updateHighlightState
(
selector
,
'
INC
'
);
this
.
addIcon
(
document
.
getElementById
(
selector
)
||
document
.
querySelector
(
`.
${
selector
}
`
),
annotation
);
updated
[
annotation
.
targetId
]
=
true
;
this
.
activeAnnotation
=
updated
;
},
addIcon
(
element
,
annotation
)
{
const
contentType
=
annotation
.
body
[
'
x-content-type
'
];
try
{
const
svg
=
this
.
createSVG
(
this
.
getIconName
(
contentType
));
svg
.
setAttribute
(
'
data-annotation-icon
'
,
this
.
stripTargetId
(
annotation
),
);
element
.
prepend
(
svg
);
}
catch
(
err
)
{
// TODO : Handle Here
}
},
createSVG
(
name
)
{
const
[
path
,
viewBox
]
=
Icons
[
name
].
split
(
'
|
'
);
const
svg
=
document
.
createElementNS
(
'
http://www.w3.org/2000/svg
'
,
'
svg
'
);
...
...
@@ -205,7 +253,10 @@ export default {
svg
.
setAttribute
(
'
role
'
,
'
presentation
'
);
svg
.
setAttribute
(
'
viewBox
'
,
viewBox
);
const
newPath
=
document
.
createElementNS
(
'
http://www.w3.org/2000/svg
'
,
'
path
'
);
const
newPath
=
document
.
createElementNS
(
'
http://www.w3.org/2000/svg
'
,
'
path
'
,
);
newPath
.
setAttribute
(
'
d
'
,
path
);
svg
.
appendChild
(
newPath
);
...
...
@@ -213,25 +264,14 @@ export default {
return
svg
;
},
/**
* filter for configured annotation types (index.html)
*
* @return array of annotations excluding unconfigured ones
*/
filterAnnotationTypes
()
{
return
this
.
annotations
.
filter
((
annotation
)
=>
{
this
.
$set
(
annotation
,
'
status
'
,
this
.
config
.
annotations
.
show
);
annotation
.
strippedId
=
this
.
stripAnnotationId
(
annotation
.
target
.
id
);
const
annotationIds
=
this
.
ids
.
includes
(
annotation
.
strippedId
);
if
(
this
.
configuredTypes
.
find
((
type
)
=>
type
===
annotation
.
body
[
'
x-content-type
'
])
&&
annotationIds
)
{
this
.
setText
(
annotation
);
return
true
;
}
return
false
;
});
onContentUpdate
(
ids
)
{
try
{
this
.
currentTab
=
this
.
annotationTabs
[
0
].
key
;
this
.
contentIds
=
ids
;
this
.
highlightActiveContent
(
this
.
filteredAnnotations
);
}
catch
(
err
)
{
setTimeout
(()
=>
this
.
onContentUpdate
(
ids
),
100
);
}
},
getIcon
(
contentType
)
{
...
...
@@ -239,62 +279,56 @@ export default {
},
getIconName
(
contentType
)
{
return
this
.
config
.
annotations
.
types
.
filter
((
annotation
)
=>
annotation
.
contenttype
===
contentType
)[
0
].
icon
;
return
this
.
config
.
annotations
.
types
.
filter
(
(
annotation
)
=>
annotation
.
contenttype
===
contentType
,
)[
0
].
icon
;
},
onHighlightAll
()
{
this
.
current
Annotations
.
forEach
(
(
annotation
)
=>
this
.
updateToggleState
(
annotation
,
'
remove
'
,
'
add
'
));
this
.
selectedAll
=
true
;
this
.
selectedNone
=
false
;
this
.
filtered
Annotations
.
forEach
(
(
annotation
)
=>
!
this
.
activeAnnotation
[
annotation
.
targetId
]
&&
this
.
addAnnotation
(
annotation
),
)
;
},
onHighlightNone
()
{
this
.
current
Annotations
.
forEach
(
(
annotation
)
=>
this
.
updateToggleState
(
annotation
,
'
add
'
,
'
remove
'
));
this
.
selectedAll
=
false
;
this
.
selectedNone
=
true
;
this
.
filtered
Annotations
.
forEach
(
(
annotation
)
=>
this
.
activeAnnotation
[
annotation
.
targetId
]
&&
this
.
removeAnnotation
(
annotation
),
)
;
},
setText
(
annotation
)
{
const
contentType
=
annotation
.
body
[
'
x-content-type
'
];
const
id
=
this
.
stripAnnotationId
(
annotation
.
target
.
id
);
const
textElement
=
document
.
getElementById
(
id
);
let
svg
=
null
;
try
{
svg
=
this
.
createSVG
(
this
.
getIconName
(
contentType
));
removeIcon
(
annotation
)
{
const
stripeId
=
this
.
stripTargetId
(
annotation
);
const
el
=
document
.
querySelector
(
`svg[data-annotation-icon='
${
stripeId
}
']`
);
svg
.
setAttribute
(
'
id
'
,
`annotation-icon-
${
id
}
`
);
}
catch
(
err
)
{
svg
=
null
;
}
if
(
svg
)
{
textElement
.
prepend
(
svg
);
if
(
el
)
{
el
.
remove
();
}
},
statusCheck
(
)
{
const
num
=
this
.
current
Annotation
s
.
length
;
const
active
=
this
.
currentAnnotations
.
filter
((
annotation
)
=>
annotation
.
status
===
true
).
length
;
removeAnnotation
(
annotation
,
level
)
{
const
updated
=
{
...
this
.
active
Annotation
}
;
let
selector
=
this
.
stripTargetId
(
annotation
,
false
)
;
if
(
num
===
active
)
{
this
.
selectedAll
=
false
;
this
.
selectedNone
=
true
;
}
else
if
(
active
===
0
)
{
this
.
selectedAll
=
true
;
this
.
selectedNone
=
false
;
}
else
{
this
.
selectedAll
=
false
;
this
.
selectedNone
=
false
;
if
(
selector
.
startsWith
(
'
.
'
))
{
selector
=
selector
.
replace
(
/
\.
/g
,
''
);
}
this
.
updateHighlightState
(
selector
,
'
DEC
'
,
level
);
this
.
removeIcon
(
annotation
);
delete
updated
[
annotation
.
targetId
];
this
.
activeAnnotation
=
updated
;
},
toggle
(
annotation
)
{
annotation
.
status
=
!
annotation
.
status
;
this
.
updateToggleState
(
annotation
,
'
toggle
'
,
'
toggle
'
);
const
exists
=
!!
this
.
activeAnnotation
[
annotation
.
targetId
];
if
(
exists
)
{
this
.
removeAnnotation
(
annotation
);
}
else
{
this
.
addAnnotation
(
annotation
);
}
},
},
};
...
...
@@ -326,9 +360,49 @@ export default {
padding-bottom
:
inherit
;
}
.annotation-disabled-overlap
{
border-bottom
:
1px
;
border-bottom-style
:
dotted
!
important
;
padding-bottom
:
inherit
;
}
.annotation-disabled
>
svg
{
display
:
none
;
}
*[
data-annotation-level
=
'0'
]
{
background-color
:
$grey-3
;
}
*[
data-annotation-level
=
'1'
],
*[
data-annotation-level
=
'1'
]
*
{
background-color
:
$blue-1
;
border-bottom
:
1px
solid
#000
;
}
*[
data-annotation-level
=
'2'
],
*[
data-annotation-level
=
'2'
]
*
{
background-color
:
$blue-2
;