PageSourceSearch

https://vault80.com/assets/js/app.js

js vault80.com collected 2026-09-25 22:57:42 UTC 9,499 bytes, 290 lines download raw bytes

1class App {
2
3    constructor() {
4
5        this.currentPalette = []
6        this.currentPalette2 = []
7        this.initialDarkest = ""
8        this.currentHSL = [0, 0, 0]
9        this.elements = {
10            palettes: document.querySelectorAll("#palette li"),
11            newPalette: document.querySelector("#new-palette"),
12            shufflePalette: document.querySelector("#shuffle-palette"),
13            resetPalette: document.querySelector("#reset-palette"),
14            sliders: {},
15            labels: document.querySelectorAll("#adjustments strong em"),
16            exportColors: document.querySelectorAll("a[data-export-format]")
17        }
18
19        this.createSliders()
20        this.attachEvents()
21        this.pickPalette()
22    }
23
24    createSliders() {
25        this.elements.sliders.hue = noUiSlider.create(document.querySelector("#range-slider-hue"), {
26            start: 0,
27            step: 2,
28            connect: true,
29            range: {
30                min: -180,
31                max: 180
32            }
33        })
34
35        this.elements.sliders.saturation = noUiSlider.create(document.querySelector("#range-slider-saturation"), {
36            start: 0,
37            step: 2,
38            connect: true,
39            range: {
40                min: -100,
41                max: 100
42            }
43        })
44
45        this.elements.sliders.lightness = noUiSlider.create(document.querySelector("#range-slider-lightness"), {
46            start: 0,
47            step: 2,
48            connect: true,
49            range: {
50                min: -100,
51                max: 100
52            }
53        })
54    }
55
56    attachEvents() {
57        this.elements.newPalette.addEventListener("click", (event) => {
58            event.preventDefault()
59            event.stopPropagation()
60            this.pickPalette()
61        })
62
63        let self = this
64        for (let [index, paletteCont] of this.elements.palettes.entries()) {
65            paletteCont.addEventListener("click", function (event) {
66                event.preventDefault()
67                event.stopPropagation()
68                self.copyColor(this)
69            })
70
71            // bring back the default text when pointe leaves the palette
72            paletteCont.addEventListener("pointerleave", function (event) {
73                this.classList.remove("copied")
74            })
75        }
76
77        // shuffle the HSL sliders (within boundaries) for the currently selected palette
78        this.elements.shufflePalette.addEventListener("click", (event) => {
79            event.preventDefault()
80            event.stopPropagation()
81            this.shufflePalette()
82        })
83
84        // reset HSL sliders
85        this.elements.resetPalette.addEventListener("click", (event) => {
86            event.preventDefault()
87            event.stopPropagation()
88            this.resetSliders()
89        })
90
91        for (let [index, exportColor] of this.elements.exportColors.entries()) {
92            exportColor.addEventListener("click", function (event) {
93                event.preventDefault()
94                event.stopPropagation()
95                self.exportColors(this.dataset.exportFormat)
96            })
97        }
98
99        let setHue = (values, handle) => {
100            let value = parseFloat(values.pop())
101            this.currentHSL[0] = value
102            this.elements.labels[0].innerHTML = `${value}°`
103            this.setHSL()
104        }
105
106        let setSaturation = (values, handle) => {
107            let value = parseFloat(values.pop())
108            this.currentHSL[1] = value / 100
109            this.elements.labels[1].innerHTML = `${value}%`
110            this.setHSL()
111        }
112
113        let setLightness = (values, handle) => {
114            let value = parseFloat(values.pop())
115            this.currentHSL[2] = value / 100
116            this.elements.labels[2].innerHTML = `${value}%`
117            this.setHSL()
118        }
119
120        this.elements.sliders.hue.on("slide", setHue)
121        this.elements.sliders.hue.on("set", setHue)
122
123        this.elements.sliders.saturation.on("slide", setSaturation)
124        this.elements.sliders.saturation.on("set", setSaturation)
125
126        this.elements.sliders.lightness.on("slide", setLightness)
127        this.elements.sliders.lightness.on("set", setLightness)
128    }
129
130    copyColor(element) {
131        let hex = element.querySelector("h2").innerText
132
133        try {
134            navigator.clipboard.writeText(hex)
135            element.classList.add("copied")
136        } catch (err) {
137            console.error('Failed to copy: ', err)
138        }
139    }
140
141    setHSL() {
142
143        // clone the current palette so the original always stays intact
144        let contextPalette = structuredClone(this.currentPalette)
145
146        for (let [index, palette] of contextPalette.entries()) {
147            contextPalette[index].color = this.modifyHSL(contextPalette[index].color)
148        }
149
150        this.currentPalette2 = this.withLighter(contextPalette)
151        this.renderPalette(this.currentPalette2)
152        this.toggleReset()
153    }
154
155    modifyHSL(color) {
156        let hue = chroma(color).hsl()[0]
157
158        color = chroma(color)
159            .set("hsl.h", hue + this.currentHSL[0])
160            .saturate(this.currentHSL[1])
161            .brighten(this.currentHSL[2])
162            .hex()
163
164        return color
165    }
166
167    toggleReset() {
168        this.elements.resetPalette.classList[this.currentHSL[0] == 0 && this.currentHSL[1] == 0 && this.currentHSL[2] == 0 ? "remove" : "add"]("show")
169    }
170
171    withLighter(bases) {
172        let lights = bases.map(item => ({
173            color: chroma(item.color).brighten(0.8).hex(),
174            name: item.name + " Light"
175        }))
176
177        return bases.concat(lights)
178    }
179
180    pickPalette() {
181
182        // pick a random palette
183        this.currentPalette = window.hexes[Math.floor(Math.random() * window.hexes.length)]
184
185        this.pickDarkest()
186        this.currentPalette2 = this.withLighter(this.currentPalette)
187        this.renderPalette(this.currentPalette2)
188        this.resetSliders()
189    }
190
191    randomValue(min, max) {
192        return Math.floor(min + Math.random() * (max - min + 1))
193    }
194
195    shufflePalette() {
196        this.elements.sliders.hue.set(this.randomValue(-30, 30))
197        this.elements.sliders.saturation.set(this.randomValue(-60, 30))
198        this.elements.sliders.lightness.set(this.randomValue(-30, 60))
199    }
200
201    resetSliders() {
202
203        // reset the sliders to their default position
204        for (let [index, slider] of Object.entries(this.elements.sliders)) {
205            slider.reset()
206        }
207
208        // reset any adjusted HSL values
209        this.currentHSL = [0, 0, 0]
210    }
211
212    renderPalette(contextPalette) {
213        for (let [index, paletteCont] of this.elements.palettes.entries()) {
214            this.setInfo(paletteCont, contextPalette[index])
215        }
216
217        // update the slider knob colors with the darkest (initially) color from the palette
218        this.updateUI(this.modifyHSL(this.initialDarkest))
219    }
220
221    setInfo(element, palette) {
222        element.style.backgroundColor = palette.color
223
224        element.innerHTML = `
225      <h2>${palette.color}</h2>
226      <h3>${palette.name}</h3>
227    `
228
229        // add class if single palette color is dark for an alternative text color
230        element.classList[chroma(palette.color).luminance() > 0.4 ? "remove" : "add"]("lod")
231    }
232
233    darkestColor(colors) {
234
235        // flatten the array of objects to an array of colors
236        colors = colors.map(item => item.color)
237
238        // convert the colors to luminance values
239        let inputArray = colors.map(color => chroma(color).luminance())
240
241        // find the index of the darkest color
242        const minNumber = Math.min(...inputArray)
243
244        // return the color with the darkest luminance
245        return colors[inputArray.indexOf(minNumber)]
246    }
247
248    pickDarkest() {
249
250        // pick the darkest color from the palette unless the first one isn't 
251        // actually that light in the luminance estimation. This is because the UI looks better
252        // if the accent color is the same as the first color in the palette
253        if (chroma(this.currentPalette[0].color).luminance() > 0.6) {
254            this.initialDarkest = this.darkestColor(this.currentPalette)
255        } else {
256            this.initialDarkest = this.currentPalette[0].color
257        }
258    }
259
260    updateUI(color) {
261        let cssRoot = document.querySelector(":root")
262        cssRoot.style.setProperty("--ui-accent", color)
263    }
264
265    exportColors(format) {
266        let mode = format === "hex" ? "hex" : "css"
267        let templateColors = []
268        let template = ""
269
270        for (let [index, paletteCont] of this.currentPalette2.entries()) {
271            templateColors.push(`  --color-${index + 1}: ${chroma(paletteCont.color)[mode](format)};`)
272        }
273
274        template = `:root {\n${templateColors.join("\n")}\n}\n\n`
275
276        document.getElementById('css-output').value = template;
277
278        // push color payload to the parent frame so it can handle the injection
279        /*
280        window.parent.postMessage({
281          type:    "export-colors",
282          payload: template
283        }, "*")
284        /**/
285    }
286}
287
288window.addEventListener("DOMContentLoaded", () => {
289    new App()
290})

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.