Face editor working a bit more
Signed-off-by: James Ketrenos <james_gitlab@ketrenos.com>
This commit is contained in:
parent
d1fa906526
commit
a2de430c38
@ -22,7 +22,9 @@ router.put("/:id", function(req, res/*, next*/) {
|
|||||||
return res.status(400).send("Invalid request");
|
return res.status(400).send("Invalid request");
|
||||||
});
|
});
|
||||||
|
|
||||||
router.delete("/:id?", function(req, res/*, next*/) {
|
router.delete("/:id", function(req, res/*, next*/) {
|
||||||
|
console.log(`DELETE /${req.params.id}`);
|
||||||
|
|
||||||
if (!req.user.maintainer) {
|
if (!req.user.maintainer) {
|
||||||
return res.status(401).send("Unauthorized to delete photos.");
|
return res.status(401).send("Unauthorized to delete photos.");
|
||||||
}
|
}
|
||||||
|
46
src/App.css
46
src/App.css
@ -134,8 +134,7 @@ a.Link {
|
|||||||
|
|
||||||
.Body {
|
.Body {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin-top: 120px; /* .Header's two 60px chunks */
|
display: flex;
|
||||||
margin-bottom: 64px;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -147,18 +146,18 @@ a.Link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.Body > * {
|
.Body > * {
|
||||||
|
display: flex;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Main {
|
.Main {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
padding-top: 64px;
|
||||||
|
padding-bottom: 64px;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -216,6 +215,10 @@ body {
|
|||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
#identities {
|
#identities {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -229,6 +232,36 @@ body {
|
|||||||
margin: 0.25em;
|
margin: 0.25em;
|
||||||
padding: 0.25em;
|
padding: 0.25em;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#photo-explorer {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: 0%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#photo-explorer .face-box {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid black;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#photo-explorer #photo-info {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.more:hover {
|
.more:hover {
|
||||||
@ -305,6 +338,7 @@ body {
|
|||||||
.Identities {
|
.Identities {
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-direction:column;
|
flex-direction:column;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Identities > div:first-child {
|
.Identities > div:first-child {
|
||||||
|
230
src/App.js
230
src/App.js
@ -64,22 +64,22 @@ class Identities extends React.Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let params = window.location.search ? window.location.search.replace(/^\?/, "").split("&") : [],
|
let params = window.location.search ? window.location.search.replace(/^\?/, "").split("&") : [],
|
||||||
id;
|
face;
|
||||||
|
|
||||||
for (let i in params) {
|
for (let i in params) {
|
||||||
let parts = params[i].split("=");
|
let parts = params[i].split("=");
|
||||||
switch (parts[0]) {
|
switch (parts[0]) {
|
||||||
case "id":
|
case "face":
|
||||||
id = parseInt(parts[1]);
|
face = parseInt(parts[1]);
|
||||||
if (id != parts[1]) {
|
if (face != parts[1]) {
|
||||||
id = undefined;
|
face = undefined;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadFace(id);
|
loadFace(face);
|
||||||
const newIdentity = document.getElementById("new-identity");
|
const newIdentity = document.getElementById("new-identity");
|
||||||
newIdentity.appendChild(createNewIdenityEditor());
|
newIdentity.appendChild(createNewIdentityEditor());
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -89,6 +89,7 @@ class Identities extends React.Component {
|
|||||||
<div id="face-editor" className="block"></div>
|
<div id="face-editor" className="block"></div>
|
||||||
<div id="best-match" className="block"></div>
|
<div id="best-match" className="block"></div>
|
||||||
<div id="new-identity" className="block"></div>
|
<div id="new-identity" className="block"></div>
|
||||||
|
<div id="photo-explorer" className="block"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="identities" className="block"></div>
|
<div id="identities" className="block"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -149,7 +150,11 @@ function createFace(faceId, photoId, selectable) {
|
|||||||
if (event.shiftKey) { /* identities */
|
if (event.shiftKey) { /* identities */
|
||||||
let faceId = parseInt(event.currentTarget.getAttribute("face-id"));
|
let faceId = parseInt(event.currentTarget.getAttribute("face-id"));
|
||||||
if (faceId) {
|
if (faceId) {
|
||||||
window.location.href = "identities.html?id=" + faceId;
|
if (event.ctrlKey) {
|
||||||
|
window.open("identities?face=" + faceId, "faceId-" + faceId);
|
||||||
|
} else {
|
||||||
|
window.location.href = "identities?face=" + faceId;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
alert("No face id mapped to face.");
|
alert("No face id mapped to face.");
|
||||||
}
|
}
|
||||||
@ -171,10 +176,101 @@ function createFace(faceId, photoId, selectable) {
|
|||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadPhoto(photo) {
|
||||||
|
const tmp = document.querySelector("base");
|
||||||
|
let base;
|
||||||
|
if (tmp) {
|
||||||
|
base = new URL(tmp.href).pathname.replace(/\/$/, "") + "/"; /* Make sure there is a trailing slash */
|
||||||
|
} else {
|
||||||
|
base = "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
const photoExplorerBlock = document.getElementById("photo-explorer");
|
||||||
|
photoExplorerBlock.innerHTML = "<div id='photo-info'></div>";
|
||||||
|
|
||||||
|
const url = base + photo.path + "thumbs/scaled/" + photo.filename,
|
||||||
|
taken = new Date(photo.taken),
|
||||||
|
xml = new XMLHttpRequest(),
|
||||||
|
loading = document.getElementById("loading");
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
loading.textContent = "0%";
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = photoExplorerBlock.querySelector("div");
|
||||||
|
info.textContent = photo.path + photo.filename;
|
||||||
|
const bar = document.getElementById("bar");
|
||||||
|
if (bar) {
|
||||||
|
bar.parentElement.removeChild(bar);
|
||||||
|
}
|
||||||
|
xml.onprogress = function (event) {
|
||||||
|
var alpha = 0;
|
||||||
|
if (event.total) {
|
||||||
|
alpha = event.loaded / event.total;
|
||||||
|
if (loading) {
|
||||||
|
loading.textContent = Math.ceil(100 * alpha) + "%";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (loading) {
|
||||||
|
loading.textContent = "0%";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xml.onload = function(event) {
|
||||||
|
photoExplorerBlock.style.backgroundImage = "url(" + encodeURI(url).replace(/\(/g, '%28').replace(/\)/g, '%29') + ")";
|
||||||
|
/* Give the DOM time to update before fitting boxes... */
|
||||||
|
setTimeout(() => {
|
||||||
|
makeFaceBoxes(photo, photoExplorerBlock);
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
xml.open("GET", url, true);
|
||||||
|
xml.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeFaceBoxes(photo, photoExplorer) {
|
||||||
|
if (!photo.faces || !photo.faces.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scale;
|
||||||
|
|
||||||
|
/* If photo aspect ratio is higher than viewport aspect ratio, width will be scaled */
|
||||||
|
if (photo.width / photo.height > photoExplorer.offsetWidth / photoExplorer.offsetHeight) {
|
||||||
|
scale = photoExplorer.offsetWidth / photo.width;
|
||||||
|
} else {
|
||||||
|
scale = photoExplorer.offsetHeight / photo.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
photo.faces.forEach((face) => {
|
||||||
|
const box = document.createElement("div");
|
||||||
|
box.classList.add("face-box");
|
||||||
|
photoExplorer.appendChild(box);
|
||||||
|
box.style.left = (photo.width * face.left * scale) + "px";
|
||||||
|
box.style.top = (photo.height * face.top * scale) + "px";
|
||||||
|
box.style.width = (photo.width * (face.right - face.left) * scale) + "px";
|
||||||
|
box.style.height = (photo.height * (face.bottom - face.top) * scale) + "px";
|
||||||
|
box.addEventListener("click", () => {
|
||||||
|
if (event.ctrlKey) { /* face-explorer */
|
||||||
|
window.open("face-explorer.html?" + photo.id, "photo-" + photo.id);
|
||||||
|
}
|
||||||
|
if (event.shiftKey) {
|
||||||
|
window.open("identities?face=" + face.id, "faceId-" + face.id);
|
||||||
|
} else {
|
||||||
|
loadFace(face.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function loadFace(id) {
|
function loadFace(id) {
|
||||||
const faceEditorBlock = document.getElementById("face-editor");
|
const faceEditorBlock = document.getElementById("face-editor");
|
||||||
faceEditorBlock.innerHTML = "";
|
faceEditorBlock.innerHTML = "";
|
||||||
|
|
||||||
|
const photoExplorerBlock = document.getElementById("photo-explorer");
|
||||||
|
photoExplorerBlock.innerHTML = "";
|
||||||
|
|
||||||
window.fetch("api/v1/faces" + (id ? "/" + id : "")).then(res => res.json()).then((faces) => {
|
window.fetch("api/v1/faces" + (id ? "/" + id : "")).then(res => res.json()).then((faces) => {
|
||||||
if (faces.length == 0) {
|
if (faces.length == 0) {
|
||||||
return;
|
return;
|
||||||
@ -182,6 +278,10 @@ function loadFace(id) {
|
|||||||
|
|
||||||
const face = faces[0];
|
const face = faces[0];
|
||||||
|
|
||||||
|
window.fetch("api/v1/photos/random/" + face.photoId).then(res => res.json()).then((photo) => {
|
||||||
|
loadPhoto(photo);
|
||||||
|
});
|
||||||
|
|
||||||
if (face.identityId) {
|
if (face.identityId) {
|
||||||
getIdentity(face.identityId);
|
getIdentity(face.identityId);
|
||||||
} else {
|
} else {
|
||||||
@ -259,7 +359,7 @@ function getIdentity(identityId) {
|
|||||||
window.fetch("api/v1/identities/" + identityId).then(res => res.json()).then((identities) => {
|
window.fetch("api/v1/identities/" + identityId).then(res => res.json()).then((identities) => {
|
||||||
identities.forEach((identity) => {
|
identities.forEach((identity) => {
|
||||||
const block = createIdentityBlock(identity, true),
|
const block = createIdentityBlock(identity, true),
|
||||||
button = createUseThisIdentityButton(identity);
|
button = createUseThisIdentityButton(identity.id);
|
||||||
block.insertBefore(button, block.firstChild);
|
block.insertBefore(button, block.firstChild);
|
||||||
identitiesBlock.appendChild(block);
|
identitiesBlock.appendChild(block);
|
||||||
})
|
})
|
||||||
@ -279,16 +379,49 @@ function getIdentities(faceId) {
|
|||||||
return a.lastName.localeCompare(b.lastName);
|
return a.lastName.localeCompare(b.lastName);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let minDistance = {
|
||||||
|
distance: 1
|
||||||
|
};
|
||||||
|
|
||||||
identities.forEach((identity) => {
|
identities.forEach((identity) => {
|
||||||
|
identity.relatedFaces.forEach((face) => {
|
||||||
|
if (face.distance < minDistance.distance) {
|
||||||
|
minDistance.distance = face.distance;
|
||||||
|
minDistance.photoId = face.photoId;
|
||||||
|
minDistance.faceId = face.faceId;
|
||||||
|
minDistance.identity = identity;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const block = createIdentityBlock(identity),
|
const block = createIdentityBlock(identity),
|
||||||
button = createUseThisIdentityButton(identity);
|
button = createUseThisIdentityButton(identity.id);
|
||||||
block.insertBefore(button, block.firstChild);
|
block.insertBefore(button, block.firstChild);
|
||||||
identitiesBlock.appendChild(block);
|
identitiesBlock.appendChild(block);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const bestMatch = document.getElementById("best-match");
|
||||||
|
const buttonBlock = createActionButtons();
|
||||||
|
if (minDistance.distance != 1) {
|
||||||
|
bestMatch.innerHTML = "";
|
||||||
|
bestMatch.appendChild(document.createElement("div"));
|
||||||
|
bestMatch.firstChild.textContent = `Best match: ${minDistance.identity.name}`;
|
||||||
|
const distance = document.createElement("div"),
|
||||||
|
facePhoto = createFace(minDistance.faceId, minDistance.photoId);
|
||||||
|
distance.classList.add("distance");
|
||||||
|
distance.textContent = minDistance.distance.toFixed(2);
|
||||||
|
facePhoto.appendChild(distance);
|
||||||
|
bestMatch.appendChild(facePhoto);
|
||||||
|
buttonBlock.insertBefore(createUseThisIdentityButton(minDistance.identity.id), buttonBlock.firstChild);
|
||||||
|
} else {
|
||||||
|
bestMatch.innerHTML = "No best guess for match.";
|
||||||
|
}
|
||||||
|
bestMatch.insertBefore(buttonBlock, bestMatch.firstChild);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createUseThisIdentityButton(identity) {
|
|
||||||
|
function createUseThisIdentityButton(identityId) {
|
||||||
const button = document.createElement("button");
|
const button = document.createElement("button");
|
||||||
button.textContent = "Use this identity";
|
button.textContent = "Use this identity";
|
||||||
button.addEventListener("click", (event) => {
|
button.addEventListener("click", (event) => {
|
||||||
@ -300,7 +433,7 @@ function createUseThisIdentityButton(identity) {
|
|||||||
object.faces.push(face.getAttribute("face-id"));
|
object.faces.push(face.getAttribute("face-id"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window.fetch("api/v1/identities/faces/add/" + identity.id, {
|
window.fetch("api/v1/identities/faces/add/" + identityId, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
@ -316,12 +449,49 @@ function createUseThisIdentityButton(identity) {
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createActionButtons() {
|
||||||
|
const buttonBlock = document.createElement("div");
|
||||||
|
let button = document.createElement("button");
|
||||||
|
|
||||||
|
button = document.createElement("button");
|
||||||
|
button.textContent = "Load random face";
|
||||||
|
button.addEventListener("click", (event) => {
|
||||||
|
loadFace();
|
||||||
|
});
|
||||||
|
buttonBlock.appendChild(button);
|
||||||
|
|
||||||
|
button = document.createElement("button");
|
||||||
|
button.textContent = "Not a face";
|
||||||
|
button.addEventListener("click", (event) => {
|
||||||
|
window.fetch("api/v1/face", {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ faceId: faceId })
|
||||||
|
}).then(res => res.json()).then(() => {
|
||||||
|
loadFace();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
buttonBlock.appendChild(button);
|
||||||
|
|
||||||
|
return buttonBlock;
|
||||||
|
}
|
||||||
|
|
||||||
function createIdentityBlock(identity, nolimit) {
|
function createIdentityBlock(identity, nolimit) {
|
||||||
const block = document.createElement("div");
|
const block = document.createElement("div");
|
||||||
|
|
||||||
block.classList.add("block");
|
block.classList.add("block");
|
||||||
let div = document.createElement("div");
|
let div = document.createElement("div");
|
||||||
div.textContent = identity.name;
|
if (identity.lastName && identity.firstName) {
|
||||||
|
div.textContent = `${identity.lastName}, ${identity.firstName}`;
|
||||||
|
} else if (identity.lastName) {
|
||||||
|
div.textContent = identity.lastName;
|
||||||
|
} else {
|
||||||
|
div.textContent = identity.name;
|
||||||
|
}
|
||||||
|
|
||||||
block.appendChild(div);
|
block.appendChild(div);
|
||||||
|
|
||||||
@ -339,10 +509,6 @@ function createIdentityBlock(identity, nolimit) {
|
|||||||
div.classList.add("face-block-nolimit");
|
div.classList.add("face-block-nolimit");
|
||||||
}
|
}
|
||||||
|
|
||||||
let minDistance = {
|
|
||||||
distance: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let i = 0; i < identity.relatedFaces.length; i++) {
|
for (let i = 0; i < identity.relatedFaces.length; i++) {
|
||||||
if (!nolimit && i >= 4) {
|
if (!nolimit && i >= 4) {
|
||||||
break;
|
break;
|
||||||
@ -351,39 +517,18 @@ function createIdentityBlock(identity, nolimit) {
|
|||||||
const facePhoto = createFace(target.faceId, target.photoId),
|
const facePhoto = createFace(target.faceId, target.photoId),
|
||||||
distance = document.createElement("div");
|
distance = document.createElement("div");
|
||||||
|
|
||||||
if (target.distance < minDistance.distance) {
|
|
||||||
minDistance.distance = target.distance;
|
|
||||||
minDistance.photoId = target.photoId;
|
|
||||||
minDistance.faceId = target.faceId;
|
|
||||||
}
|
|
||||||
distance.classList.add("distance");
|
distance.classList.add("distance");
|
||||||
distance.textContent = target.distance.toFixed(2);
|
distance.textContent = target.distance.toFixed(2);
|
||||||
facePhoto.appendChild(distance);
|
facePhoto.appendChild(distance);
|
||||||
div.appendChild(facePhoto);
|
div.appendChild(facePhoto);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minDistance.distance < 0.45) {
|
|
||||||
const bestMatch = document.getElementById("best-match");
|
|
||||||
const distance = document.createElement("div"),
|
|
||||||
facePhoto = createFace(minDistance.faceId, minDistance.photoId);
|
|
||||||
distance.classList.add("distance");
|
|
||||||
distance.textContent = minDistance.distance.toFixed(2);
|
|
||||||
facePhoto.appendChild(distance);
|
|
||||||
|
|
||||||
bestMatch.innerHTML = "";
|
|
||||||
bestMatch.appendChild(facePhoto);
|
|
||||||
} else {
|
|
||||||
const bestMatch = document.getElementById("best-match");
|
|
||||||
bestMatch.innerHTML = "No best guess for match.";
|
|
||||||
}
|
|
||||||
|
|
||||||
block.appendChild(div);
|
block.appendChild(div);
|
||||||
|
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNewIdentityEditor() {
|
||||||
function createNewIdenityEditor() {
|
|
||||||
const block = document.createElement("div");
|
const block = document.createElement("div");
|
||||||
block.classList.add("block");
|
block.classList.add("block");
|
||||||
const editor = document.createElement("div");
|
const editor = document.createElement("div");
|
||||||
@ -417,7 +562,6 @@ function createNewIdenityEditor() {
|
|||||||
object.faces.push(face.getAttribute("face-id"));
|
object.faces.push(face.getAttribute("face-id"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log(object);
|
|
||||||
window.fetch("api/v1/identities", {
|
window.fetch("api/v1/identities", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@ -433,8 +577,10 @@ function createNewIdenityEditor() {
|
|||||||
el.value = "";
|
el.value = "";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const face = document.body.querySelector("#face-editor .face");
|
const face = document.body.querySelector("#face-editor .face"),
|
||||||
loadFace(parseInt(face.getAttribute("face-id")));
|
faceId = parseInt(face.getAttribute("face-id"));
|
||||||
|
loadFace(faceId);
|
||||||
|
getIdentities(faceId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
block.appendChild(editor);
|
block.appendChild(editor);
|
||||||
|
@ -18,7 +18,7 @@ module.exports = merge(common, {
|
|||||||
target: "http://localhost:8123",
|
target: "http://localhost:8123",
|
||||||
bypass: function(req, res, proxyOptions) {
|
bypass: function(req, res, proxyOptions) {
|
||||||
console.log(req.url);
|
console.log(req.url);
|
||||||
if (req.url == '/photos/identities') {
|
if (req.url.match(/^\/photos\/identities[/?]?/)) {
|
||||||
return 'index.html';
|
return 'index.html';
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user