Rewrote everything to improve UI and security
This commit is contained in:
parent
64b462559b
commit
1e3aba9ed6
64
.eslintrc.js
64
.eslintrc.js
@ -1,34 +1,34 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"commonjs": true,
|
"commonjs": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
"globals": {
|
"globals": {
|
||||||
"Atomics": "readonly",
|
"Atomics": "readonly",
|
||||||
"SharedArrayBuffer": "readonly"
|
"SharedArrayBuffer": "readonly"
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2018
|
"ecmaVersion": 2018
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": [
|
"indent": [
|
||||||
"error",
|
"error",
|
||||||
2
|
4
|
||||||
],
|
],
|
||||||
"linebreak-style": [
|
"linebreak-style": [
|
||||||
"error",
|
"error",
|
||||||
"unix"
|
"unix"
|
||||||
],
|
],
|
||||||
"quotes": [
|
"quotes": [
|
||||||
"error",
|
"error",
|
||||||
"double"
|
"double"
|
||||||
],
|
],
|
||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"always"
|
"always"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
assets/skins/download/*
|
assets/skins/download/*
|
||||||
assets/skins/edit/*
|
assets/skins/edit/*
|
||||||
|
old/
|
@ -1 +1,7 @@
|
|||||||
{"theme":"light","user":"arne.v.iterson@hotmail.nl","password":"","session":"","skinDefault":"http://assets.mojang.com/SkinTemplates/steve.png"}
|
{
|
||||||
|
"theme": "light",
|
||||||
|
"user": "",
|
||||||
|
"accessToken": "",
|
||||||
|
"clientId": "",
|
||||||
|
"skinDefault": "http://assets.mojang.com/SkinTemplates/steve.png"
|
||||||
|
}
|
BIN
assets/skins/dummy.png
Normal file
BIN
assets/skins/dummy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 902 B |
110
css/index.css
110
css/index.css
@ -1,110 +0,0 @@
|
|||||||
body {
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
body header h1 {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body header h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content {
|
|
||||||
display: -ms-grid;
|
|
||||||
display: grid;
|
|
||||||
-ms-grid-columns: 50% 50%;
|
|
||||||
grid-template-columns: 50% 50%;
|
|
||||||
grid-template-areas: 'message message'
'left right';
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content form label {
|
|
||||||
font-size: small;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content input, body div.content button {
|
|
||||||
margin: 0.5em;
|
|
||||||
padding: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content button {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content .message {
|
|
||||||
-ms-grid-row: 1;
|
|
||||||
-ms-grid-column: 1;
|
|
||||||
-ms-grid-column-span: 2;
|
|
||||||
grid-area: message;
|
|
||||||
width: calc(100% - 22px);
|
|
||||||
height: auto;
|
|
||||||
margin: 5px auto;
|
|
||||||
padding: 5px 10px 5px 10px;
|
|
||||||
font-size: small;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content .message.info {
|
|
||||||
border: 1px solid blue;
|
|
||||||
background: rgba(0, 0, 255, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content .message.warning {
|
|
||||||
border: 1px solid red;
|
|
||||||
background: rgba(255, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content .message.success {
|
|
||||||
border: 1px solid limegreen;
|
|
||||||
background: rgba(50, 205, 50, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content div.left {
|
|
||||||
-ms-grid-row: 2;
|
|
||||||
-ms-grid-column: 1;
|
|
||||||
grid-area: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content div.right {
|
|
||||||
-ms-grid-row: 2;
|
|
||||||
-ms-grid-column: 2;
|
|
||||||
grid-area: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content div.right div#list canvas {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0 2px 0 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body div.content img.skin {
|
|
||||||
width: 60%;
|
|
||||||
padding: 0 1em 1em 1em;
|
|
||||||
-ms-interpolation-mode: nearest-neighbor;
|
|
||||||
image-rendering: -webkit-optimize-contrast;
|
|
||||||
image-rendering: -moz-crisp-edges;
|
|
||||||
image-rendering: -o-pixelated;
|
|
||||||
image-rendering: pixelated;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.dark {
|
|
||||||
color: #a9a9a9;
|
|
||||||
background-color: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.light {
|
|
||||||
color: #000;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
body footer {
|
|
||||||
font-size: small;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
/*# sourceMappingURL=index.css.map */
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"mappings": "AAAA,AAAA,IAAI,CAAC;EACD,WAAW,EAAE,4BAA4B;EACzC,UAAU,EAAE,MAAM;CAqFrB;;AAvFD,AAIQ,IAJJ,CAGA,MAAM,CACF,EAAE,CAAC;EACC,aAAa,EAAE,CAAC;CACnB;;AANT,AAOQ,IAPJ,CAGA,MAAM,CAIF,EAAE,CAAC;EACC,MAAM,EAAE,CAAC;CACZ;;AATT,AAWI,IAXA,CAWA,GAAG,AAAA,QAAQ,CAAC;EACR,OAAO,EAAE,IAAI;EACb,qBAAqB,EAAE,OAAO;EAC9B,mBAAmB,EACf,+BAEJ;EACA,KAAK,EAAE,IAAI;CAmDd;;AArEL,AAoBY,IApBR,CAWA,GAAG,AAAA,QAAQ,CAQP,IAAI,CACA,KAAK,CAAC;EACF,SAAS,EAAE,KAAK;CACnB;;AAtBb,AAwBQ,IAxBJ,CAWA,GAAG,AAAA,QAAQ,CAaP,KAAK,EAxBb,IAAI,CAWA,GAAG,AAAA,QAAQ,CAaA,MAAM,CAAC;EACV,MAAM,EAAE,KAAK;EACb,OAAO,EAAE,MAAM;CAClB;;AA3BT,AA4BQ,IA5BJ,CAWA,GAAG,AAAA,QAAQ,CAiBP,MAAM,CAAC;EACH,KAAK,EAAE,GAAG;CACb;;AA9BT,AA+BQ,IA/BJ,CAWA,GAAG,AAAA,QAAQ,CAoBP,QAAQ,CAAC;EACL,SAAS,EAAE,OAAO;EAClB,KAAK,EAAE,iBAAiB;EACxB,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,QAAQ;EAChB,OAAO,EAAE,iBAAiB;EAC1B,SAAS,EAAE,KAAK;EAChB,KAAK,EAAE,KAAK;CAaf;;AAnDT,AAuCY,IAvCR,CAWA,GAAG,AAAA,QAAQ,CAoBP,QAAQ,AAQH,KAAK,CAAC;EACH,MAAM,EAAE,cAAc;EACtB,UAAU,EAAE,oBAAoB;CACnC;;AA1Cb,AA2CY,IA3CR,CAWA,GAAG,AAAA,QAAQ,CAoBP,QAAQ,AAYH,QAAQ,CAAC;EACN,MAAM,EAAE,aAAa;EACrB,UAAU,EAAE,oBAAoB;CACnC;;AA9Cb,AA+CY,IA/CR,CAWA,GAAG,AAAA,QAAQ,CAoBP,QAAQ,AAgBH,QAAQ,CAAC;EACN,MAAM,EAAE,mBAAmB;EAC3B,UAAU,EAAE,sBAAsB;CACrC;;AAlDb,AAoDQ,IApDJ,CAWA,GAAG,AAAA,QAAQ,CAyCP,GAAG,AAAA,KAAK,CAAC;EACL,SAAS,EAAE,IAAI;CAClB;;AAtDT,AAuDQ,IAvDJ,CAWA,GAAG,AAAA,QAAQ,CA4CP,GAAG,AAAA,MAAM,CAAC;EACN,SAAS,EAAE,KAAK;CAOnB;;AA/DT,AA0DgB,IA1DZ,CAWA,GAAG,AAAA,QAAQ,CA4CP,GAAG,AAAA,MAAM,CAEL,GAAG,AAAA,KAAK,CACJ,MAAM,CAAC;EACH,MAAM,EAAE,OAAO;EACf,OAAO,EAAE,WAAW;CACvB;;AA7DjB,AAgEQ,IAhEJ,CAWA,GAAG,AAAA,QAAQ,CAqDP,GAAG,AAAA,KAAK,CAAC;EACL,KAAK,EAAE,GAAG;EACV,OAAO,EAAE,aAAa;EACtB,eAAe,EAAE,SAAS;CAC7B;;AApET,AAsEI,IAtEA,AAsEC,KAAK,CAAC;EACH,KAAK,EAAE,OAAO;EACd,gBAAgB,EAAE,IAAI;CACzB;;AAzEL,AA0EI,IA1EA,AA0EC,MAAM,CAAC;EACJ,KAAK,EAAE,IAAI;EACX,UAAU,EAAE,IAAI;CACnB;;AA7EL,AA8EI,IA9EA,CA8EA,MAAM,CAAC;EACH,SAAS,EAAE,KAAK;EAChB,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;EACZ,QAAQ,EAAE,QAAQ;EAClB,MAAM,EAAE,CAAC;EACT,IAAI,EAAE,CAAC;EACP,UAAU,EAAE,MAAM;CACrB",
|
|
||||||
"sources": [
|
|
||||||
"index.scss"
|
|
||||||
],
|
|
||||||
"names": [],
|
|
||||||
"file": "index.css"
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
body {
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
header {
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div.content {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 50% 50%;
|
|
||||||
grid-template-areas:
|
|
||||||
'message message'
|
|
||||||
'left right'
|
|
||||||
;
|
|
||||||
width: 100%;
|
|
||||||
form {
|
|
||||||
label {
|
|
||||||
font-size: small;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input, button {
|
|
||||||
margin: 0.5em;
|
|
||||||
padding: 0.25em;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
.message {
|
|
||||||
grid-area: message;
|
|
||||||
width: calc(100% - 22px);
|
|
||||||
height: auto;
|
|
||||||
margin: 5px auto;
|
|
||||||
padding: 5px 10px 5px 10px;
|
|
||||||
font-size: small;
|
|
||||||
color: white;
|
|
||||||
&.info {
|
|
||||||
border: 1px solid blue;
|
|
||||||
background: rgba(0, 0, 255, 0.5);
|
|
||||||
}
|
|
||||||
&.warning {
|
|
||||||
border: 1px solid red;
|
|
||||||
background: rgba(255, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
&.success {
|
|
||||||
border: 1px solid limegreen;
|
|
||||||
background: rgba(50, 205, 50, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div.left {
|
|
||||||
grid-area: left;
|
|
||||||
}
|
|
||||||
div.right {
|
|
||||||
grid-area: right;
|
|
||||||
div#list {
|
|
||||||
canvas {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0 2px 0 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
img.skin {
|
|
||||||
width: 60%;
|
|
||||||
padding: 0 1em 1em 1em;
|
|
||||||
image-rendering: pixelated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.dark {
|
|
||||||
color: #a9a9a9;
|
|
||||||
background-color: #111;
|
|
||||||
}
|
|
||||||
&.light {
|
|
||||||
color: #000;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
font-size: small;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ css }}">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="{{ theme }}">
|
|
||||||
<header>
|
|
||||||
<h1>{{ header }}</h1>
|
|
||||||
<h3>Login to Minecraft</h3>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<p class="message"></p>
|
|
||||||
<div class="left">
|
|
||||||
<p class="message info">
|
|
||||||
Your password will stored in plaintext because the Mojang API requires it, do not use this option unless
|
|
||||||
you are on a private computer.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<form action="#">
|
|
||||||
<label for="user">Username or E-mail</label><br>
|
|
||||||
<input type="text" id="user" placeholder="Username or E-mail" required><br>
|
|
||||||
|
|
||||||
<label for="password">Password:</label><br>
|
|
||||||
<input type="password" id="password" placeholder="Password" required><br>
|
|
||||||
|
|
||||||
<label for="save">Save password:</label>
|
|
||||||
<input type="checkbox" id="save"><br>
|
|
||||||
|
|
||||||
<button type="submit">Submit</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="../src/auth.js"></script>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
{{ footer }}
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
34
html/layout.html
Normal file
34
html/layout.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>SkinSwitcher</title>
|
||||||
|
<link rel="stylesheet" href="../sass/index.css">
|
||||||
|
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="../node_modules/@fortawesome/fontawesome-free/css/all.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="tabs is-right">
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://gitea.arnweb.nl/Hecc-inc./skinswitcher">SkinSwitcher</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/partials/profile.html">Profile</a></li>
|
||||||
|
<li><a href="/partials/change.html">Change</a></li>
|
||||||
|
<li><a href="/partials/random.html">Random</a></li>
|
||||||
|
<li><a href="/partials/gallery.html">Saved</a></li>
|
||||||
|
<li><a href="/partials/settings.html">Settings</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container px-4" id="content"></div>
|
||||||
|
|
||||||
|
<div class="container" id="notification-area"></div>
|
||||||
|
|
||||||
|
<script src="../src/index.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -1,45 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ css }}">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="{{ theme }}">
|
|
||||||
<header>
|
|
||||||
<h1>{{ header }}</h1>
|
|
||||||
<h3>Main menu</h3>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<p class="message success"></p>
|
|
||||||
<div class="left">
|
|
||||||
<p class="message info">
|
|
||||||
Logging out will delete your password from storage, if you want it to autofill next time you can just
|
|
||||||
close this window when you are done.
|
|
||||||
</p>
|
|
||||||
<p class="message warning">
|
|
||||||
Hecc inc. is not responsible for loss of data or explicit content (using random skin selection) seen
|
|
||||||
when using this program.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<button id="current">Current Skin</button><br>
|
|
||||||
<button id="upload">Upload Skin</button><br>
|
|
||||||
<button id="random">Random Skin</button><br>
|
|
||||||
<button id="gallery">Saved skins</button><br>
|
|
||||||
<button id="logout">Logout</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="../src/main.js"></script>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
{{ footer }}
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
88
html/partials/auth.html
Normal file
88
html/partials/auth.html
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<h1 class="title">Login</h1>
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column"></div>
|
||||||
|
<div class="column">
|
||||||
|
<form action="" class="form">
|
||||||
|
<div class="field">
|
||||||
|
<input type="text" class="input" id="user" placeholder="Username or E-mail">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<input type="password" class="input" id="password" placeholder="Password">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" id="keep">
|
||||||
|
Stay logged in
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button type="submit" class="button" id="submit">Submit</button>
|
||||||
|
<button class="button is-danger" id="clear">Clear</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
if (settings.accessToken != "") {
|
||||||
|
notify("primary", "Attempting auto-login...");
|
||||||
|
api.validate(
|
||||||
|
settings.accessToken
|
||||||
|
).then((res) => {
|
||||||
|
if (res.status == 204) {
|
||||||
|
api.refresh(
|
||||||
|
settings.accessToken
|
||||||
|
).then((res) => {
|
||||||
|
api.accessToken = res.data.accessToken;
|
||||||
|
api.profile = res.data.selectedProfile;
|
||||||
|
|
||||||
|
settings.accessToken = res.data.accessToken;
|
||||||
|
save();
|
||||||
|
|
||||||
|
notify("success", "Logged in as " + res.data.selectedProfile.name);
|
||||||
|
load("/partials/profile.html");
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
notify("danger", "Login error: " + res.status);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
notify("danger", "Login error: " + error.response.data.errorMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$("input#user")[0].value = settings.user;
|
||||||
|
|
||||||
|
$("button#submit").on("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
api.authenticate(
|
||||||
|
$("input#user")[0].value,
|
||||||
|
$("input#password")[0].value
|
||||||
|
).then((res) => {
|
||||||
|
api.accessToken = res.data.accessToken;
|
||||||
|
api.profile = res.data.selectedProfile;
|
||||||
|
|
||||||
|
if ($("input#keep")[0].checked) {
|
||||||
|
settings.user = $("input#user")[0].value;
|
||||||
|
settings.accessToken = res.data.accessToken;
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
notify("success", "Logged in as " + res.data.selectedProfile.name);
|
||||||
|
load("/partials/profile.html");
|
||||||
|
}).catch((error) => {
|
||||||
|
notify("danger", "Login failed: " + error.response.data.errorMessage);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
$("button#clear").on("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$("input#user")[0].value = "";
|
||||||
|
$("input#user")[0].value = "";
|
||||||
|
$("input#keep")[0].checked = false;
|
||||||
|
|
||||||
|
settings.user = "";
|
||||||
|
save();
|
||||||
|
});
|
||||||
|
</script>
|
58
html/partials/change.html
Normal file
58
html/partials/change.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<h1 class="title">Change skin</h1>
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column">
|
||||||
|
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
||||||
|
<div id="mesh" style="display: inline;"></div>
|
||||||
|
<button class="button" id="switch" style="display: none;">Switch to 2D</button>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<article class="message is-info">
|
||||||
|
<div class="message-body">
|
||||||
|
Your existing skin will be overwritten, be sure to make a backup
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<div class="buttons has-addons">
|
||||||
|
<button class="button" id="remote">Fetch from URL</button>
|
||||||
|
<button class="button" id="local">Upload file</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="#" id="remote" style="display: none;">
|
||||||
|
<div class="field">
|
||||||
|
<input class="input" type="text" id="url" placeholder="URL">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<button class="button" type="submit">Submit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<form action="#" id="local" style="display: none;">
|
||||||
|
<div class="field">
|
||||||
|
<div class="file has-name">
|
||||||
|
<label class="file-label">
|
||||||
|
<input class="file-input" type="file" name="resume">
|
||||||
|
<span class="file-cta">
|
||||||
|
<span class="file-icon">
|
||||||
|
<i class="fas fa-upload"></i>
|
||||||
|
</span>
|
||||||
|
<span class="file-label">
|
||||||
|
Choose a file…
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="file-name"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<button class="button" type="submit">Submit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$("button#remote, button#local").on("click", (e) => {
|
||||||
|
const id = e.target.id;
|
||||||
|
$("button#remote, button#local").css("display", "none");
|
||||||
|
$(`form#${id}`).css("display", "initial");
|
||||||
|
});
|
||||||
|
</script>
|
17
html/partials/edit.html
Normal file
17
html/partials/edit.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<h1 class="title">Edit Skin</h1>
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column">
|
||||||
|
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
||||||
|
<div id="mesh" style="display: inline;"></div>
|
||||||
|
<button class="button" id="switch" style="display: none;">Switch to 2D</button>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button" id="set">Use this skin</button>
|
||||||
|
</div>
|
||||||
|
<div class="buttons has-addons">
|
||||||
|
<button class="button" id="download">Download</button>
|
||||||
|
<button class="button" id="save">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
56
html/partials/gallery.html
Normal file
56
html/partials/gallery.html
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<h1 class="title">Saved skins</h1>
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column">
|
||||||
|
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
||||||
|
<div id="mesh" style="display: inline;"></div>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<div class="box">
|
||||||
|
<div id="list"></div>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button" id="set">Use this skin</button>
|
||||||
|
<button class="button" id="switch" style="display: none;">Switch to 2D</button>
|
||||||
|
</div>
|
||||||
|
<div class="buttons has-addons">
|
||||||
|
<button class="button" id="download">Download</button>
|
||||||
|
<button class="button is-danger" id="delete">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../src/skin.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
fs.readdir(path.join(__dirname + "/../assets/skins/download/"), (err, files) => {
|
||||||
|
files.forEach(file => {
|
||||||
|
var ctx = document.createElement("canvas");
|
||||||
|
ctx.width = 32;
|
||||||
|
ctx.height = 32;
|
||||||
|
$(ctx).addClass("py-1 px-1");
|
||||||
|
|
||||||
|
var canvas = ctx.getContext("2d");
|
||||||
|
|
||||||
|
canvas["imageSmoothingEnabled"] = false; /* standard */
|
||||||
|
canvas["mozImageSmoothingEnabled"] = false; /* Firefox */
|
||||||
|
canvas["oImageSmoothingEnabled"] = false; /* Opera */
|
||||||
|
canvas["webkitImageSmoothingEnabled"] = false; /* Safari */
|
||||||
|
canvas["msImageSmoothingEnabled"] = false; /* IE */
|
||||||
|
|
||||||
|
var img = new Image();
|
||||||
|
img.onload = function () {
|
||||||
|
canvas.drawImage(img, 8, 8, 8, 8, 0, 0, 32, 32);
|
||||||
|
};
|
||||||
|
img.src = "data:image/png;base64," + fs.readFileSync(path.join(__dirname +
|
||||||
|
"/../assets/skins/download/" + file), {
|
||||||
|
encoding: "base64"
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.addEventListener("click", (e) => {
|
||||||
|
setView(img.src);
|
||||||
|
});
|
||||||
|
|
||||||
|
list.appendChild(ctx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
1
html/partials/login.html
Normal file
1
html/partials/login.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<h1 class="title">Login</h1>
|
51
html/partials/profile.html
Normal file
51
html/partials/profile.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<h1 class="title">Profile</h1>
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column">
|
||||||
|
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
||||||
|
<div id="mesh" style="display: inline;"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<article class="message is-info">
|
||||||
|
<div class="message-body" id="profile"></div>
|
||||||
|
</article>
|
||||||
|
<div class="buttons">
|
||||||
|
<a class="button" href="/partials/edit.html" id="edit">Edit</a>
|
||||||
|
</div>
|
||||||
|
<div class="buttons has-addons">
|
||||||
|
<button class="button" id="download">Download</button>
|
||||||
|
<button class="button" id="save">Save</button>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button" id="switch" style="display: none;">Switch to 2D</button>
|
||||||
|
<button class="button is-danger" id="logout">Logout</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../src/skin.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
axios({
|
||||||
|
method: "GET",
|
||||||
|
url: "https://sessionserver.mojang.com/session/minecraft/profile/" + api.profile.id
|
||||||
|
}).then((data) => {
|
||||||
|
var result = JSON.parse(atob(data.data.properties[0].value));
|
||||||
|
var model = result.textures.SKIN.metadata.model;
|
||||||
|
$("div#profile").html(`
|
||||||
|
Username: ${result.profileName} <br>
|
||||||
|
Skin type: ${model.substr(0,1).toUpperCase() + model.substr(1)}
|
||||||
|
`);
|
||||||
|
setView(result.textures.SKIN.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("button#logout").on("click", () => {
|
||||||
|
api.invalidate(
|
||||||
|
settings.accessToken
|
||||||
|
).then((res) => {
|
||||||
|
settings.accessToken = "";
|
||||||
|
save();
|
||||||
|
load("/partials/auth.html");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
64
html/partials/random.html
Normal file
64
html/partials/random.html
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<h1 class="title">Random Skin</h1>
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column">
|
||||||
|
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
||||||
|
<div id="mesh" style="display: inline;"></div>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<article class="message is-warning">
|
||||||
|
<div class="message-body">
|
||||||
|
I am not responsible for any explicit content displayed when using the random skin function.
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button" id="random">Random</button>
|
||||||
|
<button class="button" id="set">Use this skin</button>
|
||||||
|
</div>
|
||||||
|
<div class="buttons has-addons">
|
||||||
|
<button class="button" id="download">Download</button>
|
||||||
|
<button class="button" id="save">Save</button>
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button" id="switch" style="display: none;">Switch to 2D</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../src/skin.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var parser = new DOMParser();
|
||||||
|
var randomUrls = [];
|
||||||
|
|
||||||
|
axios({
|
||||||
|
method: "GET",
|
||||||
|
url: "https://nl.namemc.com/minecraft-skins/random"
|
||||||
|
}).then((data) => {
|
||||||
|
var namemc = parser.parseFromString(data.data, "text/html");
|
||||||
|
namemc.querySelectorAll("a").forEach((e) => {
|
||||||
|
var href = e.getAttribute("href");
|
||||||
|
if (href.includes("skin")) {
|
||||||
|
var id = href.split("/")[2];
|
||||||
|
if (id && id.length == 16 && !(id.includes("-"))) {
|
||||||
|
randomUrls.push("https://nl.namemc.com/texture/" + id + ".png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function random() {
|
||||||
|
if (randomUrls.length == 0) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
var id = Math.floor(Math.random() * (randomUrls.length - 0)) + 0;
|
||||||
|
setView(randomUrls[id]);
|
||||||
|
randomUrls.splice(id, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
random();
|
||||||
|
|
||||||
|
$("button#random")[0].addEventListener("click", (e) => {
|
||||||
|
random();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
26
html/partials/settings.html
Normal file
26
html/partials/settings.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<h1 class="title">Settings</h1>
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column">
|
||||||
|
<h6 class="title is-6">Credits</h6>
|
||||||
|
Code by <a href="https://arnweb.nl">McArn</a><br>
|
||||||
|
Assets by Cirkel
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<h6 class="title is-6">Theme</h6>
|
||||||
|
<div class="buttons has-addons">
|
||||||
|
<button class="button" id="light">Light</button>
|
||||||
|
<button class="button is-dark" id="dark">Dark</button>
|
||||||
|
<p>
|
||||||
|
Use a theme with SkinSwitcher.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h6 class="title is-6">Reset all settings</h6>
|
||||||
|
<div class="buttons">
|
||||||
|
<button class="button is-danger" id="reset">Reset</button>
|
||||||
|
<p>
|
||||||
|
Resetting will delete all settings, credentials and saved skins.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,41 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ css }}">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="{{ theme }}">
|
|
||||||
<header>
|
|
||||||
<h1>{{ header }}</h1>
|
|
||||||
<h3>Current Skin</h3>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="content" id="current">
|
|
||||||
<p class="message"></p>
|
|
||||||
|
|
||||||
<div class="left">
|
|
||||||
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
|
||||||
<div id="mesh" style="display: inline;"></div>
|
|
||||||
<button id="switch" style="display: none;">Switch to 2D</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right">
|
|
||||||
<button id="edit">Edit</button>
|
|
||||||
<button id="download">Download</button>
|
|
||||||
<button id="save">Save</button>
|
|
||||||
<button id="main">Back</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="../src/skin.js"></script>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
{{ footer }}
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,40 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ css }}">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="{{ theme }}">
|
|
||||||
<header>
|
|
||||||
<h1>{{ header }}</h1>
|
|
||||||
<h3>Edit Skin</h3>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="content" id="edit">
|
|
||||||
<p class="message"></p>
|
|
||||||
|
|
||||||
<div class="left">
|
|
||||||
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
|
||||||
<div id="mesh" style="display: inline;"></div>
|
|
||||||
<button id="switch" style="display: none;">Switch to 2D</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right">
|
|
||||||
<button id="set">Use this skin</button>
|
|
||||||
<button id="download">Download</button>
|
|
||||||
<button id="main">Back</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="../src/skin.js"></script>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
{{ footer }}
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,43 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ css }}">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="{{ theme }}">
|
|
||||||
<header>
|
|
||||||
<h1>{{ header }}</h1>
|
|
||||||
<h3>Saved skins</h3>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="content" id="gallery">
|
|
||||||
<p class="message"></p>
|
|
||||||
|
|
||||||
<div class="left">
|
|
||||||
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
|
||||||
<div id="mesh" style="display: inline;"></div>
|
|
||||||
<button id="switch" style="display: none;">Switch to 2D</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right">
|
|
||||||
<div id="list">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<button id="set">Use this skin</button>
|
|
||||||
<button id="delete">Delete</button>
|
|
||||||
<button id="main">Back</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="../src/skin.js"></script>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
{{ footer }}
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,42 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ css }}">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="{{ theme }}">
|
|
||||||
<header>
|
|
||||||
<h1>{{ header }}</h1>
|
|
||||||
<h3>Random Skin</h3>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="content" id="random">
|
|
||||||
<p class="message"></p>
|
|
||||||
|
|
||||||
<div class="left">
|
|
||||||
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
|
||||||
<div id="mesh" style="display: inline;"></div>
|
|
||||||
<button id="switch" style="display: none;">Switch to 2D</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right">
|
|
||||||
<button id="random">Random</button>
|
|
||||||
<button id="set">Use this skin</button>
|
|
||||||
<button id="download">Download</button>
|
|
||||||
<button id="save">Save</button>
|
|
||||||
<button id="main">Back</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="../src/skin.js"></script>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
{{ footer }}
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,49 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<link rel="stylesheet" href="{{ css }}">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="{{ theme }}">
|
|
||||||
<header>
|
|
||||||
<h1>{{ header }}</h1>
|
|
||||||
<h3>Upload Skin</h3>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="content" id="upload">
|
|
||||||
<p class="message"></p>
|
|
||||||
|
|
||||||
<div class="left">
|
|
||||||
<img src="" class="skin" id="flat" alt="Current Skin" style="display: none;">
|
|
||||||
<div id="mesh" style="display: inline;"></div>
|
|
||||||
<button id="switch" style="display: none;">Switch to 2D</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right">
|
|
||||||
<button id="remote">Fetch from URL</button>
|
|
||||||
<button id="local">Upload file</button>
|
|
||||||
<form action="#" id="remote" style="display: none;">
|
|
||||||
<input type="text" id="url" placeholder="URL">
|
|
||||||
<button type="submit">Submit</button>
|
|
||||||
</form>
|
|
||||||
<form action="#" id="local" style="display: none;">
|
|
||||||
<input type="file" id="skin">
|
|
||||||
<button type="submit">Submit</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<button id="main">Back</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="../src/skin.js"></script>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
{{ footer }}
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
108
main.js
108
main.js
@ -1,79 +1,43 @@
|
|||||||
const { app, BrowserWindow, ipcMain } = require("electron");
|
const { app, BrowserWindow, ipcMain } = require("electron");
|
||||||
const { download } = require("electron-dl");
|
const { download } = require("electron-dl");
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
if (process.env.NODE_ENV === "dev") {
|
if (process.env.NODE_ENV === "dev") {
|
||||||
require("electron-reload")(__dirname, {
|
require("electron-reload")(__dirname, {
|
||||||
electron: path.join(__dirname, "node_modules", ".bin", "electron"),
|
electron: path.join(__dirname, "node_modules", ".bin", "electron"),
|
||||||
ignored: [
|
ignored: [
|
||||||
/node_modules|[/\\]\./,
|
/node_modules|[/\\]\./,
|
||||||
/assets\/skins|[/\\]\./
|
/assets|[/\\]\./
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWindow () {
|
function createWindow () {
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
width: 640,
|
width: 720,
|
||||||
height: 520,
|
height: 480,
|
||||||
backgroundColor: "#ffffff",
|
backgroundColor: "#ffffff",
|
||||||
resizable: false,
|
resizable: false,
|
||||||
frame: true,
|
frame: true,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true
|
nodeIntegration: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// and load the index.html of the app.
|
// and load the index.html of the app.
|
||||||
win.loadFile(path.join("html/auth.hbs"));
|
win.loadFile(path.join("html/layout.html"));
|
||||||
|
|
||||||
// Open the DevTools.
|
// Open the DevTools.
|
||||||
if (process.env.NODE_ENV === "dev") win.webContents.openDevTools();
|
if (process.env.NODE_ENV === "dev") win.webContents.openDevTools();
|
||||||
|
|
||||||
|
win.removeMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load previous credentials and settings
|
|
||||||
var authData = JSON.parse(fs.readFileSync("./assets/data.json"));
|
|
||||||
|
|
||||||
// Define handlebars with loaded settings
|
|
||||||
require("electron-handlebars")({
|
|
||||||
css: "../css/index.css",
|
|
||||||
title: "SkinSwitcher",
|
|
||||||
theme: authData.theme,
|
|
||||||
header: "⇄ SkinSwitcher",
|
|
||||||
footer: "Made by Hecc-inc."
|
|
||||||
});
|
|
||||||
|
|
||||||
// Respond to renderer requests
|
|
||||||
ipcMain.on("getAuth", (event) => {
|
|
||||||
authData = JSON.parse(fs.readFileSync("./assets/data.json"));
|
|
||||||
event.returnValue = authData;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("setAuth", (event, data) => {
|
|
||||||
authData = JSON.parse(fs.readFileSync("./assets/data.json"));
|
|
||||||
if (data != authData) {
|
|
||||||
fs.writeFileSync("./assets/data.json", JSON.stringify(data));
|
|
||||||
}
|
|
||||||
event.returnValue = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("download", (event, info) => {
|
ipcMain.on("download", (event, info) => {
|
||||||
download(BrowserWindow.getFocusedWindow(), info.url, info.properties)
|
download(BrowserWindow.getFocusedWindow(), info.url, info.properties)
|
||||||
.then(dl => event.reply("downloadResult", dl.getSavePath()));
|
.then(dl => event.reply("downloadResult", dl.getSavePath()));
|
||||||
});
|
|
||||||
|
|
||||||
// Create session variables for uuid storage
|
|
||||||
var session = {};
|
|
||||||
|
|
||||||
ipcMain.on("getSession", (event) => {
|
|
||||||
event.returnValue = session;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("setSession", (event, data) => {
|
|
||||||
session = data;
|
|
||||||
event.returnValue = true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// This method will be called when Electron has finished
|
// This method will be called when Electron has finished
|
||||||
@ -83,19 +47,19 @@ app.whenReady().then(createWindow);
|
|||||||
|
|
||||||
// Quit when all windows are closed.
|
// Quit when all windows are closed.
|
||||||
app.on("window-all-closed", () => {
|
app.on("window-all-closed", () => {
|
||||||
// On macOS it is common for applications and their menu bar
|
// On macOS it is common for applications and their menu bar
|
||||||
// to stay active until the user quits explicitly with Cmd + Q
|
// to stay active until the user quits explicitly with Cmd + Q
|
||||||
if (process.platform !== "darwin") {
|
if (process.platform !== "darwin") {
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on("activate", () => {
|
app.on("activate", () => {
|
||||||
// On macOS it's common to re-create a window in the app when the
|
// On macOS it's common to re-create a window in the app when the
|
||||||
// dock icon is clicked and there are no other windows open.
|
// dock icon is clicked and there are no other windows open.
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
createWindow();
|
createWindow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// In this file you can include the rest of your app's specific main process
|
// In this file you can include the rest of your app's specific main process
|
||||||
|
1073
package-lock.json
generated
1073
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,10 @@
|
|||||||
"author": "Arne van Iterson",
|
"author": "Arne van Iterson",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-free": "^5.13.1",
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
|
"bulma": "^0.9.0",
|
||||||
|
"bulma-prefers-dark": "0.1.0-beta.0",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"electron": "^8.2.5",
|
"electron": "^8.2.5",
|
||||||
"electron-builder": "^22.6.0",
|
"electron-builder": "^22.6.0",
|
||||||
@ -24,10 +27,14 @@
|
|||||||
"electron-handlebars": "^2.0.0",
|
"electron-handlebars": "^2.0.0",
|
||||||
"electron-reload": "^1.5.0",
|
"electron-reload": "^1.5.0",
|
||||||
"eslint": "^7.0.0",
|
"eslint": "^7.0.0",
|
||||||
|
"jquery": "^3.5.1",
|
||||||
"merge-images": "^2.0.0",
|
"merge-images": "^2.0.0",
|
||||||
|
"node-sass": "^4.14.1",
|
||||||
"pngjs": "^5.0.0",
|
"pngjs": "^5.0.0",
|
||||||
"skinview3d": "^2.0.0-alpha.1",
|
"skinview3d": "^2.0.0-alpha.1",
|
||||||
"three": "^0.116.1"
|
"three": "^0.116.1",
|
||||||
|
"unix-timestamp": "^0.2.0",
|
||||||
|
"uuid": "^8.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
12365
sass/index.css
Normal file
12365
sass/index.css
Normal file
File diff suppressed because it is too large
Load Diff
71
sass/index.css.map
Normal file
71
sass/index.css.map
Normal file
File diff suppressed because one or more lines are too long
3
sass/index.sass
Normal file
3
sass/index.sass
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@charset "utf-8"
|
||||||
|
@import "index.scss"
|
||||||
|
@import "../node_modules/bulma/bulma.sass";
|
11
sass/index.scss
Normal file
11
sass/index.scss
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
img.skin#flat {
|
||||||
|
image-rendering: pixelated;
|
||||||
|
height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#notification-area {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(50% - 1em);
|
||||||
|
bottom: 1em;
|
||||||
|
right: 1em;
|
||||||
|
}
|
70
src/api.js
Normal file
70
src/api.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
const axios = require("axios");
|
||||||
|
|
||||||
|
class MojangAPI {
|
||||||
|
constructor(clientId) {
|
||||||
|
this.clientId = clientId;
|
||||||
|
this.accessToken;
|
||||||
|
this.profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticate(user, password) {
|
||||||
|
var payload = {
|
||||||
|
"agent": {
|
||||||
|
"name": "Minecraft",
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"username": user,
|
||||||
|
"password": password,
|
||||||
|
"clientToken": this.clientId
|
||||||
|
};
|
||||||
|
|
||||||
|
return axios({
|
||||||
|
method: "POST",
|
||||||
|
url: "https://authserver.mojang.com/authenticate",
|
||||||
|
data: payload,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh(token) {
|
||||||
|
var payload = {
|
||||||
|
"accessToken": token,
|
||||||
|
"clientToken": this.clientId,
|
||||||
|
"requestUser": true
|
||||||
|
};
|
||||||
|
|
||||||
|
return axios({
|
||||||
|
method: "POST",
|
||||||
|
url: "https://authserver.mojang.com/refresh",
|
||||||
|
data: payload,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(token) {
|
||||||
|
var payload = {
|
||||||
|
"accessToken": token,
|
||||||
|
"clientToken": this.clientId
|
||||||
|
};
|
||||||
|
|
||||||
|
return axios({
|
||||||
|
method: "POST",
|
||||||
|
url: "https://authserver.mojang.com/validate",
|
||||||
|
data: payload,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate(token) {
|
||||||
|
var payload = {
|
||||||
|
"accessToken": token,
|
||||||
|
"clientToken": this.clientId
|
||||||
|
};
|
||||||
|
|
||||||
|
return axios({
|
||||||
|
method: "POST",
|
||||||
|
url: "https://authserver.mojang.com/invalidate",
|
||||||
|
data: payload,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MojangAPI;
|
72
src/auth.js
72
src/auth.js
@ -1,72 +0,0 @@
|
|||||||
/* eslint-disable no-unused-vars */
|
|
||||||
const { ipcRenderer, remote } = require("electron");
|
|
||||||
const axios = require("axios").default;
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
// Check if a session already exists
|
|
||||||
var session = ipcRenderer.sendSync("getSession");
|
|
||||||
if (session.accessToken) {
|
|
||||||
console.log("Session does exist, go to main");
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/main.hbs`));
|
|
||||||
} else {
|
|
||||||
console.log("Session does not exist, continue");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read auth data from storage and define variables
|
|
||||||
var authData = ipcRenderer.sendSync("getAuth");
|
|
||||||
var save = true;
|
|
||||||
|
|
||||||
async function authenticate(user, password) {
|
|
||||||
var payload = {
|
|
||||||
"agent": {
|
|
||||||
"name": "Minecraft",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
"username": user,
|
|
||||||
"password": password
|
|
||||||
};
|
|
||||||
|
|
||||||
axios({
|
|
||||||
method: "POST",
|
|
||||||
url: "https://authserver.mojang.com/authenticate",
|
|
||||||
data: payload,
|
|
||||||
}).then((data) => {
|
|
||||||
console.log("Login successfull");
|
|
||||||
session = data.data;
|
|
||||||
|
|
||||||
// Save username (& password) for future login
|
|
||||||
authData.user = user;
|
|
||||||
authData.password = (save) ? password : "";
|
|
||||||
|
|
||||||
if (ipcRenderer.sendSync("setAuth", authData)) {
|
|
||||||
if (ipcRenderer.sendSync("setSession", session)) {
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/main.hbs`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch((data) => {
|
|
||||||
console.log("Login failure");
|
|
||||||
console.log(data.response);
|
|
||||||
|
|
||||||
const msg = document.querySelectorAll("div.content > p.message")[0];
|
|
||||||
msg.classList.add("warning");
|
|
||||||
msg.innerHTML = data.response.data.errorMessage;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get access token from Mojang Authserver
|
|
||||||
const form = document.querySelectorAll("div.content > div.right > form")[0];
|
|
||||||
form.querySelectorAll("input#user")[0].value = authData.user;
|
|
||||||
form.querySelectorAll("input#password")[0].value = authData.password;
|
|
||||||
|
|
||||||
if (authData.password != "") {
|
|
||||||
form.querySelectorAll("input#save")[0].checked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for form submit
|
|
||||||
form.addEventListener("submit", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
authData.user = form.querySelectorAll("input#user")[0].value;
|
|
||||||
authData.password = form.querySelectorAll("input#password")[0].value;
|
|
||||||
save = form.querySelectorAll("input#save")[0].checked;
|
|
||||||
authenticate(authData.user, authData.password);
|
|
||||||
});
|
|
@ -1,17 +0,0 @@
|
|||||||
//fs.createReadStream("../data/in.png")
|
|
||||||
// .pipe(
|
|
||||||
// new PNG({
|
|
||||||
// filterType: 4,
|
|
||||||
// })
|
|
||||||
// )
|
|
||||||
// .on("parsed", (image) => {
|
|
||||||
// axios({
|
|
||||||
// method: "PUT",
|
|
||||||
// url: `https://api.mojang.com/user/profile/${data.data.selectedProfile.id}/skin`,
|
|
||||||
// data: {
|
|
||||||
// model: "",
|
|
||||||
// file: image
|
|
||||||
// },
|
|
||||||
// headers: {"Authorization": "Bearer " + data.data.accessToken}
|
|
||||||
// }).catch(console.log);
|
|
||||||
// });
|
|
96
src/index.js
Normal file
96
src/index.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
const {
|
||||||
|
shell,
|
||||||
|
ipcRenderer
|
||||||
|
} = require("electron");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const $ = require("jquery");
|
||||||
|
const skinview3d = require("skinview3d/dist/skinview3d.min.js");
|
||||||
|
const axios = require("axios");
|
||||||
|
const uuid = require("uuid");
|
||||||
|
|
||||||
|
const MojangAPI = require(path.join(__dirname + "/../src/api.js"));
|
||||||
|
|
||||||
|
var settings = JSON.parse(fs.readFileSync(path.join(__dirname + "/../assets/data.json")));
|
||||||
|
var api;
|
||||||
|
|
||||||
|
if (settings.clientId == "") {
|
||||||
|
notify("warning", "Generating client id");
|
||||||
|
settings.clientId = uuid.v4().replace(/-/g, "");
|
||||||
|
save();
|
||||||
|
|
||||||
|
api = new MojangAPI(settings.clientId);
|
||||||
|
} else {
|
||||||
|
api = new MojangAPI(settings.clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function load(url) {
|
||||||
|
var re = new RegExp("^(http|https)://", "i");
|
||||||
|
if (re.test(url)) {
|
||||||
|
shell.openExternal(url);
|
||||||
|
} else {
|
||||||
|
if (!api.accessToken && url != "/partials/auth.html") {
|
||||||
|
notify("warning", "You need to login first");
|
||||||
|
} else {
|
||||||
|
$("#content").load(path.join(__dirname + url), () => {
|
||||||
|
$("a").each((i, e) => {
|
||||||
|
$(e).off("click");
|
||||||
|
$(e).on("click", (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
load(e.getAttribute("href"));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!re.test($(e).attr("href"))) {
|
||||||
|
if ($(e).is("div.tabs > ul > li > a")) {
|
||||||
|
if (url == $(e).attr("href")) {
|
||||||
|
$(e).parent().addClass("is-active");
|
||||||
|
} else {
|
||||||
|
$(e).parent().removeClass("is-active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(type, message) {
|
||||||
|
var notificationArea = document.getElementById("notification-area");
|
||||||
|
|
||||||
|
var notification = document.createElement("div");
|
||||||
|
notification.className = `notification is-${type}`;
|
||||||
|
|
||||||
|
var close = document.createElement("div");
|
||||||
|
close.className = "delete";
|
||||||
|
|
||||||
|
notification.appendChild(close);
|
||||||
|
notification.innerHTML += message;
|
||||||
|
|
||||||
|
notificationArea.appendChild(notification);
|
||||||
|
|
||||||
|
const delay = setTimeout(() => {
|
||||||
|
notificationArea.removeChild(notification);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
notification.addEventListener("click", (e) => {
|
||||||
|
if ($(e.target).is("div.notification > div.delete")) {
|
||||||
|
clearTimeout(delay);
|
||||||
|
notificationArea.removeChild(notification);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
fs.writeFile(path.join(__dirname + "/../assets/data.json"), JSON.stringify(settings, null, " "), (e) => {
|
||||||
|
if (e) {
|
||||||
|
notify("danger", "Setting failed to save");
|
||||||
|
} else {
|
||||||
|
notify("success", "Settings saved successfully");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
load("/partials/auth.html");
|
||||||
|
|
38
src/main.js
38
src/main.js
@ -1,38 +0,0 @@
|
|||||||
/* eslint-disable no-unused-vars */
|
|
||||||
const { ipcRenderer, remote } = require("electron");
|
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
const session = ipcRenderer.sendSync("getSession");
|
|
||||||
if (!session.accessToken) {
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/auth.hbs`));
|
|
||||||
}
|
|
||||||
|
|
||||||
const msg = document.querySelectorAll("div.content > p.message")[0];
|
|
||||||
msg.classList.add("success");
|
|
||||||
msg.innerHTML = `Logged in as ${session.selectedProfile.name}`;
|
|
||||||
|
|
||||||
document.querySelectorAll("div.content > div.right > button#current")[0].addEventListener("click", (e) => {
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/skin_current.hbs`));
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll("div.content > div.right > button#upload")[0].addEventListener("click", (e) => {
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/skin_upload.hbs`));
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll("div.content > div.right > button#random")[0].addEventListener("click", (e) => {
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/skin_random.hbs`));
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll("div.content > div.right > button#gallery")[0].addEventListener("click", (e) => {
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/skin_gallery.hbs`));
|
|
||||||
});
|
|
||||||
|
|
||||||
document.querySelectorAll("div.content > div.right > button#logout")[0].addEventListener("click", (e) => {
|
|
||||||
if (ipcRenderer.sendSync("setSession", {})) {
|
|
||||||
var authData = ipcRenderer.sendSync("getAuth");
|
|
||||||
authData.password = "";
|
|
||||||
if (ipcRenderer.sendSync("setAuth", authData)) {
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/auth.hbs`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
284
src/skin.js
284
src/skin.js
@ -1,49 +1,18 @@
|
|||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-undef */
|
||||||
const {
|
var button = {};
|
||||||
BrowserWindow,
|
$("button").each((i, e) => {
|
||||||
ipcRenderer,
|
button[e.id] = e;
|
||||||
remote
|
});
|
||||||
} = require("electron");
|
|
||||||
const axios = require("axios").default;
|
|
||||||
const path = require("path");
|
|
||||||
const fs = require("fs");
|
|
||||||
const skinview3d = require("skinview3d/dist/skinview3d.min.js");
|
|
||||||
|
|
||||||
const session = ipcRenderer.sendSync("getSession");
|
|
||||||
if (!session.accessToken) {
|
|
||||||
console.log("Session does not exist, return to auth");
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/auth.hbs`));
|
|
||||||
} else {
|
|
||||||
console.log("Session does exist, continue");
|
|
||||||
}
|
|
||||||
|
|
||||||
const regex = /(?!\w*_)\w*(?=\.\w*)/g;
|
|
||||||
var action = __filename.match(regex)[0];
|
|
||||||
var parser = new DOMParser();
|
|
||||||
|
|
||||||
var randomUrls = [];
|
|
||||||
|
|
||||||
// Switch button and views
|
|
||||||
var button = {
|
|
||||||
switch: document.querySelectorAll("div.left > button#switch"),
|
|
||||||
edit: document.querySelectorAll("div.right > button#edit"),
|
|
||||||
random: document.querySelectorAll("div.right > button#random"),
|
|
||||||
save: document.querySelectorAll("div.right > button#save"),
|
|
||||||
download: document.querySelectorAll("div.right > button#download"),
|
|
||||||
main: document.querySelectorAll("div.right > button#main"),
|
|
||||||
set: document.querySelectorAll("div.right > button#set")
|
|
||||||
};
|
|
||||||
var message = document.querySelectorAll("div.content > p.message")[0];
|
|
||||||
var list = document.querySelectorAll("div.right > div#list")[0];
|
|
||||||
var view = {
|
var view = {
|
||||||
flat: document.querySelectorAll("div.left > img.skin#flat")[0],
|
flat: document.querySelectorAll("div.column > img.skin#flat")[0],
|
||||||
mesh: document.querySelectorAll("div.left > div#mesh")[0]
|
mesh: document.querySelectorAll("div.column > div#mesh")[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
var skin = new skinview3d.SkinViewer({
|
var skin = new skinview3d.SkinViewer({
|
||||||
domElement: view.mesh,
|
domElement: view.mesh,
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 250
|
height: 300
|
||||||
});
|
});
|
||||||
|
|
||||||
var control = skinview3d.createOrbitControls(skin);
|
var control = skinview3d.createOrbitControls(skin);
|
||||||
@ -51,192 +20,73 @@ control.enableRotate = true;
|
|||||||
control.enableZoom = false;
|
control.enableZoom = false;
|
||||||
control.enablePan = false;
|
control.enablePan = false;
|
||||||
|
|
||||||
function setView(url, controls = true) {
|
function setView(src, controls = true) {
|
||||||
button.switch[0].style.display = "initial";
|
button.switch.style.display = "initial";
|
||||||
|
|
||||||
view.flat.src = url;
|
view.flat.src = src;
|
||||||
skin.skinUrl = url;
|
skin.skinUrl = src;
|
||||||
control.enableRotate = controls;
|
control.enableRotate = controls;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch button action
|
button.switch.addEventListener("click", () => {
|
||||||
button.switch[0].addEventListener("click", (e) => {
|
if (view.flat.style.display == "inline") {
|
||||||
if (view.flat.style.display == "inline") {
|
view.flat.style.display = "none";
|
||||||
view.flat.style.display = "none";
|
view.mesh.style.display = "inline";
|
||||||
view.mesh.style.display = "inline";
|
button.switch.innerHTML = "Switch to 2D";
|
||||||
button.switch[0].innerHTML = "Switch to 2D";
|
} else {
|
||||||
} else {
|
view.flat.style.display = "inline";
|
||||||
view.flat.style.display = "inline";
|
view.mesh.style.display = "none";
|
||||||
view.mesh.style.display = "none";
|
button.switch.innerHTML = "Switch to 3D";
|
||||||
button.switch[0].innerHTML = "Switch to 3D";
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (button.download && button.save) {
|
||||||
switch (action) {
|
button.download.addEventListener("click", () => {
|
||||||
|
ipcRenderer.send("download", {
|
||||||
case "current":
|
url: view.flat.src,
|
||||||
axios({
|
properties: {
|
||||||
method: "GET",
|
saveAs: true
|
||||||
url: "https://sessionserver.mojang.com/session/minecraft/profile/" + session.selectedProfile.id
|
}
|
||||||
}).then((data) => {
|
|
||||||
setView(JSON.parse(atob(data.data.properties[0].value)).textures.SKIN.url);
|
|
||||||
|
|
||||||
button.edit[0].addEventListener("click", (e) => {
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/skin_edit.hbs`));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "upload":
|
|
||||||
console.log(window);
|
|
||||||
|
|
||||||
var buttons = document.querySelectorAll("div.right > button");
|
|
||||||
buttons.forEach(element => {
|
|
||||||
if (element.id != "main") {
|
|
||||||
element.addEventListener("click", (e) => {
|
|
||||||
buttons.forEach(btn => {
|
|
||||||
if (btn.id != "main") {
|
|
||||||
btn.style.display = "none";
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
document.querySelectorAll(`div.right > form#${element.id}`)[0].style.display = "block";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
ipcRenderer.on("downloadResult", () => {
|
||||||
|
notify("success", "Download successful.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
button.save.addEventListener("click", () => {
|
||||||
|
const src = view.flat.src;
|
||||||
|
const r = new RegExp(/^data:image\/png;base64,/);
|
||||||
|
|
||||||
case "random":
|
if (r.test(src)) {
|
||||||
axios({
|
notify("primary", "Decoding Base64...");
|
||||||
method: "GET",
|
var data = src.replace(/^data:image\/png;base64,/, "");
|
||||||
url: "https://nl.namemc.com/minecraft-skins/random"
|
fs.writeFile(path.join(__dirname + `/../assets/skins/download/${Date.now()}.png`), data, "base64", function(err) {
|
||||||
}).then((data) => {
|
if (err) {
|
||||||
var namemc = parser.parseFromString(data.data, "text/html");
|
notify("danger", "Skin has not been saved: " + err);
|
||||||
namemc.querySelectorAll("a").forEach((e) => {
|
} else {
|
||||||
var href = e.getAttribute("href");
|
notify("success", "Skin has been saved");
|
||||||
if (href.includes("skin")) {
|
}
|
||||||
var id = href.split("/")[2];
|
});
|
||||||
if (id && id.length == 16 && !(id.includes("-"))) {
|
} else {
|
||||||
randomUrls.push("https://nl.namemc.com/texture/" + id + ".png");
|
notify("primary", "Getting skin...");
|
||||||
|
axios({
|
||||||
|
method: "get",
|
||||||
|
url: src,
|
||||||
|
responseType: "stream",
|
||||||
|
adapter: require("axios/lib/adapters/http")
|
||||||
|
}).then((res) => {
|
||||||
|
var p = res.request.path.split("/")[2];
|
||||||
|
if (p.split(".").length < 2) {
|
||||||
|
p = p + ".png";
|
||||||
|
}
|
||||||
|
res.data.pipe(fs.createWriteStream(path.join(__dirname + `/../assets/skins/download/${p}`)));
|
||||||
|
notify("success", "Save successful");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function random() {
|
|
||||||
console.log(randomUrls.length);
|
|
||||||
if (randomUrls.length == 0) {
|
|
||||||
window.location.reload();
|
|
||||||
} else {
|
|
||||||
var id = Math.floor(Math.random() * (randomUrls.length - 0)) + 0;
|
|
||||||
setView(randomUrls[id]);
|
|
||||||
randomUrls.splice(id, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
random();
|
|
||||||
|
|
||||||
button.random[0].addEventListener("click", (e) => {
|
|
||||||
random();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "edit":
|
|
||||||
axios({
|
|
||||||
method: "GET",
|
|
||||||
url: "https://sessionserver.mojang.com/session/minecraft/profile/" + session.selectedProfile.id
|
|
||||||
}).then((data) => {
|
|
||||||
const url = JSON.parse(atob(data.data.properties[0].value)).textures.SKIN.url;
|
|
||||||
setView(url);
|
|
||||||
|
|
||||||
axios({
|
|
||||||
method: "get",
|
|
||||||
url: url,
|
|
||||||
responseType: "stream",
|
|
||||||
adapter: require("axios/lib/adapters/http")
|
|
||||||
}).then((res) => {
|
|
||||||
res.data.pipe(fs.createWriteStream(path.join(__dirname + "/../assets/skins/edit/in.png")));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "gallery":
|
|
||||||
fs.readdir(path.join(__dirname + "/../assets/skins/download/"), (err, files) => {
|
|
||||||
files.forEach(file => {
|
|
||||||
var ctx = document.createElement("canvas");
|
|
||||||
ctx.width = 32;
|
|
||||||
ctx.height = 32;
|
|
||||||
var canvas = ctx.getContext("2d");
|
|
||||||
|
|
||||||
canvas["imageSmoothingEnabled"] = false; /* standard */
|
|
||||||
canvas["mozImageSmoothingEnabled"] = false; /* Firefox */
|
|
||||||
canvas["oImageSmoothingEnabled"] = false; /* Opera */
|
|
||||||
canvas["webkitImageSmoothingEnabled"] = false; /* Safari */
|
|
||||||
canvas["msImageSmoothingEnabled"] = false; /* IE */
|
|
||||||
|
|
||||||
var img = new Image();
|
|
||||||
img.onload = function() {
|
|
||||||
canvas.drawImage(img, 8, 8, 8, 8, 0, 0, 32, 32);
|
|
||||||
};
|
|
||||||
img.src = "data:image/png;base64," + fs.readFileSync(path.join(__dirname + "/../assets/skins/download/" + file), {encoding: "base64"});
|
|
||||||
|
|
||||||
ctx.addEventListener("click", (e) => {
|
|
||||||
setView(img.src);
|
|
||||||
});
|
|
||||||
|
|
||||||
list.appendChild(ctx);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set button action
|
fs.readFile(path.join(__dirname + "/../assets/skins/dummy.png"), "base64", (err, data) => {
|
||||||
if (button.set.length > 0) {
|
if (err) throw err;
|
||||||
button.set[0].addEventListener("click", (e) => {
|
setView("data:image/png;base64," + data);
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/skin_upload.hbs?url=test`));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save button action
|
|
||||||
if (button.save.length > 0) {
|
|
||||||
button.save[0].addEventListener("click", (e) => {
|
|
||||||
axios({
|
|
||||||
method: "get",
|
|
||||||
url: view.flat.src,
|
|
||||||
responseType: "stream",
|
|
||||||
adapter: require("axios/lib/adapters/http")
|
|
||||||
}).then((res) => {
|
|
||||||
var p = res.request.path.split("/")[2];
|
|
||||||
if (p.split(".").length < 2) {
|
|
||||||
p = p + ".png";
|
|
||||||
}
|
|
||||||
res.data.pipe(fs.createWriteStream(path.join(__dirname + `/../assets/skins/download/${p}`)));
|
|
||||||
message.classList.add("success");
|
|
||||||
message.innerHTML = "Saved successfully";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download button action
|
|
||||||
if (button.download.length > 0) {
|
|
||||||
button.download[0].addEventListener("click", (e) => {
|
|
||||||
ipcRenderer.send("download", {
|
|
||||||
url: view.flat.src,
|
|
||||||
properties: {
|
|
||||||
saveAs: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcRenderer.on("downloadResult", (event, arg) => {
|
|
||||||
message.classList.add("success");
|
|
||||||
message.innerHTML = "Downloaded successfully";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Back button action
|
|
||||||
button.main[0].addEventListener("click", (e) => {
|
|
||||||
remote.getCurrentWindow().loadURL(path.join(`file://${__dirname}/main.hbs`));
|
|
||||||
});
|
});
|
Loading…
Reference in New Issue
Block a user