@mfcc64/ytms 15.0.2 → 16.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/script.mjs +240 -109
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mfcc64/ytms",
3
- "version": "15.0.2",
3
+ "version": "16.0.0",
4
4
  "description": "YouTube Musical Spectrum",
5
5
  "main": "script.mjs",
6
6
  "scripts": {
package/script.mjs CHANGED
@@ -28,7 +28,7 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
28
28
  const defaults = {
29
29
  height: { def: 33, min: 20, max:100 },
30
30
  bar: { def: 17, min: 1, max: 33 },
31
- waterfall: { def: 33, min: 0, max: 70 },
31
+ waterfall: { def: 33, min: 0, max:100 },
32
32
  brightness: { def: 17, min: 1, max: 70 },
33
33
  bass: { def:-30, min:-50, max: 0 },
34
34
  speed: { def: 2, min: 1, max: 12 },
@@ -42,11 +42,16 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
42
42
  left_color: { def:0xdcb900, min:0, max:0xffffff },
43
43
  right_color:{ def:0x00b9dc, min:0, max:0xffffff },
44
44
  mid_color: { def:0xdcdcdc, min:0, max:0xffffff },
45
+ saturation: { def: 0, min: 0, max: 30 },
46
+ hue: { def: 0, min:-18, max: 19 },
47
+ hue_range: { def: 18, min:-36, max: 36 },
45
48
  interval: { def: 1, min: 1, max: 4 },
46
49
  bar_scale: { def: 0, min: 0, max: 4 },
47
50
  line_mode: { def: 0, min: 0, max: 4 },
48
51
  line_width: { def: 1, min: 1, max: 3 },
49
52
  line_color: { def:0xffffff, min:0, max:0xffffff },
53
+ scroll: { def: 0, min: 0, max: 1 },
54
+ coord_color:{ def:0x000000, min:0, max:0xffffff },
50
55
  transparent:{ def: 1, min: 0, max: 1 },
51
56
  visible: { def: document.location.hostname != "www.youtube.com" ? 1 : 0,
52
57
  min: 0, max: 1 },
@@ -121,7 +126,7 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
121
126
  e("li", "If you want to change the axis, click it."),
122
127
  e("li", "If you want to make your change persistent, click ", e("b", "Set as Default Settings"), " button."),
123
128
  e("li", e("b", "New Features:"), " Custom color, custom range," +
124
- " peak color, bar scale, presets, line visualizer."),
129
+ " peak color, bar scale, line visualizer, hue rotation, more color presets, horizontal scroll, coordinate line."),
125
130
  e("li", e("a", {href: "https://github.com/mfcc64/youtube-musical-spectrum#settings"}, {target: "_blank"}, "Read more..."))
126
131
  ),
127
132
  e("p",
@@ -152,7 +157,7 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
152
157
  af_links.style.display = !af_links_timeout || (child_menu.visible?.checked ?? true) ? "block" : "none";
153
158
  }
154
159
 
155
- const message_version = 11;
160
+ const message_version = 12;
156
161
  af_links.shadowRoot.getElementById("message").style.display = get_opt("message_version") == message_version ? "none" : "block";
157
162
  af_links.shadowRoot.getElementById("close_message").addEventListener("click", function() {
158
163
  set_opt("message_version", message_version);
@@ -164,8 +169,6 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
164
169
 
165
170
  const cqt = new ShowCQTElement();
166
171
  set_fixed_style(cqt, 9999999);
167
- cqt.style.left = cqt.style.bottom = 0;
168
- cqt.style.width = "100%";
169
172
  let stop_count = 0;
170
173
  const videos = document.getElementsByTagName("video");
171
174
  let svideos = [];
@@ -199,10 +202,10 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
199
202
 
200
203
  function update_cqt_bottom() {
201
204
  if (document.fullscreenElement) {
202
- cqt.style.bottom = 0;
205
+ document.body.style.setProperty("--ytms-cqt-bottom", "0px");
203
206
  af_links.style.visibility = "hidden";
204
207
  } else {
205
- cqt.style.bottom = "var(--ytmusic-player-bar-height, 0)";
208
+ document.body.style.setProperty("--ytms-cqt-bottom", "var(--ytmusic-player-bar-height, 0px)");
206
209
  af_links.style.visibility = "visible";
207
210
  }
208
211
  }
@@ -214,6 +217,51 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
214
217
  if (document.location.hostname == "music.youtube.com")
215
218
  ytmusic_layout();
216
219
 
220
+ const coord_line_h = document.createElement("div");
221
+ const coord_line_v = document.createElement("div");
222
+ set_fixed_style(coord_line_h, cqt.style.zIndex);
223
+ set_fixed_style(coord_line_v, cqt.style.zIndex);
224
+ coord_line_h.style.left = 0;
225
+ coord_line_h.style.width = "100%";
226
+ coord_line_h.style.height = "1px";
227
+ coord_line_v.style.top = 0;
228
+ coord_line_v.style.height = "100%";
229
+ coord_line_v.style.width = "1px";
230
+ coord_line_h.style.pointerEvents = coord_line_v.style.pointerEvents = "none";
231
+ coord_line_h.style.display = coord_line_v.style.display = "none";
232
+
233
+ function update_cqt_layout(child) {
234
+ if (!child_menu.height || !child_menu.scroll || !child_menu.base_note || !child_menu.semitones)
235
+ return;
236
+
237
+ const width = child_menu.semitones.value * 1;
238
+ const base = child_menu.base_note.value - 16;
239
+ if (base + width > 120) {
240
+ child == child_menu.semitones ?
241
+ (child_menu.base_note.value = 120 - width + 16, child_menu.base_note.onchange()) :
242
+ (child_menu.semitones.value = 120 - base, child_menu.semitones.onchange());
243
+ return;
244
+ }
245
+
246
+ const vleft = -base / width;
247
+ const vwidth = 120 / width;
248
+
249
+ if (child_menu.scroll.value == "0") {
250
+ cqt.style.height = `calc(${ child_menu.height.value / 100} * (100% - var(--ytms-cqt-bottom, 0px)))`;
251
+ cqt.style.width = vwidth * 100 + "%";
252
+ cqt.style.left = vleft * 100 + "%";
253
+ cqt.style.bottom = "var(--ytms-cqt-bottom, 0px)";
254
+ cqt.style.transform = "";
255
+ } else {
256
+ cqt.style.height = child_menu.height.value + "vw";
257
+ cqt.style.width = `calc(${vwidth} * (100vh - var(--ytms-cqt-bottom, 0px)))`;
258
+ cqt.style.left = "100vw";
259
+ cqt.style.bottom = `calc(var(--ytms-cqt-bottom, 0px) + ${vleft} * (100vh - var(--ytms-cqt-bottom, 0px)))`;
260
+ cqt.style.transform = "rotate(-90deg)";
261
+ cqt.style.transformOrigin = "bottom left";
262
+ }
263
+ }
264
+
217
265
  child_menu.axis = { value: get_opt("axis") };
218
266
  child_menu.axis.onchange = function() { cqt.dataset.axis = axis_list[child_menu.axis.value]; };
219
267
  cqt.shadowRoot.getElementById("axis").addEventListener("click", function() {
@@ -249,10 +297,13 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
249
297
  menu_div.style.backgroundColor = "#000000DD";
250
298
  menu_div.style.verticalAlign = "middle";
251
299
  menu_div.style.maxHeight = "90%";
300
+ menu_div.style.maxWidth = "90%";
252
301
  menu_div.style.overflow = "auto";
253
- menu_div.style.scrollbarWidth = "none";
302
+ menu_div.style.scrollbarWidth = "8px";
303
+ menu_div.style.scrollbarColor = "#555555 #222222dd";
254
304
  menu_div.style.visibility = "hidden";
255
305
  menu_div.style.cursor = "default";
306
+ menu_table.style.width = "860px";
256
307
 
257
308
  var current_tr = null;
258
309
  var current_tr_count = 0;
@@ -316,7 +367,7 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
316
367
  mic.pan.connect(cqt.audio_input);
317
368
  mic.gain.connect(mic.pan);
318
369
 
319
- create_child_range_menu("Height", "height", (child) => cqt.style.height = child.value + "%");
370
+ create_child_range_menu("Height", "height", update_cqt_layout);
320
371
  create_child_range_menu("Bar", "bar", (child) => cqt.dataset.bar = child.value);
321
372
  create_child_range_menu("Waterfall", "waterfall", (child) => cqt.dataset.waterfall = child.value);
322
373
  create_child_range_menu("Brightness", "brightness", (child) => cqt.dataset.brightness = child.value);
@@ -355,8 +406,7 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
355
406
  create_child_range_menu("Scale X", "scale_x", (child) => cqt.dataset.scaleX = child.value);
356
407
  create_child_range_menu("Scale Y", "scale_y", (child) => cqt.dataset.scaleY = child.value);
357
408
 
358
- let bar_scale_func = null;
359
- function create_child_select_bar_scale(title, name) {
409
+ function create_child_select_menu(title, name, select_opt, callback) {
360
410
  var tr = get_menu_table_tr();
361
411
  set_common_tr_style(tr);
362
412
  var td = document.createElement("td");
@@ -365,60 +415,48 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
365
415
  tr.appendChild(td);
366
416
  td = document.createElement("td");
367
417
  td.colSpan = 3;
368
- var child = child_menu[name] = document.createElement("select");
418
+ var child = document.createElement("select");
419
+ if (name) child_menu[name] = child;
369
420
  child.style.cursor = "pointer";
370
421
  child.style.width = "100%";
371
- var select_opt = [ "Linear", "Sqrt", "Log (40 dB)", "Log (60 dB)", "Log (80 dB)" ];
372
- for (var k = 0; k < select_opt.length; k++) {
373
- var opt = document.createElement("option");
422
+ for (let k = 0; k < select_opt.length; k++) {
423
+ const opt = document.createElement("option");
374
424
  opt.textContent = select_opt[k];
375
425
  opt.value = k;
376
426
  child.appendChild(opt);
377
427
  }
378
- child.value = get_opt("bar_scale");
428
+ if (name) child.value = get_opt(name);
379
429
  td.appendChild(child);
380
430
  tr.appendChild(td);
381
- child.onchange = function() {
382
- switch (child.value) {
383
- case "1": bar_scale_func = bar_scale_sqrt; break;
384
- case "2": bar_scale_func = c => bar_scale_db(c, 2); break;
385
- case "3": bar_scale_func = c => bar_scale_db(c, 3); break;
386
- case "4": bar_scale_func = c => bar_scale_db(c, 4); break;
387
- default: bar_scale_func = null;
388
- }
389
- };
431
+ child.onchange = () => callback?.(child);
390
432
  child.onchange();
433
+ }
391
434
 
392
- function bar_scale_sqrt(color) {
393
- for (let k = 3; k < color.length; k += 4)
394
- color[k] = 0.5 * Math.sqrt(color[k]);
395
- }
435
+ let bar_scale_func = null;
396
436
 
397
- function bar_scale_db(color, range) {
398
- for (let k = 3; k < color.length; k += 4)
399
- color[k] = Math.max(0, (Math.log10(color[k]) + range) / range);
400
- }
437
+ function bar_scale_sqrt(color) {
438
+ for (let k = 3; k < color.length; k += 4)
439
+ color[k] = 0.5 * Math.sqrt(color[k]);
401
440
  }
402
- create_child_select_bar_scale("Bar Scale", "bar_scale");
403
441
 
404
- function update_range(child) {
405
- if (!child_menu.base_note || !child_menu.semitones)
406
- return;
442
+ function bar_scale_db(color, range) {
443
+ for (let k = 3; k < color.length; k += 4)
444
+ color[k] = Math.max(0, (Math.log10(color[k]) + range) / range);
445
+ }
407
446
 
408
- const width = child_menu.semitones.value * 1;
409
- const base = child_menu.base_note.value - 16;
410
- if (base + width > 120) {
411
- child == child_menu.semitones ?
412
- (child_menu.base_note.value = 120 - width + 16, child_menu.base_note.onchange()) :
413
- (child_menu.semitones.value = 120 - base, child_menu.semitones.onchange());
414
- return;
447
+ create_child_select_menu("Bar Scale", "bar_scale",
448
+ [ "Linear", "Sqrt", "Log (40 dB)", "Log (60 dB)", "Log (80 dB)" ], (child) => {
449
+ switch (child.value) {
450
+ case "1": bar_scale_func = bar_scale_sqrt; break;
451
+ case "2": bar_scale_func = c => bar_scale_db(c, 2); break;
452
+ case "3": bar_scale_func = c => bar_scale_db(c, 3); break;
453
+ case "4": bar_scale_func = c => bar_scale_db(c, 4); break;
454
+ default: bar_scale_func = null;
415
455
  }
416
- cqt.style.left = -base / width * 100 + "%";
417
- cqt.style.width = 12000 / width + "%";
418
- }
456
+ });
419
457
 
420
- create_child_range_menu("Base Note", "base_note", child => update_range(child));
421
- create_child_range_menu("Semitones", "semitones", child => update_range(child));
458
+ create_child_range_menu("Base Note", "base_note", update_cqt_layout);
459
+ create_child_range_menu("Semitones", "semitones", update_cqt_layout);
422
460
 
423
461
  const number2color = n => "#" + (n|0).toString(16).padStart(6, "0");
424
462
  const color2number = c => Number.parseInt(c.slice(1), 16);
@@ -426,6 +464,78 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
426
464
  const color_table = new Array(9);
427
465
  const peak_color = new Array(3);
428
466
 
467
+ class ColorRotation {
468
+ constructor() {
469
+ const kr = 0.2126;
470
+ const kb = 0.0722;
471
+ const kg = 1 - (kr + kb);
472
+ const kmul = 2 / Math.sqrt(3);
473
+
474
+ this.mat_r = [ 1, 2 * (kb + kg), kmul * (kb - kg) ];
475
+ this.mat_g = [ 1, -2 * kr, kmul * (kb - kg + 1) ];
476
+ this.mat_b = [ 1, -2 * kr, kmul * (kb - kg - 1) ];
477
+
478
+ this.saturation = 0;
479
+ this.hue = 0;
480
+ this.hue_range = 0;
481
+ this.table_len = 1000;
482
+ this.table = new Float32Array(this.table_len * 3 + 6);
483
+ }
484
+
485
+ rgb_from_yuv(res, y, u, v) {
486
+ res[0] = y + this.mat_r[1] * u + this.mat_r[2] * v;
487
+ res[1] = y + this.mat_g[1] * u + this.mat_g[2] * v;
488
+ res[2] = y + this.mat_b[1] * u + this.mat_b[2] * v;
489
+ }
490
+
491
+ update(saturation, hue, hue_range) {
492
+ this.saturation = saturation;
493
+ this.hue = hue;
494
+ this.hue_range = hue_range;
495
+
496
+ if (this.saturation == 0)
497
+ return;
498
+
499
+ const res = [ 0, 0, 0 ];
500
+ const hue_step = Math.PI / 18 * hue_range;
501
+ const hue_start = Math.PI / 18 * hue - 0.5 * hue_step;
502
+ const sat_g = 10 ** (saturation / 10 - 1);
503
+
504
+ for (let k = 0; k <= this.table_len + 1; k++) {
505
+ let y = Math.min(1, k / this.table_len);
506
+ let h = hue_start + Math.sin(0.5 * Math.PI * y) * hue_step;
507
+ h = h - 0.23 * Math.sin(3 * h);
508
+ y = y*y;
509
+ let u = Math.cos(h), v = Math.sin(h), s = 0, sum_rcp = 0;
510
+ this.rgb_from_yuv(res, y, u, v);
511
+ for (let c = 0; c < 3; c++) {
512
+ sum_rcp += Math.exp((res[c] - y) * y * sat_g);
513
+ sum_rcp += Math.exp((y - res[c]) * (1 - y) * sat_g);
514
+ }
515
+ s = y * (1 - y) * sat_g / Math.log(sum_rcp);
516
+ this.rgb_from_yuv(res, y, s*u, s*v);
517
+ const gamma = 1/2.2;
518
+ this.table[3*k+0] = Math.max(0, res[0])**gamma;
519
+ this.table[3*k+1] = Math.max(0, res[1])**gamma;
520
+ this.table[3*k+2] = Math.max(0, res[2])**gamma;
521
+ }
522
+ }
523
+
524
+ transform(color) {
525
+ const len = color.length / 4;
526
+ for (let k = 0; k < len; k++) {
527
+ const y = Math.max(0, Math.min(1, color[4*k+1])) * this.table_len;
528
+ const idx = Math.floor(y);
529
+ const frac = y - idx;
530
+ const ifrac = 1 - frac;
531
+ color[4*k+0] = this.table[3*idx+0] * ifrac + this.table[3*idx+3] * frac;
532
+ color[4*k+1] = this.table[3*idx+1] * ifrac + this.table[3*idx+4] * frac;
533
+ color[4*k+2] = this.table[3*idx+2] * ifrac + this.table[3*idx+5] * frac;
534
+ }
535
+ }
536
+ }
537
+ const color_rotation = new ColorRotation();
538
+
429
539
  function create_child_color_menu(title, name, callback) {
430
540
  var tr = get_menu_table_tr();
431
541
  set_common_tr_style(tr);
@@ -466,7 +576,10 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
466
576
  if (color[k+3] <= color[k-1] || color[k+3] < color[k+7])
467
577
  continue;
468
578
 
469
- const alpha = (1 - (k+2) / color.length) ** 2;
579
+ const alpha = (1 - (k+2) / color.length) ** 2 - 0.16;
580
+ if (alpha <= 0)
581
+ break;
582
+
470
583
  for (let m = 0; m < 3; m++)
471
584
  color[k+m] = Math.min(color[k+m], 1) * (1 - alpha + peak_color[m] * alpha);
472
585
  }
@@ -496,7 +609,25 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
496
609
  create_child_color_menu("Mid Color", "mid_color", child => color_int[1] = color2number(child.value));
497
610
  create_child_color_menu("Right Color", "right_color", child => color_int[2] = color2number(child.value));
498
611
 
612
+ function update_color_rotation() {
613
+ color_rotation.update(Number(child_menu.saturation?.value ?? 0),
614
+ Number(child_menu.hue?.value ?? 0),
615
+ Number(child_menu.hue_range?.value ?? 0));
616
+ }
617
+ create_child_range_menu("Saturation", "saturation", update_color_rotation);
618
+ create_child_range_menu("Hue", "hue", child => {
619
+ if (child.value == 19)
620
+ return child.value = -17, child.onchange();
621
+ if (child.value == -18)
622
+ return child.value = 18, child.onchange();
623
+ update_color_rotation();
624
+ });
625
+ create_child_range_menu("Hue Range", "hue_range", update_color_rotation);
626
+
499
627
  function transform_color(color) {
628
+ if (color_rotation.saturation > 0)
629
+ return color_rotation.transform(color);
630
+
500
631
  if (color_int[0] == 0xdcb900 && color_int[1] == 0xdcdcdc && color_int[2] == 0x00b9dc)
501
632
  return;
502
633
 
@@ -508,31 +639,7 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
508
639
  }
509
640
  }
510
641
 
511
- function create_child_select_line_mode(title, name) {
512
- var tr = get_menu_table_tr();
513
- set_common_tr_style(tr);
514
- var td = document.createElement("td");
515
- set_common_left_td_style(td);
516
- td.textContent = title;
517
- tr.appendChild(td);
518
- td = document.createElement("td");
519
- td.colSpan = 3;
520
- var child = child_menu[name] = document.createElement("select");
521
- child.style.cursor = "pointer";
522
- child.style.width = "100%";
523
- var select_opt = [ "Off", "Static", "Mid Color", "Average", "Spectrum" ];
524
- for (var k = 0; k < select_opt.length; k++) {
525
- var opt = document.createElement("option");
526
- opt.textContent = select_opt[k];
527
- opt.value = k;
528
- child.appendChild(opt);
529
- }
530
- child.value = get_opt("line_mode");
531
- td.appendChild(child);
532
- tr.appendChild(td);
533
- child.onchange = () => {};
534
- }
535
- create_child_select_line_mode("Line Mode", "line_mode");
642
+ create_child_select_menu("Line Mode", "line_mode", [ "Off", "Static", "Mid Color", "Average", "Spectrum" ]);
536
643
  create_child_range_menu("Line Width", "line_width");
537
644
  create_child_color_menu("Line Color", "line_color");
538
645
 
@@ -639,50 +746,55 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
639
746
  tr.appendChild(td);
640
747
  }
641
748
 
749
+ create_child_select_menu("Scroll", "scroll", [ "Vertical", "Horizontal" ], update_cqt_layout);
642
750
  create_child_checkbox_menu("Transparent", "transparent", (child) => cqt.dataset.opacity = child.checked ? "transparent" : "opaque");
643
751
  create_child_checkbox_menu("Visible", "visible", (child) => {
644
752
  cqt.style.display = child.checked ? "block" : "none";
753
+ if (!child.checked)
754
+ coord_line_h.style.display = coord_line_v.style.display = "none";
645
755
  update_af_links();
646
756
  });
647
757
 
648
- function create_child_select_presets() {
649
- var tr = get_menu_table_tr();
650
- set_common_tr_style(tr);
651
- var td = document.createElement("td");
652
- set_common_left_td_style(td);
653
- td.textContent = "Presets";
654
- tr.appendChild(td);
655
- td = document.createElement("td");
656
- td.colSpan = 3;
657
- var child = document.createElement("select");
658
- child.style.cursor = "pointer";
659
- child.style.width = "100%";
758
+ function cqt_coord_line_leave(ev) {
759
+ coord_line_h.style.display = coord_line_v.style.display = "none";
760
+ }
660
761
 
661
- const presets = {
662
- none: { text: "-- Choose Preset --" },
663
- color_default: { text: "Color: Default", command: set_color_default },
664
- color_deep_blue: { text: "Color: Deep Blue", command: set_color_deep_blue },
665
- color_mono_fire: { text: "Color: Mono Fire", command: set_color_mono_fire },
666
- color_juicy_lemon: { text: "Color: Juicy Lemon", command: set_color_juicy_lemon },
667
- color_rain_forest: { text: "Color: Rain Forest", command: set_color_rain_forest },
668
- scale_960: { text: "Scale: 960", command: () => set_scale_preset(960) },
669
- scale_1280: { text: "Scale: 1280", command: () => set_scale_preset(1280) }
670
- };
762
+ function cqt_coord_line_move(ev) {
763
+ coord_line_h.style.display = coord_line_v.style.display = "block";
764
+ coord_line_h.style.top = ev.clientY + "px";
765
+ coord_line_v.style.left = ev.clientX + "px";
766
+ }
671
767
 
672
- for (const name of Object.keys(presets)) {
673
- var opt = document.createElement("option");
674
- opt.textContent = presets[name].text;
675
- opt.value = name;
676
- child.appendChild(opt);
768
+ create_child_color_menu("Coord Color", "coord_color", child => {
769
+ if (child.value == "#000000") {
770
+ coord_line_h.style.display = coord_line_v.style.display = "none";
771
+ cqt.removeEventListener("mouseleave", cqt_coord_line_leave);
772
+ cqt.removeEventListener("mousemove", cqt_coord_line_move);
773
+ } else {
774
+ cqt.addEventListener("mouseleave", cqt_coord_line_leave);
775
+ cqt.addEventListener("mousemove", cqt_coord_line_move);
776
+ coord_line_h.style.backgroundColor = coord_line_v.style.backgroundColor = child.value;
677
777
  }
778
+ });
678
779
 
679
- child.onchange = function() {
680
- presets[child.value]?.command?.();
681
- child.value = "none";
682
- };
683
-
684
- td.appendChild(child);
685
- tr.appendChild(td);
780
+ function create_child_select_presets() {
781
+ const presets = [
782
+ { title: "-- Choose Preset --" },
783
+ { title: "Color: Default", callback: set_color_default },
784
+ { title: "Color: Deep Blue", callback: set_color_deep_blue },
785
+ { title: "Color: Flame on Violet", callback: set_color_flame_on_violet },
786
+ { title: "Color: Mono Fire", callback: set_color_mono_fire },
787
+ { title: "Color: Evergreen", callback: set_color_evergreen },
788
+ { title: "Color: Juicy Lemon", callback: set_color_juicy_lemon },
789
+ { title: "Color: Rain Forest", callback: set_color_rain_forest },
790
+ { title: "Scale: 960", callback: () => set_scale_preset(960) },
791
+ { title: "Scale: 1280", callback: () => set_scale_preset(1280) }
792
+ ];
793
+
794
+ create_child_select_menu("Presets", null, presets.map(v => v.title), child => {
795
+ presets[child.value|0]?.callback?.();
796
+ child.value = 0;
797
+ });
686
798
 
687
799
  function set_color_preset(...args) {
688
800
  child_menu.left_color.value = number2color(args[0]), child_menu.left_color.onchange();
@@ -690,6 +802,15 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
690
802
  child_menu.right_color.value = number2color(args[2]), child_menu.right_color.onchange();
691
803
  child_menu.peak_color.value = number2color(args[3]), child_menu.peak_color.onchange();
692
804
  child_menu.brightness.value = args[4], child_menu.brightness.onchange();
805
+ child_menu.saturation.value = 0, child_menu.saturation.onchange();
806
+ }
807
+
808
+ function set_color_hue_preset(sat, hue, range, br, peak) {
809
+ child_menu.saturation.value = sat, child_menu.saturation.onchange();
810
+ child_menu.hue.value = hue, child_menu.hue.onchange();
811
+ child_menu.hue_range.value = range, child_menu.hue_range.onchange();
812
+ child_menu.brightness.value = br, child_menu.brightness.onchange();
813
+ child_menu.peak_color.value = number2color(peak), child_menu.peak_color.onchange();
693
814
  }
694
815
 
695
816
  function set_color_default() {
@@ -701,10 +822,18 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
701
822
  set_color_preset(0x6400b9, 0x6464dc, 0x0064b9, 0x0000ff, 50);
702
823
  }
703
824
 
825
+ function set_color_flame_on_violet() {
826
+ set_color_hue_preset(20, -3, 18, 10, 0x6633cc);
827
+ }
828
+
704
829
  function set_color_mono_fire() {
705
830
  set_color_preset(0xb94a25, 0xdc582c, 0xb94a25, 0xff0000, 70);
706
831
  }
707
832
 
833
+ function set_color_evergreen() {
834
+ set_color_hue_preset(20, 15, -18, 10, 0x3366cc);
835
+ }
836
+
708
837
  function set_color_juicy_lemon() {
709
838
  set_color_preset(0xff004a, 0xffff58, 0x00ff4a, 0xffffff, 33);
710
839
  }
@@ -819,6 +948,8 @@ import {ShowCQTElement} from "../../showcqt-element@2/showcqt-element.mjs";
819
948
 
820
949
  create_menu();
821
950
  document.body.appendChild(cqt);
951
+ document.body.appendChild(coord_line_h);
952
+ document.body.appendChild(coord_line_v);
822
953
  document.body.appendChild(af_links);
823
954
  dispatchEvent(new CustomEvent("youtube-musical-spectrum-is-available"));
824
955
  })().catch(e => console.error(e));