Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
computersignale
stereometrie
Commits
bab3529f
Commit
bab3529f
authored
Mar 03, 2022
by
stahl
Browse files
src
parent
c1bfa9a7
Changes
9
Hide whitespace changes
Inline
Side-by-side
public/index.html
0 → 100644
View file @
bab3529f
<!DOCTYPE html>
<html
lang=
""
>
<head>
<meta
charset=
"utf-8"
>
<meta
http-equiv=
"X-UA-Compatible"
content=
"IE=edge"
>
<meta
name=
"viewport"
content=
"width=device-width,initial-scale=1.0"
>
<link
rel=
"icon"
href=
"data:,"
>
<link
href=
"https://fonts.googleapis.com/css?family=Lato|Vollkorn:400,600,700"
rel=
"stylesheet"
>
<title><
%=
htmlWebpackPlugin.options.title
%
></title>
</head>
<body>
<noscript>
<strong>
We're sorry but
<
%=
htmlWebpackPlugin.options.title
%
>
doesn't work properly without JavaScript enabled. Please enable it to continue.
</strong>
</noscript>
<div
id=
"app"
></div>
<!-- built files will be auto injected -->
</body>
</html>
src/.dockerignore
0 → 100644
View file @
bab3529f
**/node_modules
**/dist
src/App.vue
0 → 100644
View file @
bab3529f
<
template
>
<StereometrieEditor
/>
<br
/>
<br
/>
<br
/>
<footer>
<img
alt=
"ZHdK logo"
width=
"200"
src=
"./assets/logo.png"
>
</footer>
</
template
>
<
script
>
import
StereometrieEditor
from
'
./components/StereometrieEditor.vue
'
export
default
{
name
:
'
App
'
,
components
:
{
StereometrieEditor
}
}
</
script
>
<
style
>
html
{
font-family
:
"Lato"
,
sans-serif
;
}
body
{
color
:
#aaa
;
background
:
#444
;
}
footer
{
bottom
:
0px
;
width
:
88vw
;
color
:
#111
;
background
:
#fff
;
padding
:
30px
;
}
</
style
>
src/Dockerfile
0 → 100644
View file @
bab3529f
FROM
node:latest
as
build-stage
WORKDIR
/app
COPY
package*.json ./
RUN
npm
install
COPY
./ .
RUN
npm run build
FROM
nginx
as
production-stage
RUN
mkdir
/app
COPY
--from=build-stage /app/dist /app
COPY
nginx.conf /etc/nginx/nginx.conf
src/assets/logo.png
0 → 100644
View file @
bab3529f
28.3 KB
src/components/SelectThumbDialogue.vue
0 → 100644
View file @
bab3529f
<
template
>
<div
class=
"dialogue"
>
<div
v-if=
selection.length
class=
"files"
>
<div
v-for=
"(file, index) in selection"
:key=
index
@
click=
"onClickFile(file)"
:class=
"fileStyle"
class=
"file"
>
<div>
{{
file
}}
</div>
<img
:src=
"imgUrl(file)"
class=
"image"
/>
</div>
</div>
<div
v-else
>
Keine Daten vorhanden.
</div>
<button
@
click=
"cancel"
>
Abbrechen
</button>
</div>
</
template
>
<
script
>
import
{
computed
,
//ref,
}
from
"
vue
"
;
export
default
{
emits
:
[
'
submit
'
,
'
cancel
'
],
props
:
{
files
:
{
type
:
Array
,
default
:
null
,
},
originalFiles
:
{
type
:
Array
,
default
:
null
,
},
placeholder
:
{
type
:
String
,
default
:
null
,
},
index
:
{
type
:
Number
,
default
:
null
,
},
},
setup
:
function
(
props
)
{
const
selection
=
computed
(()
=>
{
const
placeholder
=
props
.
placeholder
;
const
index
=
props
.
index
;
const
files
=
props
.
files
;
const
originalFiles
=
props
.
originalFiles
;
// find thumbs range at index
let
backwardSlice
=
files
.
slice
(
0
,
index
+
1
);
backwardSlice
=
backwardSlice
.
filter
(
item
=>
item
!==
placeholder
);
let
forwardSlice
=
files
.
slice
(
index
+
1
);
forwardSlice
=
forwardSlice
.
filter
(
item
=>
item
!==
placeholder
);
const
a
=
originalFiles
.
indexOf
(
backwardSlice
.
at
(
-
1
));
const
b
=
originalFiles
.
indexOf
(
forwardSlice
.
at
(
0
));
return
originalFiles
.
slice
(
a
+
1
,
b
);
});
const
imgUrl
=
(
item
)
=>
{
return
`
${
document
.
settings
.
MEDIA_URL
}
/
${
item
}
`
;
};
const
fileStyle
=
computed
(()
=>
{
return
{
'
grid-template-rows
'
:
`repeat(
${
props
.
files
.
length
}
, auto)`
}
});
return
{
selection
,
imgUrl
,
fileStyle
,
};
},
methods
:
{
onClickFile
(
file
)
{
this
.
$emit
(
"
submit
"
,
file
);
},
cancel
()
{
this
.
$emit
(
"
cancel
"
);
},
}
};
</
script
>
<
style
scoped
>
.dialogue
{
position
:
absolute
;
padding
:
20px
;
right
:
20px
;
background
:
rgba
(
1.0
,
1.0
,
1.0
,
1.0
);
}
.files
{
margin
:
10px
;
padding
:
10px
;
}
.file
{
padding
:
10px
;
cursor
:
pointer
;
display
:
grid
;
grid-template-columns
:
repeat
(
2
,
auto
);
grid-gap
:
20px
;
border
:
1px
solid
grey
;
align-items
:
center
;
}
.file
:hover
{
border
:
1px
solid
yellow
;
}
.image
{
width
:
200px
;
}
</
style
>
src/components/StereometrieEditor.vue
0 → 100644
View file @
bab3529f
<
template
>
<header>
<h1>
Stereometrie
</h1>
<div
class=
"actions top"
>
<div>
Index:
<text-reader
once
@
load=
"load"
></text-reader>
</div>
<div>
Öffnen:
<text-reader
@
load=
"importData"
></text-reader>
</div>
<button
@
click=
"save"
>
Speichern ...
</button>
<div>
Seite
<select
v-model=
"page"
>
<option
v-for=
"(page, index) in Array.from(Array(numPages).keys())"
:key=
index
:value=
page
>
{{
page
+
1
}}
</option>
</select>
</div>
<div>
Zoom
<select
v-model=
"zoomLevel"
>
<option
v-for=
"(level, index) in Array.from(Array(20).keys())"
:key=
index
:value=
level+1
>
{{
level
+
1
}}
</option>
</select>
</div>
</div>
<div
class=
"actions options"
>
<label
style=
"color: magenta;"
>
Bilder-Lücken anzeigen
<input
type=
checkbox
v-model=
showGaps
/>
</label>
<label
style=
"color: red;"
>
Fehler anzeigen
<input
type=
checkbox
v-model=
showLeftRightMarker
/>
</label>
<label>
Vorschau
<input
type=
checkbox
v-model=
showPreview
/>
</label>
</div>
<div
class=
"actions editor"
>
<button
@
click=
"replaceWithPlaceholder()"
>
Mit Platzhalter ersetzen [r]
</button>
<button
@
click=
"addPlaceholder()"
>
Platzhalter einfügen [p]
</button>
<button
@
click=
"toggleAddThumbsDialogue()"
>
Bild einfügen [a]
</button>
<button
@
click=
"removeThumb()"
>
Löschen [delete/backspace]
</button>
</div>
<div
class=
"actions info"
>
<div>
{{
x
+
1
}}
/
{{
y
+
1
}}
</div>
<div>
{{
filenameIndexAtIndex
(
index
)
}}
</div>
<div>
{{
filenameDateAtIndex
(
index
)
}}
</div>
<div>
{{
filenameTimeAtIndex
(
index
)
}}
</div>
<div>
{{
files
[
index
]
}}
</div>
</div>
</header>
<div
class=
"thumb-preview"
>
<img
v-lazy=
"imgUrl(files[index])"
/>
</div>
<div
class=
"thumbs"
>
<div
v-if=
"files && files.length && originalFiles && originalFiles.length"
>
<select-thumb-dialogue
v-if=
"isAddThumbsDialogueActive"
@
cancel=
"cancelSelect"
@
submit=
"insertFile"
:files=
"files"
:originalFiles=
"originalFiles"
:index=
"index"
:placeholder=
"placeholder"
></select-thumb-dialogue>
<div
:style=
"pageStyle"
>
<div
v-for=
"(item, index) in filesForPage"
:key=
index
>
<img
v-lazy=
"imgUrl(item)"
@
click=
"thumbClicked(index)"
:title=
"item"
:style=
"[thumbStyle(item, index), checkLeftRight(item, index), cursorStyle(index)]"
/>
</div>
</div>
</div>
<div
v-else
>
Keine Daten vorhanden.
</div>
</div>
</
template
>
<
script
>
import
{
useKeypress
}
from
'
vue3-keypress
'
;
import
{
computed
,
ref
,
inject
,
}
from
"
vue
"
;
import
TextReader
from
"
./TextReader
"
;
import
SelectThumbDialogue
from
"
./SelectThumbDialogue
"
;
export
default
{
name
:
'
StereometrieEditor
'
,
components
:
{
TextReader
,
SelectThumbDialogue
,
},
setup
:
function
()
{
const
Lazyload
=
inject
(
'
Lazyload
'
);
const
keyboardIsActive
=
ref
(
true
);
Lazyload
.
$on
(
'
error
'
,
function
({
el
,
src
})
{
//console.table(Lazyload.performance())
console
.
log
(
el
,
src
);
});
const
placeholder
=
'
placeholder.jpg
'
;
const
originalFiles
=
ref
([]);
const
files
=
ref
([]);
/*
const files = ref(
Array(
'2019/0207906-20191130-12-31-54.58-L.jpg',
placeholder,
'2019/0207908-20191130-13-01-46.25-R.jpg',
'2019/0207909-20191130-13-31-52.48-L.jpg',
'2019/0207910-20191130-13-31-52.73-R.jpg',
'2019/0207911-20191130-14-02-18.33-L.jpg',
'2019/0207912-20191130-14-02-18.59-R.jpg',
'2019/0207913-20191130-14-31-34.88-L.jpg',
'2019/0207914-20191130-14-31-35.15-R.jpg',
'2019/0207915-20191130-15-01-50.00-L.jpg',
'2019/0207916-20191130-15-01-50.27-R.jpg',
'2019/0207917-20191130-15-31-43.16-L.jpg',
'2019/0207918-20191130-15-31-43.44-R.jpg',
'2019/0207919-20191130-16-01-31.35-L.jpg',
'2019/0207920-20191130-16-01-31.64-R.jpg',
'2019/0207921-20191130-16-31-37.58-L.jpg',
'2019/0207922-20191130-16-31-37.87-R.jpg',
'2019/0207923-20191130-17-01-45.27-L.jpg',
'2019/0207924-20191130-17-01-45.57-R.jpg',
'2019/0207925-20191130-17-31-53.68-L.jpg',
'2019/0207926-20191130-18-01-33.63-L.jpg',
'2019/0207927-20191130-18-01-33.94-R.jpg',
'2019/0207928-20191130-18-31-31.46-L.jpg',
'2019/0207929-20191130-18-31-31.78-R.jpg',
'2019/0207930-20191130-19-01-38.28-L.jpg',
'2019/0207931-20191130-19-01-38.60-R.jpg',
)
);
*/
const
filesToInsert
=
ref
(
null
);
const
isAddThumbsDialogueActive
=
ref
(
false
);
const
page
=
ref
(
0
);
const
zoomLevel
=
ref
(
8
);
const
numPages
=
88
;
const
rows
=
48
;
const
cols
=
32
;
const
pageSize
=
rows
*
cols
;
const
x
=
ref
(
0
);
// cursor x
const
y
=
ref
(
0
);
// cursor y
const
showGaps
=
ref
(
true
);
const
showLeftRightMarker
=
ref
(
true
);
const
showPreview
=
ref
(
false
);
const
pageIndex
=
computed
(()
=>
{
return
x
.
value
+
y
.
value
*
cols
;
});
const
index
=
computed
(()
=>
{
return
page
.
value
*
pageSize
+
pageIndex
.
value
;
});
const
removeThumb
=
()
=>
{
files
.
value
.
splice
(
index
.
value
,
1
);
};
const
replaceWithPlaceholder
=
()
=>
{
files
.
value
[
index
.
value
]
=
placeholder
;
};
const
addPlaceholder
=
()
=>
{
files
.
value
[
index
.
value
+
1
]
=
placeholder
;
};
const
toggleAddThumbsDialogue
=
()
=>
{
if
(
isAddThumbsDialogueActive
.
value
==
true
)
{
isAddThumbsDialogueActive
.
value
=
false
;
//keyboardIsActive.value = true;
}
else
{
isAddThumbsDialogueActive
.
value
=
true
;
//keyboardIsActive.value = false;
}
}
const
insertFile
=
(
file
)
=>
{
console
.
log
(
'
insert
'
,
file
);
files
.
value
.
splice
(
index
.
value
+
1
,
0
,
file
);
toggleAddThumbsDialogue
();
};
const
cancelSelect
=
()
=>
{
toggleAddThumbsDialogue
();
};
const
filesForPage
=
computed
(()
=>
{
return
files
.
value
.
slice
(
page
.
value
*
pageSize
,
page
.
value
*
pageSize
+
pageSize
);
});
const
imgUrl
=
(
item
)
=>
{
if
(
item
==
placeholder
)
{
return
`
${
document
.
settings
.
MEDIA_URL
}
/
${
placeholder
}
`
;
}
return
`
${
document
.
settings
.
MEDIA_URL
}
/
${
item
}
`
;
}
const
mod
=
(
a
,
n
)
=>
{
return
((
a
%
n
)
+
n
)
%
n
;
}
const
onKeyDown
=
({
event
})
=>
{
switch
(
event
.
keyCode
)
{
case
37
:
// left
case
72
:
// h
x
.
value
=
mod
(
x
.
value
-
1
,
cols
);
break
;
case
39
:
// right
case
76
:
// l
x
.
value
=
mod
(
x
.
value
+
1
,
cols
);
break
;
case
38
:
// up
case
75
:
// k
y
.
value
=
mod
(
y
.
value
-
1
,
rows
);
break
;
case
40
:
// down
case
74
:
// j
y
.
value
=
mod
(
y
.
value
+
1
,
rows
);
break
;
case
46
:
// delete
removeThumb
()
break
;
case
8
:
// backspace
removeThumb
()
x
.
value
=
mod
(
x
.
value
-
1
,
cols
);
break
;
case
82
:
// r
replaceWithPlaceholder
()
break
;
case
80
:
//p
addPlaceholder
()
x
.
value
=
mod
(
x
.
value
+
1
,
cols
);
break
;
case
65
:
// a
toggleAddThumbsDialogue
()
break
;
}
}
useKeypress
({
keyEvent
:
"
keydown
"
,
onAnyKey
:
onKeyDown
,
keyBinds
:
[],
isActive
:
keyboardIsActive
,
})
return
{
originalFiles
,
files
,
filesToInsert
,
textFileToSave
:
null
,
x
,
y
,
rows
,
cols
,
page
,
pageIndex
,
index
,
numPages
,
pageSize
,
filesForPage
,
thumbWidth
:
407
,
thumbHeight
:
273
,
zoomLevel
,
placeholder
,
imgUrl
,
keyboardIsActive
,
cancelSelect
,
insertFile
,
removeThumb
,
replaceWithPlaceholder
,
addPlaceholder
,
toggleAddThumbsDialogue
,
isAddThumbsDialogueActive
,
showGaps
,
showLeftRightMarker
,
showPreview
,
mod
,
};
},
computed
:
{
pageStyle
()
{
const
width
=
this
.
thumbWidth
/
this
.
zoomLevel
;
const
height
=
this
.
thumbHeight
/
this
.
zoomLevel
;
let
style
=
{
'
display
'
:
'
grid
'
,
'
grid-template-columns
'
:
`repeat(
${
this
.
cols
}
,
${
width
}
px)`
,
'
grid-template-rows
'
:
`repeat(
${
this
.
rows
}
,
${
height
}
px)`
,
};
if
(
!
this
.
showPreview
)
{
style
[
'
grid-gap
'
]
=
'
4px
'
;
}
return
style
;
},
itemsWithGaps
()
{
const
thumbsWithoutPlaceholder
=
this
.
files
.
filter
(
item
=>
item
!==
this
.
placeholder
);
const
items
=
thumbsWithoutPlaceholder
.
filter
((
item
,
index
)
=>
{
const
difference
=
Math
.
abs
(
this
.
filenameIndexAtIndex
(
index
)
-
this
.
filenameIndexAtIndex
(
index
+
1
));
return
difference
!=
1
;
});
console
.
log
(
items
);
return
items
;
},
},
methods
:
{
thumbClicked
(
i
)
{
this
.
y
=
parseInt
(
i
/
this
.
cols
);
this
.
x
=
i
%
this
.
cols
;
},
filenameIndexAtIndex
(
i
)
{
if
(
this
.
files
&&
this
.
files
[
i
])
{
const
index
=
this
.
files
[
i
].
slice
(
-
34
,
-
34
+
7
);
if
(
index
)
{
return
index
;
}
else
{
return
'
no valid index
'
;
}
}
},
filenameDateAtIndex
(
i
)
{
if
(
this
.
files
&&
this
.
files
[
i
])
{
const
d
=
this
.
files
[
i
].
slice
(
-
26
,
-
26
+
8
);
if
(
!
d
)
{
return
'
no valid date
'
;
}
const
year
=
d
.
slice
(
0
,
4
);
const
month
=
d
.
slice
(
4
,
6
);
const
day
=
d
.
slice
(
6
,
8
);
return
`
${
day
}
.
${
month
}
.
${
year
}
`
}
},
filenameTimeAtIndex
(
i
)
{
if
(
this
.
files
&&
this
.
files
[
i
])
{
const
d
=
this
.
files
[
i
].
slice
(
-
17
,
-
17
+
8
);
if
(
!
d
)
{
return
'
no valid date
'
;
}
const
hour
=
d
.
slice
(
0
,
2
);
const
minute
=
d
.
slice
(
3
,
5
);
const
seconds
=
d
.
slice
(
6
,
8
);
return
`
${
hour
}
:
${
minute
}
:
${
seconds
}
`
}
},
cursorStyle
(
index
)
{
if
(
this
.
showPreview
)
{
return
{};
}
let
style
=
{};
if
(
index
===
this
.
pageIndex
)
{
style
.
border
=
'
2px solid yellow
'
;
}
return
style
;
},
// eslint-disable-next-line
thumbStyle
(
item
,
index
)
{
let
style
=
{};
const
width
=
this
.
thumbWidth
/
this
.
zoomLevel
;
const
height
=
this
.
thumbHeight
/
this
.
zoomLevel
;
style
.
width
=
`
${
width
}
px`
;
style
.
height
=
`
${
height
}
px`
;
if
(
!
this
.
showPreview
&&
this
.
showGaps
)
{
if
(
this
.
itemsWithG