Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
FogNetwork
GitHub Repository: FogNetwork/Ingot
Path: blob/main/ingot.js
2505 views
1
//Checks for dev mode
2
function isPageDev() {
3
if (localStorage.getItem("dev")) {
4
return "dev"
5
} else {
6
return "";
7
}
8
}
9
10
function isButtonDev() {
11
if (!localStorage.getItem("dev")) {
12
return "unchecked"
13
} else {
14
return "";
15
}
16
}
17
18
//Set base page code
19
document.documentElement.innerHTML = `<html><head><link rel="icon" href="data:image/svg+xml,<svg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1'><path fill='white' d='M20.5 11H19V7c0-1.1-.9-2-2-2h-4V3.5C13 2.12 11.88 1 10.5 1S8 2.12 8 3.5V5H4c-1.1 0-1.99.9-1.99 2v3.8H3.5c1.49 0 2.7 1.21 2.7 2.7s-1.21 2.7-2.7 2.7H2V20c0 1.1.9 2 2 2h3.8v-1.5c0-1.49 1.21-2.7 2.7-2.7 1.49 0 2.7 1.21 2.7 2.7V22H17c1.1 0 2-.9 2-2v-4h1.5c1.38 0 2.5-1.12 2.5-2.5S21.88 11 20.5 11z'></path></svg>">
20
<title>Ingot</title>
21
</head>
22
<body ` + isPageDev() + `>
23
<div class="nav">
24
<div class="nav-left">
25
<div class="nav-title">Ingot</div>
26
<div class="nav-right">
27
<div class="nav-dev">Developer mode</div>
28
<div ` + isButtonDev() + ` class="item-toggle item-toggle-dev" id="toggle" onclick="toggle(this);devMode()" onmousedown="togglePress(this, 'down')" onmouseup="togglePress(this, 'up')">
29
<div class="item-bar"></div>
30
<div class="item-knob">
31
<div class="item-ripple">
32
<div class="ripple"></div>
33
</div>
34
</div>
35
</div>
36
</div>
37
</div>
38
</div>
39
40
<div class="items-main">
41
<div class="items" id="items">
42
<div class="patched">Error: This may have been patched</div>
43
<div class="wrongpage">You are not on the correct page.<br>To use Ingot click the button below to redirect and run the bookmarklet again.<div class="item-left-buttons" style="justify-content: center; margin: 20px;">
44
<div class="item-left-button" onclick="window.location='https://chrome.google.com/webstorex'">Redirect</div>
45
</div></div>
46
</div>
47
</div>
48
49
<style>
50
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
51
52
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap');
53
54
* {
55
font-family: "Roboto";
56
}
57
58
:root {
59
color-scheme: dark;
60
}
61
62
body {
63
background: #202124;
64
margin: 0;
65
padding: 0;
66
}
67
68
.nav {
69
width: 100%;
70
height: 55px;
71
background: #292a2d;
72
border-bottom: 1px solid rgba(255, 255, 255, .1);
73
position: fixed;
74
top: 0;
75
right: 0;
76
left: 0;
77
z-index: 9;
78
}
79
80
.nav-left {
81
align-items: center;
82
box-sizing: border-box;
83
display: flex;
84
padding-inline-start: calc(12px + 6px);
85
height: 55px;
86
}
87
88
.nav-right {
89
position: absolute;
90
right: 0;
91
left: 0;
92
display: flex;
93
justify-content: flex-end;
94
}
95
96
.nav-dev {
97
color: rgb(154, 160, 166);
98
font-size: 13px;
99
margin-inline-end: calc(16px + 30px);
100
margin-bottom: 3px;
101
}
102
103
.item-toggle-dev {
104
transform: translateX(-30px);
105
}
106
107
.nav-title {
108
color: rgb(232, 234, 237);
109
font-size: 22px;
110
letter-spacing: .25px;
111
line-height: normal;
112
margin-inline-start: 6px;
113
padding-inline-end: 12px;
114
font-weight: 500;
115
}
116
117
.items-main {
118
min-width: 400px;
119
padding: 24px 60px 64px;
120
margin-top: 57px;
121
}
122
123
.items {
124
display: grid;
125
grid-column-gap: 12px;
126
grid-row-gap: 12px;
127
grid-template-columns: repeat(auto-fill,400px);
128
justify-content: center;
129
margin: auto;
130
/*max-width: calc(400px * 3 + 12pz * 3);*/;
131
}
132
133
.item {
134
height: 160px;
135
width: 400px;
136
background: #292a2d;
137
border-radius: 8px;
138
box-shadow: rgba(0, 0, 0, .3) 0 1px 2px 0, rgba(0, 0, 0, .15) 0 2px 6px 2px;
139
/*transition: height .3s cubic-bezier(.25,.1,.25,1);*/;
140
}
141
142
.item-main {
143
display: flex;
144
flex: 1;
145
min-height: 0;
146
padding: 16px 20px;
147
height: 80px;
148
}
149
150
.item-img-wrapper {
151
align-self: flex-start;
152
display: flex;
153
padding: 6px;
154
position: relative;
155
}
156
157
.item-img {
158
height: 36px;
159
width: 36px;
160
border-radius: 6px;
161
background: #202124;
162
text-indent: -10000px;
163
}
164
165
.item-img-source {
166
align-items: center;
167
background: #f1592b;
168
border-radius: 50%;
169
box-shadow: 0 1px 1px 0 rgb(0 0 0 / 22%), 0 2px 2px 0 rgb(0 0 0 / 12%);
170
display: flex;
171
height: 22px;
172
justify-content: center;
173
width: 22px;
174
margin-inline-start: 24px;
175
margin-top: 24px;
176
position: absolute;
177
display: none;
178
}
179
180
.item[managed] .item-img-source {
181
display: flex;
182
}
183
184
.item-img-source-icon {
185
pointer-events: none;
186
display: block;
187
height: 16px;
188
width: 16px;
189
color: white;
190
}
191
192
.item-content {
193
display: flex;
194
flex: 1;
195
flex-direction: column;
196
margin-inline-start: 24px;
197
width: 288px;
198
overflow: hidden;
199
}
200
201
.item-title-and-version {
202
display: flex;
203
align-items: center;
204
flex-direction: row;
205
}
206
207
.item-title {
208
margin-inline-end: 8px;
209
color: rgb(232, 234, 237);
210
white-space: nowrap;
211
margin-bottom: 4px;
212
font-size: 13px;
213
margin-top: 2px;
214
text-overflow: ellipsis;
215
overflow: hidden;
216
}
217
218
.item-version {
219
color: rgb(154, 160, 166);
220
font-size: 13px;
221
margin-bottom: 4px;
222
display: none;
223
}
224
225
.item-description-overflow {
226
height: 84px;
227
overflow: hidden;
228
}
229
230
.item-description {
231
color: rgb(154, 160, 166);
232
overflow: hidden;
233
text-overflow: ellipsis;
234
flex: 1;
235
font-size: 13px;
236
line-height: 20.02px;
237
margin-top: 3px;
238
}
239
240
.item-id {
241
color: rgb(154, 160, 166);
242
font-size: 13px;
243
margin-top: 5px;
244
display: none;
245
}
246
247
.item-buttons {
248
height: 48px;
249
display: flex;
250
flex-direction: row;
251
align-items: center;
252
justify-content: flex-end;
253
padding-right: 38px;
254
padding-bottom: 8px;
255
padding-top: 8px;
256
box-sizing: border-box;
257
}
258
259
.item-left-buttons {
260
display: flex;
261
flex-direction: row;
262
align-items: center;
263
flex: 1;
264
flex-basis: 1e-9px;
265
}
266
267
.item-left-button {
268
border: 1px solid rgb(95, 99, 104);
269
align-items: center;
270
border-radius: 4px;
271
box-sizing: border-box;
272
color: rgb(138, 180, 248);
273
cursor: pointer;
274
display: inline-flex;
275
font-weight: 500;
276
height: 32px;
277
justify-content: center;
278
min-width: 5.14em;
279
overflow: hidden;
280
padding: 8px 16px;
281
user-select: none;
282
margin-inline-start: 8px;
283
font-size: 13px;
284
line-height: 20.02px;
285
}
286
287
.item-left-button:hover {
288
background: rgba(138, 180, 248, 0.08);
289
}
290
291
.item-left-button:active {
292
background: rgba(138, 180, 248, 0.25);
293
}
294
295
.item-toggle {
296
position: relative;
297
cursor: pointer;
298
}
299
300
.item-toggle[unchecked] .item-bar {
301
background: rgb(154, 160, 166);
302
opacity: 1;
303
}
304
305
.item-toggle[unchecked] .item-knob {
306
background: rgb(218, 220, 224);
307
transform: initial;
308
}
309
310
.item-bar {
311
background: rgb(138, 180, 248);
312
border-radius: 8px;
313
height: 12px;
314
left: 3px;
315
position: absolute;
316
top: 2px;
317
transition: background-color linear 80ms;
318
width: 28px;
319
opacity: 0.5;
320
}
321
322
.item-knob {
323
background: rgb(138, 180, 248);
324
transform: translate3d(18px, 0, 0);
325
border-radius: 50%;
326
display: block;
327
height: 16px;
328
position: relative;
329
transition: transform linear 80ms, background-color linear 80ms;
330
width: 16px;
331
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 40%);
332
}
333
334
.item-ripple {
335
color: rgb(218, 220, 224);
336
height: 40px;
337
left: 50%;
338
outline: none;
339
pointer-events: none;
340
position: absolute;
341
top: 50%;
342
transform: translate(-50%, -50%);
343
transition: color linear 80ms;
344
width: 40px;
345
}
346
347
.ripple {
348
height: 40px;
349
width: 40px;
350
border-radius: 50%;
351
background-color: currentcolor;
352
left: 0;
353
opacity: 0.25;
354
pointer-events: none;
355
position: absolute;
356
will-change: height, transform, width;
357
transform: scaleX(0) scaleY(0);
358
transition: transform linear 80ms;
359
}
360
361
.ripple[open] {
362
transform: initial;
363
}
364
365
body[dev] .item {
366
height: 208px;
367
}
368
369
body[dev] .item-main {
370
height: 125px;
371
}
372
373
body[dev] .item-version, body[dev] .item-id {
374
display: initial;
375
}
376
377
.patched, .wrongpage {
378
color: rgb(154, 160, 166);
379
font-size: 15.99px;
380
font-weight: 500;
381
margin-top: 80px;
382
text-align: center;
383
display: none;
384
}
385
386
.items[patched], .items[wrongpage] {
387
grid-template-columns: initial;
388
}
389
390
.items[patched] .patched {
391
display: initial;
392
}
393
394
.items[wrongpage] .wrongpage {
395
display: flow-root;
396
}
397
</style>
398
</body>
399
</html>`
400
401
//Remove extension
402
function removeExtension(extensionId) {
403
chrome.management.uninstall(extensionId)
404
}
405
406
//Simple function to make image to data url
407
function blobToDataURL(blob) {
408
return new Promise((resolve, reject) => {
409
var reader = new FileReader();
410
reader.onload = function(e) {
411
resolve(e.target.result)
412
}
413
reader.onerror = function(e) {
414
reject(reader.error)
415
}
416
reader.onabort = function(e) {
417
reject(new Error("Read aborted"))
418
}
419
reader.readAsDataURL(blob);
420
})
421
}
422
423
//Gets the icon from extensions
424
async function getIconFromExtension(extensionID) {
425
if (!extensionID) return "";
426
427
var extensionPage = await fetch("https://chrome.google.com/webstore/detail/" + extensionID)
428
var extensionPageCode = await extensionPage.text()
429
var dom = new DOMParser().parseFromString(extensionPageCode, "text/html")
430
if (!dom.querySelector("img.e-f-s[src]")) return "";
431
var extensionImage = dom.querySelector("img.e-f-s[src]").src;
432
var getImage = await fetch(extensionImage);
433
return await blobToDataURL(await getImage.blob());
434
}
435
436
//Toggle extensions
437
function toggleExtension(e, extensionId) {
438
if (e.hasAttribute("unchecked")) {
439
chrome.management.setEnabled(extensionId, true)
440
} else {
441
chrome.management.setEnabled(extensionId, false)
442
}
443
}
444
445
//Toggle the toggle
446
function toggle(e) {
447
if (e.hasAttribute("unchecked")) {
448
e.removeAttribute("unchecked")
449
} else {
450
e.setAttribute("unchecked", "")
451
}
452
}
453
454
//Toggle animation
455
function togglePress(e, dir) {
456
if (dir == "down") {
457
e.children[1].children[0].children[0].setAttribute("open", "")
458
} else {
459
setTimeout(function() {
460
e.children[1].children[0].children[0].style.display = "none"
461
e.children[1].children[0].children[0].removeAttribute("open")
462
e.children[1].children[0].children[0].style.display = "initial"
463
}, 80)
464
}
465
}
466
467
//Sets dev mode
468
function devMode() {
469
if (document.body.hasAttribute("dev")) {
470
document.body.removeAttribute("dev")
471
localStorage.removeItem("dev")
472
} else {
473
document.body.setAttribute("dev", "")
474
localStorage.setItem("dev", "true")
475
}
476
}
477
478
//Creates extension element
479
function addExtension(data) {
480
var items = document.getElementById("items")
481
482
var item = document.createElement("div")
483
item.className = "item"
484
item.setAttribute("data-id", data.id)
485
if (data.managed) {
486
item.setAttribute("managed", "")
487
}
488
489
var itemMain = document.createElement("div")
490
itemMain.className = "item-main"
491
492
var itemImgWrapper = document.createElement("div")
493
itemImgWrapper.className = "item-img-wrapper"
494
495
var itemImg = document.createElement("img")
496
itemImg.className = "item-img"
497
itemImg.src = data.logo
498
499
var itemImgSource = document.createElement("div")
500
itemImgSource.className = "item-img-source"
501
itemImgSource.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 24 24" class="item-img-source-icon"><path d="M12 7V3H2v18h20V7H12zM6 19H4v-2h2v2zm0-4H4v-2h2v2zm0-4H4V9h2v2zm0-4H4V5h2v2zm4 12H8v-2h2v2zm0-4H8v-2h2v2zm0-4H8V9h2v2zm0-4H8V5h2v2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8v10zm-2-8h-2v2h2v-2zm0 4h-2v2h2v-2z" style="fill: currentColor"></path></svg>`
502
503
itemImgWrapper.appendChild(itemImg)
504
itemImgWrapper.appendChild(itemImgSource)
505
506
itemMain.appendChild(itemImgWrapper)
507
508
var itemContent = document.createElement("div")
509
itemContent.className = "item-content"
510
511
var itemTitleAndVersion = document.createElement("div")
512
itemTitleAndVersion.className = "item-title-and-version"
513
514
var itemTitle = document.createElement("div")
515
itemTitle.className = "item-title"
516
itemTitle.innerText = data.title
517
518
var itemVersion = document.createElement("div")
519
itemVersion.className = "item-version"
520
itemVersion.innerText = data.version
521
522
itemTitleAndVersion.appendChild(itemTitle)
523
itemTitleAndVersion.appendChild(itemVersion)
524
525
itemContent.appendChild(itemTitleAndVersion)
526
527
var itemDescriptionOverflow = document.createElement("div")
528
itemDescriptionOverflow.className = "item-description-overflow"
529
530
var itemDescription = document.createElement("div")
531
itemDescription.className = "item-description"
532
itemDescription.innerText = data.description
533
534
itemDescriptionOverflow.appendChild(itemDescription)
535
536
itemContent.appendChild(itemDescriptionOverflow)
537
538
var itemId = document.createElement("div")
539
itemId.className = "item-id"
540
itemId.innerText = "ID: " + data.id
541
542
itemContent.appendChild(itemId)
543
544
itemMain.appendChild(itemContent)
545
546
item.appendChild(itemMain)
547
548
var itemButtons = document.createElement("div")
549
itemButtons.className = "item-buttons"
550
551
//Does not work on admin extensions
552
/*
553
var itemLeftButtons = document.createElement("div")
554
itemLeftButtons.className = "item-left-buttons"
555
556
var itemLeftButton = document.createElement("div")
557
itemLeftButton.className = "item-left-button"
558
itemLeftButton.innerText = "Remove"
559
itemLeftButton.setAttribute("onclick", "removeExtension('" + data.id + "')")
560
itemLeftButtons.appendChild(itemLeftButton)
561
562
itemButtons.appendChild(itemLeftButtons)
563
*/
564
565
var itemToggle = document.createElement("div")
566
itemToggle.className = "item-toggle"
567
itemToggle.setAttribute("onclick", "toggleExtension(this, '" + data.id + "');toggle(this)")
568
itemToggle.setAttribute("onmousedown", "togglePress(this, 'down')")
569
itemToggle.setAttribute("onmouseup", "togglePress(this, 'up')")
570
if (!data.enabled) {
571
itemToggle.setAttribute("unchecked", "")
572
}
573
574
var itemBar = document.createElement("div")
575
itemBar.className = "item-bar"
576
577
var itemKnob = document.createElement("div")
578
itemKnob.className = "item-knob"
579
580
var itemRipple = document.createElement("div")
581
itemRipple.className = "item-ripple"
582
583
var ripple = document.createElement("div")
584
ripple.className = "ripple"
585
586
itemRipple.appendChild(ripple)
587
588
itemKnob.appendChild(itemRipple)
589
590
itemToggle.appendChild(itemBar)
591
592
itemToggle.appendChild(itemKnob)
593
594
itemButtons.appendChild(itemToggle)
595
596
item.appendChild(itemButtons)
597
598
items.appendChild(item)
599
}
600
601
//Gets all extensions and adds them
602
async function getExtensions() {
603
chrome.management.getAll(async function(allExtensions) {
604
for (let anExtension in allExtensions)
605
if (!allExtensions[anExtension].isApp) {
606
addExtension({
607
title: allExtensions[anExtension].name,
608
version: allExtensions[anExtension].version,
609
description: allExtensions[anExtension].description,
610
id: allExtensions[anExtension].id,
611
logo: "",
612
managed: allExtensions[anExtension].installType == "admin" ? true : false,
613
enabled: allExtensions[anExtension].enabled,
614
})
615
}
616
})
617
setTimeout(function() {
618
setIcons()
619
}, 100)
620
}
621
622
async function setIcons() {
623
var items = document.querySelectorAll(".items .item")
624
for (let item in items) {
625
try {
626
items[item].querySelector(".item-main .item-img-wrapper .item-img").src = await getIconFromExtension(items[item].dataset.id)
627
} catch {
628
}
629
}
630
}
631
632
//Show wrong page popup
633
if (!window.location.toString().startsWith("https://chrome.google.com/webstore")) {
634
document.getElementById("items").setAttribute("wrongpage", "")
635
} else {
636
//Checks if it still works
637
if (chrome.management) {
638
getExtensions()
639
} else {
640
document.getElementById("items").setAttribute("patched", "")
641
}
642
}
643
644