Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
jdurrant
binana
Commits
e3f65fbb
Commit
e3f65fbb
authored
Nov 20, 2021
by
jdurrant
Browse files
Added FileLoaderSystem.
parent
5ac46e88
Changes
27
Hide whitespace changes
Inline
Side-by-side
web_app/src/UI/FileLoaderSystem/Common/FileLoaderFormGroup.Vue/FileLoaderFormGroup.Vue.ts
0 → 100644
View file @
e3f65fbb
// Released under the Apache 2.0 License. See
// LICENSE.md or go to https://opensource.org/licenses/Apache-2.0 for full
// details. Copyright 2021 Jacob D. Durrant.
import
{
addCSS
}
from
"
../Utils
"
;
declare
var
Vue
;
/** An object containing the vue-component computed functions. */
let
computedFunctions
=
{
/**
* Determines whether this component has a label.
* @returns boolean True if it does, false otherwise.
*/
hasLabel
():
boolean
{
return
this
[
"
label
"
]
!==
""
&&
this
[
"
label
"
]
!==
undefined
;
},
/**
* Determines if label should be placed to the left or above. Number of
* columns for the label width 'xs' screens and up. Always 0 (labele above
* on small screens).
* @returns number 0
*/
"
labelCols
"
():
number
{
return
0
;
// Used to return 2, but now I think it's good to have label on top if
// there isn't plenty of room.
// return ((this.hasLabel === true) && (this["labelToLeft"] === true)) ? 2 : 0;
},
/**
* Determines if label should be placed to the left or above. Number of
* columns for the label width 'md' screens and up.
* @returns number Returns 2 if it has a label, 0 otherwise.
*/
"
labelColsMd
"
():
number
{
return
((
this
.
hasLabel
===
true
)
&&
(
this
[
"
labelToLeft
"
]
===
true
))
?
2
:
0
;
}
}
/**
* Setup the file-loader-form-group Vue commponent.
* @returns void
*/
export
function
setupFileLoaderFormGroup
():
void
{
Vue
.
component
(
'
file-loader-form-group
'
,
{
/**
* Get the data associated with this component.
* @returns any The data.
*/
"
data
"
:
function
()
{
return
{}
},
"
computed
"
:
computedFunctions
,
"
template
"
:
/* html */
`
<span class="file-loader-form-group">
<!-- :label-cols="labelCols"
:label-cols-lg="labelColsMd" -->
<b-form-group
v-if="formGroupWrapper"
:label="label"
:label-for="id"
:id="'input-group-' + id"
:style="styl"
:label-cols="0"
:label-cols-md="labelColsMd"
>
<slot></slot>
<small
tabindex="-1"
:id="'input-group-input-group-' + id + '__BV_description_'"
class="form-text text-muted" style="display:inline;"
v-html="description">
</small>
<small class="form-text text-muted" style="display:inline;">
<slot name="extraDescription"></slot>
</small>
</b-form-group>
<div v-else>
<slot></slot>
</div>
</span>
`
,
"
props
"
:
{
"
label
"
:
String
,
"
id
"
:
String
,
"
styl
"
:
String
,
"
description
"
:
String
,
"
formGroupWrapper
"
:
{
"
type
"
:
Boolean
,
"
default
"
:
true
},
"
labelToLeft
"
:
{
"
type
"
:
Boolean
,
"
default
"
:
true
}
},
"
methods
"
:
{},
"
mounted
"
()
{
addCSS
(
`.file-loader-form-group .col-form-label { hyphens: auto; max-width: 100px !important; }`
);
}
})
}
web_app/src/UI/FileLoaderSystem/Common/FileLoaderFormGroup.Vue/README.md
0 → 100644
View file @
e3f65fbb
You can omit this if this component is registered globally (lots of other
components you use might already be using this one).
\ No newline at end of file
web_app/src/UI/FileLoaderSystem/Common/FileLoaderTextInput.ts
0 → 100644
View file @
e3f65fbb
// Released under the Apache 2.0 License. See LICENSE.md or go to
// https://opensource.org/licenses/Apache-2.0 for full details. Copyright 2021
// Jacob D. Durrant.
declare
var
Vue
;
/**
* Setup the file-loader-text-input Vue commponent.
* @returns void
*/
export
function
setupFileLoaderTextInput
():
void
{
Vue
.
component
(
"
file-loader-text-input
"
,
{
/**
* Get the data associated with this component.
* @returns any The data.
*/
"
data
"
():
any
{
return
{
"
localValue
"
:
""
};
},
"
methods
"
:
{
"
onLoad
"
():
void
{
this
.
$emit
(
"
onLoad
"
,
this
[
"
value
"
]);
},
"
keydown
"
(
e
:
KeyboardEvent
):
void
{
if
(
e
.
key
===
"
Enter
"
)
{
this
[
"
onLoad
"
]();
}
},
"
keyup
"
(
e
:
KeyboardEvent
)
:
void
{
this
.
$emit
(
"
input
"
,
this
[
"
localValue
"
]);
}
// "clearText"(): void {
// this["val"] = "";
// }
},
"
template
"
:
/*html*/
`
<b-input-group>
<b-form-input
v-model="localValue"
style="border-top-left-radius:4px; border-bottom-left-radius:4px;"
:placeholder="placeholder"
:formatter="formatter"
@keydown="keydown"
@keyup="keyup"
:state="valid"
></b-form-input>
<b-input-group-append>
<b-button
style="background-color:#e9ecef; color:#4a5056; border:1px solid #ced4da; border-top-right-radius:4px; border-bottom-right-radius:4px;"
variant="outline-primary"
@click="onLoad"
:disabled="btnDisabledFunc(value)"
>
Load
</b-button>
</b-input-group-append>
</b-input-group>`
,
"
props
"
:
{
"
placeholder
"
:
{
"
type
"
:
String
,
"
default
"
:
"
Type here...
"
,
},
"
formatter
"
:
{
"
type
"
:
Function
,
"
default
"
:
(
t
)
=>
{
return
t
;}
},
"
btnDisabledFunc
"
:
{
"
type
"
:
Function
,
"
default
"
:
()
=>
{
return
false
;}
},
"
valid
"
:
{
"
type
"
:
Boolean
,
"
default
"
:
true
},
"
value
"
:
{
"
type
"
:
String
,
"
default
"
:
""
}
},
"
computed
"
:
{},
/**
* Runs when the vue component is mounted.
* @returns void
*/
"
mounted
"
:
()
=>
{},
});
}
web_app/src/UI/FileLoaderSystem/Common/Interfaces.ts
0 → 100644
View file @
e3f65fbb
// export interface IInputFileName {
// type: string;
// filename: string;
// }
export
interface
IVueXVar
{
name
:
string
;
val
:
any
;
}
export
interface
IConvert
extends
IFileInfo
{
onConvertDone
:
Function
;
onConvertCancel
:
Function
;
}
export
interface
IFileInfo
{
filename
:
string
;
fileContents
:
string
;
// onConvertDone: IConvert;
// convertedResolveFunc?: Function;
// convertedRejectFunc?: Function;
// id?: string; // associated component id
}
export
interface
IFileLoadError
{
title
:
string
;
body
:
string
;
}
// export interface IFileFromTextField {
// placeholder: string;
// tabName: string;
// loadFunc: Function
// onSuccess: Function;
// onError: Function;
// }
export
interface
IAllFiles
{
selectedFilename
:
string
;
allFiles
:
{[
key
:
string
]:
string
};
// filename => contents
}
export
interface
IResidueInfo
{
residueId
:
string
[],
residuePdbLines
:
string
}
\ No newline at end of file
web_app/src/UI/FileLoaderSystem/Common/Utils.ts
0 → 100644
View file @
e3f65fbb
import
{
IFileInfo
,
IFileLoadError
}
from
"
./Interfaces
"
;
export
function
extsStrToList
(
exts
:
string
):
string
[]
{
return
exts
.
toLowerCase
()
.
split
(
/,/g
)
.
map
(
(
e
)
=>
e
.
replace
(
/ /g
,
""
).
replace
(
/
\.
/
,
""
)
);
}
export
function
getExt
(
filename
:
string
):
string
{
let
fileNameParts
=
filename
.
toLowerCase
().
split
(
/
\.
/g
);
let
ext
=
fileNameParts
[
fileNameParts
.
length
-
1
];
return
ext
;
}
/**
* Given a file object, returns a promise that resolves the text
* in that file.
* @param {*} fileObj The file object.
* @returns Promise
*/
export
function
getFileObjContents
(
fileObj
):
Promise
<
any
>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
var
fr
=
new
FileReader
();
fr
.
onload
=
()
=>
{
// @ts-ignore: Not sure why this causes Typescript problems.
var
data
=
new
Uint8Array
(
fr
.
result
);
resolve
(
new
TextDecoder
(
"
utf-8
"
).
decode
(
data
));
};
fr
.
readAsArrayBuffer
(
fileObj
);
// Reset the show non-protein atom's link.
// if (this["id"] === "receptor") {
// this.$store.commit("setVar", {
// name: "showKeepProteinOnlyLink",
// val: true,
// });
// }
});
}
export
function
loadRemote
(
url
:
string
,
vueComp
:
any
):
Promise
<
boolean
>
{
let
urlUpper
=
url
.
toUpperCase
();
if
(
(
urlUpper
.
slice
(
0
,
7
)
!==
"
HTTP://
"
)
&&
(
urlUpper
.
slice
(
0
,
8
)
!==
"
HTTPS://
"
)
)
{
vueComp
.
onError
({
title
:
"
Bad URL
"
,
body
:
`The URL should start with http:// or https://.`
}
as
IFileLoadError
);
return
Promise
.
resolve
(
false
);
}
return
new
Promise
((
resolve
,
reject
)
=>
{
fetch
(
url
)
.
then
(
response
=>
{
if
(
!
response
.
ok
)
{
vueComp
.
onError
({
title
:
"
Bad URL
"
,
body
:
`Could not load the URL
${
url
}
. Status `
+
response
.
status
.
toString
()
+
"
:
"
+
response
.
statusText
}
as
IFileLoadError
);
resolve
(
false
);
}
else
{
return
response
.
text
()
}
})
.
then
(
text
=>
{
let
flnm
=
url
.
split
(
"
/
"
).
pop
();
let
filesInfo
:
IFileInfo
[]
=
[{
filename
:
flnm
,
fileContents
:
text
}
as
IFileInfo
]
let
allFilesLoaded
=
vueComp
.
onFilesLoaded
(
filesInfo
);
// false if invalid files or something.
resolve
(
allFilesLoaded
);
})
.
catch
((
err
)
=>
{
vueComp
.
onError
({
title
:
"
Bad URL
"
,
body
:
`Could not load the URL
${
url
}
: `
+
err
.
message
}
as
IFileLoadError
);
resolve
(
false
);
});
})
}
export
function
deepCopy
(
obj
:
any
):
any
{
return
JSON
.
parse
(
JSON
.
stringify
(
obj
));
}
export
function
addCSS
(
css
:
string
):
void
{
document
.
head
.
appendChild
(
Object
.
assign
(
document
.
createElement
(
"
style
"
),
{
textContent
:
css
}));
}
export
function
slugify
(
complexString
:
string
):
string
{
// With help from codex
var
slug
=
complexString
.
toLowerCase
().
replace
(
/
[^
a-z0-9
]
+/g
,
'
-
'
);
return
slug
;
}
\ No newline at end of file
web_app/src/UI/FileLoaderSystem/FileList.Vue/Methods.VueFuncs.ts
0 → 100644
View file @
e3f65fbb
// import { dbVueFuncs } from "../Common/DB.VueFuncs";
// import { clearAllInDatabase } from "../DataBase";
// import { removeFileFromDatabase } from "../DataBase/Internal";
import
{
IFileInfo
}
from
"
../Common/Interfaces
"
;
import
{
deepCopy
}
from
"
../Common/Utils
"
;
/** An object containing the vue-component methods functions. */
export
let
fileLoaderFileListMethodsFunctions
=
{
"
fileDismissed
"
(
filename
)
{
// removeFileFromDatabase(this["id"], filename, this["associatedFileLoaderComponent"])
let
files
=
deepCopy
(
this
[
"
value
"
]);
let
keys
=
Object
.
keys
(
files
);
let
idx
=
keys
.
indexOf
(
filename
);
let
newIdx
=
(
idx
===
0
)
?
idx
+
1
:
idx
-
1
;
let
newFilename
=
keys
[
newIdx
];
this
[
"
fileNameClicked
"
](
newFilename
);
delete
files
[
filename
];
this
.
$emit
(
"
input
"
,
files
);
this
.
$emit
(
"
onSelectedFilenameChange
"
,
newFilename
);
// this.$nextTick(() => {
// this.$emit("onRequestRemoveFile", filename);
// });
},
"
clearAll
"
():
void
{
// Clears all entries in the list.
// this.$emit("onRequestRemoveAllFiles");
this
.
$emit
(
"
input
"
,
{});
this
.
$emit
(
"
onSelectedFilenameChange
"
,
""
);
// clearAllInDatabase(this["id"]);
// clearAllInDatabase();
},
// ...dbVueFuncs,
"
fileNameClicked
"
(
filename
:
string
):
void
{
// this.loadSingleFileFromIndexedDB(filename, this["associatedFileLoaderComponent"]);
this
[
"
currentlySelectedFilenameToUse
"
]
=
filename
;
this
.
$nextTick
(()
=>
{
this
.
$emit
(
"
onSelectedFilenameChange
"
,
filename
);
// this.$emit("onSelectFile", {
// filename: filename,
// fileContents: this["files"][filename]
// } as IFileInfo);
});
},
"
scrollToBottom
"
():
void
{
setTimeout
(()
=>
{
let
div
=
(
this
.
$refs
[
"
filesDiv
"
]
as
HTMLDivElement
);
div
.
scrollTo
({
top
:
div
.
clientHeight
,
left
:
0
,
behavior
:
'
smooth
'
});
},
500
);
},
};
web_app/src/UI/FileLoaderSystem/FileList.Vue/index.ts
0 → 100644
View file @
e3f65fbb
// Released under the Apache 2.0 License. See LICENSE.md or go to
// https://opensource.org/licenses/Apache-2.0 for full details. Copyright 2021
// Jacob D. Durrant.
import
{
addCSS
}
from
"
../Common/Utils
"
;
import
{
fileLoaderFileListMethodsFunctions
}
from
"
./Methods.VueFuncs
"
;
declare
var
Vue
;
/** An object containing the vue-component computed functions. */
let
computedFunctions
=
{
"
filenames
"
():
string
[]
{
return
Object
.
keys
(
this
[
"
value
"
]);
}
};
/**
* The vue-component mounted function.
* @returns void
*/
function
mountedFunction
():
void
{
// Add some CSS
addCSS
(
`.alert-dismissible { padding: 0.15rem !important;} .alert-dismissible .close {padding-top: 4px; padding-right: 8px; padding-left: 8px; padding-bottom: 0;}`
);
}
/**
* Setup the file-list Vue commponent.
* @returns void
*/
export
function
setupFileList
():
void
{
Vue
.
component
(
"
file-list
"
,
{
/**
* Get the data associated with this component.
* @returns any The data.
*/
"
data
"
():
any
{
return
{
"
currentlySelectedFilenameToUse
"
:
""
};
},
"
watch
"
:
{
"
selectedFilename
"
(
newVal
:
string
,
oldVal
:
string
):
void
{
// this["currentlySelectedFilenameToUse"] = newVal;
this
[
"
fileNameClicked
"
](
newVal
);
}
},
"
methods
"
:
fileLoaderFileListMethodsFunctions
,
"
template
"
:
/*html*/
`
<div>
<div
ref="filesDiv"
style="max-height:135px; overflow-y:scroll;"
:class="filenames.length > 0 ? 'mt-2' : ''"
>
<!-- <div style="margin-bottom:15px;margin-top:15px;">{{filenames}}</div> -->
<b-alert
v-for="filename in filenames"
:key="filename"
class="mb-1 p-3 py-3"
dismissible
fade
show
:variant="(filename === currentlySelectedFilenameToUse) ? 'primary' : 'secondary'"
@dismissed="fileDismissed(filename)"
>
<div style="cursor:pointer; position:relative; top:1px; left:4px;" @click="fileNameClicked(filename)">
📎 {{filename}}
</div>
</b-alert>
</div>
<!-- style="padding-top:3px; float:right; cursor:pointer;" -->
<div
v-if="filenames.length > 1"
style="height:22.5px;"
>
<b-form-tag
style="padding-top:3px; cursor:pointer; position:relative; top:1px; float:right;"
:no-remove="true"
:pill="true"
@click.native="clearAll"
>Clear All</b-form-tag>
</div>
<!-- variant="secondary" -->
<!-- <span style="clear:both;"></span> -->
</div>
`
,
"
props
"
:
{
"
selectedFilename
"
:
{
"
type
"
:
String
,
"
default
"
:
""
},
// "database": {
// "type": Object,
// "default": undefined
// },
"
id
"
:
{
"
type
"
:
String
,
"
default
"
:
undefined
},
// for v-model
"
value
"
:
{
"
type
"
:
Object
,
"
default
"
:
{}
}
// "associatedFileLoaderComponent": {
// "type": Object,
// "default": undefined
// }
},
"
computed
"
:
computedFunctions
,
/**
* Runs when the vue component is mounted.
* @returns void
*/
"
mounted
"
:
mountedFunction
,
});
}
web_app/src/UI/FileLoaderSystem/FileLoader/FileLoader.Vue/Emits.VueFuncs.ts
0 → 100644
View file @
e3f65fbb
import
{
IAllFiles
,
IConvert
,
IFileInfo
,
IFileLoadError
}
from
"
../../Common/Interfaces
"
;
import
{
deepCopy
}
from
"
../../Common/Utils
"
;
// Note that plugin emits (with same name) are present in PluginParent.ts. These
// emit from the encapsulating FileLoader itself.
// These functions are called when plugin children emit data. They process that
// data a bit if necessary and emit it to the encompassing component
// (FileLoaderWrapper). Like relay functions.
export
let
fileLoaderEmitFunctions
=
{
// Start converting files that need to be converted.
"
onStartConvertFiles
"
:
function
(
val
:
IConvert
):
void
{
this
.
$emit
(
"
onStartConvertFiles
"
,
val
);
},
// When an error occurs, handle that as well. No need to process. Just pass
// up the chain.
"
onError
"
:
function
(
val
:
IFileLoadError
):
void
{
this
.
$emit
(
"
onError
"
,
val
);
},
// When the file is completely ready, after any conversion, error handling,
// etc. Fires for every file loaded.
"
onFileReady
"
:
function
(
val
:
IFileInfo
):
void
{
let
files
=
{};
// If multiple not files allowed, copy current files.
if
(
this
[
"
multipleFiles
"
]
!==
false
)
{
files
=
deepCopy
(
this
[
"
value
"
]);
}
// Add this file to the object containing all files
files
[
val
.
filename
]
=
val
.
fileContents
;
// this["selectedFilename"] = val.filename;
// Send all the data up the chain (via v-bind).
this
.
$emit
(
"
input
"
,
files
);
this
.