From 1109fc79839d90ed4fc2d26b3c90a1de10d4f594 Mon Sep 17 00:00:00 2001 From: Luciano Iam Date: Sun, 21 Jun 2020 23:29:27 +0200 Subject: [PATCH] WS: Improve mixer demo --- .../builtin/mixer-demo/css/main.css | 134 -- .../builtin/mixer-demo/css/widget.css | 52 - .../builtin/mixer-demo/index.html | 23 - .../builtin/mixer-demo/js/main.js | 185 --- .../builtin/mixer-demo/js/widget.js | 147 -- .../builtin/mixer-demo/manifest.xml | 6 - .../mixer/ardour-toolkit-theme/css/button.css | 91 ++ .../ardour-toolkit-theme/css/buttonarray.css | 103 ++ .../mixer/ardour-toolkit-theme/css/chart.css | 67 + .../ardour-toolkit-theme/css/circular.css | 62 + .../mixer/ardour-toolkit-theme/css/clock.css | 44 + .../ardour-toolkit-theme/css/colorpicker.css | 52 + .../css/colorpickerdialog.css | 22 + .../ardour-toolkit-theme/css/container.css | 21 + .../ardour-toolkit-theme/css/crossover.css | 21 + .../mixer/ardour-toolkit-theme/css/dialog.css | 10 + .../ardour-toolkit-theme/css/dynamics.css | 41 + .../ardour-toolkit-theme/css/expander.css | 68 + .../mixer/ardour-toolkit-theme/css/fader.css | 140 ++ .../mixer/ardour-toolkit-theme/css/frame.css | 28 + .../css/frequencyresponse.css | 29 + .../mixer/ardour-toolkit-theme/css/gauge.css | 34 + .../ardour-toolkit-theme/css/globalcursor.css | 106 ++ .../mixer/ardour-toolkit-theme/css/graph.css | 37 + .../mixer/ardour-toolkit-theme/css/grid.css | 41 + .../mixer/ardour-toolkit-theme/css/icon.css | 26 + .../mixer/ardour-toolkit-theme/css/knob.css | 46 + .../mixer/ardour-toolkit-theme/css/label.css | 21 + .../ardour-toolkit-theme/css/levelmeter.css | 156 ++ .../ardour-toolkit-theme/css/meterbase.css | 101 ++ .../ardour-toolkit-theme/css/multimeter.css | 89 ++ .../ardour-toolkit-theme/css/notification.css | 113 ++ .../css/notifications.css | 10 + .../mixer/ardour-toolkit-theme/css/pager.css | 35 + .../css/responsehandle.css | 227 +++ .../css/responsehandler.css | 27 + .../mixer/ardour-toolkit-theme/css/root.css | 21 + .../mixer/ardour-toolkit-theme/css/scale.css | 289 ++++ .../mixer/ardour-toolkit-theme/css/select.css | 63 + .../mixer/ardour-toolkit-theme/css/slider.css | 21 + .../mixer/ardour-toolkit-theme/css/state.css | 49 + .../mixer/ardour-toolkit-theme/css/toggle.css | 42 + .../ardour-toolkit-theme/css/tooltip.css | 47 + .../mixer/ardour-toolkit-theme/css/value.css | 47 + .../ardour-toolkit-theme/css/valuebutton.css | 88 ++ .../ardour-toolkit-theme/css/valueknob.css | 22 + .../mixer/ardour-toolkit-theme/css/window.css | 117 ++ .../images/background.png | Bin 0 -> 125246 bytes .../images/chart/background.png | Bin 0 -> 18559 bytes .../images/chart/background.xcf | Bin 0 -> 784595 bytes .../fader/background_horizontal_center.png | Bin 0 -> 135 bytes .../fader/background_horizontal_left.png | Bin 0 -> 234 bytes .../fader/background_horizontal_right.png | Bin 0 -> 223 bytes .../fader/background_vertical_bottom.png | Bin 0 -> 215 bytes .../fader/background_vertical_center.png | Bin 0 -> 131 bytes .../images/fader/background_vertical_top.png | Bin 0 -> 232 bytes .../images/fader/handle_horizontal.png | Bin 0 -> 1198 bytes .../images/fader/handle_horizontal_warn.png | Bin 0 -> 1212 bytes .../images/fader/handle_vertical.png | Bin 0 -> 1209 bytes .../images/fader/handle_vertical_warn.png | Bin 0 -> 1194 bytes .../images/gradients/blue_in.jpg | Bin 0 -> 1823 bytes .../images/gradients/blue_in.png | Bin 0 -> 8083 bytes .../images/gradients/blue_out.jpg | Bin 0 -> 1822 bytes .../images/gradients/blue_out.png | Bin 0 -> 6755 bytes .../images/gradients/grey_in.jpg | Bin 0 -> 1761 bytes .../images/gradients/grey_in.png | Bin 0 -> 7083 bytes .../images/gradients/grey_out.jpg | Bin 0 -> 1761 bytes .../images/gradients/grey_out.png | Bin 0 -> 6009 bytes .../images/knob/background.svg | 313 ++++ .../meterbase/bevel_soft_horizontal.png | Bin 0 -> 258 bytes .../images/meterbase/bevel_soft_vertical.png | Bin 0 -> 250 bytes .../meterbase/bevel_strong_horizontal.png | Bin 0 -> 268 bytes .../meterbase/bevel_strong_vertical.png | Bin 0 -> 255 bytes .../images/meterbase/grid_soft.png | Bin 0 -> 148 bytes .../images/meterbase/grid_strong.png | Bin 0 -> 165 bytes .../images/meterbase/led_horizontal.png | Bin 0 -> 137 bytes .../images/meterbase/led_vertical.png | Bin 0 -> 138 bytes .../images/meterbase/slim_soft_horizontal.png | Bin 0 -> 141 bytes .../images/meterbase/slim_soft_vertical.png | Bin 0 -> 154 bytes .../meterbase/slim_strong_horizontal.png | Bin 0 -> 139 bytes .../images/meterbase/slim_strong_vertical.png | Bin 0 -> 160 bytes .../images/select/arrow_down.png | Bin 0 -> 231 bytes .../images/state/circle.png | Bin 0 -> 7594 bytes .../images/state/horizontal.png | Bin 0 -> 4138 bytes .../images/state/over.xcf | Bin 0 -> 82092 bytes .../images/state/square.png | Bin 0 -> 705 bytes .../images/state/vertical.png | Bin 0 -> 3606 bytes .../images/window/background.png | Bin 0 -> 81473 bytes .../images/window/resize.png | Bin 0 -> 269 bytes .../mixer/ardour-toolkit-theme/theme.css | 41 + share/web_surfaces/builtin/mixer/index.html | 12 + share/web_surfaces/builtin/mixer/js/main.js | 114 ++ share/web_surfaces/builtin/mixer/js/scale.js | 82 + .../web_surfaces/builtin/mixer/js/tkloader.js | 90 ++ .../web_surfaces/builtin/mixer/js/tkwidget.js | 185 +++ share/web_surfaces/builtin/mixer/js/widget.js | 91 ++ share/web_surfaces/builtin/mixer/main.css | 61 + share/web_surfaces/builtin/mixer/manifest.xml | 6 + .../builtin/mixer/toolkit/.gitignore | 1 + .../builtin/mixer/toolkit/COPYING | 674 ++++++++ share/web_surfaces/builtin/mixer/toolkit/G.js | 218 +++ .../builtin/mixer/toolkit/implements/README | 1 + .../mixer/toolkit/implements/anchor.js | 82 + .../mixer/toolkit/implements/audiomath.js | 324 ++++ .../builtin/mixer/toolkit/implements/base.js | 921 +++++++++++ .../mixer/toolkit/implements/globalcursor.js | 71 + .../mixer/toolkit/implements/gradient.js | 211 +++ .../builtin/mixer/toolkit/implements/notes.js | 102 ++ .../mixer/toolkit/implements/ranged.js | 664 ++++++++ .../mixer/toolkit/implements/ranges.js | 83 + .../mixer/toolkit/implements/warning.js | 58 + .../builtin/mixer/toolkit/modules/README | 6 + .../builtin/mixer/toolkit/modules/circular.js | 626 ++++++++ .../builtin/mixer/toolkit/modules/drag.js | 179 +++ .../mixer/toolkit/modules/dragcapture.js | 345 +++++ .../mixer/toolkit/modules/dragvalue.js | 283 ++++ .../builtin/mixer/toolkit/modules/eqband.js | 185 +++ .../builtin/mixer/toolkit/modules/filter.js | 401 +++++ .../builtin/mixer/toolkit/modules/graph.js | 352 +++++ .../builtin/mixer/toolkit/modules/grid.js | 272 ++++ .../builtin/mixer/toolkit/modules/range.js | 103 ++ .../builtin/mixer/toolkit/modules/resize.js | 136 ++ .../mixer/toolkit/modules/responsehandle.js | 1354 ++++++++++++++++ .../builtin/mixer/toolkit/modules/scale.js | 571 +++++++ .../mixer/toolkit/modules/scrollvalue.js | 174 +++ .../builtin/mixer/toolkit/polyfill/raf.js | 39 + .../mixer/toolkit/styles/fonts/Toolkit.css | 141 ++ .../mixer/toolkit/styles/fonts/Toolkit.css.in | 32 + .../mixer/toolkit/styles/fonts/Toolkit.html | 117 ++ .../toolkit/styles/fonts/Toolkit.html.in | 41 + .../mixer/toolkit/styles/fonts/Toolkit.svg | 849 ++++++++++ .../mixer/toolkit/styles/fonts/Toolkit.ttf | Bin 0 -> 14144 bytes .../builtin/mixer/toolkit/styles/toolkit.css | 1304 ++++++++++++++++ .../builtin/mixer/toolkit/toolkit.js | 1370 +++++++++++++++++ .../builtin/mixer/toolkit/widgets/README | 5 + .../builtin/mixer/toolkit/widgets/button.js | 114 ++ .../mixer/toolkit/widgets/buttonarray.js | 459 ++++++ .../builtin/mixer/toolkit/widgets/chart.js | 839 ++++++++++ .../builtin/mixer/toolkit/widgets/clock.js | 382 +++++ .../mixer/toolkit/widgets/colorpicker.js | 565 +++++++ .../toolkit/widgets/colorpickerdialog.js | 75 + .../builtin/mixer/toolkit/widgets/colors.js | 819 ++++++++++ .../mixer/toolkit/widgets/confirmbutton.js | 162 ++ .../mixer/toolkit/widgets/container.js | 411 +++++ .../mixer/toolkit/widgets/crossover.js | 209 +++ .../builtin/mixer/toolkit/widgets/dialog.js | 219 +++ .../builtin/mixer/toolkit/widgets/dynamics.js | 284 ++++ .../mixer/toolkit/widgets/equalizer.js | 346 +++++ .../builtin/mixer/toolkit/widgets/expander.js | 272 ++++ .../builtin/mixer/toolkit/widgets/fader.js | 411 +++++ .../builtin/mixer/toolkit/widgets/frame.js | 59 + .../toolkit/widgets/frequencyresponse.js | 160 ++ .../builtin/mixer/toolkit/widgets/gauge.js | 171 ++ .../builtin/mixer/toolkit/widgets/icon.js | 88 ++ .../builtin/mixer/toolkit/widgets/knob.js | 212 +++ .../builtin/mixer/toolkit/widgets/label.js | 69 + .../mixer/toolkit/widgets/levelmeter.js | 542 +++++++ .../builtin/mixer/toolkit/widgets/list.js | 73 + .../builtin/mixer/toolkit/widgets/listitem.js | 40 + .../mixer/toolkit/widgets/meterbase.js | 534 +++++++ .../mixer/toolkit/widgets/multimeter.js | 247 +++ .../mixer/toolkit/widgets/notification.js | 156 ++ .../mixer/toolkit/widgets/notifications.js | 72 + .../builtin/mixer/toolkit/widgets/pager.js | 461 ++++++ .../mixer/toolkit/widgets/responsehandler.js | 51 + .../builtin/mixer/toolkit/widgets/root.js | 76 + .../builtin/mixer/toolkit/widgets/select.js | 739 +++++++++ .../builtin/mixer/toolkit/widgets/slider.js | 207 +++ .../mixer/toolkit/widgets/sortablelist.js | 76 + .../mixer/toolkit/widgets/sortablelistitem.js | 65 + .../builtin/mixer/toolkit/widgets/state.js | 109 ++ .../builtin/mixer/toolkit/widgets/tag.js | 135 ++ .../builtin/mixer/toolkit/widgets/taggable.js | 155 ++ .../mixer/toolkit/widgets/taggablelistitem.js | 35 + .../mixer/toolkit/widgets/taggabletreeitem.js | 35 + .../builtin/mixer/toolkit/widgets/tagger.js | 99 ++ .../builtin/mixer/toolkit/widgets/tagnode.js | 93 ++ .../builtin/mixer/toolkit/widgets/tags.js | 87 ++ .../builtin/mixer/toolkit/widgets/toggle.js | 237 +++ .../builtin/mixer/toolkit/widgets/tooltips.js | 205 +++ .../builtin/mixer/toolkit/widgets/treeitem.js | 173 +++ .../builtin/mixer/toolkit/widgets/value.js | 262 ++++ .../mixer/toolkit/widgets/valuebutton.js | 218 +++ .../mixer/toolkit/widgets/valueknob.js | 144 ++ .../builtin/mixer/toolkit/widgets/widget.js | 799 ++++++++++ .../builtin/mixer/toolkit/widgets/window.js | 819 ++++++++++ .../img => protocol}/ardour-icon.svg | 0 .../web_surfaces/builtin/protocol/index.html | 19 + share/web_surfaces/builtin/protocol/main.css | 67 + share/web_surfaces/builtin/protocol/main.js | 68 + .../builtin/protocol/manifest.xml | 6 + .../builtin/transport/manifest.xml | 2 +- 192 files changed, 29395 insertions(+), 548 deletions(-) delete mode 100644 share/web_surfaces/builtin/mixer-demo/css/main.css delete mode 100644 share/web_surfaces/builtin/mixer-demo/css/widget.css delete mode 100644 share/web_surfaces/builtin/mixer-demo/index.html delete mode 100644 share/web_surfaces/builtin/mixer-demo/js/main.js delete mode 100644 share/web_surfaces/builtin/mixer-demo/js/widget.js delete mode 100644 share/web_surfaces/builtin/mixer-demo/manifest.xml create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/button.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/buttonarray.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/chart.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/circular.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/clock.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/colorpicker.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/colorpickerdialog.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/container.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/crossover.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/dialog.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/dynamics.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/expander.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/fader.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/frame.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/frequencyresponse.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/gauge.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/globalcursor.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/graph.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/grid.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/icon.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/knob.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/label.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/levelmeter.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/meterbase.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/multimeter.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/notification.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/notifications.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/pager.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/responsehandle.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/responsehandler.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/root.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/scale.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/select.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/slider.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/state.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/toggle.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/tooltip.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/value.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/valuebutton.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/valueknob.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/window.css create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/background.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/chart/background.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/chart/background.xcf create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_horizontal_center.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_horizontal_left.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_horizontal_right.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_vertical_bottom.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_vertical_center.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_vertical_top.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_horizontal.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_horizontal_warn.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_vertical.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_vertical_warn.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/blue_in.jpg create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/blue_in.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/blue_out.jpg create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/blue_out.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_in.jpg create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_in.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_out.jpg create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_out.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/knob/background.svg create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_soft_horizontal.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_soft_vertical.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_strong_horizontal.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_strong_vertical.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/grid_soft.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/grid_strong.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/led_horizontal.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/led_vertical.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/slim_soft_horizontal.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/slim_soft_vertical.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/slim_strong_horizontal.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/slim_strong_vertical.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/select/arrow_down.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/circle.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/horizontal.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/over.xcf create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/square.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/vertical.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/window/background.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/window/resize.png create mode 100644 share/web_surfaces/builtin/mixer/ardour-toolkit-theme/theme.css create mode 100644 share/web_surfaces/builtin/mixer/index.html create mode 100644 share/web_surfaces/builtin/mixer/js/main.js create mode 100644 share/web_surfaces/builtin/mixer/js/scale.js create mode 100644 share/web_surfaces/builtin/mixer/js/tkloader.js create mode 100644 share/web_surfaces/builtin/mixer/js/tkwidget.js create mode 100644 share/web_surfaces/builtin/mixer/js/widget.js create mode 100644 share/web_surfaces/builtin/mixer/main.css create mode 100644 share/web_surfaces/builtin/mixer/manifest.xml create mode 100644 share/web_surfaces/builtin/mixer/toolkit/.gitignore create mode 100644 share/web_surfaces/builtin/mixer/toolkit/COPYING create mode 100644 share/web_surfaces/builtin/mixer/toolkit/G.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/README create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/anchor.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/audiomath.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/base.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/globalcursor.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/gradient.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/notes.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/ranged.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/ranges.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/implements/warning.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/README create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/circular.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/drag.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/dragcapture.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/dragvalue.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/eqband.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/filter.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/graph.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/grid.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/range.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/resize.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/responsehandle.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/scale.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/modules/scrollvalue.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/polyfill/raf.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.css create mode 100644 share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.css.in create mode 100644 share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.html create mode 100644 share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.html.in create mode 100644 share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.svg create mode 100644 share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.ttf create mode 100644 share/web_surfaces/builtin/mixer/toolkit/styles/toolkit.css create mode 100644 share/web_surfaces/builtin/mixer/toolkit/toolkit.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/README create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/button.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/buttonarray.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/chart.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/clock.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/colorpicker.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/colorpickerdialog.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/colors.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/confirmbutton.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/container.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/crossover.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/dialog.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/dynamics.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/equalizer.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/expander.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/fader.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/frame.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/frequencyresponse.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/gauge.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/icon.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/knob.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/label.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/levelmeter.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/list.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/listitem.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/meterbase.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/multimeter.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/notification.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/notifications.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/pager.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/responsehandler.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/root.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/select.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/slider.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/sortablelist.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/sortablelistitem.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/state.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/tag.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/taggable.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/taggablelistitem.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/taggabletreeitem.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/tagger.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/tagnode.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/tags.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/toggle.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/tooltips.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/treeitem.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/value.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/valuebutton.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/valueknob.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/widget.js create mode 100644 share/web_surfaces/builtin/mixer/toolkit/widgets/window.js rename share/web_surfaces/builtin/{mixer-demo/img => protocol}/ardour-icon.svg (100%) create mode 100644 share/web_surfaces/builtin/protocol/index.html create mode 100644 share/web_surfaces/builtin/protocol/main.css create mode 100644 share/web_surfaces/builtin/protocol/main.js create mode 100644 share/web_surfaces/builtin/protocol/manifest.xml diff --git a/share/web_surfaces/builtin/mixer-demo/css/main.css b/share/web_surfaces/builtin/mixer-demo/css/main.css deleted file mode 100644 index f0543be875..0000000000 --- a/share/web_surfaces/builtin/mixer-demo/css/main.css +++ /dev/null @@ -1,134 +0,0 @@ -html { - height: 100%; -} - -body { - background: rgb(62,61,61); - color: rgb(248,248,242); - font-family: Helvetica, Arial, sans-serif; - height: 100%; - width: 100%; - position: fixed; - margin: 0; -} - -div { - box-sizing: border-box; -} - -#main { - display: flex; - flex-direction: column; - height: 100%; -} - -#surface { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; - box-shadow: 0px 0px 10px #000; -} - -#top { - display: flex; - align-items: center; - padding: 0.25em 0.5em; - box-shadow: 0px 0px 10px #000; -} - -#top > img { - height: 24px; -} - -#top > span { - margin-left: 12px; -} - -#log { - height: 7em; - overflow: scroll; - overflow-x: hidden; - background: rgba(0,0,0,0.4); -} - -#strips { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - overflow: scroll; - overflow-x: hidden; - background: rgba(0,0,0,0.2); -} - -#log pre { - margin: 0; - font-family: Menlo, monospace; - font-size: 1em; -} - -.message-in { - color: rgb(248,248,242); -} - -.message-out { - color: rgb(172,128,255); -} - -.info { - color: rgb(99,208,230); -} - -.error { - color: rgb(249,36,114); -} - -.comp-name { - font-size: 1.5em; - font-weight: bold; -} - -.strip { - margin: 20px; - padding: 20px 40px; - background: rgba(0,0,0,0.1); - border-radius: 5px; - width: 100%; - max-width: 768px; -} - -.slider-meter { - float: right; -} - -.strip-slider { - margin-top: 20px; -} - -.plugin { - margin: 40px 0; - padding: 20px; - background: rgba(0,0,0,0.05); - border-radius: 5px; -} - -.plugin-enable { - float: right; -} - -.plugin-param { - margin: 20px 0; -} - -.plugin-param.boolean { - text-align: center; -} - -.plugin-param > .widget-switch { - margin: 10px 40px; -} - -.plugin-param.boolean { - display: inline-block; -} diff --git a/share/web_surfaces/builtin/mixer-demo/css/widget.css b/share/web_surfaces/builtin/mixer-demo/css/widget.css deleted file mode 100644 index 9681f5d651..0000000000 --- a/share/web_surfaces/builtin/mixer-demo/css/widget.css +++ /dev/null @@ -1,52 +0,0 @@ -.widget-switch { - display: block; - -webkit-appearance:none; - width: 37px; - height: 37px; - border: 3.5px solid rgb(248,248,242); - border-radius: 50%; -} - -.widget-switch:checked { - background: rgb(72,89,118); -} - -.widget-slider { - display: block; - -webkit-appearance: none; - height: 37px; - width: 100%; - background: transparent; -} - -.widget-slider::-webkit-slider-runnable-track { - height: 4px; - background: rgb(248,248,242); -} - -.widget-slider::-webkit-slider-thumb { - -webkit-appearance: none; - height: 36px; - width: 36px; - margin-top: -16px; - border: 3.5px solid rgb(248,248,242); - border-radius: 50%; - background: rgb(72,89,118); -} - -/* repeat slider style for firefox */ - -.widget-slider::-moz-range-track { - height: 4px; - background: rgb(248,248,242); -} - -.widget-slider::-moz-range-thumb { - -webkit-appearance: none; - height: 36px; - width: 36px; - margin-top: -16px; - border: 3.5px solid rgb(248,248,242); - border-radius: 50%; - background: rgb(72,89,118); -} diff --git a/share/web_surfaces/builtin/mixer-demo/index.html b/share/web_surfaces/builtin/mixer-demo/index.html deleted file mode 100644 index a2262f2b3f..0000000000 --- a/share/web_surfaces/builtin/mixer-demo/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - Ardour WebSockets Demo - - - - - -
-
-
- - -
-
-
-
-
- - - diff --git a/share/web_surfaces/builtin/mixer-demo/js/main.js b/share/web_surfaces/builtin/mixer-demo/js/main.js deleted file mode 100644 index 83ae67c829..0000000000 --- a/share/web_surfaces/builtin/mixer-demo/js/main.js +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2020 Luciano Iam - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -import ArdourClient from '/shared/ardour.js'; - -import { Switch, DiscreteSlider, ContinuousSlider, LogarithmicSlider, - StripPanSlider, StripGainSlider, StripMeter } from './widget.js'; - -(() => { - - const MAX_LOG_LINES = 1000; - - const ardour = new ArdourClient(); - - async function main () { - ardour.on('connected', (connected) => { - if (connected) { - log('Client connected', 'info'); - } else { - log('Client disconnected', 'error'); - } - }); - - ardour.on('message', (msg, inbound) => { - if (inbound) { - log(`↙ ${msg}`, 'message-in'); - } else { - log(`↗ ${msg}`, 'message-out'); - } - }); - - ardour.mixer.on('ready', () => { - const div = document.getElementById('strips'); - for (const strip of ardour.mixer.strips) { - createStrip(strip, div); - } - }); - - await ardour.connect(); - - const manifest = await ardour.getSurfaceManifest(); - document.getElementById('manifest').innerHTML = manifest.name.toUpperCase() - + ' v' + manifest.version + ' — ' + manifest.description; - } - - function createStrip (strip, parentDiv) { - const domId = `strip-${strip.addrId}`; - if (document.getElementById(domId) != null) { - return; - } - - const div = createElem(`
`, parentDiv); - createElem(``, div); - - // meter - const meter = new StripMeter(); - meter.el.classList.add('slider-meter'); - meter.appendTo(div); - bind(strip, 'meter', meter); - - // gain - let holder = createElem(`
`, div); - createElem(``, holder); - const gain = new StripGainSlider(); - gain.appendTo(holder); - bind(strip, 'gain', gain); - - if (!strip.isVca) { - // pan - holder = createElem(`
`, div); - createElem(``, holder); - const pan = new StripPanSlider(); - pan.appendTo(holder); - bind(strip, 'pan', pan); - } - - for (const plugin of strip.plugins) { - createStripPlugin(plugin, div); - } - } - - function createStripPlugin (plugin, parentDiv) { - const domId = `plugin-${plugin.addrId}`; - if (document.getElementById(domId) != null) { - return; - } - - const div = createElem(`
`, parentDiv); - createElem(``, div); - - const enable = new Switch(); - enable.el.classList.add('plugin-enable'); - enable.appendTo(div); - bind(plugin, 'enable', enable); - - for (const param of plugin.parameters) { - createStripPluginParam(param, div); - } - } - - function createStripPluginParam (param, parentDiv) { - const domId = `param-${param.addrId}`; - if (document.getElementById(domId) != null) { - return; - } - - let widget, cssClass; - - if (param.valueType.isBoolean) { - cssClass = 'boolean'; - widget = new Switch(); - } else if (param.valueType.isInteger) { - cssClass = 'discrete'; - widget = new DiscreteSlider(param.min, param.max); - } else if (param.valueType.isDouble) { - cssClass = 'continuous'; - if (param.isLog) { - widget = new LogarithmicSlider(param.min, param.max); - } else { - widget = new ContinuousSlider(param.min, param.max); - } - } - - const div = createElem(`
`, parentDiv); - createElem(``, div); - - widget.el.name = domId; - widget.appendTo(div); - bind(param, 'value', widget); - } - - function createElem (html, parent) { - const t = document.createElement('template'); - t.innerHTML = html; - - const elem = t.content.firstChild; - - if (parent) { - parent.appendChild(elem); - } - - return elem; - } - - function bind (component, property, widget) { - // ardour → ui - widget.value = component[property]; - component.on(property, (value) => widget.value = value); - // ui → ardour - widget.callback = (value) => component[property] = value; - } - - function log (message, className) { - const output = document.getElementById('log'); - - if (output.childElementCount > MAX_LOG_LINES) { - output.removeChild(output.childNodes[0]); - } - - const pre = document.createElement('pre'); - pre.innerHTML = message; - pre.className = className; - - output.appendChild(pre); - output.scrollTop = output.scrollHeight; - } - - main(); - -})(); diff --git a/share/web_surfaces/builtin/mixer-demo/js/widget.js b/share/web_surfaces/builtin/mixer-demo/js/widget.js deleted file mode 100644 index 74c9d75cd4..0000000000 --- a/share/web_surfaces/builtin/mixer-demo/js/widget.js +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2020 Luciano Iam - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -export class Widget { - - constructor (html) { - const template = document.createElement('template'); - template.innerHTML = html; - this.el = template.content.firstChild; - } - - appendTo (parent) { - parent.appendChild(this.el); - } - - callback (value) { - // do nothing by default - } - -} - -export class Switch extends Widget { - - constructor () { - super (``); - this.el.addEventListener('input', (ev) => this.callback(this.value)); - } - - get value () { - return this.el.checked; - } - - set value (val) { - this.el.checked = val; - } - -} - -export class Slider extends Widget { - - constructor (min, max, step) { - const html = ``; - super(html); - this.min = min; - this.max = max; - this.el.addEventListener('input', (ev) => this.callback(this.value)); - } - - get value () { - return parseFloat(this.el.value) - } - - set value (val) { - this.el.value = val; - } - -} - -export class DiscreteSlider extends Slider { - - constructor (min, max, step) { - super(min, max, step || 1); - } - -} - -export class ContinuousSlider extends Slider { - - constructor (min, max) { - super(min, max, 0.001); - } - -} - -export class LogarithmicSlider extends ContinuousSlider { - - constructor (min, max) { - super(0, 1.0); - this.minVal = Math.log(min); - this.maxVal = Math.log(max); - this.scale = this.maxVal - this.minVal; - } - - get value () { - return Math.exp(this.minVal + this.scale * super.value); - } - - set value (val) { - this.el.value = (Math.log(val) - this.minVal) / this.scale; - } - -} - -export class StripPanSlider extends ContinuousSlider { - - constructor () { - super(-1.0, 1.0); - } - -} - -export class StripGainSlider extends ContinuousSlider { - - constructor () { - super(0, 1.0) - this.minVal = -58.0; - this.maxVal = 6.0; - this.scale = (this.maxVal - this.minVal); - } - - get value () { - return this.maxVal + Math.log10(super.value) * this.scale; - } - - set value (val) { - this.el.value = Math.pow(10.0, (val - this.maxVal) / this.scale); - } - -} - -export class StripMeter extends Widget { - - constructor () { - super(``); - } - - set value (val) { - this.el.innerHTML = val == -Infinity ? '-∞' : `${Math.round(val)} dB`; - } - -} diff --git a/share/web_surfaces/builtin/mixer-demo/manifest.xml b/share/web_surfaces/builtin/mixer-demo/manifest.xml deleted file mode 100644 index 75476a4f9e..0000000000 --- a/share/web_surfaces/builtin/mixer-demo/manifest.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/button.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/button.css new file mode 100644 index 0000000000..b76a40bad9 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/button.css @@ -0,0 +1,91 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-button { + margin: 4px; + padding: 8px 16px; + color: #002f42; + text-shadow: 0px 1px 0px rgba(255,255,255,0.6); + background-color: rgb(245,245,245); + background-image: url(../images/gradients/grey_out.png); + background-repeat: repeat-x; + background-size: 100% 200%; + background-position: 50% 0; + background-clip: padding-box; + border: 1px solid #aaa; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + text-decoration: none; + box-sizing: border-box; + transition: border 0.25s ease-in-out, box-shadow 0.25s ease-in-out; +} +.toolkit-button:hover { + border: 1px solid #002f42; + background-size: 100% 120%; + background-position: 50% 0; + -webkit-box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1); + box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.1); +} +.toolkit-button:active { + background-image: url(../images/gradients/grey_in.png); + background-size: 100% 250%; + background-position: 50% 75%; + -webkit-box-shadow: 0px 3px 0px rgba(255,255,255,0.5), 0px -3px 0px rgba(0,0,0,0.05), 2px 0px 0px rgba(255,255,255,0.3), 0px -2px 0px rgba(0,0,0,0.025); + -moz-box-shadow: 0px 3px 0px rgba(255,255,255,0.5), 0px -3px 0px rgba(0,0,0,0.05), 2px 0px 0px rgba(255,255,255,0.3), 0px -2px 0px rgba(0,0,0,0.025); + box-shadow: 0px 3px 0px rgba(255,255,255,0.5), 0px -3px 0px rgba(0,0,0,0.05), 2px 0px 0px rgba(255,255,255,0.3), 0px -2px 0px rgba(0,0,0,0.025); + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../images/gradients/grey_in.png',sizingMethod='scale'); +} + +.toolkit-button > .toolkit-label { + line-height: 1.1em; + text-align: center; + margin: auto; + padding: 0 3px; + transition: background 0.33s ease-in-out, color 0.33s ease-in-out; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.toolkit-button > .toolkit-icon { + color: #aaaaaa; + width: 32px; + height: 32px; + font-size: 32px; + line-height: 32px; +} + +.toolkit-button.toolkit-has-label.toolkit-vertical > .toolkit-icon { + margin-bottom: 5px; +} +.toolkit-button.toolkit-has-label.toolkit-horizontal > .toolkit-icon { + margin-right: 5px; +} + +.toolkit-button.toolkit-active { + border: 1px solid #002f42; +} +.toolkit-button.toolkit-active > .toolkit-label { + background-color: #002f42; + color: white; + text-shadow: 0px -1px 0px black; +} +.toolkit-button.toolkit-active > .toolkit-icon { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/buttonarray.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/buttonarray.css new file mode 100644 index 0000000000..39bde00ed0 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/buttonarray.css @@ -0,0 +1,103 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-buttonarray { + height: 50px; +} + +.toolkit-buttonarray.toolkit-vertical { + text-align: left; +} +.toolkit-buttonarray.toolkit-vertical > .toolkit-previous { + margin-bottom: 5px; +} +.toolkit-buttonarray.toolkit-vertical > .toolkit-next { + margin-top: 5px; +} +.toolkit-buttonarray.toolkit-horizontal > .toolkit-previous { + width: 40px; + padding-left: 4px; + padding-right: 4px; + margin-right: 5px; +} +.toolkit-buttonarray.toolkit-horizontal > .toolkit-next { + width: 40px; + padding-left: 4px; + padding-right: 4px; + margin-left: 5px; +} + +.toolkit-buttonarray > .toolkit-clip { +s -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + border: 1px solid #aaa; + transition: left 0.5s ease-in-out, top 0.5s ease-in-out, right 0.5s ease-in-out, bottom 0.5s ease-in-out; + background-color: rgb(245,245,245); + background-image: url(../images/gradients/grey_out.png); + background-repeat: repeat-x; + background-size: 100% 200%; + background-position: 50% 0; +} +.toolkit-buttonarray.toolkit-over.toolkit-vertical > .toolkit-clip { + background-color: rgb(245,245,245); + background-image: url(../images/gradients/grey_out.png); + background-repeat: repeat-x; + background-size: 100% 200%; + background-position: 50% 0; +} + +.toolkit-buttonarray > .toolkit-clip > .toolkit-container { + transition: left 0.5s ease-in-out, top 0.5s ease-in-out; +} + +.toolkit-buttonarray.toolkit-horizontal > .toolkit-clip > .toolkit-container { + +} +.toolkit-buttonarray.toolkit-vertical > .toolkit-clip > .toolkit-container { + +} + +.toolkit-buttonarray > .toolkit-clip > .toolkit-container > .toolkit-button { + margin: 0; + border: none; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + border-radius: 0px; +} +.toolkit-buttonarray > .toolkit-clip > .toolkit-container > .toolkit-button > .toolkit-label { + white-space: nowrap; + width: 100%; + box-sizing: border-box; +} +.toolkit-buttonarray.toolkit-horizontal > .toolkit-clip > .toolkit-container > .toolkit-button { + height: 100%; +} +.toolkit-buttonarray.toolkit-vertical > .toolkit-clip > .toolkit-container > .toolkit-button { + display: flex; + width: 100%; + background: none; +} +.toolkit-buttonarray.toolkit-vertical > .toolkit-clip > .toolkit-container > .toolkit-button:hover, +.toolkit-buttonarray.toolkit-vertical > .toolkit-clip > .toolkit-container > .toolkit-button:active + { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + background: rgba(0,0,0,0.05); +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/chart.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/chart.css new file mode 100644 index 0000000000..dd7806b536 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/chart.css @@ -0,0 +1,67 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-chart > svg { + background-color: rgb(0, 56, 103); + background-image: url(../images/chart/background.png); + background-repeat: repeat-x; + background-size: 100% 100%; + background-position: 50% 50%; + background-clip: padding-box; + filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../images/chart/background.png',sizingMethod='scale'); + -webkit-box-shadow: inset 0px 4px 12px 0px rgba(0, 0, 0, 0.9); + -moz-box-shadow: inset 0px 4px 12px 0px rgba(0, 0, 0, 0.9); + box-shadow: inset 0px 4px 12px 0px rgba(0, 0, 0, 0.9); + border-top: 3px solid #ddd; + border-bottom: 3px solid #fff; + border-left: 2px solid #e8e8e8; + border-right: 2px solid #f3f3f3; +} + +.toolkit-chart { +} + +.toolkit-chart > svg > .toolkit-background { + fill: rgba(0, 0, 0, 0.2); +} +.toolkit-chart > svg > .toolkit-background.toolkit-hover { + fill: rgba(0, 0, 0, 0.5); +} +.toolkit-chart > svg > .toolkit-key { + margin: 8px; + padding: 8px; + fill: white; + opacity: 0.8; +} +.toolkit-chart > svg > .toolkit-key.toolkit-hover { + opacity: 1; +} +.toolkit-chart > svg > .toolkit-key > text > .toolkit-label { + line-height: 1.2em; /* sets the distance between key entries */ + margin: 5px; /* sets the distance between labels and example rects */ +} +.toolkit-chart > svg > .toolkit-key > .toolkit-rect { + +} + +.toolkit-chart > svg > .toolkit-title { + fill: rgba(255,255,255,0.2); + margin: 8px 26px; + font-weight: bold; + font-size: 2em; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/circular.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/circular.css new file mode 100644 index 0000000000..dc32918cc8 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/circular.css @@ -0,0 +1,62 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-circular { + fill: #101010; + stroke: none; + stroke-width: 0; +} + +.toolkit-circular > .toolkit-base { + /*opacity: 0.2;*/ +} +.toolkit-circular.toolkit-warn > .toolkit-base { + opacity: 0.5; +} + +.toolkit-circular > .toolkit-value { + +} +.toolkit-circular.toolkit-warn > .toolkit-value { + opacity: 1; + fill: white; +} + +.toolkit-circular > .toolkit-hand { + fill: #c00; +} + +.toolkit-circular > .toolkit-dots { + +} +.toolkit-circular > .toolkit-dots > .toolkit-dot { +} + +.toolkit-circular > .toolkit-markers { + +} +.toolkit-circular > .toolkit-markers > .toolkit-marker { + fill: #c00; +} + +.toolkit-circular > .toolkit-labels { + +} +.toolkit-circular > .toolkit-labels > .toolkit-label { + font-size: 0.7em; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/clock.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/clock.css new file mode 100644 index 0000000000..623a554ba4 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/clock.css @@ -0,0 +1,44 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-widget.toolkit-clock > svg { + -webkit-border-radius: 100%; + -moz-border-radius: 100%; + border-radius: 100%; + -webkit-box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.2); + box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.2); + fill: rgb(0,42,66); +} + +.toolkit-widget.toolkit-clock > svg > .toolkit-circular { + fill: rgb(0,42,66); +} + +.toolkit-widget.toolkit-clock > svg > .toolkit-circular > .toolkit-value { + +} + +.toolkit-widget.toolkit-clock > svg > .toolkit-circular > .toolkit-base { + fill: white; + opacity: 0.5; +} + +.toolkit-widget.toolkit-clock > svg > .toolkit-label { + margin: 5px; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/colorpicker.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/colorpicker.css new file mode 100644 index 0000000000..72a3957b9e --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/colorpicker.css @@ -0,0 +1,52 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + + .toolkit-color-picker { + +} + +.toolkit-color-picker > .toolkit-canvas { +} +.toolkit-color-picker > .toolkit-canvas > .toolkit-grayscale { +} +.toolkit-color-picker > .toolkit-canvas > .toolkit-indicator { + border: 0.5px solid; + -webkit-box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5); + -moz-box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5); + -ms-box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5); +} + + +.toolkit-color-picker > .toolkit-hex { + border: 0px solid black; +} + +.toolkit-color-picker > .toolkit-hue { +} +.toolkit-color-picker >.toolkit-saturation { +} +.toolkit-color-picker > .toolkit-lightness { +} + +.toolkit-color-picker > .toolkit-red { +} +.toolkit-color-picker > .toolkit-green { +} +.toolkit-color-picker > .toolkit-blue { +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/colorpickerdialog.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/colorpickerdialog.css new file mode 100644 index 0000000000..ff478b40db --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/colorpickerdialog.css @@ -0,0 +1,22 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +.toolkit-color-picker-dialog { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/container.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/container.css new file mode 100644 index 0000000000..43873665fa --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/container.css @@ -0,0 +1,21 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-container { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/crossover.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/crossover.css new file mode 100644 index 0000000000..c7c3babfa4 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/crossover.css @@ -0,0 +1,21 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-crossover { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/dialog.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/dialog.css new file mode 100644 index 0000000000..0952b46d71 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/dialog.css @@ -0,0 +1,10 @@ +.toolkit-dialog { + border: 1px solid #fff; + background: #eee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0px 2px 9px 0px rgba(0, 0, 0, 0.75); + -moz-box-shadow: 0px 2px 9px 0px rgba(0, 0, 0, 0.75); + box-shadow: 0px 2px 9px 0px rgba(0, 0, 0, 0.75); +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/dynamics.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/dynamics.css new file mode 100644 index 0000000000..0c57e931c6 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/dynamics.css @@ -0,0 +1,41 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-dynamics { +} + +.toolkit-dynamics > svg { + position:absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.toolkit-dynamics > svg > .toolkit-grid > .toolkit-grid-line.toolkit-highlight { + stroke: rgba(255,0,0,0.66); +} +.toolkit-dynamics > svg > .toolkit-grid > .toolkit-grid-label.toolkit-highlight { + fill: rgb(255,255,255); + font-weight: bold; +} + +.toolkit-dynamics > svg > .toolkit-graphs > .toolkit-steady { + stroke: rgba(255,255,255,0.5); + stroke-dasharray: 1.41421,1.41421; + stroke-width: 1px; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/expander.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/expander.css new file mode 100644 index 0000000000..f011612c58 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/expander.css @@ -0,0 +1,68 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +/* POPUP */ +.toolkit-expander.toolkit-popup { + background: rgba(216, 216, 216, 0.9); +} +.toolkit-expander.toolkit-popup.toolkit-expanded > .toolkit-toggle-expand { + width: 42px; + height: 42px; +} +.toolkit-expander.toolkit-popup.toolkit-expanded > .toolkit-toggle-expand::after, +.toolkit-expander.toolkit-popup.toolkit-expanded > .toolkit-toggle-expand::before { + content: ""; + display: block; + position: absolute; + height: 4px; + width: 32px; + top: 19px; + left: 5px; + transform: rotate(45deg); + background: #003050; +} +.toolkit-expander.toolkit-popup.toolkit-expanded > .toolkit-toggle-expand::before { + transform: rotate(135deg); +} + +/* POPUP INPLACE */ + +.toolkit-expander.toolkit-popup-inplace { + background: rgba(216, 216, 216, 0.9); +} + +.toolkit-expander.toolkit-popup-inplace.toolkit-expanded > .toolkit-toggle-expand { + width: 42px; + height: 42px; +} +.toolkit-expander.toolkit-popup-inplace.toolkit-expanded > .toolkit-toggle-expand::after, +.toolkit-expander.toolkit-popup-inplace.toolkit-expanded > .toolkit-toggle-expand::before { + content: ""; + display: block; + position: absolute; + height: 4px; + width: 32px; + top: 19px; + left: 5px; + transform: rotate(45deg); + background: #003050; +} +.toolkit-expander.toolkit-popup.toolkit-expanded > .toolkit-toggle-expand::before { + transform: rotate(135deg); +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/fader.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/fader.css new file mode 100644 index 0000000000..b3d36cc621 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/fader.css @@ -0,0 +1,140 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-fader { + margin: 4px; + padding: 0px; + background-repeat: no-repeat; +} + +.toolkit-fader.toolkit-horizontal { + grid-column-gap: 4px; + grid-row-gap: 2px; +} +.toolkit-fader.toolkit-vertical { + grid-column-gap: 2px; + grid-row-gap: 4px; +} + +/* TRACK */ + + +.toolkit-fader > .toolkit-track { + background-color: #303030; + border-radius: 5px; +} + +.toolkit-fader.toolkit-vertical > .toolkit-track { + width: 46px; +} +.toolkit-fader.toolkit-horizontal > .toolkit-track { + height: 46px; +} + +.toolkit-fader.toolkit-top > .toolkit-track { + align-self: end; +} +.toolkit-fader.toolkit-bottom > .toolkit-track { + align-self: start; +} +.toolkit-fader.toolkit-left > .toolkit-track { + justify-self: end; +} +.toolkit-fader.toolkit-right > .toolkit-track { + justify-self: start; +} + +/* HANDLE */ + +.toolkit-fader > .toolkit-track > .toolkit-handle { + cursor: pointer; + cursor: hand; + margin: 1px; + box-shadow: 1px 3px 8px rgba(0, 0, 0, 0.5); + border-radius: 4px; +} +.toolkit-fader.toolkit-vertical > .toolkit-track > .toolkit-handle { + width: 44px; + height: 80px; + background: url(../images/fader/handle_vertical.png) no-repeat; +} +.toolkit-fader.toolkit-vertical.toolkit-warn > .toolkit-track > .toolkit-handle { + width: 44px; + height: 80px; + background: url(../images/fader/handle_vertical_warn.png) no-repeat; +} +.toolkit-fader.toolkit-horizontal > .toolkit-track > .toolkit-handle { + height: 44px; + width: 80px; + background: url(../images/fader/handle_horizontal.png) no-repeat; +} +.toolkit-fader.toolkit-horizontal.toolkit-warn > .toolkit-track > .toolkit-handle { + height: 44px; + width: 80px; + background: url(../images/fader/handle_horizontal_warn.png) no-repeat; +} + + +/* SCALE */ + +.toolkit-fader.toolkit-horizontal > .toolkit-scale { + margin: 0 40px; +} +.toolkit-fader.toolkit-vertical > .toolkit-scale { + margin: 40px 0; +} + +.toolkit-fader.toolkit-top > .toolkit-scale { + align-self: start; +} +.toolkit-fader.toolkit-bottom > .toolkit-scale { + align-self: end; +} +.toolkit-fader.toolkit-left > .toolkit-scale { + justify-self: start; +} +.toolkit-fader.toolkit-right > .toolkit-scale { + justify-self: end; +} + +/* LABEL */ + +.toolkit-fader > .toolkit-label { + line-height: 23px; + height: 23px; + text-align: center; +} + + +/* VALUE */ + +.toolkit-fader > .toolkit-value, +.toolkit-fader > .toolkit-value > .toolkit-input { + line-height: 23px; + height: 23px; + font-size: 14px; +} +.toolkit-fader > .toolkit-value > .toolkit-input { + background: transparent; +} +.toolkit-fader > .toolkit-value.toolkit-active { + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5); +} +.toolkit-fader > .toolkit-value.toolkit-active > .toolkit-input { + background: #002f42; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/frame.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/frame.css new file mode 100644 index 0000000000..b0e890744b --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/frame.css @@ -0,0 +1,28 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-frame { + padding: 8px; + margin-top: 8px; +} +.toolkit-frame > .toolkit-frame-label { + font-weight: bold; + position: absolute; + top: -8px; + left: 8px; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/frequencyresponse.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/frequencyresponse.css new file mode 100644 index 0000000000..614f7dc085 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/frequencyresponse.css @@ -0,0 +1,29 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-frequency-response { + +} + +.toolkit-frequency-response > svg > .toolkit-grid > .toolkit-grid-line.toolkit-highlight { + stroke: rgba(255,0,0,0.66); +} +.toolkit-frequency-response > svg > .toolkit-grid > .toolkit-grid-label.toolkit-highlight { + fill: rgb(255,255,255); + font-weight: bold; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/gauge.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/gauge.css new file mode 100644 index 0000000000..ba130426e4 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/gauge.css @@ -0,0 +1,34 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-gauge { + margin: 4px; +} + +.toolkit-gauge > svg > .toolkit-circular { + fill: rgb(0,42,66); +} + +.toolkit-gauge > svg > .toolkit-circular > .toolkit-base { + fill: white; + opacity: 0.5; +} + +.toolkit-gauge > svg > .toolkit-title { + fill: rgb(0,42,66); +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/globalcursor.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/globalcursor.css new file mode 100644 index 0000000000..fb21dd90bb --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/globalcursor.css @@ -0,0 +1,106 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +body.toolkit-cursor-default, +body.toolkit-cursor-default * { + cursor:default !important; +} +body.toolkit-cursor-crosshair, +body.toolkit-cursor-crosshair * { + cursor:crosshair !important; +} +body.toolkit-cursor-pointer, +body.toolkit-cursor-pointer * { + cursor:pointer !important; +} +body.toolkit-cursor-move, +body.toolkit-cursor-move * { + cursor:move !important; +} +body.toolkit-cursor-text, +body.toolkit-cursor-text * { + cursor:text !important; +} +body.toolkit-cursor-wait, +body.toolkit-cursor-wait * { + cursor:wait !important; +} +body.toolkit-cursor-help, +body.toolkit-cursor-help * { + cursor:help !important; +} +body.toolkit-cursor-n-resize, +body.toolkit-cursor-n-resize * { + cursor:n-resize !important; +} +body.toolkit-cursor-ne-resize, +body.toolkit-cursor-ne-resize * { + cursor:ne-resize !important; +} +body.toolkit-cursor-e-resize, +body.toolkit-cursor-e-resize * { + cursor:e-resize !important; +} +body.toolkit-cursor-se-resize, +body.toolkit-cursor-se-resize * { + cursor:se-resize !important; +} +body.toolkit-cursor-s-resize, +body.toolkit-cursor-s-resize * { + cursor:s-resize !important; +} +body.toolkit-cursor-sw-resize, +body.toolkit-cursor-sw-resize * { + cursor:sw-resize !important; +} +body.toolkit-cursor-w-resize, +body.toolkit-cursor-w-resize * { + cursor:w-resize !important; +} +body.toolkit-cursor-nw-resize, +body.toolkit-cursor-nw-resize * { + cursor:nw-resize !important; +} +body.toolkit-cursor-progress, +body.toolkit-cursor-progress * { + cursor:progress !important; +} +body.toolkit-cursor-not-allowed, +body.toolkit-cursor-not-allowed * { + cursor:not-allowed !important; +} +body.toolkit-cursor-no-drop, +body.toolkit-cursor-no-drop * { + cursor:no-drop !important; +} +body.toolkit-cursor-vertical-text, +body.toolkit-cursor-vertical-text * { + cursor:vertical-text !important; +} +body.toolkit-cursor-all-scroll, +body.toolkit-cursor-all-scroll * { + cursor:all-scroll !important; +} +body.toolkit-cursor-col-resize, +body.toolkit-cursor-col-resize * { + cursor:col-resize !important; +} +body.toolkit-cursor-row-resize, +body.toolkit-cursor-row-resize * { + cursor:row-resize !important; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/graph.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/graph.css new file mode 100644 index 0000000000..063ee8701a --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/graph.css @@ -0,0 +1,37 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-graph { + stroke: #fff; + fill: none; + stroke-width: 1.2px; + stroke-linecap: round; + stroke-linejoin: round; +} + +.toolkit-graph.toolkit-filled { + stroke-width: 1.2px; + stroke: #fff; + fill: rgba(255,255,255,0.25); +} + +.toolkit-graph.toolkit-outline { + stroke-width: 2px; + stroke: #fff; + fill: none; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/grid.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/grid.css new file mode 100644 index 0000000000..7f6207c244 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/grid.css @@ -0,0 +1,41 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-grid-line { + stroke: rgba(255,255,255,0.25); + stroke-dasharray: 1,1; +} +.toolkit-grid-line.toolkit-vertical { + +} +.toolkit-grid-line.toolkit-horizontal { + +} + +.toolkit-grid-label { + fill: rgba(255,255,255,0.99); + /* ONLY USE PADDING FOR POSITIONING! */ + padding: 1px 2px; + font-size: 0.7em; +} +.toolkit-grid-label.toolkit-vertical { + +} +.toolkit-grid-label.toolkit-horizontal { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/icon.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/icon.css new file mode 100644 index 0000000000..70bf570a93 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/icon.css @@ -0,0 +1,26 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +.toolkit-icon { + margin: auto; + width: 32px; + height: 32px; + font-size: 32px; + line-height: 32px; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/knob.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/knob.css new file mode 100644 index 0000000000..724e31663a --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/knob.css @@ -0,0 +1,46 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-knob { + /*background: url(../images/knob/background.svg) no-repeat 50% 50%;*/ + background-size: contain; + cursor: pointer; + width: 100px; + height: 100px; +} +.toolkit-knob > svg { + width: 100%; + height: 100%; +} +.toolkit-knob > svg > .toolkit-circular > .toolkit-hand { + fill: #002a42; +} + +.toolkit-knob > svg > .toolkit-circular > .toolkit-base { + +} +.toolkit-knob > svg > .toolkit-circular > .toolkit-value { + fill: #487c92; +} +.toolkit-knob > svg > .toolkit-circular > .toolkit-dots > .toolkit-dot { + fill: #002f42; +} +.toolkit-knob > svg > .toolkit-circular > .toolkit-labels > .toolkit-label { + font-size: 8px; + fill: #002f42; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/label.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/label.css new file mode 100644 index 0000000000..dc6b32df24 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/label.css @@ -0,0 +1,21 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-label { + overflow: hidden; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/levelmeter.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/levelmeter.css new file mode 100644 index 0000000000..094357aaa2 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/levelmeter.css @@ -0,0 +1,156 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +/* UNIVERSAL */ + +.toolkit-level-meter.toolkit-clipping > .toolkit-label { + color: #ff8000; + text-shadow: 0 0 3px white,0 0 3px white; +} + +/* CLIP */ + +.toolkit-level-meter > .toolkit-clip { + background-color: #f60 !important; + margin: 0; +} +.toolkit-level-meter.toolkit-horizontal > .toolkit-clip { + height: 32px; + width: 8px; +} + +/* PEAK */ + +.toolkit-level-meter > .toolkit-bar > .toolkit-peak { + line-height: 16px; +} + +.toolkit-level-meter.toolkit-vertical > .toolkit-bar > .toolkit-peak { + top: 0; + width: 100%; + height: 12px; + transform: translateY(-50%); +} +.toolkit-level-meter.toolkit-vertical.toolkit-left > .toolkit-bar > .toolkit-peak { + left: 0px; + text-align: left; +} +.toolkit-level-meter.toolkit-vertical.toolkit-left > .toolkit-bar > .toolkit-peak::before { + content: ""; + display: block; + position: absolute; + top: 50%; + left: 0; + height: 0; + width: 0; + border-left: 5px solid #efefef; + border-top: 3px solid transparent; + border-bottom: 3px solid transparent; + z-index: 1; + transform: translateY(-3px); +} +.toolkit-level-meter.toolkit-vertical.toolkit-right > .toolkit-bar > .toolkit-peak { + right: 0px; + text-align: right; +} +.toolkit-level-meter.toolkit-vertical.toolkit-right > .toolkit-bar > .toolkit-peak::before { + content: ""; + display: block; + position: absolute; + top: 50%; + right: 0; + height: 0; + width: 0; + border-right: 5px solid #f5f5f5; + border-top: 3px solid transparent; + border-bottom: 3px solid transparent; + z-index: 1; + transform: translateY(-3px); +} + +.toolkit-level-meter.toolkit-horizontal > .toolkit-bar > .toolkit-peak { + transform: translateX(-50%); +} +.toolkit-level-meter.toolkit-horizontal.toolkit-top > .toolkit-bar > .toolkit-peak { + top: 0px; +} +.toolkit-level-meter.toolkit-horizontal.toolkit-top > .toolkit-bar > .toolkit-peak::before { + content: ""; + display: block; + position: absolute; + left: 50%; + top: 0; + height: 0; + width: 0; + border-top: 5px solid #ddd; + border-left: 3px solid transparent; + border-right: 3px solid transparent; + z-index: 1; + transform: translateX(-3px); +} +.toolkit-level-meter.toolkit-horizontal.toolkit-bottom > .toolkit-bar > .toolkit-peak { + bottom: 0px; +} +.toolkit-level-meter.toolkit-horizontal.toolkit-bottom > .toolkit-bar > .toolkit-peak::before { + content: ""; + display: block; + position: absolute; + left: 50%; + bottom: 0; + height: 0; + width: 0; + border-bottom: 5px solid #f5f5f5; + border-right: 3px solid transparent; + border-left: 3px solid transparent; + z-index: 1; + transform: translateX(-3px); +} +/* PEAK LABEL */ + +.toolkit-level-meter > .toolkit-bar > .toolkit-peak > .toolkit-peak-label { + color: white; + font-size: 8px; + line-height: 12px; + opacity: 0.9; + overflow: hidden; +} +.toolkit-level-meter.toolkit-vertical > .toolkit-bar > .toolkit-peak .toolkit-peak-label { + text-shadow: 0 1px 2px black,0 1px 2px black; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} +.toolkit-level-meter.toolkit-vertical.toolkit-left > .toolkit-bar > .toolkit-peak > .toolkit-peak-label { + padding-left: 7px; +} +.toolkit-level-meter.toolkit-vertical.toolkit-right > .toolkit-bar > .toolkit-peak > .toolkit-peak-label { + padding-right: 7px; +} + +.toolkit-level-meter.toolkit-horizontal > .toolkit-bar > .toolkit-peak .toolkit-peak-label { + text-shadow: -1px 0 2px black,-1px 0 2px black; + padding: 7px; +} +.toolkit-level-meter.toolkit-horizontal.toolkit-top > .toolkit-bar > .toolkit-peak > .toolkit-peak-label { + +} +.toolkit-level-meter.toolkit-horizontal.toolkit-bottom > .toolkit-bar > .toolkit-peak > .toolkit-peak-label { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/meterbase.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/meterbase.css new file mode 100644 index 0000000000..f91cfc5332 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/meterbase.css @@ -0,0 +1,101 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +/* + AVAILABLE DEFAULT BACKGROUNDS + background-image: url(../images/meterbase/grid_soft.png); + background-image: url(../images/meterbase/grid_strong.png); + background-image: url(../images/meterbase/slim_soft_vertical.png); + background-image: url(../images/meterbase/slim_strong_vertical.png); + background-image: url(../images/meterbase/slim_soft_horizontal.png); + background-image: url(../images/meterbase/slim_strong_horizontal.png); + background-image: url(../images/meterbase/led_vertical.png); + background-image: url(../images/meterbase/led_horizontal.png); + background-image: url(../images/meterbase/bevel_soft_vertical.png); + background-image: url(../images/meterbase/bevel_strong_vertical.png); + background-image: url(../images/meterbase/bevel_soft_horizontal.png); + background-image: url(../images/meterbase/bevel_strong_horizontal.png); +*/ + +/* BASE */ + +.toolkit-meter-base { + /*padding: 4px; + background: #eee; + border: 1px solid #fff; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2); + box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2); + box-sizing: border-box; + margin: 4px;*/ + background: #139513; +} + +/* LABEL */ + +.toolkit-meter-base > .toolkit-label { + line-height: 1.2em; + height: 1.2em; + overflow: visible; + font-size: 0.8em; +} + +/* TITLE */ + +.toolkit-meter-base > .toolkit-title { + line-height: 1.2em; + height: 1.2em; + overflow: visible; + font-size: 0.8em; +} + +/* SCALE */ + +.toolkit-meter-base > .toolkit-scale { + color: #aaa; + border: 0 solid transparent; + border-width: 3px 2px; +} +.toolkit-meter-base > .toolkit-scale > .toolkit-label.toolkit-base { + color: #666; +} + +/* BAR */ + +.toolkit-meter-base > .toolkit-bar { + /*border-top: 3px solid #ddd; + border-bottom: 3px solid #fff; + border-left: 2px solid #e8e8e8; + border-right: 2px solid #f3f3f3; + background-clip: padding-box !important;*/ +} +.toolkit-meter-base.toolkit-horizontal > .toolkit-bar { + height: 32px; +} +.toolkit-meter-base.toolkit-vertical > .toolkit-bar { + width: 8px; +} + +/* MASK */ + +.toolkit-meter-base > .toolkit-bar > .toolkit-mask { + background: #303030; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/multimeter.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/multimeter.css new file mode 100644 index 0000000000..a94bf787cb --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/multimeter.css @@ -0,0 +1,89 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +/* BASIC STUFF */ + +.toolkit-multi-meter { + position: relative; + background: #eee; + border: 1px solid #fff; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2); + box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.2); + margin: 3px; + padding: 3px; + padding-top: calc(1.2em + 8px); +} + +.toolkit-multi-meter > .toolkit-title { + height: 1.2em; + line-height: 1.2em; + position: absolute; + top: 3px; + left: 3px; +} + +/* levelmeter */ + +.toolkit-multi-meter > .toolkit-level-meter { + border: none; + background: transparent; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + margin: 0; + padding: 0; + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + border-radius: 0px; +} + +.toolkit-multi-meter.toolkit-vertical > .toolkit-level-meter > .toolkit-bar, +.toolkit-multi-meter.toolkit-vertical > .toolkit-level-meter > .toolkit-state { + border-width: 3px 1px 3px 0; + width: 20px; +} +.toolkit-multi-meter.toolkit-vertical > .toolkit-level-meter:nth-of-type(1) > .toolkit-bar, +.toolkit-multi-meter.toolkit-vertical > .toolkit-level-meter:nth-of-type(1) > .toolkit-state { + border-left-width: 2px; +} +.toolkit-multi-meter.toolkit-vertical > .toolkit-level-meter:nth-last-of-type(1) > .toolkit-bar, +.toolkit-multi-meter.toolkit-vertical > .toolkit-level-meter:nth-last-of-type(1) > .toolkit-state { + border-right-width: 2px; +} +.toolkit-multi-meter.toolkit-horizontal > .toolkit-level-meter > .toolkit-bar, +.toolkit-multi-meter.toolkit-horizontal > .toolkit-level-meter > .toolkit-state { + border-width: 1px 2px 0px 2px; + height: 20px; +} +.toolkit-multi-meter.toolkit-horizontal > .toolkit-level-meter:nth-of-type(1) > .toolkit-bar, +.toolkit-multi-meter.toolkit-horizontal > .toolkit-level-meter:nth-of-type(1) > .toolkit-state { + border-top-width: 2px; +} +.toolkit-multi-meter.toolkit-horizontal > .toolkit-level-meter:nth-last-of-type(1) > .toolkit-bar, +.toolkit-multi-meter.toolkit-horizontal > .toolkit-level-meter:nth-last-of-type(1) > .toolkit-state { + border-bottom-width: 2px; +} + +.toolkit-multi-meter.toolkit-horizontal .toolkit-level-meter > .toolkit-title { + margin-right: 4px; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/notification.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/notification.css new file mode 100644 index 0000000000..be6e3e4d2d --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/notification.css @@ -0,0 +1,113 @@ +.toolkit-notification { + background: rgba(238, 238, 238, 0.9); + border: 1px solid rgba(255, 255, 255, 0.9); + margin: 4px; + padding: 4px 8px; + min-height: 32px; + + font-size: 1rem; + text-align: left; + + -webkit-box-shadow: 0px 2px 9px 0px rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0px 2px 9px 0px rgba(0, 0, 0, 0.25); + box-shadow: 0px 2px 9px 0px rgba(0, 0, 0, 0.25); + + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + + display: block; +} + +.toolkit-notification > .toolkit-button { + background: transparent !important; + border: none !important; + position: absolute; + top: 4px; + right: 4px; + width: 20px !important; + height: 20px !important; + margin: 0; + padding: 0; + box-shadow: none !important; +} +.toolkit-notification > .toolkit-button.toolkit-close .toolkit-icon { + font-size: 20px !important; + line-height: 20px !important; + width: 20px !important; + height: 20px !important; +} + +.toolkit-notification > .toolkit-icon { + position: absolute; + font-size: 32px; + line-height: 32px; + color: #002A42; + top: 8px; + left: 8px; +} + +.toolkit-notification.toolkit-has-icon { + padding-left: 56px; +} + +.toolkit-notification.toolkit-has-close { + padding-right: 48px; +} + + +.toolkit-notification { + opacity: 0; + + -webkit-animation: -webkit-fade-in 0.5s; + -moz-animation: -moz-fade-in 0.5s; + -ms-animation: -ms-fade-in 0.5s; + -o-animation: -o-fade-in 0.5s; + animation: fade-in 0.5s; + + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); +} + +.toolkit-notification.toolkit-show { + opacity: 1; +} +.toolkit-notification.toolkit-hiding { + opacity: 0; + + -webkit-transition: opacity 0.5s, transform 0.5s; + -moz-transition: opacity 0.5s, transform 0.5s; + -ms-transition: opacity 0.5s, transform 0.5s; + -o-transition: opacity 0.5s, transform 0.5s; + transition: opacity 0.5s, transform 0.5s; + + -webkit-transform: scale(0); + -moz-transform: scale(0); + -ms-transform: scale(0); + -o-transform: scale(0); + transform: scale(0); +} + +@-webkit-keyframes -webit-fade-in { + 0% { opacity: 0; -webit-transform: scale(1); } + 100% { opacity: 1; -webit-transform: scale(1);} +} +@-moz-keyframes -moz-fade-in { + 0% { opacity: 0; -moz-transform: scale(1); } + 100% { opacity: 1; -moz-transform: scale(1);} +} +@-ms-keyframes -ms-fade-in { + 0% { opacity: 0; -ms-transform: scale(1); } + 100% { opacity: 1; -ms-transform: scale(1);} +} +@-o-keyframes -o-fade-in { + 0% { opacity: 0; -o-transform: scale(1); } + 100% { opacity: 1; -o-transform: scale(1);} +} +@keyframes fade-in { + 0% { opacity: 0; transform: scale(1); } + 100% { opacity: 1; transform: scale(1);} +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/notifications.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/notifications.css new file mode 100644 index 0000000000..5ecfc3bb42 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/notifications.css @@ -0,0 +1,10 @@ +.toolkit-notifications { + position: fixed; + z-index: 100000000000; + bottom: 0; + right: 0; + width: 100%; + max-width: 400px; + + transform: translate3d(0, 0, 0); +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/pager.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/pager.css new file mode 100644 index 0000000000..cd5466c28b --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/pager.css @@ -0,0 +1,35 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +/* GENERAL */ + +.toolkit-pager { + width: 100%; + height: 100%; +} +.toolkit-pager > .toolkit-clip { + overflow: hidden; +} + +.toolkit-pager > .toolkit-clip > .toolkit-page { + opacity: 1; + overflow: auto; +} +.toolkit-container.toolkit-hide { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/responsehandle.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/responsehandle.css new file mode 100644 index 0000000000..57f2b27ca8 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/responsehandle.css @@ -0,0 +1,227 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-response-handle { + fill: rgb(255,255,255); +} +.toolkit-response-handle.toolkit-disabled { + display: none; +} +.toolkit-response-handle:hover { + +} +.toolkit-response-handle.toolkit-inactive { + +} +.toolkit-response-handle.toolkit-active { + cursor: move !important; +} +.toolkit-response-handle.toolkit-warn { + +} +.toolkit-response-handle.toolkit-circular { + +} +.toolkit-response-handle.toolkit-circular:hover { + +} +.toolkit-response-handle.toolkit-circular.toolkit-active { + +} +.toolkit-response-handle.toolkit-line { + +} +.toolkit-response-handle.toolkit-line-vertical { + +} +.toolkit-response-handle.toolkit-line-horizontal { + +} +.toolkit-response-handle.toolkit-block { + +} +.toolkit-response-handle.toolkit-block-left { + +} +.toolkit-response-handle.toolkit-block-right { + +} +.toolkit-response-handle.toolkit-block-top { + +} +.toolkit-response-handle.toolkit-block-bottom { + +} + +.toolkit-response-handle > .toolkit-z-handle { + fill: rgb(255,255,255); + opacity: 0.5; + cursor: pointer; + stroke: rgba(255,255,255, 0.5); + stroke-width: 4; +} +.toolkit-response-handle > .toolkit-z-handle:hover { + opacity: 0.75; + stroke: rgb(255,255,255); +} +.toolkit-response-handle.toolkit-active > .toolkit-z-handle { + opacity: 0.75; + cursor: move; + stroke: rgb(255,255,255); +} + +.toolkit-response-handle > .toolkit-label { + cursor: pointer; + font-size: 0.8em; +} +.toolkit-response-handle:hover > .toolkit-label { + +} +.toolkit-response-handle.toolkit-active > .toolkit-label { + cursor:move; +} +.toolkit-response-handle > .toolkit-label > tspan { + +} + +.toolkit-response-handle.toolkit-circular > .toolkit-label { + opacity: 0.5; +} +.toolkit-response-handle.toolkit-circular:hover > .toolkit-label { + opacity: 0.66; +} +.toolkit-response-handle.toolkit-circular.toolkit-active > .toolkit-label { + opacity: 0.8; +} +.toolkit-response-handle.toolkit-line > .toolkit-label { + opacity: 0.5; +} +.toolkit-response-handle.toolkit-line:hover > .toolkit-label { + opacity: 0.66; +} +.toolkit-response-handle.toolkit-line.toolkit-active > .toolkit-label { + opacity: 0.8; +} +.toolkit-response-handle.toolkit-block > .toolkit-label { + opacity: 0.5; +} +.toolkit-response-handle.toolkit-block:hover > .toolkit-label { + opacity: 0.66; +} +.toolkit-response-handle.toolkit-block.toolkit-active > .toolkit-label { + opacity: 0.8; +} + + + +.toolkit-response-handle > .toolkit-handle { + cursor:pointer; +} +.toolkit-response-handle.toolkit-active > .toolkit-handle { + cursor:move; +} +.toolkit-response-handle.toolkit-warn > .toolkit-handle { + stroke: rgba(255,255,255,0.33); + stroke-width: 20; +} +.toolkit-response-handle.toolkit-circular > .toolkit-handle { + opacity: 0.33; +} +.toolkit-response-handle.toolkit-circular:hover > .toolkit-handle { + opacity: 0.5; +} +.toolkit-response-handle.toolkit-circular.toolkit-active > .toolkit-handle { + opacity: 0.66; +} +.toolkit-response-handle.toolkit-line > .toolkit-handle { + opacity: 0.33; +} +.toolkit-response-handle.toolkit-line:hover > .toolkit-handle { + opacity: 0.5; +} +.toolkit-response-handle.toolkit-line.toolkit-active > .toolkit-handle { + opacity: 0.66; +} +.toolkit-response-handle.toolkit-block > .toolkit-handle { + opacity: 0.2; +} +.toolkit-response-handle.toolkit-block:hover > .toolkit-handle { + opacity: 0.33; +} +.toolkit-response-handle.toolkit-block.toolkit-active > .toolkit-handle { + opacity: 0.5; +} + + + +.toolkit-response-handle > .toolkit-line { + stroke-width: 1; +} +.toolkit-response-handle:hover > .toolkit-line { + +} +.toolkit-response-handle.toolkit-active > .toolkit-line { + +} +.toolkit-response-handle.toolkit-circular > .toolkit-line { + stroke: white; +/* stroke-dasharray: 1,1; */ + opacity: 0.5; +} +.toolkit-response-handle.toolkit-circular.toolkit-active > .toolkit-line { + +} + +.toolkit-response-handle.toolkit-line > .toolkit-line-1 { + stroke: #fff; + stroke-dasharray: 1,1; + opacity: 0.6; +} +.toolkit-response-handle.toolkit-line:hover > .toolkit-line-1 { + stroke-dasharray: 0; +} +.toolkit-response-handle.toolkit-line.toolkit-active > .toolkit-line-1 { + stroke-dasharray: 0; +} +.toolkit-response-handle.toolkit-line > .toolkit-line-2 { + stroke: #fff; + opacity: 0.5; +} +.toolkit-response-handle.toolkit-line.toolkit-active > .toolkit-line-2 { + +} + +.toolkit-response-handle.toolkit-block > .toolkit-line-1 { + stroke: #fff; + stroke-dasharray: 1,1; + opacity: 0.6; +} +.toolkit-response-handle.toolkit-block:hover > .toolkit-line-1 { + stroke-dasharray: 0; +} +.toolkit-response-handle.toolkit-block.toolkit-active > .toolkit-line-1 { + stroke-dasharray: 0; + opacity: 0.9; +} +.toolkit-response-handle.toolkit-block > .toolkit-line-2 { + stroke: #fff; + opacity: 0.5; +} +.toolkit-response-handle.toolkit-block.toolkit-active > .toolkit-line-2 { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/responsehandler.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/responsehandler.css new file mode 100644 index 0000000000..35f3903e71 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/responsehandler.css @@ -0,0 +1,27 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-response-handler { + +} +.toolkit-response-handler.toolkit-dragging { + cursor: move; +} +.toolkit-response-handler > .toolkit-response-handles { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/root.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/root.css new file mode 100644 index 0000000000..8f8d78aab1 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/root.css @@ -0,0 +1,21 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-root { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/scale.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/scale.css new file mode 100644 index 0000000000..e70686ce1d --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/scale.css @@ -0,0 +1,289 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-scale { + +} +.toolkit-scale > .toolkit-label { + font-size: 0.7em; + height: 1em; + line-height: 1em; + color: #aaa; + white-space: nowrap; + margin: 0 0 0 0; + overflow: visible; +} +.toolkit-scale > .toolkit-label.toolkit-base { + color: #f10000; + font-weight: 500; +} +.toolkit-scale > .toolkit-label.toolkit-min { + color: #666; +} +.toolkit-scale > .toolkit-label.toolkit-max { + color: #666; +} +.toolkit-scale > .toolkit-dot { + width: 1px; + height: 1px; + background: #aaa; +} +.toolkit-scale > .toolkit-dot.toolkit-base { + background: #951c26; +} +.toolkit-scale > .toolkit-dot.toolkit-min { + background: #666; +} +.toolkit-scale > .toolkit-dot.toolkit-max { + background: #666; +} +.toolkit-scale > .toolkit-dot.toolkit-marker { + background: #666; +} +/* VERTICAL */ + +.toolkit-scale.toolkit-vertical { + width: 40px; +} +.toolkit-scale.toolkit-vertical > .toolkit-label { +} +.toolkit-scale.toolkit-vertical > .toolkit-label.toolkit-base { + +} +.toolkit-scale.toolkit-vertical > .toolkit-label.toolkit-min { +} +.toolkit-scale.toolkit-vertical > .toolkit-label.toolkit-max { +} +.toolkit-scale.toolkit-vertical > .toolkit-dot { + width: 2px; +} +.toolkit-scale.toolkit-vertical > .toolkit-dot.toolkit-base { + width: 4px; +} +.toolkit-scale.toolkit-vertical > .toolkit-dot.toolkit-min { + width: 4px; +} +.toolkit-scale.toolkit-vertical > .toolkit-dot.toolkit-max { + width: 4px; +} +.toolkit-scale.toolkit-vertical > .toolkit-dot.toolkit-marker { + width: 4px; +} +/* VERTICAL sticked to LEFT */ + +.toolkit-scale.toolkit-vertical.toolkit-left { + text-align: left; + padding-right: 0px; + -webkit-border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-topleft: 4px; + -moz-border-radius-bottomleft: 4px; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.toolkit-scale.toolkit-vertical.toolkit-left > .toolkit-label { + left: 6px; + margin-right: 6px; +} +.toolkit-scale.toolkit-vertical.toolkit-left > .toolkit-label.toolkit-base { + +} +.toolkit-scale.toolkit-vertical.toolkit-left > .toolkit-label.toolkit-min { + +} +.toolkit-scale.toolkit-vertical.toolkit-left > .toolkit-label.toolkit-max { + +} +.toolkit-scale.toolkit-vertical.toolkit-left > .toolkit-dot { + left: 0; +} +.toolkit-scale.toolkit-vertical.toolkit-left > .toolkit-dot.toolkit-base { + +} +.toolkit-scale.toolkit-vertical.toolkit-left > .toolkit-dot.toolkit-min { + +} +.toolkit-scale.toolkit-vertical.toolkit-left > .toolkit-dot.toolkit-max { + +} +.toolkit-scale.toolkit-vertical.toolkit-left > .toolkit-dot.toolkit-marker { + +} +/* VERTICAL sticked to RIGHT */ + +.toolkit-scale.toolkit-vertical.toolkit-right { + text-align: right; + padding-left: 0px; + -webkit-border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-topright: 4px; + -moz-border-radius-bottomright: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.toolkit-scale.toolkit-vertical.toolkit-right > .toolkit-label { + right: 6px; + margin-left: 6px; +} +.toolkit-scale.toolkit-vertical.toolkit-right > .toolkit-label.toolkit-base { + +} +.toolkit-scale.toolkit-vertical.toolkit-right > .toolkit-label.toolkit-min { + +} +.toolkit-scale.toolkit-vertical.toolkit-right > .toolkit-label.toolkit-max { + +} +.toolkit-scale.toolkit-vertical.toolkit-right > .toolkit-dot { + right: 0; +} +.toolkit-scale.toolkit-vertical.toolkit-right > .toolkit-dot.toolkit-base { + +} +.toolkit-scale.toolkit-vertical.toolkit-right > .toolkit-dot.toolkit-min { + +} +.toolkit-scale.toolkit-vertical.toolkit-right > .toolkit-dot.toolkit-max { + +} +.toolkit-scale.toolkit-vertical.toolkit-right > .toolkit-dot.toolkit-marker { + +} +/* HORIZONTAL */ + +.toolkit-scale.toolkit-horizontal { + height: 24px; +} +.toolkit-scale.toolkit-horizontal > .toolkit-label { +} +.toolkit-scale.toolkit-horizontal > .toolkit-label.toolkit-base { + +} +.toolkit-scale.toolkit-horizontal > .toolkit-label.toolkit-min { +} +.toolkit-scale.toolkit-horizontal > .toolkit-label.toolkit-max { +} +.toolkit-scale.toolkit-horizontal > .toolkit-dot { + height: 2px; +} +.toolkit-scale.toolkit-horizontal > .toolkit-dot.toolkit-base { + height: 4px; +} +.toolkit-scale.toolkit-horizontal > .toolkit-dot.toolkit-min { + height: 4px; +} +.toolkit-scale.toolkit-horizontal > .toolkit-dot.toolkit-max { + height: 4px; +} +.toolkit-scale.toolkit-horizontal > .toolkit-dot.toolkit-marker { + height: 4px; +} +/* HORIZONTAL sticked to BOTTOM */ + +.toolkit-scale.toolkit-horizontal.toolkit-bottom { + +} +.toolkit-scale.toolkit-horizontal.toolkit-bottom > .toolkit-label { + margin-top: 8px; + bottom: 6px; +} +.toolkit-scale.toolkit-horizontal.toolkit-bottom > .toolkit-label.toolkit-base { + +} +.toolkit-scale.toolkit-horizontal.toolkit-bottom > .toolkit-label.toolkit-min { + +} +.toolkit-scale.toolkit-horizontal.toolkit-bottom > .toolkit-label.toolkit-max { + +} +.toolkit-scale.toolkit-horizontal.toolkit-bottom > .toolkit-dot { + bottom: 0; +} +.toolkit-scale.toolkit-horizontal.toolkit-bottom > .toolkit-dot.toolkit-base { + +} +.toolkit-scale.toolkit-horizontal.toolkit-bottom > .toolkit-dot.toolkit-min { + +} +.toolkit-scale.toolkit-horizontal.toolkit-bottom > .toolkit-dot.toolkit-max { + +} +.toolkit-scale.toolkit-horizontal.toolkit-bottom > .toolkit-dot.toolkit-marker { + +} +/* HORIZONTAL sticked to TOP */ + +.toolkit-scale.toolkit-horizontal.toolkit-top { + +} +.toolkit-scale.toolkit-horizontal.toolkit-top > .toolkit-label { + margin-bottom: 8px; + top: 6px; +} +.toolkit-scale.toolkit-horizontal.toolkit-top > .toolkit-label.toolkit-base { + +} +.toolkit-scale.toolkit-horizontal.toolkit-top > .toolkit-label.toolkit-min { + +} +.toolkit-scale.toolkit-horizontal.toolkit-top > .toolkit-label.toolkit-max { + +} +.toolkit-scale.toolkit-horizontal.toolkit-top > .toolkit-dot { + top: 0; +} +.toolkit-scale.toolkit-horizontal.toolkit-top > .toolkit-dot.toolkit-base { + +} +.toolkit-scale.toolkit-horizontal.toolkit-top > .toolkit-dot.toolkit-min { + +} +.toolkit-scale.toolkit-horizontal.toolkit-top > .toolkit-dot.toolkit-max { + +} +.toolkit-scale.toolkit-horizontal.toolkit-top > .toolkit-dot.toolkit-marker { + +} + + +/* MARKER */ + +.toolkit-scale.toolkit-left > .toolkit-pointer { + border-right-color: #BA1B25; +} +.toolkit-scale.toolkit-right > .toolkit-pointer { + border-left-color: #BA1B25; +} +.toolkit-scale.toolkit-top > .toolkit-pointer { + border-bottom-color: #BA1B25; +} +.toolkit-scale.toolkit-bottom > .toolkit-pointer { + border-top-color: #BA1B25; +} +.toolkit-scale > .toolkit-bar { + background: #002f42; +} + +/* BAR */ + +.toolkit-scale.toolkit-vertical > .toolkit-bar { + width: 4px; +} +.toolkit-scale.toolkit-horizontal > .toolkit-bar { + height: 4px; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/select.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/select.css new file mode 100644 index 0000000000..f56a5847ab --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/select.css @@ -0,0 +1,63 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-button.toolkit-select { + padding-left: 8px; + padding-right: 8px; +} + +.toolkit-button.toolkit-select > .toolkit-cell > .toolkit-label { + margin-right: 20px; +} +.toolkit-button.toolkit-select > .toolkit-cell > .toolkit-arrow { + background: url(../images/select/arrow_down.png) 50% 50% no-repeat; + width: 16px; + position: absolute; + top: 0; + right: 0; + bottom: 0; +} + +.toolkit-select-list { + background: #efefef; + border: 1px solid white; + margin: 0; + padding: 0; + -webkit-box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); + box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); + transition: opacity 200ms; + text-align: left; +} + +.toolkit-select-list > .toolkit-option { + list-style: none; + margin: 0; + padding: 0 5px; + line-height: 2em; + cursor: pointer; + transition: background 0.25s ease-in-out, color 0.25s ease-in-out; +} + +.toolkit-select-list > .toolkit-option:hover { + background: #002f42; + color: white; +} +.toolkit-select-list > .toolkit-option.toolkit-active { + background: #dfdfdf; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/slider.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/slider.css new file mode 100644 index 0000000000..e0aee775b5 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/slider.css @@ -0,0 +1,21 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-slider { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/state.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/state.css new file mode 100644 index 0000000000..3e5aa9eac4 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/state.css @@ -0,0 +1,49 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +/* +available overlays: +circle.png +square.png +vertical.png +horizontal.png +*/ + +.toolkit-state { + width: 32px; + height: 10px; + margin: 10px; + background-color: white; + background-image: url(../images/state/circle.png); + background-position: 0 0; + background-size: 100% 100%; + background-clip: padding-box !important; + border-top: 3px solid #ddd; + border-bottom: 3px solid #fff; + border-left: 2px solid #e8e8e8; + border-right: 2px solid #f3f3f3; +} + +.toolkit-state > .toolkit-mask { + background: black; + opacity: 0.8; +} + +.toolkit-state.toolkit-state-on > .toolkit-mask { + opacity: 0; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/toggle.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/toggle.css new file mode 100644 index 0000000000..75e7b28bb6 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/toggle.css @@ -0,0 +1,42 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-toggle { + +} +.toolkit-toggle:hover { + +} +.toolkit-toggle:active { + +} +.toolkit-toggle > .toolkit-cell > .toolkit-label { + +} +.toolkit-toggle > .toolkit-cell > .toolkit-icon { + +} +.toolkit-toggle.active { + +} +.toolkit-toggle.active > .toolkit-cell > .toolkit-label { + +} +.toolkit-toggle.active > .toolkit-cell > .toolkit-icon { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/tooltip.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/tooltip.css new file mode 100644 index 0000000000..92936dfb5c --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/tooltip.css @@ -0,0 +1,47 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-tooltip { + +} +.toolkit-tooltip > .toolkit-table { + +} +.toolkit-tooltip > .toolkit-table > .toolkit-row { + +} +.toolkit-tooltip > .toolkit-table > .toolkit-row > .toolkit-cell { + +} + +.toolkit-tooltip > .toolkit-table > .toolkit-row > .toolkit-cell > .toolkit-entry { + background: #e7e7e7; + color: black; + opacity: 0.9; + font-size: 0.9em; + color: #666; + padding: 2px 4px; + -webkit-box-shadow: 0px 2px 5px rgba(0,0,0,0.3); + -moz-box-shadow: 0px 2px 5px rgba(0,0,0,0.3); + box-shadow: 0px 2px 5px rgba(0,0,0,0.3); + border: 1px solid #f3f3f3; +} + +.toolkit-tt-container { + padding: 24px 0 0 24px; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/value.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/value.css new file mode 100644 index 0000000000..a24db0dd44 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/value.css @@ -0,0 +1,47 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-value > .toolkit-input { +/* + border-top: 1px solid #ddd; + border-bottom: 1px solid #fff; + border-left: 1px solid #e8e8e8; + border-right: 1px solid #f3f3f3; +*/ + border: none; + color: #002f42; + background: rgba(0, 0, 0, 0.05); + font-size: 1.2em; + font-family: inherit; + line-height: 1.1em; + padding: 0; + margin: 0; + outline: 0; + text-align: center; + overflow: hidden; + width: 100%; + cursor: pointer; +} + +.toolkit-value.toolkit-active > .toolkit-input { + background: #002a42; + color: white; +/* + border: 1px solid transparent; +*/ +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/valuebutton.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/valuebutton.css new file mode 100644 index 0000000000..e9dee2769b --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/valuebutton.css @@ -0,0 +1,88 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-valuebutton { + padding: 0; + + grid-column-gap: 8px; + grid-row-gap: 0px; +} + +.toolkit-valuebutton.toolkit-dragging, +.toolkit-valuebutton.toolkit-scrolling { + border: 1px solid #002f42; +} +.toolkit-valuebutton.toolkit-warn { + border: 1px solid #c00; +} + +.toolkit-valuebutton > .toolkit-icon { + margin-left: 8px; + margin-top: 4px; + margin-bottom: 4px; +} +.toolkit-valuebutton > .toolkit-label { + margin-top: 4px; + margin-bottom: 4px; +} +.toolkit-valuebutton > .toolkit-scale { + margin-left: 8px; + margin-bottom: 4px; + height: 16px; +} +.toolkit-valuebutton > .toolkit-scale::before { + content: ""; + display: block; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 4px; + background: white; +} + +.toolkit-valuebutton > .toolkit-scale > .toolkit-dot { + height: 4px; +} + +.toolkit-valuebutton > .toolkit-value { + background: rgba(0,0,0,0.033); + border: none; + -webkit-border-top-right-radius: 5px; + -webkit-border-bottom-right-radius: 5px; + -moz-border-radius-topright: 5px; + -moz-border-radius-bottomright: 5px; + border-top-right-radius: 5px; + border-bottom-right-radius: 5px; + + justify-self: stretch; + align-self: stretch; +} +.toolkit-valuebutton > .toolkit-value > .toolkit-input { + width: 100%; + height: 100%; +} +.toolkit-valuebutton.toolkit-dragging > .toolkit-label, +.toolkit-valuebutton.toolkit-scrolling > .toolkit-label { + background-color: #002f42; + color: white; + text-shadow: 0px -1px 0px black; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/valueknob.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/valueknob.css new file mode 100644 index 0000000000..c79583a80d --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/valueknob.css @@ -0,0 +1,22 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +.toolkit-valueknob { + +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/window.css b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/window.css new file mode 100644 index 0000000000..343c65a43b --- /dev/null +++ b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/css/window.css @@ -0,0 +1,117 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +.toolkit-window { + border: 1px solid #fff; + background: #eee url(../images/window/background.png) no-repeat; + -webkit-box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.33); + -moz-box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.33); + box-shadow: 0px 4px 12px 0px rgba(0, 0, 0, 0.33); + opacity: 1; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.toolkit-window.toolkit-inactive { + opacity: 0.5; + -webkit-box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.2); + box-shadow: 0px 3px 10px 0px rgba(0, 0, 0, 0.2); +} + +.toolkit-window.toolkit-resizing, +.toolkit-window.toolkit-dragging { + opacity: 0.7; +} + +.toolkit-window > .toolkit-content { + padding: 4px; +} + +.toolkit-window > .toolkit-header { + height: 32px; + line-height: 30px; + border-bottom: 1px solid rgba(0,0,0,0.033); + background: rgba(255,255,255,0.33); +} +.toolkit-window > .toolkit-footer { + height: 28px; + line-height: 28px; + border-top: 1px solid rgba(255,255,255,0.6); + background: rgba(255,255,255,0.33); +} + +.toolkit-window > .toolkit-header > .toolkit-title, +.toolkit-window > .toolkit-footer > .toolkit-title { + font-weight: 700; + text-align: left; + font-size: 14px; + color: #999; + text-shadow: 0px 1px 0px white; + margin: 0 5px; +} +.toolkit-window.toolkit-active > .toolkit-header > .toolkit-title, +.toolkit-window.toolkit-active > .toolkit-footer > .toolkit-title { + color: #002f42; +} +.toolkit-window > .toolkit-header .toolkit-status, +.toolkit-window > .toolkit-footer .toolkit-status { + color: #999; + margin: 0 5px; + text-align: left; +} +.toolkit-window > .toolkit-header > .toolkit-icon, +.toolkit-window > .toolkit-footer > .toolkit-icon { + width: 24px; + height: 24px; + margin: 2px; +} +.toolkit-window > .toolkit-header > .toolkit-button, +.toolkit-window > .toolkit-footer > .toolkit-button { + margin: 0 2px; + padding: 0px; + border: 0px solid transparent; + background: transparent; + transition: border-color 0.33s; + border-radius: 0; + box-shadow: none; +} +.toolkit-window > .toolkit-header > .toolkit-button:hover, +.toolkit-window > .toolkit-footer > .toolkit-button:hover { + background: white; +} +.toolkit-window > .toolkit-header > .toolkit-button .toolkit-icon, +.toolkit-window > .toolkit-footer > .toolkit-button .toolkit-icon { + height: 16px; + margin: 0; + font-size: 16px; + line-height: 16px; + text-shadow: none !important; +} +.toolkit-window > .toolkit-header > .toolkit-button.toolkit-close .toolkit-icon, +.toolkit-window > .toolkit-footer > .toolkit-button.toolkit-close .toolkit-icon { + color: #BA1B25 !important; +} + +.toolkit-window.toolkit-shrinked { + height: 32px !important; +} +.toolkit-window > .toolkit-header > .toolkit-size, +.toolkit-window > .toolkit-footer > .toolkit-size { + color: #ccc; +} diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/background.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/background.png new file mode 100644 index 0000000000000000000000000000000000000000..43d7a2c8cca7c516267cff0eae29365d52c91504 GIT binary patch literal 125246 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

g3%YK+`fv{aG&fMk*en%7mmw(YxwAK05L= z-(3ewpcVu^H3zOtkR?}9gr#s_-y)_UQIZrAq>b+wtu_(6ByUW<`ysnM=yclKRJMfZ6)AX6*N$HoWR$Fbk2DjLg@s zEtz$^A#Lvd*4)@R`zCa@YEDaVYTN7aj3oj}j8&R#trR@mwvC@}C!3s?sdtl|XW<>f zEuNNukl*dod#Nd7v%WgS6R)P?-CZ8yIZ*+3HN==l8yacVYUEwThU0d28Pqo$80Fy> zqLQ!aFXhht{PH~dJg2vm%DEHM@_Ws=#=o#ZsjQ0hQG z?ata&St(eHuvCi%V*^0V;=B9P zdvm_}{h$1|Em4sqssK^t2*Di1g*IeGC&NV*^t7lJLXNN+Hd?Rg4YP{t@^hig#}lv> zvKOocKxkzF_pTAmf$(_JmZWWLPD3@GeXeL0)R$e(3X0bQAPJvz+K?=u78&22-Mno1HIn#l_XaiKe#?hH4S)(bt`moW;R+9 z6HpL(%g+~SLnfUrO#0mT%6gkTy7HJb4ole?k5LS_#aJ3bD*v=RD{9IjStReq%~_=s zi-y|y!Hq=_$v?beig!WiwK@FzZ9DxQ+^aFTz^MT-r7uY-Ey4 znN|R>HJGM%CDS+FLa%P|DU#Sz9cvS?&vacZ4E0lQCvpr!>}O=mJ@`0%KC#*!S6LF` z*OR2U6nI>A*u7lr_m!i(EHJTumvqg~Y~G-Mam@KrQnE12!nZ@*Ah|f<`}X?cg^zrf zLWeqtaWlZx?~Z^X98p`%a{}|r7v0k?Em2u&F6~#BcmE=}^>nguI4(6qW_}7vCbS_q zq0HUWKO-cnJ%cz?X;>{roQvUYIvqAyS0y@cm zcV{azE}bvn(Bk3mH}hz6c9JZomXQapC&v$pvPsoV_+%#xtfCr-3!W7?t#n^C4MEHz zeaI|h%ou7gAKHdin@(-fKvlvRJadAWuR1LBnHm7`2RsQXt}U?|NtD~=;5v{8j&TzX z;$@R$jBn;MGSJG%#D0+Ev-P@qL&Bm*Cl+7++kbNSzPy7nxI@X)0}6v$GN*Z z)}dCEwoeyC?sEQT?`)@bVn(}NlG6Fovi`+^jZbNF$WVpT%SB^GCh+Y;J^gB%s)+0Q zJpMM3L;_A**vFG#4=^6Yl+`Jbr4fgOO3hR$PZjGvYgF5QB~bT3!Y1^%x14#TRzTZ0 z7^>4n)PF~aV5(!JRoq;l2rur-diOltNJuwb-NgGfb9C+SgsI_KeT;7x5$ryAqedmK zNi>1PcP4$EV&gK zV4>qCv-` zr4515y3kzjUT!G^3Wxfl2%es6wP>x8D_88c== z1O?uarN=zN1MW7ZV!VWW=`o-q#&7q|t@#1z;r>kil%v8SFMAv;DGr;b{MfS^!o&CU zAMz+<_^P%$9PV0wR37;UwR<@d7mkKf4aeu6>K{&LR?j$Vm7*Q)&%!Sni1UQpUu>9? z0v0v#>3KV}gv8|pC#tcN1?t8bem^VERiY3f8{f&GDC5_34HjeE?MMh|XWBO?$ee>0 z_gm`Shj~k7=n-?+a!`wB5avCZ8>)QLV@w%88dw7Ai?*oq&*1E|iw)3^gy_xeZOb;# zOxQt}pTqzNhe(s|vTRIW*|vWPv9IU|(I~Li9X{tJ_4v3T9m4>7E;JIP`YE7q?T_AARYhofYtv4vnVf3|L!io1vMzP(gp|-Fs zc`2XHr~egsZtvvyHab>l6DHthT9A4_eI8w@pq-=xGbtItu__R*uC8XScLoL`nJOA; zYT3=$;uO0FQg?k&$$Vw~r-i=i%O#nR!f*wH^_P4!)Q-Wc%Gyv!G5qWim$ix;eSC5c zi4weE+hY;Tp)_7#lR#JJtPov56)!SkY>(2ylFK;fJ!Cm9Yf!?xgV=Ffdk!0z6~yR- z5E$?mom}cE9#XG2+`H|~5V0>%4n~A+{)LYg3)4iK2h!Nq!%%rgO4xfW_Q$xp8S@{V zC~gJkuiLOpVp{x%^4ml9rg`YGM)M_@UQFOPzlUMcJ+xqe8?jd;;z38DW}PnV>AE}9 z#A7yKJUFGl?939f*7lGUt_0C4wD#asc?;#O4gCtJx;BS1Liv4Zybn_SwIly`k#{ww zov+Q#PjaOl{!e!_%C)vxSeg!*$p?34&H9C+?1?gQcKUBJLYaSQoE(%cl(L{U;R`+s zTKY{9?0A1{F`I$~@Y8{-*jtaj7(FZ7xRcYFd7oHZkQ;D$eyFMOkixZdS2+J=z4qyf z@y z^{|S5yuuJ^RPk_$F?^x%KU!<^(oHeGOLDwgIH<46YwY(CJw=V0c^Ucb;x79M1IX0y zMNjuSF1x#N?!Gmn0Nx72Kt8V7v=q~?yYfQ)Q3TH9`?sLW;i!r5JF?fu+mru3Fx5}% z8h$1x^76G)6QMXy=EF-yhjtqgO<~i&^LAT5h(PTj1E zv?VQM6_8wAIkmidh1xuihRJp7;hy#i#)#w}nN2mgJzs0X7#r`J7H6a6{67FfLA|~U z(SKc`S(zBCp5^$KfP-kL_EOgAri6|!twf-T)+)?(U`^xu{nn>4Y)sWhih54e zRB^shfuajasg~ptGt)~~zp)LrhVQ18-b5C`u~Kx(TqcHfk}1bdaSLU8s7(#3G1p7l znKTzf^eMuvh7=tL4^Rca>7@ukN)91GGri>IG~uUa(ci0_S@x#_EPX`I!vOXB9MA=TTYbmxK8T` z<@83+xuR0u_O?O{FqaZ-T{0JaX$W!Uo+EMU?zyDm(it~l0;8o0UGp72m7BIe*P^&Z zX)bB4heMt0DXB;3yi&h2w9B@H>5)WQ`)@MR`}=~8bhG9O%?1RU^xspOT`6_HcY8k4 zvs6$^bH)1G)LX>2U}alDIrl!>YD3yjYdu_lMRA?T&`84)KpSdV9x78S%@8Wto{|ia z!nBj_I~ypM)>_Pdoq%EA>y-ZbRCwSob$kzRb5{9jdrO^KJ+vBv!RS+<+ zbSTx4=@w0=Q%YLFLGdcNvKrcW>qzFo} zAE-^%;WJ8$UdaGoAtBR@9Rw^rD_1_cJu~QVvw8^BiL3$bt?~5q^c~vfxV>rT)6>&; z?D^FsadKEmG$j)pP zBDzODZ}!;qTywrIl)8QCb4$+X_Hxp^R9HhA*-6WwQm82seOcL&Puy}m<@m}bT6w=q zYr=kzoHq3@Jf$gPg)XY?AsnpqT%-7sg+1xmA-Me>}6es)Hyt6~7 zOj9+?Si#vC6EYg~8k2n6i*(-4^rM%ofko+$2(E`yR~vgP zVs4wqT2eIat{MLg7`j5Np@^q1Ha`52p`Gbv0x$eb?Nxy=MJr;4lsA@UCA$ z>(OniJfYLymN9xstV);s-X(9?yxz=0)HtNm63LqLa;R5P$qnH(>taK#X9g=&uw&-`-aw*t$aP0+Z6ylV<(cW}f z@`NDP)6>%xHemhq`1pAFoEpt&zGzKbz*$Mg4uq30{=IHS?rBX=^lGY?8 z_Fmoe);Vp&TyMQ$)W8hobR8tDyO*lHcda@ox}`2nELM>WCy~WW9c^#yMN=wm>b~^9 zZmQO?U@OR`!kVA;BBO7k@o22W&VSIa;D&7cqU}y6+t*Myb1j8EEcLtWWk`(@a#g6) zG-HPpJy?sXx}kQaZwbf!{%&ktfx1}~Sam&?of4zNT1kIsQPb8r1uPr<&UnepP;6C1 z*V?Th>9~o!MTx>~+&0jM4<9adiXMG(HEmOr@$_sS-~!f;T&-%gtP=q2HD!Z{hqNG) zlC!`=z>lLOW)zOSNMW-Mv6t!1SA86XNdw58>+79(J3ABU7*o${D%|_1{%IOOVzKX} zpxN(}(&Y{0zFfs;Rpe}l7rZ4K6b!cKTfhqBbHbq@`NtoBoNoU1x4*qet6D|j#`;b5 zN;e!J6h#yn5-#1vcN9hv?JlactPWZZuFMYh4f7KI?h0xHhO zwV6U{t1TmJd+^k1EJ#!ntYvV;eD5PJp8&uPrJ4yS4=%uUWQ-{zL$k>U5bEIcY^GFB zV*IGpp$=xZ>mD+DaB^(dTG&BlCm^J5={4?f=}r~c4tsif`cCakNkEC*2FQNL|Ly(j z0=<*Hq*M{NqN@*OZKN)rQDo^fZZxB*jYiYX1}E3^%>%X`1j}S-W30Q1RJCp^TNL}! zbc5BLy-iS{3OMPe?*LV|nj+VP-rl2-wg5oys`_@i8f+Lv z{n;Nse!Ltie@2l=neCM+a8pa{UZhG$e5J(=I_j(lN!yq5Hf*iD_51us9K?okB?9;E z1X2W%8fOx+_ZdWt72{P>ueC7UQ#47ZT;L+(;y>KYHDW+yFPKHOg8j|r|rX4YjhimEUz(Q>n@*DeMpBzTl<(91=u6NL!s2Q zeX<+DlmX2zbl__j1rl1hhg;rkl&p-Vks!+xJrDGzGg$Y|9D@=#>xOdEoBedCl9X7rg1)BoQi|CU0%TOJ4yGWB zCDKKUrXOe|lVhilrGuktN7`JKIJ`!I3AhT+u4I|W;xhl&v~lIzOlN87N`ZB(xkfDL$iu)VWVRrN6) z`w5g=$wftxts4d8ywTj7Z{EB)os~QjK`atCT-TLZQNAsKK3qB=s|_ef(k)hF*7VbP zHXDe$r~&~iTB=o+fQimIHc3^KGNo|NO<`8s6|zYzo=cy-!EN?CO)Vuzzk!0&GD4gM zEXCKAwD)8UuE0|_>c@{CFXU`f*Y#nOVrNMaSYx`~4c6zXmMjC&4_@TeH1y`?=JfFJ zaC&@vyb8LdgQ<2Kphd(j7nIT$APX_>dDR^?2)HIOnIzTAJ6$HjJb#CHy@LY*Nz2PKLFJfvs+JDPdk=diZW`EYk|M};iPmhm}-zkSF zIbjesy>9)5{B0Ji3u!~y^iOiPx3YH3-A;iUr_<@;Ww%lW3gRkBZ!h*p;kz|AcnH*q zS$ONefB*g}bC3*Sws;}$|Lt#odr>9ILepqr{_MDdy&*+jEUtG*an)@-begX_!{*Pg zKj=#>?NqGBTQcDy1${)%+H9dtwlCbo-DsK}uDX=HDMxsA(09R}bs5rW)SJpSjDW`u zO}2golg%X5l{u2>HuyT&T2|m#QtJ_+V6|%`v4`?G*XQ%DXzr2auM{kWW<^%ztFGPh zy6(6K9D8zx(cdA!wPLs1GqjC!^XEVR`DHm}M~&Z|C6XbsK5nLy3axn6XI)!V=^y(m zrlhGoZ5zO`cblj7Rl0o~HeEHcy$KYB4W3_Ks7+>>2W$6h|HmM<1}akCtN{!d_UGD& zJ(pUQ)*|b#X0TIu)(yA54Q44y)~!!mH)yGss#T_s8FsGWq@zZ@v(`+-H|Hv=8|~Z5 z=yy+w@(TKyF)a0PJ;){Xn`NyWyY=wTKmYviX#uHEy~Gt{HBAzY={6D)7Ss@AaZXF- zh^U{bL97fXkpo{=lt*SsP;RN1TKtarIJv90TR>53Vmjv{Dw696Nusr`g@BDy70OK5 z1L{3Udcon~JsZb6fOB3_<*FSxI0NZbUz0@#+id@kC zHv+->XR87x<6jW2l11gOey4-B1|IcU%rrzU&8EsGau)hdg2EwEuf^x*=S!o}LjC#q z`Ertvy6T>O$;w7~Ka_$&GBAqN<@Rb`Dm!pw+biRvfUqqAMN(_t+K2Hoi2%mC+@a4E zM@`eiVNDOO>06W{CFMGd4bysHoZxN~Eg;czgbJF1dh-|%-I(gO7;ptUR%df*e42zf zFJ_tZ$1PmriX(;AkwbUU|i48(#+`d}y*;XG)+`x4MC zv^QE7p>-`?qrKHmv=6eQEc-I_c9uG{G7L%+R5vK`-Ob@Zp$73zKZpz_=N3dPoy`Hx zmESY^T0eEOhV1roExvmJZW8M#Id~0#TC+*ps`+axK)7A$HvJX^cm%hsL)%btPFLvE z)NNZ6-l~0{{M*!%IDhmGy5!N8*3B1ZJRPI*DDuUQ)7o|bqwxvn)#XT zcjH~i-f9fi2CzRBKq8p1W`lwmM_e`Y@NN!oi4Pw>T-9yTel*crcOKJE_1Li44Wq#P;^p;z@ ztLlJyW6s+3bJd_ihTa zuN-(xLkt3nA^Cio#yjNLK{i6`U1x@%s8Aa#_JfQJ5z^LsA?+nwOX)HN`t<43h2^4f z&=S<`eOfPj)7eMtz|;sYpqGBDmh2p#^`qp0^*UH_V3E{!@7`SjUy?Bd2e+>6&k7Fd z_pJ4_VpkFw3XIDApIeL_1e8x_^V+Vx5VA9BGE~}}^?@qlD@sL~tnGHT$U5Sv7E|@N z)UG;6Mdh6gcf-a#R7TqE&{PV@wqVbozO7`VUIY-XpK>C>l+n)Ob( zx9+MUZy!`*u>HQRH~LIYDX;9El7_1!9co>QyP~e4gZ2DETD@%wHr_U-;X4@YI<2{e z^ZZezaZWLd9sF?1m)~BUG&Mvz7ONFF!IgFg5mg)K`a|v+R19SAqz!D+Z zjAbP$ST1S6DXM!x;O&kF)d~mi z5T7WFBO$#2sZ~W(rPTypcHBdAjIB$o^Bl*}Xv14qh}P83C|fF1%RvUdwdYE(_gw2W z;HK{2x!BT@I>7QBMTfoB`a8#@lp(ZFrJQ{I)FX3|=jZ3EQK+S-)y^nb7MN`w%K~yD zz=`pdr7H_EILWpV5#E7N_k#y?-GU1G{r=CNKVOK@1?H@tA0Sqw#vSI6rD=T4<}`zp79w`-+0D1B*_W))zLHrH={uOVv^h zSr-?_OdMqqP*{1h6}9b#vYs_c`*{V4Oqu0}>H)6HPyPwEYivhU@7U*b09Ud?K#jL& zscbi`J`1u?WYU;McR*Te$-eC&>0UcxM{Yd^_?0@e{y)n(?3BAn{b4<*jQe0@b909e&7tjSb-Oqk(}M=eG<9ogslwPE z=3QW?izN>i^>=!pw9@*|gJW8(44*^1+b>gR-ymUqRPC%)RBVzCoN1M0UX3fQ8@-vm z4TR&XKiss+O#}$Y!mu_{v3tjmyfw8emI4^U=}42H*m?>`J##Qw|9C+Xk)3+XNVI12cWS)8jFDh+Q_)o&uCXpLT2JaLCO5huLdI?kgUXNkG}Zv7xkbl*r=D>( zZIzCY4DRn2G)P-gRj~f7CVU%k(PvY3`*ueZz20lLZNJMoLO*w?q$4j)vg<{z`_OvE zsWB?ZAu_3GZF((BLN0)7ujVb~M1f;FN>0SOQmxJTNs-b$JD0=zm%sewWqT8D6sa>i zqN7ZL5(P33HpN<>Inl*>EJ4@Hn3sd+ArPvr_og7~?d|PVLA|M#=)OWBDvqy12WYCp$RU)>KkqsnL=h&8Zq@FA@^&AUzD(#3_0mo;w+8(Jz`Ug}-X@$m5QT@X?Z!L?TWwmwtO z)~#c}`2F|aFQ}fk{&G(Z(#}#U>n{007LimjYojB#TtT~+mzUH1{rv^KRU;-aqb$e0 zQrazP!kgZkbo~fCq(^2Z_WUeY&cUvshaMQ`#~hI;l9Z=b6W!n6Uu|CNs}8Njxm|hb zOsiRTtU(Z6&E`;~*-+xyti_|*fcaa}Q%f<i!g|Ib$zxy&T+JhxaUOgv^U0nwG zcWOO~%-Hi2d$uNNcJW6>JI7JA<|kt7bb(&+AzO9HcT?TC~UP ziLFff(*3HX_g0agyDDl}|FNQrMR(VtEtklUyBAQQBr-Npt%r`fu5sk4u?$p^F-Fm{_ zt>|lQRp@l;1hi8Rl6$wuw9;!u<_>^z^9?tyiO9pl!g`$mg&JSs{;bF9~t4u+gOEI1fU5(9k*Rifp zq3ejF>2u3Chyz+tZAv6mZ^RW0Sou zPEI{+Kss_kvJbkR@x7l9A3j_S-`dAZexbGAx^6{x*MiF#!j+0B7_0)p)|TtFunGo> z!WJlde@msiU>TlvBZ9fT=#zbsJjhnT%2X_hkj1QpsKvh<#~UrkceNgk-gkF*SK5_D z^4KEVn6>fl#&&|(gW5prS{Uocy#g|lrG?B@Id(ltwIuLHDAv$Ykj1o5*Bw2=yN#BV z&}Qs+^fMwglnr>B$@;Lql4Y`uV<)j~lxq&D2FcUY)79YT-ncs3Sns>05)Euw#7=TT zJ}^oWf*L5Q0hp;d3#u(v&l_{Ack$}gtAE#U!k?mCQf1WI&_hh8mtMAP0Ljej?PZhD z!?%5-5G+%uHNrE3dx(vQ)-9rFQkcAzdwwUn_qG*Pv_bDi^I2>^WC7*hCu333Z5^p} zfQP2~NO?gmdwZ^RvRwhA(x2)Vwf*f~s&eLZ{K^p+P<$@GuC|GKA+58)(|6OsasA6g znw+Q|BqHoIdIzJ=u;WY%t{F_dao2!R61!R5@4x?kI!g)a_UI)@nes+*o=ao-H5*Sl z!Fi@WRN#!L*_M33W&i*n07*naRGQ20LdfjaeRJ98+bEX2ys4}XuFeETRnsZOsAqGx zzb~Shez@sO$E^FDR+g2sI*;4WH1(-2)i*$Q8sE>#&um5S_hQW}wvV&GMuA{G-9+hneo-n9&sq-K_}5NZ1WV{! zGeg@aip~fm`2vNLyG7z;?v%s+UY}LLAUFTR!^2fgqj_Kl(*tWJ5zyoalW3k9LF=y^ zoM@a4?>BGWT(X~?=M#W3yAq+SdEG$@fRTs03f$J(l_UXAfka1Y%^FIU3aU6~ovH>- zH@qWcGOAKVJKMCC2Ph)x5UPhpTO_B5t9dkC-_5H?X&H)ZdYx&C>eR zBRZ<&e5HT}q&>Go%9|0Kca&bo7p0;#_mD|Z?NFL0wZ~rF%2BWArk-80AH2oX!1r)T z4h<>VrfJj8_VMfws%qTlTNln!(r(5THQHfWk-C+flOk6n+}4GxmZcg695@0a$^3{V zk@rLINz$_DU(mm`y0eVSPwaCBeJcuN&&q%))2LfcRNAyRbki1wQxu|?1tn`l-;X7S zWD6~>+Sk2n1&7;rDHn*68W5KdaPc^l#ou*XIwtJyOWMg|6>iPLO%YY_lJsibqN|LM zwBM)lm#Iq}Og-l=BN3QEbW^YNxSQTIX@z%s@uIOd*mo4g8`IzA6!yxQJH8TO*1Au@ zSfAMluHBbHH>xxjsc&y@ujbN>?mESoW(%M=Jz$@$jpo!z+MY)Pt*U|rdIUao$TZ*R zoeN~xFBTb5#$N%^{kLM0QvF&) z*P&EX;4*^N%Ui+v(A3P9hv|2lU%h|RIa?-5DoFK{Q|X1EPU^~_OTgdJFnh5E0_)YY z1l)ykk4(%}d0K9o(%Tl>ra@iheMsf)BB9!#UP5!v*!Og^hA^7qb(kHIU+DpF7Z*1r zrda0Ioa?^x`1p8X@EbJLrj!uv%PocLVDD!V%u$%<{{H@ofS_c6n<`GDK!}a{C}wUN z*CG%lkituMP2<|J;VF=?=J^6!om)wdTSrGYn1Za_D^fjI;?|@v_WmK1e)}K z&!jQrG|);lXWIjoV`hne{PD-><{$s~$BPkhEq1Nfc6xa05{K~Y?@9a7$lXD~m<8Sr z)-NoZ2vN{>lyF*OoRdX?C49Z=5RM6<=wYDxW%8J>(U7v4jQinNJbN2VRhy4071&uts_5Hq5I@vrSn>&Wn zOx*V8%5c27Y^g-<*VfAYRxpyLXt{Km4Rk(A2b#St_At27w;&ZTFp~H3SN$j+r_pDH zHT^mJdyAlY0Thv(ST=ar(<@Kclic3UQnsoAt)(j9ZFy`^a*eu81f)bIcjCg&C+$f6 zjdh3-uyTB6O*h+wil0_5-QV9|4v2IxH8cYm3Wy4z8aU)DE>YR{B(&b`*{xN5DVn~D z$C8g%j{g4s{>p1YW=z4-)ZTp3FGM^b9G@6@1ai?mk7y%g<~MVQvvrT?#RtP0NF2&*F8 z|CV|-=A>u9PDHoabZ0GUDs>>8%7%U$tD)N!TXo9Y=hG)##IBmqbg3J?o|rlv%M)Fs zxb%CKuUB2bUaaa{LGeA4lNtR?k!>|$j>sDAwHoc-ipql6nlWoAfIF_{&s7~Pyg)GS zvWNfUKmOxIW4dj;HYF<)!Ogu)2GJ&wFL``|CbC4<%D9cL;&U6)^hGqV`fHo%>xouV zw8*vq(MtRkxoup_gmLlxTsnA0)-8Q@&de8gB-GzyAfBxTXEj?IIz?T^XRjh zt*PrH6LwXQO= zQ4U`UV3}5XO3=uFDaxsrfj)odhBdW(swR*HvV*6l6sqp-yi^B8kx)OdjPG7(&u}EA zP-8=S5BmpbpJpnJ=$HibyyCFdsXFeMU{*vKiXOfk9S_}6(1bB3Y^#H6=wdu2}Zv`nm*;_|VK zj_BB)^8MMmQJr8()B=f>HNBsuKmPpl&zF65>0b)D_e1*7opPJnEs%wIlpF^s&*$^S z-KKomQH{RGx@IHoG9?raI`U{X)!0#d8qLr;~qMO_R z)TWBBx7wP$K-bCiuG>V?f|RI5(nOMFoJe@^y*g05PB+%&!tA1!4#=)?=Sn@Oo#XhD zc4Y2}0)Ys#Wru5K%^1jM>%pM1x~Xe?BDf|;)hUhhno&q#wqC2YSXY-mlu@`;vIY22 zG&g;+e<9nu=P2^6L#i@ut5Z?*QWe@4QXo*O!PbZ>z&fSKRTO}*7Eo|WS!|gVYQOZN zZ*Om}yyIk{_w#$X-G0)o6vfen$QFijbLxABboQB*0<@_t-?2?Bztj>$){LqA_I~^N z4w8p2;Ld=sYS)W32w-Vlwl#l~8m{d+1vzl7Hi{)jWkMj7%q@Yn^SYAsG9DivucQ?^ zSmn(!&eyhBQ$AT97RD`{lo^}hQjOL=SEb*c*+b=lR6e4YR7$k6-USH_M*RB~nDMr# zD0VDJ1Nk*x@1|47vn)aX^VV|(Y<~IWm#g~Grm?M+BO%!xUUkZR(^`9pwl7v*eC>tT zWvAb3i@+LE(~o7V1Tr~ix%+OlNNn%uG3Z`j>*e*aFd%n`?EN_}TCFbd;!R%qf38`( z8tM|5d+_q$6@1mnWep7CB|2HK#cOFH`T$8mhAYxU{+`PnARZ0~DSo0e@a`^MC1nf>0t*2Dd8O=X2*o$5CL zu9U+94Ju$O@f|wbEwjK|h^(2y1`0tG3F+%yx2%0zR025$3U7DI3DpA@{d8ib`b=J~ zb*dNI>({SOH-G>8-(SpI&4t$vO6h=pu(U1ndz-e>k)p`-a34N=xKc|NQR{;!udS|# z5vSCkg;Yb;(bP&#U6gs%x=q-$LnXv zG?Bz@tHrs!S}E%88w_RXtX9Vw+iyBraNJhLGHHB9>!?*0Q&Qg%FG=klJZpGs&L1gv z_l$viN=!)%@H2$0hlUOaJ81&wb0@|VB7EMt1Fb?4R``xic6#8H&T z^h^}&$R5cCaL*L&%m%wAkG`DMqE}19T1aG1O?g`d+pkk)ONA&i1A~^M_HIOBdTU1O zioRA7f{kl8d!R@bR+@y^0@+&IT2y3FaI2p!Vz?V$9gm&)llQv3QYm$tR&FNZt!O`W z=P!5g)pRae*BUoEBk5Fr*_WWvrk9$PIxFh4b~B&P=Whe3LC7LgE9$eeJ+=+3iF&b} zY(+bDoDAMI_cqPbO|p8j8*b;_*7VTEbz>~D7nBs}R`j+L-CMUFQxf5MZ~4-mqO2~l z1|y9=)ZcK*T?s=|l-=z0gvt(DLmRgWIrWUX9eF5CTLZ|HwOfe<1!e^{s+km9T4NV` zhf>@e!E#u4o9`mVeve}~wnbDevnjsJs{8x`Nu#0 zaaGN#zPpATqBhGUSmD%8{TAdtSl4(cU{Ts8ZXk{Q7l$}o0AHNy&dT1cH}&2ao3icv zThG-q?WoC{H*Zc)PfwQ(E zR2>~QDRRzme){z3bn~D8`JXSc9(F_N=PmHyjo>^|8#FX-kv!+*woz%1&PZ!N%lzFS z;DPt+m9hJg*sc96Ya0)m3i0!Du7l)x*3V&29E(u<8RXb<6^KMOW1m5vb6!AV5SB-flvb$-G5OE0w7Twts$p zK0Q1HB`u;GL4Tm#Wv=_Wo3&t~cxNH3b!Pk#=U;vW~c|rM=Vj zr|((r?(VM0+aisU`TO3dr>D!>c#B)YL}>6I(cw$o+E|LR;?7%@8!uvXEmDU~v7ZI~_Cducs`0i#1#9VA@rjVk!C zy*zEZY)s$Y-d=>8Oo?ikJIx*+v&~$eNuM{NcUw53YdcsfS$yT_HW(R<*UzuoPDHoA z6F~An3Iy%&r(9rbFAt|&#{yNs-NwIU?NHA4Tx%klzW#lsy|t-Mr_<@II??9ZvPEpX zIzb>L-_H{8sJ2>XPZwT9PKI@$Z1#T_GVeAW=`r@{)}UWP7tJkytw4+P0p0gfHXg z=I?+1`%Cl|p=Q&}ElS%s9gOno_;@3ay; zG@`WD^#Wo(_p&lZ;~cgou;o?PLuR>SemM-q~;!S80OW?fUr^&+r`*Cx!OdJ$9)i1qxB5K`Z1*^HajY-0od28PQm0v*p3QZc6; z7gNq|zejQ0rOc^jV9h9(@+#|C)`Ka7i?0{79@T45(F17f>y4=O$=ceV>L9!_9+X+Q$Ou0@zPLeyt zu;V_WlpeApINJ?guyE>HUa9rBlrTvFj=HRWLs^l5c?XZRQwwl0^;MdpR9^;+1IMD+ z61BU{&7=h2S_9Rv9dh0F%Vr2S|M(yZt-$y7ihHtlZjzU|nO4q(0$B z2~j++b|;*RI&La?ch*fy!rm9{0IsZe>p^>LMq_KM6;5g+*oEFmh1;#OB)fVD16h`CWDR61qu&=d)99&K&obcUQ+gXJrM;=I8=)IU?Mmw+!6o%e zBpkF+%{t z_N;!6GUxge_&eplrm$>%>N}Uaxhl)O3u{$n49|U_;8qoGMXv0%ZNJPBi=se>MZ~gA zW@G#af&BRK+_>So$dA9ZHLgFEPYi-*m zC`zk?vB;bULebiF7Fu7uf=AgzjqBCwI3g$P99wgR%bT^QwNw7ym`%FdnR zq21MD1~ch~tC!*}t%PI0*9Nriuq$e7@X-rTSwqw8BuCEY^Xcj7>54GWyXodUi_pld z5Ztg~Y2C5L8aq=a;30$Fv5%v{MXzfqUgptlR_Dli#x<3cbgLww2%&Y8wS>rP?T6Ly z@a8WtX;`q;g>U z9GBjoA8P%+?U6ZOW$}Du6}!TqatUob`+@A}%TrTW*2ZY*m>`UPac%5}hleZ8l!AxB z#W_5Cf2%XEeBe5UioAKVN+dYaj+M!c7x*32#m4T@P;b5%{C`YHZ zC8I8k@7(N8Lc5;qXVIGlBh2;J4(aLX>AMjPYpm6M*X-a(n$AL79^4Leu!zWsJ0GS5aBkh2(H4bfT#u;XBHuT%jao1Fhmu?k%O^-P3sns@f$v z!M)TW>^3tKGq83Wi*yTOT^K~xG~1*OobYB+NV5kpU2__IU*`6jdU$=gdQr6%d~r?@ zNF??u7wPk03LKR7IwGQHw2D$izEpWlkwiOXyBnx-bk$U&HN3mK`wl|4H`O6m6la8zeH>*+CI4k;tc~r|&qsqLvrgjAf6sh>wqtSAanWLw{#`8EYq0ln4OWtKA-) zK!De_wnjaC2v*-?1J$}mZIYU#>p*H+AXl`J3u}X|&0NejLC+hoBnQ-9n~?R9CQCv< zrglpdkOAYAwW*c2-9~>0ZMQX`7RNGWR{bpSC4#gZT7_0k0sH;lO8Umxw{z-Zm9_Wt zu2Ja9vN!N6dq)|KeD+p8&pKQ%Q3~P88@5N)n0a}5`KD>I-FH(7g@ZawYVmpdl;)Tg zIlO7w<}8=got=7?%A?4qR!_?YG2K$ork}SXEOIJM#TKbr;Gq;wlyIBv+hp#r7YXyT zljde>s3Pzx6L&zI@Rp5V>qNN-fs$=xuf}9Im23*rQ;85#)0(_CU0f?DwMGO%bXIb2 zm~prkj)<*Lt1dPHY1Lv&Nn8yb%OA@J(|cQAtNA17awVPa7m3=)d^TO%zRr#$tc~s} zEt$7_gZg1sean=?{z#?{p?$Knm4vWnkj7bNU zZFu)*-BK$jes_0wi6)xNZ?kpbYAHfH3gJNKiq@$6*Jw#m)JEqHms%r;wp#O9ORScO zs#gw|e#Z{y_jf=g5Z~Y5U)4Kq{G}{;aB9SF>a$bq62AM|-pP?-vOveGPOU(Zy-siS zySuyZ02}o}*6CJM(F{YwNZ-BcS{>wCGa|^6Fiej{;O+S;<-ZdOQoPz%1w%!9{XPee zF9Y32s#T_#9=TZM@5xWJ0q=S$<*xNt>$UDR_W5=I*BLkwZHHjJ%>}@m`d?o= zC*Mn(KYjXiy7||?{`KP3z9R~bbu8Mr>~vxBPIxf7L#;0UYFKtCwzysu;T7i;C{Y>A zDPVbY^>_j(6r$PLwVexGq-)a$vxW|?H}Wbrd$0v@cG`NyOILECTWy){Hl}4`Y_E0a zxs3E`uq>Hb?SESbWf}E)?XkJ%tRHu7vNe~;?b-`mc<>A;W>0(QHofV`!7$flEycP)I`nc>r9-ue+pv`;f*R4V^m*@dwT}7%C`+#z zfPmim9XPjWZ`pc3aLSuou<<*);{W=u|9XjdDU@jqpcJ>PjKB(C@){XhKzg`jwk##4 z@A1lZG-9oT1qDne{a&Ydg}+4(tzCD(RI1iWXZJ!)pM;o$`c3uxe5>PF&F9VU#&J@5 zZwqhdBe_Bu>`iHycG;XZFvy{71Foq_H?35xMGn_l)B~r6N&8Eggd;Ahwck!LmbU_@$|h?yP)*zt6$!)nYv_MMW~Pin6+KE_2(>w+gx{=7s_p{pDD$%53&2 zg#Z8`07*naRIg?X+sJN_G@TF3u!rW&(w|%W3K}UQTl!{1|LLcnP7eHeGV72X{IopqvkEYnp)B&bMwzx*lCulAaQc6T zSG^@P?dX|&7xq}CwyFN+5bgR!*x28FE{bf=)ZnL8vtnDVwKsUbL*bwT(IrD$5X4h! zG|}d@hn0`F27GFOtS__0Ww7c->GTtAlzJFt1oXSRiFW{S>pWG+bg`85TuN0cCUd|; zAm6Uk+PW`xgCa+|ZEBO`T-%Q5_cKg?LcWe%XbTXf{L4fAk$cUNEWGcg=Q9|&u7W7b zCzMgYpI7+}RotwNH^|vzRPlR7o66GG8$fGp8|!;86(}-0Qqf>FSG~L{X)dZ=vaC%f zgu=a<+So^WgdVuPy7k|-SYCBquV}}AoceaSQ_I&Kch{v-jd$nugtXUASa!vcfKj!t zieEd>sZz9T5nqt$xO~yloFn#%HAgpm?y53c418{O+Z`+Tq=|*IYO*v$=nSCMU~Nd1 zu-f$NOzX&1x8A09wUl-cT8VQf8IDrsO(jzv+sXeOME2%eqV|qZsf4}SFz2q@9Sp2M z50H>Mcz=I?b%HBg-4rVWVB|KvDEd@Gu0)=6ArFKCF{U|dhpc444nq1FceLNyXIl{R z8kZWj3RLp%>>>5plxtT~+z}P^J�RMTA8+*X(ljY1*V7tRpi-=iyY7Zd0ZW;|=1v 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 literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/chart/background.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/chart/background.png new file mode 100644 index 0000000000000000000000000000000000000000..692047f87ef68e2f3e5e582a842e6f0787fd89a2 GIT binary patch literal 18559 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�??CGCdLxq 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%0DwO)aTYuCaL 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

`X@rS5!?q(br7Ss;MYy=X$ixxF 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

E`zw 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

N_)V$kcmT8vxN z8G65Kv6PmrUs`*qyS@|Z=9kQwABlu5m`;V4MsS*aHM1LiFRvI}ZXXLs9&3$qR{}Es)X3kG0b#&yOHF!=V}teXHIWbU zFn+mz8Rp-3ws;j!dLgqvj-yg$Aia8UC4TB;yTj!lLw#8B7qQ8;AAp>M7MDDXAIbc9`W%=8X4-m+2Sp= z-3I^xjEisYJcY-H%uAL@ zi%@41rLn@`=W3ZKOT%%06niViyu-}x!GNd{KWWgU|NB~m34%l#CuA73Bv0iD7s#EM zFTpXEQ~i>KPg#6}xDXPE_DOm3q*n_G#7vxDydwhW!{pVT${+{YM;|P7xmu$vAk)Kt zvd!C;i7WV*1eu*XGu`Ti?N)Qsq*RE^A>joD`tupORs2U0Y`g8CJd?-mvos+7H3Bzb zWG|8_X?#V2O>Q8bcF{_MFBNHAgP=te9MqnwQ1+ubnG^6gYzc9|8c0Q>1j7@ugdD~O z14?Bxzm4H6m>FP8q_G9Sa14V)=x{=5AO|t{0%A5$GGGKtUYHR0_)dC0V}m%5!g8_w zO^NW}W}s}S<+F9|srE=*eR_MO>UQ|?wCDSg152eUR^Q@1OGn3SHWrZ7^;QyjywjwL z35Tes75;8A5TG)OAm%a+#xX?qMlOVyQS}$l4NXBJS_l^2NFbLg_W1=aG;H~X2_bIA zd`qI;vHIE0hYyDCqr?jEb@MUWGhY1r##I9Oef4hXpHSbu$r0y@t zm2;X!G$jJCOlr)=JDN-*04K;nd`h<@W8ZAi@ng+ndw9!RS%J3@Tg^Vq<(F*xzBYl_ zH#49Um~r31cQMCj_vJbc+y>y^Q}M=sL^09|W02q=qzq(3QeQhZK(M5WD&jS%fRl(G z+-eyc`vf9`IwWL*1adSV4W!aht)msK2?%O$Pe2bGOiT#P8Tz*2N`wZ?3evPA%WsMiLhd_fK3YqV%Er?@nKOF$4t!XHeQTSsddXl^fdnG*oT9;7zN znJ`fFLJ_%M8j#TWT;}zKdibA?AkA#}$93*`grBsvr^9L1uh5nY7``c_bK!5$ckHNV zjyrZzbfd2dyCvbJL(}2*(15=4^w@?Wui_&L9P17`Wyd?%Ed{q%>n&(vqa!EZY0R!p z)TZ=0=$AqEtk0xYu-$u|c&K^wDlsgv=5EAxeaK%SmAnZ3*jL zcgWusL2Ji!NJ=wEsI8Z_SrFh=N1vGxcoj!(gVSe=bimpiMr0Du&LQVCoNqsXi-vv_ zcE(=)sGnLp&R+d74Wt9p!I|Jg!?kcL5{cmR9C*{IiKV-n75T*ZcGsRo_W;NEFh>3< zmmUP2Tv63?joC+kWk^G7x0wz~rFxd_cxeh%^y~s^H^!|QAVF0`K53^K?`^VMJF^A8 zlic+k7x8vU_Y}BjA*wbzrs!@Ij~P>!p(MSdDlb^rn;K1W5}11dnHAc#P*!2_bq1FV zFA3JjCz%6H15f@4XsRcY@U1cw2N|Z`Vv=oUNiDN$5iK{a;Jo_>U~2;R8ZcrRLxPNcgWwR;nspv1;?0UEA{$M zL^cZHmu$N>w=i0T%DJ?S)a_t{33NZEZcos)+ zoSnt+Zi0_ZgV)PmBI|~U@Aaljt`jm)p9MNk+AJUr5z_D7l#O_u^X{_eBVDmbKQ9D`DMHkic3az5L1US9b+Z749jWjsrL7w_lt_AEDl2T1`Nz4Pb_Dl9I1GV zC>04+$GX&etg>JR(P_ge7UCpleuYwT`MK$+>Ps=X6;^;gAsp1HBnF5^bhMEJfc?Mc zq09yyuSHHAP&9?^P8ecg9P38#Z(1lWvyE|VZ1PSR$+|5D9qkC4wh8(eFCJpzhIsS0 zERHslx5kiu9$Ea3bY4D+)1x@;%5NVPA{o1b*Vd?-oE1^ zeViTU^&!MwyQB6M!3Y*YV>R{_VDT^UBkbpR#z=0|O1CQwXyLa%7uFG%Z-;_*+}lz5 z1tihFMD-c;ZvIg`)jneEJ8Wg$cNE$STfZe4uy4dcjRiiVtZ}`4$*HgBbh%1BWEc9a z(#I%Yl9E5+K-l&L(X)j38RQ>9arqGiu-WwU%bWcx|62&YWang;rd|EHfYMG)JGTqo zrw0Jic}+WcTmE!T^W16;W>=X*J4Ijwr5zh)x7kNJeHmvp@6awZf&g&cwl7c&+02IR ztCOedzq9GhGZJgsYCY-8Gg7|o=s22ArTvjmWZC3yQ$UbeOW5Wq9avqMn9*&$VKQ(m zz#qwW&6BJRdpe;!v0*y{g=FKqPXy&OPAC@uFzm3>_=X9wuX)dL!efQ8-N%9d38Twn zTE-garq>vQKsAnT7-LABWK;v_A?woJYZU*;M)n+49X_&YWE1}Yd(<$pLJPN^BP%0% z(D|=2q8lKbM)p_3sAPB(|E%c88fc1L*1weasrV0dr0xz$83PPUj3;W+o{nu9XK*$hb(kxFFBG15r8L+*G;L9dAPbmO| z>D0z)Uisk=Hkz^qum(%f z<)AJXb+x$l|JihQlEFR=i__qzF@)p`Md49E0fm~Lt8`t2w0=5-ywtd%l1n@6!uN_341@j*PeSC z^=`Y>h+%sMkSl`@xrz$f14;)Z`-lAkws{qLxUADSGR9TE->2L%Rre1FmJ1O1+RESPVsLp^6U zlVe^RGk$n>cyd^BXgK7NgAY3Bzyl80f4@k~+;QFYbIV4|M3C5sqry??k;xI^@Nif- zh#~9#P zF(CSaeOLUhyVQ|8&~5y0V2J`C-x;gsji;fP;03D` zeqZUnpx1txHdPyS;_1oh;k0mSa>|3B|J=zo0xZM<5P+>;+N-CR;aEAqr0vd3&J14& zXN1!qIPKI^Pl-eL2*@z!;DG(HK4-vm&z@nov;OVQNzMspCuiM%<`>R5{q)l`0z4pv z6FzQB$XC#ro-ONpZqoV5`RRH0oqNvNXPu=H3i?l+aQw%Rz|lt?akvBsx%0ov&hzH7 znuh%?Uc`-{8_|^3_kWz7Jvkb0We-y-ww0H z|I{g8O1=~>317VL;)^c2kTK3Z`>Zp?19D^u2oWV90Yn%%*hALqwr>i!EV(RvIl1)y zFJ1D*i!ZvcZh+5z=A;wzB^U!}V4uC&yu0n{dq>`K+7;;);qvg6`!DkGg3)vsQ0`ByYVF~aGmq6G#(1a$+TKoDxpoQ81CP7JH{%*TSf>Pmi;kxwNMb~`$>Z`8O z2w%Md7B0E?q6@?UODO1NLx&v1{8|79mYcw=>(lGQcNYE6b=O{d&DB?ZO9QY5F;LJW z!s9=BTp_`IZTj78`W4m+7*{y1`8LSpL1X%NC%R@ z{vWa}Fy9!My~7W}55i61`{Bm>Z}{%@-}#^GYzUaxV1z=3wgh(~ZbLr`KT2*6KfLb; zH{E!{_r7~QBV2veH{n66a28HM3ob)hfpM_6PA_pVXZEe(*7V1VZu!y8KfLMtH)?=u zzulSsv{ODOJ9HW3A~Y39g}0@*-TTv@+5K^9*V6Q<8uwAud2R00X}c zzYf2;_m{Wd{)?aA_S2t;i<@A=79k!kx%h(f&i=ybh~T6XQKOk*ofW{s@51lGZ^LgE z{rXo7@iVx9i8#VHzJA45F1_TU3(h_33#XlOGFzdZ1+xP=fogn|Pd1$(iPciCyDKZZXhcP{+jKiu(qjqr<~ z-S(3o|0o~e%CB91*_YxHB*lUOT&Zuwop-t`xhwp6;h+9^r;h*&J^(_v`kPmN_3|%Y za?$zc!T>^)77>ArKpWAOcu%YI|m7cV^T>@yMK zXHJS7xG%Gvw9C$m!lJtu-h0nIcmKtP`2BBx%__tLV!ZC#SAFAamw%Zxz`|*rSUBha z-0_~Wg9pL`ci(^CqD2ce#Gmf`-#dQC2(04fn{NE>cdotqTUUOyjs*$fxDVq^+>&e! z4~K{Ee(1pmAGnVp{t6Q|0&=+Jhd14zMW9HD(WM|m_{gzrg+qKZ;th6rEIfAiqmMlD zkcPPT?!Wj5$l=x>{qXx*M8N{4gdu1%AV#quhs+wD3{U>$iN_y*^pS@jy#GF!&Mg{^57O`Q^`lDqVb6OrQ#^ z;T&}7tML6`J_#{A_uRA3E?x4>;-{W`{80^XH_EV8Ajt234^3bUF2~bPwM9q@M@A_? z|H2E)mOcO6(j`yF0q*_lU4Q(;?~Q?*QN(rETopBG)_4*=0U_X$TnG#1zx?t`FTJ?z z`Dd4EfJYv>f00%I111ESLRcI|kTBr_S(kuU z>M~Kk(R|R->j{r`D?M-bf(e~2)Ri~szd~Rz{y0LC^du_p(Q@+)sU;?LEy%;TN*`2p zo4QIDdxBU98br<~B4`aAv|hNFo*ILtSsn@fVxuEk#Wd;A=Tg~pI|?c_t5Vx(F0QP+7RGT+!V41p|#uj(OwxXDH}b6x5}ruH=&b zYxATw_=Yw~kX#HPVC&ZJ5FZloT8Dn)FOb1~gvy8wHU6m!QEs6D$Ot3P?ViC?>41+I zGeh^3d$%#8&={ighuXLXC8ej^Yc2vQqCj|>8qNigPpkq%T#}8mE69{=i(g|3P2zb2 zYv{18fyLv}C4PtYnhrPbQ|_}`f`k{{Ugk-)f8-ay107Z7MFDWm^oXQyBOv?D)6G?x zU{qYz+jY6Wn#L=G+_5VVUrvfL&T;6eQ)T^*6-j5GvF~Y1lI^n7CpBsaf@sC*jlk zXZ?8>#WnJ(VY3ht2{gM(<}O>;J-VGjMXL;UXO=1YbcOVK@B9nQ_9Ec-zA z0tgnEJ>9?*vbUl$-Y^sac2OcrM=Cqnx?OxH1DC|vm2IMxfg%1>;N+@<0pHi#KoPdO z1K(0iEP8l%<}GtFX6%IcB0xTX0F!1rVikVv7ylv{U>LY;P#wL2`06k0upQ(FM#bKh z*mj$A8zE1$WNTq2*w5f4lAIDRtem28E<(6Rv1&NOYsz-Tz`$)&fVz6UL%)Oo34@*+ zob#@yn3eLNBOe8Ly5)p@6M$pM#XwakU23xv8r8~iiNr=HH{F4WW41v7@(1Uqkw}yiO`D?&x2Jm!IP?+Y?moqGy4U# z*Vt)*3>FO3-;Oji9E)EJ>ju|z-X~4wE=X5FddWg~U8@`$=1V9IA)8++=2UX?1ua_E zvfbn$1WItKuA)@FiS@=3Kla~n z03s>1;9%<_(r7UZDMJV6paf0of~)E#%CFePeS{m6g{53u5FhAOTNS|so|}C>@-K+x zG;Tte9{ck(7+_isfb0=32MsI1GH)?OT;WUV6SLhU73=B>%x>T!-!`+l0^v7#fD~8` z%T0L037*47wD_p74m+%-zX z7`Pk+j6tUqo)#CzERj8F8%QD3X)VIFIyZct4q%C9U2;rXWKQ;+X;DHE8H?L~mhP2w zvjv+ZPRtwXAsjk6L_V!F-X!xxk)_oNg?XMei)&$SHd!URnPoyRkK#LmG@(Re;(5%) zq8)bFNq3T3uoW`1ouZt? zcOx>AV4sqwK(Iz88SOBeTa+Djk*1r}DU)QuCSH@1^sgCBC*zjpl<`~(iXlS7T9k#Pn;$=qi-_sG$j?m4WJzLkOW(v8GL3!}}FKrrlbJmv_No97j z9!TfxqKi*UIYjxozfr3Wz3f~^x|rO#X$59y(qIQtT^VY_)ot%!@b3Zs>(4sL^3(wl)Fz>&SFzC;jap)fZ&+ zkn#1YyNcm##xE7{VI$I-x#0+OUXUU;QkrEzN>i5c$;6dzF6bO{nbYYt(T8K8sUuji zwZ|+v2F$XB6Pb!nwbx8z@1xmsy;V}Oa~SZHg?ggpD6peHoHN<=Eozot0z=7ew^q_k?N7XsCx^>`QFzlV+I}&SR=G`CC0}ndr;DZlI54mq6 z*6Z|@&uuf?#6}D0A%`3q4owaVhcB#?x+%O9c@&zhdLNrv^B!{Op@$uI_~A!{Ba$QU z)xwLwnqVilmfFG>?2gP}e82toKj1)7A9nZ=M;v+NQAZt}9{oTJ(PD(=)G|`Bu7&5U z!#K1?eE1PZl5VaZeM~qe`S88=LE#pc&+hat^#xLxCikf|w`M-(m=AyW*yGaU7TPz( z7>^I@W_O)2dF1>6=lgJ@73pyw`DplP^07s}SA473(rt3cjdz>dShrOl$;3|k(T|aP zu73Q4aKb(LM~QZ+c3K?377z0U@5Z^&e+25|KYqdqpZLTllTR-6j|$>MOujR@bIu!FKY8M(!l#mxL@)Mj$3&BL(VN;#aALkj{qYk%0qv(w`t)a#&xFq|LiRFR zXQhfW?ex>nIO7XnsIz;7 z-UpVuua8@|`9^bS8>ukr*owxHYyUq9I?=Skpk2vaR=s$M+ z37;s)Pd@|Vv(7%}-1Cz2F4*jkE`K|AqX6bF!1mzZ1~~GlV~#!UqaTBN#QxcnKYz+; z(4Tb{)aRXl!G#xIbWu(IwtkGKMvnB3J_fzBc&0~ir<{8F8E2k#_PL_H=%S0i_{B>u zL3}bk`C4B;n|AMgP`=ndvf%zC)?TB&5ZX(=bZNLWeEEVfhi@}kfO)N40-Z-dd`M5+ z!#vYpaG}wE`LeHkR zj{OiGV}JN-cAu@EU4Jmfhx9&z@U;5R+2rS(cRq7NE#j|z{mLu9@y&0tehzi5KXT9d zCH)#Xq8IOHo_+557hDAOWnVGkZ+!DxS6y}W)w%hL+@^Q#W%@q5$i@3P=Us5o#g{Pm z6<_^26GQv$YpxB~B0_Bd(fA@ReyFvSJM;X8_yXEq$TYoWo zB>(EyjhdOS|LzSp-uV6R-z4qNw(0TPvL8*4`S|)Jd@cX{3&lH=e-qBH`yXb$;l`VO z@WUV8e6y*~rk{`Rit>Yd7XOJ8Px=hH>rDRbYejwI_iy^)%|E*3$Kl5pls7X6Fb@G> zKT&>Bfrt;=JMD~|0kO~g-?^Rve(=MaZ~5`9xBld(x6PCR;ehJoC;ONA*!s^n^PKZ8 zycqsPZu8&3^vr(iPj9>JXFvai)Mws5+Fz6(;y>{uTmRYTU2yRwBLC)9BLCivFn`OB znfd3xxc&BD{wnu>ru}`R{4_nvKUwQP_kxQqx%4YneEk~*`Hz^JnQ#BquYdEK--`X6 zV1L2x=KYH4KXD@BKUIsr=!=(LcE#6>`S0HF{U6+X3!ML)nSb-!-~IlMJIwo-^st`& z4lwok^q)By?TPy@U3U4`uGHE^{-YoNXYf7 zan`x#Uv$ZrFaPS5-@KZ&NAjQF?$mew@lSvLv)IRdYV(`#mho!!(*EgZo_*ehU)18i zdDS(D&Sw|-@9y}+oqzoEU3dNUuP*)h3()SAOg2Yrpf|8*jRqwL|{9JO201KiviO!bOYj z^W{7H2OfNg8Ncb?Rxja6b#Q;fO+UQlC%66F$YK4jP~Z2!gAbbb75n>q$b9cn$H@4l zdkJ5wXL@n}{U845R`jOX|M=&>+bD|i z^84<8@Zm=uef;q`?)6@?k$tfBSDVJ={O|$YUaZZrKYjy$t>Q`RrX>CnwL*u6@yK`ML6u zo`fH#XYo%M`HL^V@~Y@v{M!1Wck!K(YtN)F_A#A&`p5sac*(QNApghf3SEpI>(kyd z@qI>~L*kS6O!{mb*#F-@-F45R2OeVjXO=$y0_1PJ>GXbZ$#F*UpN8>Dd{KKYeWdr- zdlo&2@D?w5ZrMwazxB5E{(?RJ9equFarr0@dQJc0%dfuv=G)8bbK^}rgL9sj*u%bI zM>Q^detVFB+??2*68BNuERe%cpEyfGCSW+dFZVFL{K(tn2m zpXic_Gw$0^;J=?T1UG=v8iboQ`i0^KoDScwp}>AWwby@-{XU?v6Yl$IfsX<33dAZ5 zp{`qfLX!=OkD@u~;4Ff^AMYaQ`_OV7;QOgMz&DUH&5C4o1-3EZ`|ySc_I^AEdw-}N zEiHC+-3!(mz$G2{-y*$_DIb#^{SXLc$7mZ$`M*MWfB4w!*aG7H z#F1Tscz>h}y!+t~0PjA&Y|!pUx(@9=q0wR8k9HZ>ePTI6x*zK*r2ErlgL6O9bvXCQ zWrK1*(q$<3ZiSv~$V^-F~>s(Crx% z!vWiVunotLALueFo1D#@8*a=DlKnuJA=$!o-}Hkl;;|pP;k#YIV=o}1GVkUeWj_)a`@t@P zu?@@o<6AS3*bjCUi7hbu&bL)=yRYjgY{Sm};`R&(_I+K4U>l77SHI4{U*Fet{I!8z zfBU=4z}F9S5q#|!{yYAVfxN!ItH^6d?B4mu4A}JpU4>mc(Dbe{kn8)q3b}TW=iT>Y z_bmJXcxwj=FIeAbVip2S8doK=`4DvxgUc0GPGIdmnu)du(CX zG1da-QZn+yli8E^b{$|Hp}9{zwK!RvJhiB+@an@I;Qh=qOOhqYGm92?5n2s0Sz3Gc z+2@ky!m|sz46Qa~^1gd7TlPZoLRhx2%h2kGOn&L5my?&nOAEUUt&Y&{S6+EFc{RMU zu*=YDAokMzYp=bYydGX#*kx$7pxw~F@#b5}TlahbXmx~!zx~hU$?|*t`SzP{fX#m4 z`K8Z1^~9qOf$hHQPyY)xoJs`)w+mZ-DTNLy)u7J7u<5RVq8?}|E2%1KW=VTm9rs~ zHGt6Jf|k=9%o>zNdJREa)uzU$^v@>#6T1IENKX(Zrde(ieUwC*(FMsyXyQ*_Z4r8J zqgkD6`f<=-^L}+*vWc9{$gJj7mmEIc;{fWN>&v$Oq6UMfF9_{ynwJ}&zIf+ZZLyP< zrQP)LzY)E!S`DSu6~);1)?GO*%V}A{|I+t-&cC6$k-7^lid_RFEm<=Sf#@Et6{~sq zKe~6*7rm1jm>kYs-iG9-Gnn_IPFp%yWJ7ZYD@~aZh5$ZxqLik<(8IE+s=Js@x|Lhn ztcg&GW?O+KZ+?ZKm4Xolf}TsNcP^^XJ?+-PT9ZhqXZl(gNS%+~h&K>9c`j&S>%A{E zqN2Z2xA$9IUQilw+Wn~>J>x|Lt93n_oLHm-?2}Wfb54_x&7d|imZD}+8mUNGm5P$ELU&lmxFCP#P zT`CX$9DtW^Bt6tlI8!qj%-4I-gkG=utwl|Tu0q#g*nD+VzIJL_)PJZc;H!uiyNICK zXgT=;c&pSn0LHK8+N$)RZbL;R(-zViBy&}SL=sEr0;3>hMTelrH@Eo_Vi8tUnFLv_-54bZI-3NJ0YHycv<1;rb* z>{Rgwt=O%GC3uI|SUnPYEoSNbugy2NOSlbIP`EiJH!ZYk1NH9J#G{J(7xeP{=Dfwh zozVt-YXdQTWSrs>n&URpZ%IWTFZUHtZbObuUba-~GANtyRot^nF>7 zFs-;w+*&d-n#GBp(vm*iY&L$r9~WCS9y_@Pmrq=7k_cz!h%u1g= zZJ|)rEbxkoic)Oe6n=Tq#ODqDU(~HAEEiMfwoq829_KL1CZ|&E%rf9|E}p`I+=NV^ zh{udbWibAR3bi;|GbXKSp_jMV($Kd)ZM{NNt>Et_w3!(s{oa!a^OyaG2$FtFt?mN! zq-2&W$gi7a#Ad}_R+7MZBb{IM`0Ptt8h~*s(A3UGko!*h)yWR9y`blQ-YwvT;y|=2EtjX4FNglfGSdt~% zQKC=-2w9jQi$+nTNP$QqA`AjSgLoit6&N%Scnq-w4d`izkchh1>a3U5>78&)sLAJ=}ZFKKtxj=XbvUf3vON z>G|TLmrzp6HcMNY=ow9>%g#D6417KVN0Bfe%fhOK$4@^`Z(kw}Z}?ucwz)sCnrtm= zF1RODHGY}Dk5A4po66``3-mi5F>^@A!c#`$? z*b$yur0v%yjOAWX+-;``qm!OU*l9`|z`7PaN@7QNVbNYswpnjob~)|)>)k}pLO7F) zT~qc6Iu1^S6nz?Rv(S3?vdbx!tuoGF=(S$LulM+QVOQnJ-l^t?+K?25 z-yQazV=FBQKCjbf*9FHQ*1z~Ona}LTN;X8a0U|>|}4Z~O3&m!~~UL{=2pXy=M zmN+%tcbTDtAA~SF5^MAHXHXk&obI2nmhG^bn?okXuG#|`>!&*D(Ix!S=;8>*Z~n6V zmtP^^V~vc{#35ZjBKif_pr-)Sjc-pCJ-#kS9UuJ!~An}NjzZJS5 z2dnQcE$G{6*|tYUV%h_knCsHeW!3)^(o34h?k|Hk<4fX8qu>?#upnFQlClO5zwDn} zPmRq>@to`Jhf9NSk_NA*%OBQfJjIt?8eh7bYiz2WKg-%|Xq{q?eb>>D36E7n_fl=n zoI@wVsLfeLP@86O)hj>Sc$e&kbvOMv?@-Lmrd&nh{~I}qyyTLa3Os0w&5%f1$-07E z_bBpFivO=^TaBM#jIq29Iel`Yh&)(-^%s%z%ZI8?Np?+|95mTM7%izO)LOrY>hx9l z7yR$~jBgL3>@$?)6v@_V-~3CckK2pxraucGpu^`-%OB3%hhO%hJ2>S#6| z|Ie?t-hk>5`QqS%#urLMeLt0M)S{;@PObH5SB}=RZ*Mq!Q>n7;R6A(1q1Nf(jT!F| za13b+=o9!uL7U0`%JAz4)0NA2Hva$S9YQ#L=rV&<=MHf1?fbF!Qg>qy*!)%<2cWZ$ z&f0(fO=KKoj%Ydv?Ns}gaiR6x`A;@9|Jmy6>!p#V*6`L_b#sN&uijU0zk2Tpeb1V& zrY(n9hPA(W8=CLW)p+6iIh<lBb_gVc02$RnDajE&(#1r1L+a)^bf3`?!Q$xjW|x9 zyWj`u_oEBePvC&>xBB7hQ;E6R>YK~aAF&UgaRi^NGRN@YF6?gVPKS{CK7?bCRM{+T zv<`ie`q;Ska$NR`S%%goh+?$g~(|Am)f7zL~wgp;67bT*~AI#D_Xrj2NO zpeEoYfF8^)1i2MXAhps|8%k__&%N4z)*sMctJ2)M zpuWFfAJ#Qclj-9E7(!@{+uuLeAfWSSqk#26oE)Yx$0fvy8t3JNnMp0hHI)Ule+W}B+CZTgb zp}2JB7sJyueiPUPVIWo;@EmFnBv#e|NbpvdT1%}gsX2Q48#n^LgCu{`AVvUM*`nTET}?psRpwP;0^l)TYgcd*_LK~_R^xp$D;h<{sdVL)Mtm69hfbjK0tIcf;!S2(#%?6QcbrD++ zum-U98&qnZwjb(q5P-!bcOU;nm{#46-UOpmOx6xQm73%1qPj_h~y#QzPv@J0SI*XOWuI*GG3OKl5rp_2KLIe~>OAS{s;PdD4(- z^Du~U6w(n~n;ro#p?U)jsWxvI1o@w2!v#?5KA{0kaeJoPoCCmBH5)$;qC5e%`&7)< z8qnH*fZ81OX-BH_Z`}`vUpoQgZml(7{pwWUwK?l+_(Ataee|Ek z!)O0FWB1Qco2M6lI)wATcr+hvCD{Fg+FiYWP_=nlpO+8MK5dQId^-E#?A5gXgQ(59 z`*5*CLq)64|03CZ{QZL5+75qcwRychZew)$!L(YYBG!Bjhd-#=T=m)c?cZ2jzO)~= z%U72l#r`sN7+h`MSUAPQsm+I~Z_N)|4feiNtUrj_{OtAB`+mi29n|~-)aK#02hKq7+kx(b-j|+! z{Qodi{kqz`A2hhBmv+?r@3k!$jSn9K5B(6D!xDV-`$4>&%FL~(8*NtNWm;^zvBPii z5SiyYJLn6$4HM_R*H2}RdqdtCw|BS>DA>oK>@CCl>C6A7zrGxWVYSu)>uQ=;-!hIi z-OZ4eN5NS*W$+(1D$9Rf#rfH8th>d-06e-aJ3fM{+{4E=(#BAXeoC71o2f;|hr7-* z&AzjP=*eBjKc=+?HTG^jLreZQ(vq_w;GN>;U&Q^K?nrr0nOb-DKA&gM_n`~zr zWaCE~W#iwJTD{Z0<_=nU*DbYMgJk^8x@5er86P_a|H1Q1`dep5_a+T`@k0=O%eVEn zzm1x~GkCDdmPvQrDmx3YxRX=xL#a#deqwQb)34Nwv40)5y>4ps8_E6AZ}_RzDZlQ1 z{jcG)au8?WgBxLIArs$!7MVCKfMI}q&zZK@S&76C4lNRAtzh+hKkT(ro9kgF5#K+g zM7&_c)@B=f4^!9yU%{IE9Wp!5K@Pk8(!>2TuMAIBQwW!Vp zZTK5=hjQ_aTQswUwrVv~>g*>8zCDd(WZ_AHr}t3tjl@ zXVZnx7~EXitw=su77laIM;eTsYo^jc7QXkpXK~{_l$mr^s_@s(q6#0%Iy(1>qVS>Y zqR&MXz7Pi4gV{t&6u$H9qVOT?q21GoG~v6?rU@V17TRx4pa|c2Rz*0Cs0TBF&V8aF zd?@4Ra}$J{<@7+)=ei(#_gMttLl{20?a9m3gYQ0@9z3;~?r-$$cO+1Q?>ws-+>Dh6 z+B)YxQ4BtmmGilY!H2SOJ~uJ=P!`Uq7<|`R#o$BOH@iPdpatJ~b}jf2w$1KOl24X` z4`tVUE>iHJY?{wa3O%;zcuAIgULT!r96*)X505S#|=e>8{N#=7o2}194^Yy8r?%mMPxcE zNs;KvmP#bZuHb1px-h-~bEonZUEsdPm0|EvJLE zBR5la_#y4(NATF|KAim}eUM#AL4{_N-|oM^Smw#H-nNF|DNyslcWI9_K5%@irYJy5 zMY)T+1QXp}9Gg4nA74TP=QAv$b^7H?){dm%6z~Jsi|CG8tHXm>NL&Ar+K?ZB1EJgR zKAuGFgTJ(h55eLuIdbVGW5+<*)X&Dcu_N(p&6aoqt_J9CS5y zHS9rrbsorHi@SP1%$vRq>ogitVh1FARR7`SV~$vP4O;L2JiQNJ15e7=z#a2mSVlF> zW<{R@IC3321ZLJ}YhTTyr!QsrF^=g6*Fq_s`iZ;y&EV~WbJ~X~sA5LfC z1{sa_xgX(C2YC7C>XTVv&#BnLxufcesy;M4zFu9&eBN>pgTrQXWh{TjdvP)}pA?Ln zaP;H9Np+fCcXsk>yAT{nV^+;CWIm4x>fPt}d;v4BV8)A3l??|+tJr(A8%`X0jvYN) z-oBp>jWMGqobJ+V^yd$JL9+nSvW%9ToJ|cEJ!PZVfA$=FIWHUQyYw*}!CCNuFE-AB z#eLRc%jK%BR-SMS?ZoIHB5FT>%V$io10?$iPMf_Y_rPP^)Kzk6R3wZ%M*{9 zle)lOR_(BFZ+y&jG#oa!|u;#+ZQh zE78xzUxv%!FYl{QZ6#wt_GgZUyd(&&!;b-nGqp6P!ez~$b6-C2bMCWOr?vNV88NnteC*b?O)etw8F7+0v*~Fqfdq%=1(a0myPVcTIM(VYvZr5 z=7HK&R(5`BtprW61`+DU)h&5`-1YIb$yYq)q&-lZ!Aeu!XwDw%Q#`L~UUo-*B|4SA z-d*Re&0r?mH)=CiVXht>CwBGrUmj1ZbG8wGYSp&5?%WO0_5QlbwO_e5d+mYiIO~?C ztoqIHjn)fm*DD$~)VvWj-B+=%*JZEWdtGg+Do*pH$#Hn0>o1%wcEU z^?Pe`RH65x53vieU3sl@kl{JuHJ2VG<~+__7hQiiotbKuCYI7SPUg^JKdWI_`vuOf z`}Lxl2NkQ=Ul(IO-#xW?vC=uEe$@sx4oezVF{;CNPk3g8{uh0<`TB!jO{bz_f9D)Z zurt+g#vc+om}%(l?(5Nw(O0vWmU$nHQ>{_MEL`X~nE8l3mS=;PcIdG&OBukM{)8iUSHNYwf|fAJP1|Y*YkFmU%ftqy1Smh_j1xH&KUKO&Lw_)@OT4P zm<}>rVRkaD1L12~_04p$DVsQqr<0RCh3lICakZ-h{o+mTCijg(?TjYZzXeN6OM6~sNd3TU%v)ZTn?kwRvOB5iK^4H zroSOU%{{?}R})-?!$mWz3-ULE%^WUn)>JbOyI~im#p0||oaK!thtpc|M_mtmbcQzG2^uHXm9#6If^(%tD0|svjup zLhSAw4BXeNX~kgTOcUkMFt|*3Hd#@>r0je3ikNfy#_SFL#{KK8T{Ll+wJ18ReuK|e z(cQTBYwOm?s(H|wwZVW_wYj*`&PF#TH&k!@TGQ7W?}RyL66aikNg(f-Xe+c=N=W=W z4jluZgyT^-l?aeBK??yf4)kCUBtQ~CVebXFF?F)SD&V$7M#p93^OsxPxhBW0=7E@Y`$JyKmn=0wY+6A3H{7bDf zP;Z6w2y6#JWjuSvF2I0`XV0orMWxd};0#Jk6C~aC-4OPUy#-$$W<09$92BkpxU0U8 zJzx*g{?}WPvhe!e;}iHKTP90EMr%8O+pGO&904Vr6r|uZ@&kDAtgZ*!Xn%ic2{(^C zi9KjQo<-Ehe~pK&6QUQUAE5dW#o+0_l&Mfg-!>lgi)=rBz<<^SknX?Lmrs1#0sDTm ze(n3=hIInFAgfD9R{#24_2UoUU)PmGRi@p?Q+&8f*V*G8BxewNP5U3(&-yVt0FVES zugBhp2k7{6&Laf0p%dt?*@gPni+Kfy%b;FAw4PPRBl|ma|H_GoFvHH(?wZ}IZhhEV z&rBlNfzyKzs&ack&dHnHXQQkBQf<%g4i}J`-nya91t^%|4Y+rHZ)^sBCBtJ!=!IPErkNruQ_lkKjHHXJ+l z{S2q&L<-kLn$G7(;k2pOvDXJBIFwuzveNG_R2FDAJ@mIH>gey+VBA?{4X4r1F1v8- zbo$(u*6%MRzfX_Wc`qB9UHgvO5OML~aqJql(D3&4s6NnX`R6{5Np>)q%a2(*9rt<3 z;LtJbQp3m&FC9B;W8a~KzE4taJZI-^F=}gy9o&fQ#To>O zk=uj!FNXPnzVsJrIek64T{v|%*r?N`)z9!(Ze2#x{ovljUH5;60Z&IykGa8LbtmrynTQ!8Ci?Zx7tPPP zj8I*31nJ6C^&p`hs3Er}_8jSPPkx>I>TCM76p{B3?ehkJO7XZK<6O%?R_4dJgGT}OLw#(%o>Ibw&_=h+V$+9CXvf8{#t z{PjOf|JT&D%PXJq>6){4YGASZXuJW=%2B<_z5go>x@qUL-N)JQr$B#bFmL6+3;KW` za?rO?KOK9x2XzHK4`5J#stw-B-VF z^>G0FZwK9%W8XK&L&b{A$0Xozx&2#%IxI)u0R*_wea(I2$NbUb;E5ApClDj&Jv0`yt!D*8A!QugM1NyLsvKQ;^%!eH?su(+b|Ip!x9+H)n{k z4v7DC)B)l2n*R3B*ntL@m*qNn?(6>N z-7j=E_0o#bdVt*Rf16Ij*?zbHA)+7LX?c43(;d(OpaZZ6;|k*FZwC-O#-Hi5-0EZh z)6)vj1=Z(XST<5IY4pfeYld`%I_h>^v>^zCm?y3;x^uHc#QI=*mFujTxJALs+n|H25w0sLD!0}wK+0dN7}02=&u*WH%0 z`+of|*oo`|cnR7s0Iwi)U|s=e0Ukkcye-GwHNWMx53nwvL5P00CrsoSfD+y85HMrG z0$8C8;<#KN(B1SW=|ybgn1)fEMEwY;6KK|f!F-p8UZ_s29}!*%JPW*tLw#$+n&>a1Hl}eD^q};zbjenK<36kSq zImr~>c2Ek;38Io;?ZLJZuhznZ=Klx^N+6FIqiqF;;eJ;|w=q_h1sPR@KQRT9AN7;eCNob-ii+8cY7-JA z51*nqwVxC=dE~UF?$NA6smwL`x2Jy#^YKXdT2rtlkA5(4f}h3l;*$fX?MarNn9G@b z8mHn(x;`2(xsF>AfA@odQ|3G#KYntNO{3E=5l-^dhXbd!N!EN|v)we#SF=7|{{jB9 zIb#-@--&&P(qmehvS&Y~tC;J79{ z={IA^fBM7d4?h|(5sCYw_>VstFujQTllV_R(3~UvCJp(s_|J}WB9i`7fqef5(GQLf zoZ;gye;EDn_`r!fe)Qw$$HxcG$FXx)wCni5X*PE6iS`^HID5vv{n7pp2TqDXG1-R$ z=enRu?0rn)aK;oCKcW2V{ee?cNY?c}1V?8~IU%{$`{`T`^{FKOmi{>3SMh#RL`Yus zewcY;Z++?q$)?^PIHiN+P+cCQWq7*}x?S~W@RRxL!BHrlw0!*WC(EM(ORoFL#N!Bi z1UCMgKa-!#hWb4{r%%Gs=0E)d9CiNWO*;Dg(O-W5&-(e<{GIiI*5moq!+dTtbqXGb z*dw^x@_oIhPkeOzr=kA-J-g^A^iTiz5C7oz|HJRS{JYO@`*+W%{&zw9)B5(TNssVu zD;4^BumkV?6 zr~Vh$|Jn9`(DwV;&R=79sGL0A<9oW@fBhMTNj@6KL;TadefghhnAFe2q;L216-qzT zFqziRmnuKgF!}#h{eR~E|8v_oa0Z%|3~#t{!IJd z@E^cAx1Z(?9QYrc&fwdh{FBkx9rlIWB+ZAAD!(LEH09U0v6Ky`!X2uN+}Ehv*$oQI zs<=a{RlR=-{&(Ol)=*VFbVlIs;Q#vCf2n)!Z3W#~t(}8j_*~k#@l=yKe;*GH8j!6a z*Yv?bK(3taa?t4>zs9rw^MAny7vSc6Zb0ykCl^C~K-~j}4LSopK;)i3Kzwx3$h|ew zZR1bSIeY-0T-dV@xgZ(zw&kpQ@W0as{IGr&8XNIk4xy{*zyFW){rRd?+0x(3+V$!`2p!+^gieGFxoaXxu0ulYMO>O|DM772F041CSZMW zh<_gIVHWpyWBnt%9p0I75B%nw-(*Gx?-=hbc;`Z#C7MGVtES5u;w=sF)`mF0g`dN} z;drOJ@jQ)a`vO_`Ta{y+U`Q|7P!p(%6t>ZZ(lo0>A^4NckMOPaD5T-22P+z*z%1(Z(Df{3bH)a3r$4%MqUe}cUod=q-zdNBR`+G;4vj64MrtE9aH)Ve^yD9tL zqfOc3|Iw6d-rtnF__v#K|N4=p+%GI_%C%2#%605&%8mJ4Q*JEcxBQ?fcWZM~?wh~X zl)LReH|4%{1@`$7?rWE8`uEixuO_d$9TDCRZ;yBW)gAVVwOM_2M|MX^DgM>+j#ppZ z(Y9mcE7cvZ|sp)@_&qe(N^3tq(C<{8qQkZ>?_I z>b6GP$}Hfx;jxNA=&6Q^eBUg8#ZP(CL5xS6%>p&`1KC&6L5YJmJRDSY*-(yuWnf1sw_mB*GC)N z`u25&wd>Zc#mldAYqNNlYFn9g)wSy!gJ_*!>lsntP%2vE*19#NwQJU_UE|jjxn8@5 ziGEFCEz+=+V5@>?vL>&THEVyn?YG-k`=^etehRPEPw~w~k?1MEI{Q?aoBpZl>Zh8q ztw`HbT}|DOx2!5ZzUuK+m#=b|*s}Wgs$r|jD_5*o=~kXvv2x{#+ACgJ=7w8YU9mE| z(yi#l2FqcUTRCP$W_e-7^5rY=ik26-Vx(-V>Wbx~mLqTs{PJp7_wa56Nq4!cyQ_PI ztr8|Vi~H`DuIjR`m_eqiw5+R(8*Z6nLd3vz<(axn8P#P={Za%O28tz1qh;~Z^0KAN zmS(wHx(t;q*Db3qee5?Lt1fxWGdPcdL$Ekm+`DA)5?ioQLTvF8x1@rVTkMzk#Sz|a zNp1%pjTP=6`lx?vd); zM?8azENGv5?Zef%4@V4g4|mS4&YB&~_OqhdZkC%}o;7>cY;P;46gMlL-N#fcvu4LE z&W!x*nKNh2nmKExn|XQ`qFnfjW>y(k8WNb6WtyAmW>sg-^0O*4XU%Xk{7gTiI&((M z;0gvw*qlBC8PjKEX1M8YM&I-q(`RUL)2lP4GYNTaMoFpJ8D-|jT+Nss!x0n$_t2^7 z4^78{7lRCwBL?0IAeKxovq;-R9n;(c)oBm-2OJ`N`)Ma0m^ST!2e@c8%}?_Wd^k-B zOtRMlNUk7`AF$u4uHT4(XMKdX!`tJX-?-jhu{Nt4*Jsz4l;Ss**KgdozHR-;b=CFj zatsm%pI%p9k3lDk5kx}+$BPIXer*p2j<96b_98=DYu3hVPcyc9&Fa-_5XNhw)ei46 zQ~c@@S8MPAtE=4fYn+nStahuTH3j57`Q+*+M?G0x{ba--#~`T%(UWBqc;c2P+^WnI z)m2Z7Od}DuoCR)Gh!j??(krvFx@u)+rCU|OX2cBz7u~=_=3fC_aSDB?{eL~tJ;krB(l*)gATV{cVz|F%0$qPA*ZFQ%t0pC zRkZPDcvo!05I$pBm+$IZx@75+CCip9!N60(dMO45zx3}pIB#M5f&wmJUc+uddH#Y0 z^F6O}H$Pe6=JzyQ)tLp!{7550Jm2GL_VZ56pEoZo^XJW zg*kKQ&cVyib#tIulW8lH{AWzsrP5^cT;<@ z!E#vT?jJKXGo>(f%9N>iMN^7gF;ccwb?TH+QxG@?eoFP;`-b0#Ai1x6?|t{)H^Np4 zlbpruIajv0;{lejKpjl%g!w@?vn)^QPo+_=uM)y}ceSl=0qb)C>uu$f;yUB8eN4sDIW}f- zX5`0qK&jo)+2J})!(pWsZbltd29|~dre&GtI$UR!y0A(|XBz}$9lovF(H1kff=c4?styFZ&i=Hl`zP>RXp<6TSpjiN3u-0<1GM=pJ3Mx`$G=z z6Ne8S3d`X`hYuf$4pk2yYE>2@&4;4Horel<9y;_UUa0YA7VlDRD|4v&<{`&`ZlOFQ z3fv)l!<+b~zsCBFmV@Ot4!&{l@`LUTcd+`#!LFvY2Qvo>2M-)Lh*xx=$Q2`HTU8Gp z76eIh*i+fPXSds3+_M|&J#J65ySit0w7XZ^ zOhRUM&vC^g!N#A?Rv)YM0yTc2#!n+Ua(l-nA2JEPO>fs|+j+ z2~5i}jpeI5cX?H6+gW{Wr)QAa>0Wa?`(E4m+DU=_lyD*qEdwd;)e7Ro8rdCS7>uxcT(RSoi0I-n z2IswUatEPAN-97eV&IE{l87=2%H965d%3#(cf_>#wNe1x&_=L*_0>PFsk7<6_|@x z+ondSQGWWFXPypZ!_$Q(Fo&Rq@JIAap7}r>&m_-uKHdJkP9ThEW3=Iv@eMK#fDE^> zXhZ`y7aNLcn%j_Pl&_dk5DmOMk%qVi?n!_=>RWC@4AN01_b?iQEVsTy9W8Eh=RD4eg$qvR;+AUSrP|Pb_?!4z&KVckC#_~KirB6P{^m2 zyA|2xrvf2C0v0Dc(w!yuh#7n#y&Z|W3PK*sx)QtlNbFt%Hyn!Px(tbAk-RiujQ}~4 zrA52-a7zn};N_P3WfiOiM*LE@3^yvua!Vg3{22LI0f>X-19u+69noUo5Zsm20=J~h z{M_O)p@_Fth6UPiv(mh{K)8XWm3J)3VvqY5gCNP`3bJ@VQzrNDkM3Rw$RPkiutDq* z@Wvu!*Z481WbPv)9%-K|%Auh3;nLiPAD;Vg z@^F6c!*dl?`UoHpQIBZ$PeC{W=OF7~RGTpi%$`L)!ndDQQf@N4Oym*Ga>h3bWF2@V zvrbyX2+6FO@k~DpMQ6=SSkTYR5_RBK2neJEKhiU2 z=0ULG0}ZKiBZrL=NXG-y;;?qp%GjRPG>z~Bq=TqqBLKq7pLfkaOfS3SX(djca7v$G6h2GJ8GAdgibuvo9+8W2Rh#H}n7hh&LHvLQEG z*{fU?h{FIyZhH|&tn)EQl|)2b*J)4^Vv-eIfFBVci0ks*Cj&X@>QZhDR#FNmg`A`l zWaK2Uh%H)+&9-zU%kUA)ik87BJ`HcT^wbiJL0mvYG$<|U!&-P`iC-dcn=HvAMXzY7 zUs7N$UTxs67*_%u0Z6qGMM)m?#4QpW5k`qXSdq{1C|TUOsC{84E_;pvr!+9&x)(5U z3yL;Wcw~c-rUOLY3M`l(&qoww1TTS5@ev0s(zxa3#~>zU@)M&f7)so{61mFUF>^lv zIng3$f&hyHEU9Q+(spbZB~Qr07xVcpfsM?bLt+w%lK`yFDG;29nN;Thog#>4_bP=< z1t_I`W`|%Wpr%NPn^D2qO|M`x029&A4Dd-by_c6fUMd2X(jk^2fRX`8@pvbsDOn2& zL@ZcZ9;{4z@cx((#RH;K5@V##3s>XU=?tXTHx+1 zGe0-EOt|80m0^K45R~T01ws-mtz;+3WIxFVn37DcAj@I9On&0;*nK--i9iYg3Gqq5 zB$JR`<0vUjVM$b~P6Br6ARx&yxRAjp24pDUAte!zXu@gWks1?m6a2*LgozOdN|}jn zqMOh|eu5%g{KPWWe%}7s;qHM*yXWo~9dU>6MA6QUgav&^mdL~lrIes5>S)W=KuV^q+<^p8lnx=2OdGgK0;bZ; zh&OQJ+D-?$0(=sS=b~ux+tPzBn2D>S*Ms{parY;o5z5k^aXZe z+{CC#l^Z$Wm69k*9Mdl#q#@Z>TE=N&uwF6l^$Ox|(t#Na3e zC6TEVl>5#xcdYsj$O{omNlESV~m|7CM zaW5CGyxU%-o}@_V^3ry1TaIi-7zV6I>+LTkFZD9b%HgA+N^#Ji6o6*MLBVg}7dXOidK^V(Lx zEzjKHKr$^vGQ${*WDo<+oHF_X_`;V1k0FNvpaGN-raAR2btp)DmY9ZArWXW9rMGRp zRon$i6TTu7n3NFTLQ%?=t+A!JEfr)dg^(r)QSw1#0w@CjLqr2u^G}9uf)phrQ<>s7 zON0Wv!-4_W5ZRm}gfS2U79)TYnKY#!9m>X)-lf6R$6jZq}4}BNdC@mWd3UCeXtteK> zQ_Vtp6SxjY4wD1wDKuu{ZYmV(Y`E{zvuFgU!Kczi-eQF<-I-+r!b$G{yr`194lJX!Vdp!oP-Q0QH23}3hbIH7fbmziHPK)l$1>wOGF-<|{ zkup=-=3YW&%LoR|5BGufkor74dn}bJc#pEl-WxF=)9(y{8bjJtu*BD0|V0`WCSrE1LoFaSaNWkpw)WjO|GGlABSmdpO+TG;_2-VwFB!!lC=~bpm1f{1F6JC znipzayxsxmI4N5E10|GA@eai-WilObVy-s&jX8iDNM0y)aT^OrV{)<)#5pasaee!G zj5X`mjbe~V5l;yM7ph%Ed|KA^VSKU0t?k25qwz+X7s}`=$P(i5ST2<)2tjm3PKqNNlXEhhp!e=o*dauAVB`fqU9u( z=$=IJCp(@f6Y)d>KL9(!NOuH#$bpK;!qQ9<^u#KWAaWhB)!) zW7KOQ-5JJUB!d`(+9`1xavZ)K&<>FfSP!U=h|j4Nl*S-&g=uA6S1(|YN_ThNDue^^ z3ty7nc@AYz-U;|fq6bOMvI?@5Lf#X!G5Mf-!59RVL)rrv{ZB@Lf;uK7Q!L|67Xwnn zf`R>z{+uGuF{Xn7MuUw8AusOCK}YBZJeuk|3`{JWEN))}P`n62IRsaN1NHQS!x$)H zj0lNQV#48*7)i*1>hbOsD)c9~=+769^qKdrMrs-qs0%s?^I0U3;B zG12r0NQYQQ*S?>TqzX4F-voLhBy|e%4zwpP z$xJGuiSF+c?J={&$y{EflwyMXr@ldTI}PgQ=p^KLP5>F z#)2ZUpy`+9ZEv6 zb^;*jX(*+EsHS}9U3cD^10Zx- z@)~X-&D~zXD!HRbH4areWwNEQghW!--h$dc5WOT{+grMlc+fEA<-3nNQC1RR_%xz_+wB33M@zDqd z&ExujjYy4P3q~=GA6pP4V$5}xiHf4Jr^SmzjXK9>tXNtW%!u>PLPld^n;62HK#op= z8c~U(RVO4j!bDa!19;>*>Y|&GWJRM46Dc*Q@Sb;%jbe~VnNjIoczyu`5<+S{mQR_HJJtsxBq$^ijsqpC03Zn&d2%C< z6sDGhmE5tSmB;fz&tgp#t#{ zmN+mWC~UYPoa7F79O^Majv$dcR7??2U426bJnqfvp*Q`T2_jPH3aT7;5CrJpflQF+hze1t^D#)#ep@Ki*>AKcA}xvzl$7(C z1MR5{M@k(B-A)CF$n7tJ4+#|^U0R+1ivmnaLNT05=yEC`LCB{pCl4k>8BdCg0Ew6! zC{m#@Gp<1;fS|pOL`Xr7v1pHEJ#J4)@)fvP6Kkb{gjCBmcod6H1BmyK#6mKm^fU{|Hyt%ps7D#S$wCN*J9V&5Pe?-Ou=<`!-Vrw()loYXy zP3@Z?&k06G3DHiA@f{f;DWI7WXb*W$8{|3d1U4-UGO{b8SjPkJzzT_g2k9Q-9k=lm zw+VSRY?LIYOqwH6PEh7Zbpz_-HVj)2>!tOeMbUbe0@s0_M(P?eo0wdOTd*%$S7sCz zL{i>hW9_=^y5qt=2+~_9-c|8#i4V#d_cTeuI%5(Xvrl3Hm;{(eN0_G_I0v#C%5b8m zN@0M2rWe&IR0x_z)ZhOVsZ~qCagW)CqP<@$!;%ISI5n-b|Ndu#{z-LZ0mMi%5Bb zp%P*lZZ@YTOSSZXHc8UC1C5;Ni+gOvbz4(@uS(4mkga2~%Tso@;Ko@SHn;9jZzR6RUD_lq)k zm@W}dlmedc)EzK~_y$;p#s~gUx2U@C(b!i0(H`O)igv_!KC~_AdkA!}j9XY*uyEmm zh36CG3m=^^>0gLnZ(p!5%Rk zaUS6Axd?%0`loQ+$0HQn*ga+aq4q1T6ZfUgS zBLN%gbztp8d;^~IQ*6@q2@6c56f$E;`x040YSh8#v}kdQp&Nl^1vH8hVP3qb4I-X) zUi~c$GJ*3(3p~gTY@UqKU}zu%KE-YFoB0bQ+$j^(2-*bwjyjvPAP( z3djbk8)(-4l#qne3uhTrAxj2UrvWoR4)Zj+dRhjO2w z^6_^jHBuvv(`@1%@EH*q1%NrUi1L#%5dBE%6Q%4%`ED{e4ap3M%Orpne}|h?z5R~Z zR{oA2k{U{Tgl|5yEvbOWYOsvEy#$BN6K_ACjAl59jVz{^Ah-g>12)Cj2}N?yc=Jffc1p&8G@V388na2kknkppqW7{gg%at#*Zrq z(NNk`CazIxj#=l?Q#B(FdLOEIXd7j;2Al|{peH7p z5s!(?o+_}K6yYSmHxm5>pd+eNlGNurhHrv_ReYc%NlyZqk2hJ5EUmJPQv-AbiA}-) z>HEof_8(}jTUt@c6Yrxrm603RM-kb${lg&h=|QH+e<=Gw2H+8FtzcdSM3d~5gojF> zAn@_~k{Xl|vT5e#clJuSqX0Yw>K?Z@CZv&+rw(pB(z}{g?>Y4Us`^1af}Vn9?`(s%r%Za2Lu7=RS~b{Pp|Tlf`9Zi%_Eo>5B02+X zBX;8;<1tYW8I8vl^F7H;igHqIkL;`jZ(2>!lO?$s`O14@HZ?sDWj##s+dsAliH^NI zWjsuDFISL~K;cufvZ6vKHkboaW3FRJcIY#v2xNm7vCavswP9 z1UE9GJRui?pIF+S7skGjmv9H#9hz5B!{c5!PN5H$ki$sL#|&ss_Jslyz-}bqkvW#E zuX>E{$l^*_Notp6sHc^vN5NIJ$HJ@a*&aBTA+n)zha#RTAr2hLP}LK|4vVrLWfJiC zZPl&YVg@MstlQRMIv$^zTcKt@pE6)Ok%T=JARxCTHWxH36>TNkses=2O=VJ?V4qd9 z&;t0`f(+*Ok^hMFI5X3tW15al6)+t4OboA_o-^OYm)qpE}g3b0Ed2{SgTo zrAXi7tAhU-JO&RkpL$FW!eiVq6axeif$jLm^3?HA+!JdPs7JD&QG`BO1VA`uv6Zd`eFTG`^#QtBvN#(`m>38j z%EWfwZ;b%!Df3aeo}RbDYO9hyUe7W=TKrRrbPVi3(nIAA5D#ATKJjP-7*wLz2L@W+ zXi%QAAHpB^sC?A8g?UjQfS(x5r@5x$iJ;;^qGUa}g%!lbci_>+ywvyweYl4+)>DH* z4!3-;^uqm}o8K357ud6RJ)zfA_|M=0yzcde&ubz0$ujVd6oSe}#$8hRNCT7r3w@Eu zM;2UJgFiCwn%xen1H&w;eBuVhk1&v2*A!DU^a0YqiTKij74Q$3P-?B!Y>J-a=G!Jn zeIWSZ!a_C0k6hOTz~g4ZWGnc!snZ`AZqckOJEO>zvZrH9*atxiND!?(hGPwnIMZiD z5CRk^_L1I4+Mj%ig4}d(wq3Zbgf*8k6}X2oz&^Jb#fhQ)5%mG#p<^3U^_a#drq3IS zfxw2`w4%sR06y7`etkt)2fRnnhk(a~K7>2K+sgMuJ}CoA^*#{^kElY=blsvo5Z4~1NSqF8#LC&oUJmzYOtAD|wr-4n+t{=pJ*7-4d% z0m?p6U;?0zL_MYt_T6iOrJ`(QG?2C9g03v$8GC-6fWb$N`BuR1$V$h^mBnZ67 z%`bo(nOT>VJ$cD}#FBubNtgwljV@K@Q}82Yj~R9Oxj(I`e`EqikffB%+-Ob@wzMG8 z&FNDCnst#4(IrlH_DO?Ert+DC;fL;YBH4tIC&8f(5R=qDy6)|gcev#1bcpgF8h;5c z!J!q+z`RPZl&OH~G^h@c6mzK-Xp|y-u=g@IIWp@KA*#n($ckbZfE8JYo^{$~=oOSb zsoE!=;-UR9JFtkDNUX?B$&(e~4K4&Y)WV={2X?`I{+y=s7bH59?ubB6^@{|IR8o4{XW;@g1EpC9PgMj9Y3*8N*>euKm|mANQa zNQ|-!{B4Dx{E_XLlt0o1CE!V4BrB4ESk|bKtjET-0|&wG3+%{T>I4;#_>ny96jP@3 z0nz}N_|k$ExRIll$Be<636XrqadW{FJy0vU20um}t8cclIiHiBIk zt;Vu#MXr=R8e1teY5_v3w2f{Xjx`+jj0S5;Mi+t=SkMFIQ-BM~J^o z1rOcu1S7JPBRyV2MilqRcq~4ez*x*3=`)lBhy*u2M<^*eg8JZ7hK}O53P)fmh9!9` zI#R(}24WFOQk^2j#)hEOIa~lqGHWmiee%I059pFREb$LiKBP^7H?^Ci9Ww>Zc%@(EbwQOr<)= zn_fu9WJo1Y>V~?QKzCSbJqlGztUJ$g0E6;B)x60O0CCh(zYSg6syW4P&i%=qhbLF_{@kIcI2fdl^W z+k3%=h=j2Cmm?%W`hz6eenqnSf^%W_(ns4#eQ3?42-blW;X}-~FwDKu0^aPwuuWnl z{Bw0s_r$dO5*4Cx7r4&(3~KgWQL69h3m}Mj06#BKv=bZHX$=}44?R!nb6CE3o_pRs zA3eu6)IUH$!K<*?&()CloPgyPfRD*|py~nl+4ih^wrmuLK8Cj`({IaD=wo7^d?;_6 zXxr96e^Rzn0OP5Nds?>UP3jZOza;VTTdizyE7dzdJ_I>#Q!n`rl|8iPa+^*--~&ky zbv~5#kPI2)-Kj$$k)W6eC?n^@?N47y{&N*HKW;;% zF7J7dq8>3HL7&u?%d1SZ@uQF_B&3qG`$~v+=y<1vuqU^^2dGC32%-3oPx(&_|HEQG z74RH*(FtgWu88wSedv1_CCGj}o~Ba`6d|{7o!vR{CJLqy=6aZpcoN(2a!{;C>&iCs zFZmxH4u+Eres>(KyiS4iP}QT=Q!OAp^tQA7@!{Y%6*$$Y>3HO9rxmG*-0V0gbZD0q zEVpo51CO9IWPvajO^A;%{6v!BXtjNaI#$|Oq-)S8gd*t)qz~cEp0IFqME0o zOx)STOJlg+sVrOi*f+^949lp&ak4z_;7k_AEhw>b;9Z{RVGoL*0!voMG)}w5Tx<@NW zcZ-T3K!7}BKy;FY1p%Ibav~&s3hxToP7U##BIz-fqlfkKObUp@g|}dEsR1od1@Rbn zVv#xKFv$;Hn$Ek}1U=FlEXet6_$xB<*W59F!QVX|3lf!a`=<7t^i0gkM(D9F*PS5CuCa% zu?;0SP~?D{bVEWYhv(*v7$#WA^xb)tkWeQd${VWb?g{`V#Vc?MhF4JHv~<-Rpo=#L zFk@wlUBD?~%U~&PNiPu#RXK)NPEdn`p^7W6X>o;2RA4D;5IdF>g2k0vR3d=EI8*bh zQ|B-mN~GeUZqUKv>QW+@s}{D0D_T_7FL;kvx-NKWa~R{>>#C0yd=xUVQXvu>_%Kdo zeJvaSa`SpXTZCZ{3c+|5;46lwV&RtxkP7_d1iC_}#d!lS^iqrx#4kR0;E9miH#fM{ zK`n1O9eOW@MV4)NxiT)BoJUS~Xq6?^&Tw48+2C0Rjc&mZ)2@myNNda~5B(S4Mx9NKrNr>RaX?irGDVIE%GI~|TU!87Dv;j@on$D=ku2wP zFrBIZh@|S82!a~`k&xzU233htg;RoGW5wYThl(0%YVZ+x#>h$}z409tr8-J*lmkb_ zKq#pVRb&ozI@-=BJ`psLAr+AbILc^PR3S*(06iE{l42RNsUj@#ql3|uoDjR4o6~J6 zZj?ti#sw7R%fWDJ9MDKH71hv8N}hITLM3hz*hut^ybPrfm?0IwN$Ol@ILU}RW@b{K zvke$OfuInA)B^_s;@{~7AR!gO;*Uj0g5pNX8~0AcI9}$fSk84~qadw8P2-`gNqrd0 z%g)ia-P;lDq~yLBW;FDi1JR7>X&|2I@0-}{q9WQwrHUIflmagaP=(OM6O71QN@ANF z#5M`uN>1n1^#5scNDFeTF_j zKP0PB5YPyOH%N|CI^v+fDKI7VtaAY&NiNt*jfAb#2@(&!6wreX9Ood(ag8(?Nl|-K zbFUNci_IYqcmPy3CcKG3Ly8S%Qj*%Zy?NSA@fGbYniC$nJ??6HB30X%EtI4;5wtc% z^J<*pl6X%A=27sLOojrBH;8FqJ|zm0y3Y|1LEzz62Wp+&6~tpulW0%TG&ro%;8~{v z#f^ALrol>TJ1~f}GqQ$qyYh0NgFqr1zq0~UDcVgzY@-}6aAIavzYIVktEf?=B*{*1 zo{fW{6p}K7IW_JL7Ed516xRe_IyDbEa$%ehtoT z|56_#dJ&tzPKpLo)$K24%#E>px5Obt$q-Sfjq zMTACT#*zR@Xe{NQm;YiQmdi;^Mlb+KfrlJS#k8&W*{z(433LR+q=J>(7H!ST5e^dl z*3;xBSX9PSIiom=!@6nP)-kF61-h6Fre8uLji023!A6P11QRqFNZGnM0Ss}NiwWps zldPaXL}UOJL^4KC@&-%DPWn)kK5;hF42pcjKO-58458%hCI^4TktK?bM4XgK{6t=j z+n&ymkbLcXV`U3P*^BI;g14K~LLjD&f$0FJVS)!@z!1!ovY||P!h-8ZnMy_!B(uJk z8W>yTx_HF;4w^j~;@~VxL>`)QDPBiHf?E)IzCjd51nVG}fifn;EC^r#NXR~@gb5xa zlKQ+%VVad-7$q9QN7C(>k4gS)VBAD=D0(BN$|X$06zrX15ED(sBy*?`u$W&xMy62m zd?WRX(8%KkPU;dFnL%aEN-73GajSAG3-WYB$qRT%BPI#vPl2M4u=r4TtfxPh?~$Es4Sy~@fmVU zS4_V)hE6`V2gBIlHkL4VQwGAAcxghZ46sQDVM$_EP;zu5G0ZUYb_0|GH#m>sfpV`g z5@Hg`Vf^CLh#M}!D$>kOK^-IBg4}3{oZftk9wl_Y6D{tw=g!>Brp8bLEZN1!c}E0C zH}qvJcgA#h;~s@Ylz%i@2(p5gEl|w>q)03Cv5JG7Sk9t_a%e0+H!+1V3N)jWoE(QV z@R5amASk*w!W|QzRP*kr5bl*?b8DgdDnb*aE3S!PBHm!(X34A`uHb!?yi^S1gYJ2c zIBH`QNDL#N$5()pV3xEpLNPRqV`_+9x0b^_hhENpHRM$M2F>l85b0gxCWx_~HMJ)SdyOEaQ z%=iMhwTbc0F*Qyh%y1e#iJt)MhKZemN%Q=RPU;x?z?>3m(sk;}Ris`}z zq88Uxk+LP~%FBrj5`EWcBymw0Plb!(C=OE6-8ClFsbCb!U^b_KPo!FrZ!X$G<+_Zv zbYZ$CYFSF;BN!%N<|Il&$%+=JR)|>gMpOt``c#5=1$Iun@e=w;|6xr$3YUjd-tWwScL5i!Pu2=Cw>3s?FmQ?m$_ zWajk}l-ME<#v|r+(5{JKgwAJRmIN-M8Jyy|#3)Rp*BUGlq7b$qnqd}%DxfF?Bvh;v zW$uKlzBbL1CUmMutivmp$C11xO4G`t_ zK42EfR^X1tO^oq&6AD475>M?Co^2)VFn zYa@6ek0FJTD;s*XiNdT=%#x-hz?m}bnVO+ip~K_AW+D@=1VBUgHOd%$v%5Kwu~VKV zPE@3L6RDVPC4&1JSUD-feWT^3=o`%xuhbpaWRAIa0Z`mG}sK%75}!J8$+-{BPUD@CGbM#QbEie zt@)%mdc=&Glv-vvABvP@;S^EL@|`2YiCv^j@xixoOKR`L`J%?uXv?QesUonFIn+xX ziY;<#{B7DW5u?!2j1xuCBvA1oLKh~|Lk*S)QD{~W%_u7cEZuND18XG-Pm+Tb_?|J` zQdg(Qt|@C~Pcg6yz>~x%&89e!YvRCJ!W>e;M~xCQ2lC=B{q!g!Yoe4aI7>{Y#eDgF z9}BE-gWFibG*THzQR02c{<4WszPqp2;0gJOteO1Y(}){K!79?6QbCR)5QE%kpPbNu z&A|VR+gql08QC*k93&{PNHWhfi9wZaf)YBN@c<<;-fnjxC{5x$J*gcN@|7M+YF;S> z=6Xgv#hVBPN|b~#rL~%clSC@$QRc8X2vanx6u63ricKCx2vZVZp)#e#lq)u!)L1#A z*(ql<1zm)xtI@j4FfXhc?mty4^w z#Ys9HcANPuZXsonE1KYx#_iBnT%#}9S0qc3M8*8iWEy=G!>_S4E88&}Htm|=UFPLH zEuN4L?TQ@K;+KYD~5AED%kRLKhO7XxsZ#w&1lD&T7o8iCfXu3JS!S zR7yMs^JLRnDI%@ex+SU`H)#TGIK>G<7nJdYHu7q$d8PzwXv;)sL(?UOF` zpa~HV&oC3cgAuj2Oa-2&qz z%#bbDG$b%FteASGQW2+-2OCW`Mb|Y1I-F>m1?BE@Ly;y*HhWYB(M+mpiOnx@Uw}8k zlQ98~Su#a|)xZ@oUD})$r2*98i^>-GhhPLVy@2jSa1EMjS>WN5LKfT}rFKi^%tq;E z$Yc&}BHT}tH8f8G&Y^E&ESaZ1(%LpfcyPNEte#}lgurIkj*ZPU*Ie3& z-2{Mhn(m8J7sd(jO}q*ojA^js<)Fr6qNyIEISPY$Q(f}{r!*uudUdRzlqJTSv?dAO zZ2lj%-UZ0YGEEco`@aAG&&dlnMH7*$kbnYmhZ3F4BuQ?HAXuS*ScTOcQ(<9iTVX+z zZG*{Z+uBq}IJmkxjx3HeAz_4}0Rcf!i(3p4-90fqec9dE-R|wKo$c7FX$2u~=f3+q z&-?u+v#K}FIp6=?GVN-s;5?W2dA*e~k**m>a;MmT8;Kg({E<`4vH%R>73I<-xx*i! z5)_-MHjioNf|@4y_G6`zIa>nlE+A*n)RxqZdl?;E3>hiFIDHvW5m^7>AM}~irAm(G z4-{Wh;7%)twm+!6^arG9Mx$zsm?2A3G3F+L(FM(NUqZ7fZs673By0Rn5j`BM1c0{~ z{u$eCqgX)wxUtC&-B`Mx{@UV+)o@Bw4JaBu{ZBGdI0&_|;NnQ=K-gxvlRJQ7C*0aX zu4dv-Zbsh=(~0445skx^+_b)UhTQ<0McBs51KL~ih~gWV8+$_lM;RQTjcKGTZi+<- z?!~EOYkZ_`CUX&9h{a4`ZB)lJ=lAOYrrI#dM#Rn7rc|RQ7x$wfv%AESB}Ln~{j$W6p#d>NyNvr0h#gFoedB*r z!va0Eb0f@TM%rk{{phw)a)v#rRH_tvQZd!W8rE3SMe@wJm{06~h|(o_Uck&0Sg)i( z?Wv+$CWh6dzspWjc}{U&vBZ$#8u#;n;@Geodt`Hx7*->f%iFA<6ko&6+h}*Epc+v! zv6u;eRK0yH&D(N!Sx|8+=^2I8#JC!FOsk|x+Z6Dplx5X0i_K`U8ph;ioK{Ty{5o~ zViq&Cki97)-=^`$uXnv(hb0E>w^4^=b{XSu+-NydG4D_1Wtp)Orr3o_4?1S5AaGu@ za2xSFaiD7^kF{|}ZYQ~O8wZ9JvpQ)|0mX989Kjz?6$BHD>Itv>GwYb}VvLQ|EpZhN z*Kr|1Q!2&Oz_7=1q(GYI@E?XhY*sJH$rt}*!yjz8Ga6;g$|g4Orc=h)9@Zw|k28`v zw&_r@gj1!qTX9n?SkfPPyoELMirLjWiJm+BiKB3vR0bLS`NLyO3b4y`|D+F|H+1T5 z{(+F-4-2GU>4iZC8a1neHAeJrS!c4ypiZem1B`W_s$-hvo|za%JQ7)-n2kl|$4Zq# zBzcfO%&?n5(K}#+_@ZwnHA77T>IWv6=SM*MAj?KoN{X|A4<;{6vX~q7qq0gRi@L!d zE2vtkwK&bYSUuinJ{rXlKdxY#0(V+Dv^`$=?&IWkQdS>{?J-7& z^iH(VB(Tv0cy7?FO8y&GDdjAWXWp=Ao=El}Acx$Jij~wC94mGT2kqk?`?X~ft6`Nm z9&kH+`fVAxAH>~QT5)88+z!U!fEPkF6K-uG$un`=&FHFOBr)7G;(yX;tnrM&0f8fX zjP(Ggc_z;#zJa;XJ|ue>POJ*1MAb|5+aLqSJu;OvkB^+sWG=#%Sgi!QM}668HEN-8Jws-ui8VaVgxFNLzMu%n`*8@ffQ*TTUvT|a9l594U8FHf?*K^w_tHYjFszr)Dt(bSi8YN`n zD8o4E8W%%}{l7wOl6h*}0|gR18dRf%j2xcD7@Jfbs`7B+yd;Uq#Ohd;rxM4;;n*V$ zlr&>(UiqbplUm8|D8VLX*|=Y7l{9&umVdM$8zvAllQ=UU4tXH=487)t8NE{v_p<~_}&_oq+LNej7Z#Z8T zcvIG`zyCW_BVm@PRB1Xmo{SWbhMWT0&`h}jne)F@!1KUT!!A_}y~%RhO$;L*iBwRG zFBTotMLSK1+z%p%nRhcN)(0dJUo_vO=E_OH2zi9ykSH2ugAyZpTAG13B7!0tG{|V; zJf4__955GuOCwE!PrxUQ@iv~U4_!Hb%l6@a`!~~?1xQmk12e=}V#X1Nyu`?CG~utz zW0SI@6|FXJpvbfW^VV-!#j*E7bEqvln-rC{QKNjnRW zWWEt=qevQcqe{Tv6lk}Z!C^A0l03{bF`0&8Gzm5oM9ijBj!`vWZag-lD@PCZ-BlK` z%r)r2%XZ+$;LE|oI!i&|Mw{@w`&5gxI^=F*#Zq+aVAaCl;-f!9h0;2sXgo@jDqwN&BC#{3 zPk69Inh`NNWP98jLq_yyWIA#GAGMOoxs5~0K@OeP*TBg{+YL|-Z8uiJB*rE~42@?l zF&j?t#N%VnZV)xrB0OuR&yc>!MB%7t2_z5}fgv@oOnC*6MHoo5&7EvCC5KHRXKtis zx2r@6XQ@0lVr-frGa_afJHwCl14ngIK{+&-7?XnGWOeV8c!@;0lf9l~lqrBS z(L`A8NTF-(Gx^UJK2=%F^^CektmO$$x{zG5O(+m)xwDw5l|eQM7jhsM8t?m0pYT( zM%n{Wz}x-_PrTKji4;mS!D!PzI%2GfqJhgSQi4kc^+;e$`pB3l%S9}S280W%2C1EA z3VtbU%e#Mz6bTO(9x2NbLxO~zoA74D3vz%nt~>afBh z-=k#UHZmY$LPU{xHF**plJNqQSzvmZj2a}=8})$t=uw=Dj4Ul6KGdj*+((Uu7nxiF zOfcd{V309ej3Qaa#<2`4_?OfO-kB96dH7-KSSJ5!Slc@6exS#)SlsG5JTA;@$?&PM z2r1)9%7G>r)*&hFH>@5tzlivN*X5U}DRRG@`i~XZU}SOe|G<{soIBJWk^Dd}CQu#5 ziUiRgW6Uw2Q_L=-h~HNf)}kO5)*0PO`a>K@W;68rR>p6J0wmy81hHHr#le@oIcoze z5<`7GEHVD?Td;vY9`b%nv98FCVLrft#4;nybl`F$OjnZ*iBZ1dILL>feK!++)x{g$ z7v!|Ct#>&xA}vS&?vWz~YzU5Gz;7v!YLOELWQd3mCjx{-h@3Qz4-zL}Pw*czrnU6{ z&+6EH8(9&R9mzCv_?ABb<6(WfGjqd$>mU!3i3`yJEx@r*4bq)|DuJ({x#Q*eq zOhIjp^&5)yHNzhOs+-Bey;ot-2)z~+Iy2sXx8vP5SSkwtT^&Ipcf8YaeA?TnbCH7r z13eR{ZZS6mYFow~y9E{^`m9e>hcJ(WWaTxnN;{5R!| zC5qf&jfh}t`dW#Im9GS7wD@Yr5k;di5Vej74c{+>>8-m>JF^4%{SE_70?7KK>feg` zj0QUwyO-w@9u|NTVPYmOME?c@hz@YDj*X6{$r+X8_R3IH&RkJJxM2c2!*}Ql3^bwQ-RYp8nnxRvuU%7pmLA@*kdM zq5~D`{mK3s0hIqyg?jamtig)P3qrHEE<3F7I~Bf0LBsmjZ3@in_}5bm+XN(F`X8h) z{eS=8QYvOMTT4_hq#`792YmcMBIC`rpG&}MVM0&>H$L)n2^o^8>c+k!XitKkBC^^` zxQ#mQRM_d$F?M=iG)X5;N9Wt_KhB2%Uci%)l;tpvV4GET7mz3+d|e@|Z+y)~6+-#hBArUI8dS)-)Z3fFXly+GZrx%}!^ zz}G&XcYeSZI=;}&;k$J_J7Vd$XKA^;GEAAur*CA#^fIe-oSioDjqXl<@zPf@d6mO9 z_>Zjw98Z1AVtIFXxW$yGXgS_|$L0>}o*GOXqNcb=#E%pN5uBO=`aInE8L`wzxr$U*DLTBWlY7#-sE|Z{O2T@8e4Y4=Y-Izw7(>sqYo` zmY&+XS8uX&y#z?o)J7a%MTn6 z_ORRlo4Pz?$GUfDPh-~}{5)XM;I2Kp_Uy)c_ntlYA4j(5+HNfIzZZ^s@Wvh{P+`(KY@43W$}p`mziC{f8xjS`RSskT45J#2T6`t9F7x}AIW;pnzkY{~N) z?oqYo11H(GUvA&FeH-4}`P4&e+rzZDt*SZt^u`;!EpRrSe_E)Xp}4L4k=oWr9@*-) zHXhlUY(+A}*2*JWAKAJU=xkU|Ot(VCuJVCvP{Et`^+RrJ{gJH?uYb7l5dNSkJ@oKH z4?q0SL+k((hcISV<1xx59xCI>m#&3}@o(cHzjzQhx?yAA`o6pRs_XmkO!^RRfoJGztj8gSr0?)8m+>d2 z@5P^ty}j#tLodK!@6fv5UMzdot?ONf0*c=JQ^lG)zPHiS>w1xSf#tg1oq4y&0<1#(&f9$UAoilx~rUF-{D7KseiEYz=IFqjdm~`+=Ia; zSZU@TsOlW$6*zJaR&n>i2Qpk*+r74Y|JwW4;_cQpIJo=EYwyR_{c8i}5&U+vbQU}Med z0*9#_-0F3!+{*f@m8<-!ft7fyT(xQ?KUlk!ZdDySVP%138Y@@0 z6{VFcR_L9qsIA1TZADp2p5a2up|GN{d_{3Zb@_@FeAbrZk=nzraLWTeb!_1`@Wid? zUX6drtMDItWwPQM{8Iix{%(7(T;W$#xm@XRj{gjpdi%$4oB!a#^3sas_+5DW<&71~ zr*p_S;NRSGzsxN!ENd)Z7M4|)FI%>J*?2BoTok$JTh>^L9ktj<1({L_)oP2pZlgPZrr!nHHX8u ze7HDWG`x7x;>C*=VO-=EH5M=O9Ma}Y%VbfF=gFdSyt!^sb>X5#3-J^6g^L!3g>I2w z`0b*F+_Miyx3FSMp4V^>*YRU`ZC%*7cj0sn1rEi9_`mL6{Qv)+WI^Me1w{@ihtQl& zvZbFtvS9uKd%FdV`3pP;%?dXACLYhXOIo=F<+zJ~#(Y0-aQ?h`^Yf3NALe;%hWTz@ zeg3@pVSZ!Yd_TWB5A%6$UV}p!bG$o!XP8&(oYy(e&r3S}ygH_?^J}c_rN_KZyMTN1 zI@3-!Z(XOGTkV{Sm&YvWtZ+KFGwW>3#ZOl;pWB(>gr~04&uw(hbsP@g^2c)lfK)u8qQ+3UAy!$Io$h zhdKEF_TBjL*_BzdXW{K;H8{B0!6sN~=4VxP?sep5S8;dttc;hsXO(BpnmG$^{4fVM zvpj1iwr1i--OTZ`syf84nK`RC3;$oAS(-5uFTB$ker8ouH#3>xX4ZIsALj#aKeNH5 z!wEk)ojIdt#=7ZlT7CMo>3;gaG(4tFpFWKrtlcy>y^ftQt;{{ms(j$AqV;q)9shx+ zb^jU0tNu#k>XnQ`oVly5SFT^Za^>pPD|~6-VMVK7bp4|HtK{-GUtRv{tIPawu5p?T5_9wsn z_$2r2!_l3r*plZp+{1PJ7+&sVcH);OPnG#C!G4-oQ#z}gQ#Km#Nc6rtNS zcqPQ+4(o~O2Os(mt9&rM1*y6DefANvVXYyInX$tW zZaJ7sIBVYMelK~q@!q>d4k?GwoK3Q&KR)u#@ptU)-f0|v$8*rEV6$)H@o~GPm3yZg zce{?mK63o+c9Fxa91!nv{B5)W@oxXM>b%foLFL$o&0x9XUvIpMjvysnfVO)G1YLt2s0S@|1@s(V z+RKjAnsaRwj#PN5|A>ceB=B>%+JCseAGrvyk;FN8pMEs)=m$d-`q2lZzkFDC`dxpO zGwi$m?!y_x*)|HdRB2%wq4}M02bjPvl54thz4tJ#mUTIhX*uA&O|a4o@v}N-ry|zz zRB@MHk;Y30yAPJ-@nE=YgM&LjKL_6d^YI6(Oj*GkheZsKZ3_qdi zKd{;3r=B1GynDev-+1Bq0*9#_-1EtEjc1>m&!L3_2(VaCrBQ`?VO+cRY?d4>z{X6(1dKgF1CQZx%k zY7b_eAdOa~H24iB3Ll=BlNF9Uh1=(zF6=9Tju1N`W`ZmQWZZrJ=?VL)I&u5D#Z)AF ztD-5;p5v*#;woD94nFlgkQd)G{-T`+;wls?i7QWHSt1!hhQjZu*c2R{@e%e?jh6>?EW)0$krsRKchH_;G8MZ(KI{P@;da$PMC>M|ho2b|S3$;t1fN*GDmjeZ z2`(nPA0Lhsr|4{K1lgU%S?M{lK{@urR z_U>%#*y(oGMMl&?QiPoq%<*zN6A%}6AcA7-Egm*@K3bsj9TdbQ^npcJj~(fwVF&01 zvJ#Mmcl45JAj41x(cm97dSV!Jqar{-lt@7s3xVFU+iNy$@MkdJj#!1TJ=x~Bqg(8D zuncvu4`F*9+=JittuYapN1DQKE9K;bD9n`1L^h^hNb9zBkEC0xBqX*5kPne^09oO- zhDU-3iyD~;PXZ$jWH7)cMJ08=nb!T>Y8d@lkvd+V5@9W944 zPI%;dhru`K7Pm{->)}rw>gfiP;G1kh0usS>4Rm*PfinPkKn8(a!?!lg_sJ?G-92Cx zUU^V-!d(vxiaU6al)-}!kV4RYN{Ych(6`o;Fc4+n?s?i^%}fd$Q#@NDxsZ| z6vL{nRX4;Lh&j;Akxht90;vMGDg~S1R*r~2Ae-P;mhyAIG6>2Ovje*W$Va}wtrViq zj8hPk5F*vEa`}pJad@@CFA=>MkWVR=Q~6KEKaeCt`zW;l@}Rh^wj8X2 zkAy-7x*;X0(9U7wvJnyrq7o7i3>k?8zic??7DzjYd#EF*2NpRCWGMTmw6p;?vWjEUeCGf!h)dujF>*l+g9iOP{+$@300LnOXoVK8(#S11Fbe&1 z{N0`m!sw99soqWIpsl&>j9o~LeZcPom+;DLkqCFq8WdwNd$y9Kx)8EPr)9zX(Qqb$R4<9rTiRa9+5L}qzl{?ceQY( z1Xe&Kfd~SU1^%kL;;&A)Qq_sO(*27bkpb!D8tDOd*=y-8gBx%l3f$#7_QO|=%U^jc zlCN&&w1RkrOW{&gltF<+fp~+KOM^eZ3?kvOaS81_5N|L!@&=?4N+brz7WfMlo4SjU zPzWFtz$EYxSKNhQ2X!pMg|ZO{AO%1V6wlW#fH&}wGRQzEq}*@ku<`sbDTA|M0nURN zaA#{^1kU+$Vibl%8jw0T3o@a>p-M90T!M?qxkpJBOyH1kxc%%65&$FzNCSWsa9=ja z2DmT%7akuRf6i^YMLMC%C2o}XLwJ3D=<^D>0`dYN2Yh4>ye+Vke)j)}O8{MA>QH%^^)r|V(~>R=DT>551MY$jk7>_E)H*jqeooc^Q` zt5znVf+gUSBArAvD&!Pc*wRf7fY`qd3cyF=U>I{F2|x~%NDdf3;68?F#HJ1Y4CW_4 z4j+e;$q9cloM@Z`{;z`<2q)`c1^kI`jU&K35(xf8$+k|OxRXpl${{;ZW9o?KycGj* zKgog?asw(uNk6Kg_{91sqxZ6y1FwP<_hI@$l~ln80kl9Q1JqDCe2796?9|8^cv1^- zAh!UvpnH@kARAzd3TLp1a6R#}zu?%a24 z?;Jn=PT`#z0lzz5d?y?iJK)}N$Adm14XWY;5+sS-LD)adus>$*?e1d+>pde6@j`LQ zTi1;UFoYj>>w^9+-bUcZu`;jYjlF28a6cP=wEWql13_&>J-%{ny-U!$kI&;pJZs zK=@W+-{4spCp_}657W!dDYr|Qyvmqzl+M3&FFF`IKHLd{l z>`lC%0Oq^H1O10d1OVO>FmhWV@q!bHw`*>l>6$ zf%o115dnL42u3@Z{2WH4XR5&Ug!6a;;73G{>JLKu%uv6ueu!kiem3@n)mz0Gi7ZK2 zF+d}w@ta=myaVVh&x~Syzpn-$zfZ1&0_Xb>f><-4ug!Gd5HCa;Al3C~PssMVr!oTn zr=DW8;Zu84Y{W_9d)BXWA%T9t0Ei!+946YgmwR$h)&zt7!1SJ{IpFtCrhBS7!oRFN zBY^bq8(^eOr%b31yzfvHZ(zT|vpu`h-8V@Da4L%eZtG87X17%|Y0Qu|S0Z0PW$Oja_2Y8z%_9#BkW02ny{s-^_ z+1lZV{oylk_a}BNc7Vqfk_T(=?-D0a2h^vPU_f}ECIe2~nqL_;&}0PUKj4vv2f>Ru zz+IRNWGBENivf9o)vX-bSJ&38UJZ@_XkMOz2I&9{$tu5ch`0QUc3U0Q-!PA7nxskv%u!L>7WqmXBLjA;e$4tOej6fSqXHEw5k$@3_XMV1F<1 zJfVIasNcgf0Pn%lWvv_p{sU(o82@!5`LNV4xfSSMFkha6NljQ^Uwq%PKt~C;vRr_f_<|M_=R8r0PMdtl#h`Uyto;3 zFlD?xfIM1KML!km$&Q6L4>cW?q65qdAoDjRSI2(JUY6Svn0;tPQAz~!7K z_Sj-5-4n3~K>BRfaD?ly13=QZV*z>|SBUp#%{+X||2VS_bWX*8z`%RN>Tbrb1m6kh zXVM^GBOe}C0Y9TcR6lb@HvqkxW`ci%*d0SM-A@}Lco)1Ud~ZyjRvd%rd1C&6Lrnuf zjmo~%|H2i_uUvIkYlh^tnO+&Wb+fj1K1 zdLsTy=_U8`jEMf{KflyQ;LeRWXj;jRd6$1AQ=UV{T1?4>^6>Q)g*MRT>^IhjhsO1BlQ}=gg%f#~l_3mty`#|&+ zo}E9Jp1Vo3RnxUG|OXP+a8cO0^_)i2MUrN&<(oCjY2+H1=LT^Kn+)c=WhU=k3x0=`x^1O#Q%cCf53WY0N-pRe}%>XPC_&c$PwUR5#YwRc53$-Ly-WzyH8NvfjNGN zr5~q0$>y{6?f!?FkUoqi7rQDCOiT4QX-TMPn`SFG&0Apf@ zxQg|AUGKFwwSLc8=er|R?;}KqbgvoFyC<$c{!R;}{^T74@!2~)$0riMOUiHI49yIV zzuj)Lb%5`b?a`Rl^n6ruV*?B5`zXvdIUfNJT=Puj+7$C~9lCvk4OraIZcw|2NN*DU zTU6yauqs=bc{s2JEe?wRI3oBq4gmPX@)+=&-aMMA8atwN0Cd85X!m%!H-{p+C#Fvg z>tjE>IjD01e_q2M%E0uhz^)mZ*TbjdilH2`^mf5H2R>JjL0 zq?KU36*9emec{N66n=y8!0$(Ve?7|ad?tr0hSgDxLq$IYhEMxz2Iq(I(3>#c0mduO z4nV$xcwIm~O7~c%fbWH7l&1e_9q66FU9~#}VE9jNq9yMXzN2T}lc==bi$l5jjAyE|}=(B77D0-c^=c9i7@gv%+| zbK+k3l|lF>a+j9RBM&{_3*qA~gnCKy7$nydzdzr~q5b*V3#dw&LMn443&w+ZsRS+bm%nP!o`@+-wNUaxC5BmnB)f2q~ut${Mnj`Z%l=#-siUKI| z-!nxXvnTgzP+pyjEgw^&cDE;ea-<2=SzRC~UojN#_as(pu;)pS36E8H06k()8LB&_ zcT`~Sp)fy8RBp)J?+$yesf<7Xp7K2N?9di+k7{zdns9ymF89PFt_mDlceR^1Uwxc4 z1Z`F~0A*g)1S%sKmFL@t)LS{s!LYMu=OE!a0Xs(rV1C`ET65qo`a{M_gB!f9w|)#T z9Jo9@dW~S+pu0T@)`#*bPP|sSfmLs_G{B>lATJc)%xxbq3{S8QFwY&Y+Z6a49Lhxb zC=WCFyD&Wf?#C@lIBq+ zfTDuBstBTFA0H!x_C)sp_mKKkj)3Cutv!a+RaqeIzWw3kA;EX|a76mCfIwHM=^uK? z8Ei)vOI8oK&2?b-@m!>k>a$H1)&^|e(H_%Z3JlN6E7lk zx8YjfMnmlp(F?pwviE&8-sUCg`z^%n0`!9M?LhD#1&HjS5Uv+W_c@He9uYb)yFqmz z`NU9tnaCZW9Yq2J`GECU^QXsv_*ypr`fmVr4{=|m0={dQhus43l=quJ-wXM7LD=tq zFlzS(1d93Nh|mGh3GM^7MwR{&H*vOl0TKF|)st3t%kS@2rqTEB=<_E+4=V$}dRSG$ zoT%KeI#hD(*02RwK1NIzB!@ar*p8vKSqtEa+bPVuG{Mj<3#_sEbhudfOEgHD)rv2Y|XKI9TI)(Xh}d+>;Gu^3(wHH4XnoKz2d(Y+0_wmrS@{x~#CQMtT15Ek_NrS7rBa<3gBS#(z2fM@9KIg7#Jp zb2uDYEIWT#bd4Ikp>=x_pAY3z>GNFa23Eb%x9@=1J;1rS_&IY62Mo3gpA*^h4$mp+ zH*#Hn3*q|Yh1Gi(LZTnX#i&Hzw!qz!;nPOs-oYV)^gDAoz7E7bo{LmSy+nl07f|$k zs_lNvvx(R%Lhkbu;CN#BWd1eWl)er@Jw6G;-MnjDX_KSwMBUw+{LV|#==-3&r=8aX z&f-Lh&9z@j6hqo0dlJy`$HjKs-zfz~o_i33GyPgXA8N z+)t}<%W!zV8SxWS)`iH^E8!|XQrm^Z!<7MP?F8q*;{xII*{1;FK{7g@klAU%7c$S{ zuDfjBeE(Hd>bm<1NZY`79V&Y3s52jG`ejqoF}rw4L&BN6RK0lV(nSDv0CVWz7e|^{ zoGyK#btvt8`3s48_AgxYnDAI97tT+oa6XYk!Xa?zI$wrLPO03VbLT0f51R>}Alsh{ z=da0sFDyB|-)2=4+hV}l-Nn8~;w4SX1PvafF{J(*$C6dExPYXUJb`A92 zyDzJP>F$e)eE0z5!*+`x{m|z$;=3~f+1Y0$Q_P#7TPA#S=<`;FPc=gDHbU)I4s$S^ z?m0aulnunqCz`FEx^7dgIdB)kcy#RFAnw*%0Q?iV@xv$Ah_(%T+mmQ}D4*iQYo!}n zbvHfs9;W=3#H6J^QigxO_j>(tsuA{yte zKY`!M6T)-dkS~8S+j{_%k0Ty$JwcH@g(F{ij~d#VlDk45+dm5C$(MQ#k3E%npzo%t zE-dap7$GDlJ_kIn;$bFz|3QyoZQ1hWyKjF#c~1b`y$=9R1U~A|H`%@Gy*9)^X85!b zj(5B}0&Sh9`{VE2X(oGjypF|qE>dXd+1nMG>5ns}5euBcSw9X>q@o>vyY21XVz=iNz6+M%axE1Dv|Il4IB2I!4%i6hRXL*jNy8yPsVh zZ{p3qsBZ{i``04`zaG&w@V3Ehpzy>nIOEjdw1>E!LvpP7(~#8*l+Xdfi&08X<5~)O zYg|xO2dqY{8R50-9|N;JWOdo-(PYqYxm|IiFx~Kb7qB=$H{J8H(Wft4(tJC2Z+p28 zxxM?PD3}`(M_#;AGop)Lu)Ih|y#@z3?hb^56@%are0!77rJ}bGr^`;C@D|aygU#Ns zHbNT?G+sQA90)JwTj(hYY#ySE9@l7`hpKK9WNqMYI(p(Mr$ap-N2m>?O_Uz6H7c!_ zxQVmp7Z7T{@cg9bi$vGAagci6{#;#Bc={}#0Z1CwcLMCNKPaDraUBHJ2DLpdpi?Ne zfT|I12&G)2xDYkuaH4JutxbD8q73)P2(}@N=UIIc)YnGy}aG1Hmf<)=%#X zaK?vybjIsdFiZ~1{KS1DCX_$DFJf;%=%<)k_SD|u-U@Vbyb613e5BDZU$iAc&0@Tn zD*M?e5YStLe=-1A^HYU#o&)W~aCm-=*qhdP&+S8d2(X6 zY%ZnT?fwRLc!ioeo|x_4)lIA&D3m|3YqC8Azf5Ebqdvd1bEqmarf|`q;aiS zmG1$wt2pGGD*E+!Q3|@QI#}gjc}NO*(G+s%=n~II)$$0%gCTO6=#wq$HrIf;VXT*M zo)CAB=b)M2R8s9eZe-X*xxPcWJ}dNTnn%4oCsAL=LmSZ5H}s(-A1J-FVIyEUZjUG4 zPB^r4m}E=BZ&c{_5LE+bUzakz4hu~%vOeGU$VX4`jW@RHc%;2`ywnB*1s{ZPb5Oe0re{h)paekp%1z48*~hp@;G@``+T zBnfOw*B z*4*DT7|wm-amONjgXpNHCpAd__Hx9x&-HVrVehr3XT5LhUN=Vpe1)+5>XmTCT@CWZ3#Jzg(Zeu5@ydv) z=vS`*x%WU1A2EgeD;nVk<#q>{$6H2t>FPsRBA?b?1rThl%eYn*+#0hX;0trcS|~_#WV2s=7P(4b^t6sK4Wh z*}1db1oPo+?aQ-Yo}FwD;ro>P#k0W#bIR*{Ihp0ZwuP|1YU_#qlP?-n<-Pqtiv1fas~K zM>jk{yw zuE$kwqX%kdJv@u$*#i@$@0>*S9S?0lbjFnol^ibC_gHwM`&ymxk`& zs1UeQKaaNhMU zzq%CF^wj48?~|r8KCmdBuMj~!i{n);Kk>+Q?(-1n-ah{DP`&{0_lHYd@x@|#1N8pz z4ei9A!66|&Kt1I40zv(F4sy#2@M}n*xeL#$X9E24DKj!@oR5b2vKi^Iv|9Og!Tj`K zasYXlP~FkpK+ig0zdL|72?q|Q#Q8o}$U6w{{@}N=!fPPhC(idTw#Xt6IA0Xx=YG>H z-#7rpot@@zBZ&zh*dl^%F|X;YSY6uIx8OdMb2 z07jsR=rJ>2JSMro{>szIz62tB9iSd5<$(D7)PeB5=79HU+=wDPc0#Ovm&7iZ?+nhP zy#1-Yc}@Iy3hg?bO_V=tZyn;ff3hMp50vkpL>ONa?yCSkl~bO(xYULl_?iR!-jD%) z&p!gZOI!z5mn&XKpPqMt`rY`QL(rY(Em347ORTSg>@b{DDe0VxRgW@e*3n7UGAtj zemtVq#~B--wDo`j`KYqzRq_$(kJiJ>Iu9PeQH^)T1Qakwg?x0*-%hj-^e;I*jf;%( zJ00{5>gv?d(`bVaCa&jh`PMN2UnoED9*XEaB=^Kp`6ipd4xsNKw=0&2vA>uu6}PHF+IHSz3cqCjG{UbJ8I^sl>e#+ zhIwH2h~#l3J)MbrbzBI&6&ws67#|XZ#*lg!tOVFj_p%-5a>TI~p-rFHAo| z2#@yz4+dD`VUm|kKBn%~xC{^I!B{@Z>?)pbd7yjkfGO$#_Y}<&)Xvi>PkVgOJQmI? zdC#w1CwF|ZX29ff;(N>p+Vj9Y?0Dtu(L%2Z`V^qPWsMT{0)f2-x#7d=GTiWHh^Mn1 z2KOB8Lr7n_D&tbyjM&3FfDe#ko^gCP`4h$m;BdtR$>9LsLf~%2a1Os$8VS&%bH(W; zEK+Bc`UGPA+n~VH*}fe2a*+SZ$`R271o4v8CEgceMZVPb_z%*lRm;OGutEa6puIB) zuiE$|!us(|;1Bq=9Da8y><;OB7P=>3KaRTvn(3DZbfgaF(@n*nxl)szuZl&_T{gmAS^UYPC$T*L3`zFq(B0oZgKor0R!u|0J z{k`K1{p-)w62((GMl3Kqa*d$9pX(VGX^-OXDU zNdPA85%ZTj=SHX>=6ao>LS8%eAYH&6u537b%W=x{oXE z@ za0p&0dvxZjmOcedumvaT+;a8UN`QwLMvH3vtR=-KbZlM z1{kcU-hGu`md-Ddz!@2!68a<(1mmgp>vT3bf?1d9(C_`linsu91fI!yO|n20^r;;A zX1l-8h8ylet7-YT9t-8g35?@zfkyoEaO8IZ^EYI$KmU&a{8IdZ??D0(>=XHu2B5wE z91QwH68g<+@6VNt5IA>s8&SV}_KMt#W_#rCm7ws`G!H!=IeQZRzl8Dr8!!P7{Z(1d zZA9>a0{{_#`gvIZJZ$>h#^=!I6T||L2?+SeYwzt*w1>lmdMuZx$6j%Ja@J2JH&D!n z)7?P(p7rwz-U9{Tte9R50Sy2FGXJ$EaX^Zo=>T9weXNm>E_;8f0xCdkfYSJkH7Jlm zaNr4wsHI0Ug^$s~k$~T7=s^%r(gz=44*WzW`+)OuBm}7T50Xj{R{*>(a^M7hi`CHQ zx;_sa3;_5IxRgW>z{wKTU{^x_OWWd8y_85slj z(Fn@xNgBi=`K0N;kCeb@^}KxeAPi!uz0M0T3B2F5;onYLK#T!&|1>T#Iss_MM{mcj z_d3W5v~W-XeM>B$&)%i7pGI$eAX}jHJn;psW5@w91cCQJB6yep5)0OwY?41n0tW+t zlJz`l>Z$RgHeTK6sr|dRvSWj0$OkWgGk0u+SK>U{@D;3w#uG1q>M7#?$^!CUjRXLWB=x_FqdG1Gr0y|j4^)8fud!A>0qhUP z3YhZmkCX}japZ7BL%w|YF>cQa?AZ^4*gt6o=vnc+>u+#^VYJYm#`_y!1IQL=#AN;9 zgk*pW_~STJ{rotP0V67!C+H8b-UIy$|BE7s_WO`G(=a!Hc!Cx*jvp}LUsC@;0DL0< z4E#WH5cTq60aR2Uj2*~8062K?WxdBh{olVhL=u4f0N(zLnHP8_)wxQ2B^N)4!teFiU7)vnL z7mz3XhaC*nv$(t+UflqOu>p@hi2+#a<;+iZ)UZM=eu)pha1|5*%>v%c0wK}|@)&f3 z5m25#i30isl(Uy?2e+^t&VKOq*fJa|=)o35DnMy@ET@+T-)}DANLGQ=d;>i+1Z>_k z(@X$PwtR1-fko-5qTYiOK!Z2g@vE@n=b?E>=GPofd^+`q`>>{Op9|6la^6e-S9QH3 zb8v@Pg9Kv#0A+sA0;C9f;I#KJ-v_4Zd8PH=0Fl5$UBE~yeh>cZu9$w`WCzmjZ^qIA zT}BKra!=GjETbn|;JVkzc5m<>Yyj)$lLwT}KU!8VmLQAje}Sh7Sb|*KZ&uLLW}k27 z&zP`RA(6nuJvsq!CMP~4`(FbuFdQiZkOKwq0wM?s7QAOUeC|7i+%K@Y!@T&ap>HE~ zFdm)&x3XeVzd7z1*PpH;F%U!c#1yQe5-(?f-O_wTi4h!s6JIxZgGhZzC13N z1HhXDAS^O=K*oV?0Di^olLw#)K-qf9LQn?_;q4b~;1&+aCqO3vugR6avI+f{5%o`4 zFdWO}A@@f#uOK|u&dY-D=a+CKA5<*-L^eu5J_Iyl<(kx zY5VvPx%&MQ2h=KKCR?(Wt+BH!IzB#ck;wZGfjG~@QD1)q+4neAuIcC%64 zPXB!_=SRq%MODAR)4rx?-z<-3%w4YJ=g*jU7oGTC3O~-GEB`+Noe$3hOg8{u06G^; zFIW(s<>I-S1=h&(QWv1Rn^rLpZl?P>FnKziP(5bfMTpMRI_7rkH*4e@SFZjhYL^|K z@Es@K9Qv~9w^QxUFvyz^uRQ=eBkmmI?ZQ_%UQaHU0Os`;X6FS;`{8ogAU@0AsqNPh zayMfXtN;AcnoG5dFyZ@4W~fKzy}vLf^B$1hT^x`xpM~&zc|df3!Ek*k?(xS=z0cF| zI|$aFfHpr&L@s;|ZJxRKb%1YoZbXtiP&x$s|4M0IkQ@LWGQOZY;k)*`O{H%mRKJ~r z67I&G^Hy;wY9}$iEbBdG`G1n<-jKVXJRv-i@u}SN2k_s~&n@fz%xA6C=M$@T zw-kJzJM%v~6TSK=^m-!n!l%_UpW->;)7lw4QX+H>8E5{}?$b5X=Ys+Cv4VY`ksnSm z{mydjPI=|&4`Ty}otWG)=nk*sRE-aJs>BChxEfV_Zz_I>nRsbb^J5}DAv%S8<>4hK z3k2;&L-g+CkhFfP`MieGcFOgGfZZoKrC=X5eRpEOc6f$YP#~T=S;d^7J~!+$=X;?e8bruMe)k9AIf)dz?ge{sJt_nu1b|1 zK)VXVeI8YZuzJnejvPnBN3o_$p9=zV{BYFaRRgce_dAZ(0i`9sv#8y@!H=iSzQ`eY zlkR%GW2Ai@3w+;tBZ1I9Ady`vJK(f`-NRfTUPE@B$Dw=e2GBSUbpa!-_OIe+Uv>Sh zFwX;26I>UGyOVtFQSh##jN3e?w4MX%wEvs22s&bEXE%Iq!6(3W{p$_`Qg=4x_Li4> zWSmziJ8Rk#y5_@}Qg?ajvna6_c$xrFTgJQ_t!h7TFllb)hcT%x8tT1(HqK9YjRF{n;R`q}+Fr5E3BY5q)$ z6|c9(a5^gOa@+$Z$K3fmYOX-wjPSZ2J~I*M8M$@84WB)LvIKP^<9!KM?fV!wr$TmV z@#W~VCy0*FnU(Ca)@xu4T@>E|pHavD)ZP}8;csOn`;$-Zof<3J(K?UHJH7NS;$`{h z-JX)6=KPdadg5$869&-%ZXeon19(^ldV1yom=!Q@U>QIe_jlirA}{c)yR_Y-qMga~ zRU+(Nn+Tv=n=1T7&g8AncBzKlBaTiIyfW!DBoSRpkk_TQW=h=zqGw6)8ahmUuK{Yq zf}bIM4(W1|*fYRogwYc&d&u&X=KYw4d8~Vf=)MyXa)YsQeN=5%AYBRw?a+Q4DEd)| z^Y|^Y+^+{fri=dTsHxL@pHES?j)vuinIW=Um>mW9yb9jrb2KSe-8!Xow8~^jd!9?r zD7t96Z^5kwL3g&zJu;8rImXckU}yp7fEar9!1JRM;ORm$vQ7>3Xt|f#&hB_UQBe;j zrjN;@9};>t7k+|M+U>{5S`W0Gp?qED`cWh;b2KV2A!Ri>;)6qS7{eN3Q?W9dmV~9dZl9?djB~BY>Myz(0K8ZTUW)D$~%+k8+o(4h^UKQC{;JK%!oR4 zO1Jzu&u)0w@Br3qcGnLZy=3+W36PaRFCeWMto4xCTXlijyD9S6M4vl%|A4f1pkm@= zB+~_po@2ebV(W#Nxw@$fyr(V4z`C_DppNc&Puxto9i8svv=_YA3~NV2y;F(1W!JIT z-DW!A&stDjk4(0baAMT`X#MD}POa9ZQb!rANkmI>XkOxRp}~q1(;-j+x|g zRk%w(XE{5tG?MM9yQ2u6A@O(>0ImhKbqpz;_WMx{-s;!+8)9|4iSjhj1DjLVS7SY_ z-O3uJddsUrY(9Q@N|>!7Q!;&SxmT%si;44xmQR5Ep1@i!JH4Frh^4dDhGyBjgm*aX z3B{$pBfs8|cwFm3#xu(BubB+*mPmzHYMq88qHt;QDu-{QXfF|d0#Wv8@4Jqs0FVEw zA((zJTJ1-*_k?IX1>$y#Mu@*z4G#=1+x>drX@K#CUq>fB@HF2xq$al$V&i3jca-Dv zviS`Q-M#ZDy2r%&QSBXY`2}7-Dxh3h7kR zV^ke8?dZbi1`zz1IkWC^*F6I21=#(3;Ay7N<29~xP{r`l-D$!XCJ*ycyMZ%`?!?zL z*b9|o2zh%u)XW2%Ya8|QQr?BjO>9pPUnfwW5SdTn5CiPsQ3E`eY@W^uV}*1FWDZE4 z&KZ#3o>DtMehwV?RNl>j-#=TpT5#^Z|+L0N$~4 zU9jjnmd7ipUZ9+-n^Aw~P2qH4cni3r@!k`fQ&)$=&I#Q3K=7Jj?dZjKs;KAj*qIJ6 z(50UMoV#gkV;2kP-Lzj*TgQ@PeCd%*L*$)-cuesBy=KmxyDCkc@Eq#8yV|Z`J5|(6 zjxWowfAtEHyzskK%~NoXHT9;!1Gl5(9x8hit^?!Oi06~b3BW!|?d8D-f=97DTim&y zM+F`$#nrc%7Jn;i>MvfrG&R=NqZuCMciQn?gzU27y9K2JDUq zdt!GFd7cuzAJhMi<@XTZPs4maD7ig)@&ko-1>2>-^X`)oqI;gVdjxc9r?le&HvYU$O%4ubpfcQPc|$%j7*?j^LR?~K~=b(BF?XDNNb zG&Ky)^iL;h4jPE(xYWV`U3g@`S9bvd2}L&TRE_%p17S2>hd7^khiL%1|Mu2HTNI` zBHlNVJwdQt{~IL$eWCaSu-?61rTboDI-!0Y^YHpVM=s!1TJAOHCHGpDB0Q9KLV3yO z>5(v2P>%_B>5&1s?J2qQGQp_)h)ZWO(TC=t}1f3`k%Hyk`YH z4EVuv?W{-L2yws8+`*f=z(Vn?^Jzn`FO0ux0w$3vlfO|Bs*~T^PmOpBsVaT zgZkA`bC;Oz)QT=v-Mjt2rkoBhB)+iqWAD zPQZaCkmfGZpnz4tN7jA6x6GP+yziq!|JJ?64#Zw`fxYQWiB}7{vKAx=NF|_0ot`MM4tjb?D*M*05BE|wZ{ff)d(R*{zP$AIxEoAb$12IWfI_s?3-3>gKKwL~ zsINm(@7ml-xX-%zO_~MH+@`83>RUFIt+F09^$m*S6`}sVtgnuYdOGe|WzQ$#?8phI ztlr_YZ-d`3WRg0zH}p;GtF7;g>GYwmy1oyKiBi;k?+DPnA^jjqK<(`*sz;$6$9SvG zp5y^o0TKod{p_f!caII82L;eA#XRkD-2+^3i8sjrw5qdLzUjNG+ObqSb-4#C;K>Te zfgcm;<-oVhdYq(DVvk+yde)Wz@+F+pfnPDmAJ($Y-rvuf`xNYeC;(CeR({`L4L;BO zTI%aZzJt#95%`BS)kq1b!k&foA_Nj+1;79(5U+^J`vT#AW>Wj))o#tLSR2W*s|in1_{vIX{3M^WMu69}Dk+(uLvy*(VXQqr{#t-FSeB1pG>> zS7tqI^&|z_#xMXCx$yCS1_SQ`w01XIz)$&}+mzse{#kr45e+)cszChRwKAD#FC@%84$cMF46;7g9*PS`(*!_=ngF3>N- zJtFR+>mG>zo(1i)-p^l9h;Dkp{7KP*pXNFCbx83<{)F>2itJ&2W)1G)uWtf>H?O2Q z1~>mYuJL9B{X+5(*#Y!BGk|-Kk9D@^T>a-%bMHFqxa#K(nI12apa&@5b^f=A>;dx$ z^|iv7H@C+RiY2In1qh>=cy2B?7-au%i2uiPm=G=ZMJ`&P)&HAZmsfJVfPMf!@Gm;t zyE*>u7W3odTc$$u)V?0zev|+}93D}>to4-fV^zM@;%l*SR*A6QaJ|CoISsQ|lJ8ZX zkMun_?W1gOmHG{q>$7y<9QSHjFQ6Z0RA(9mpkjR1;A=lI764#hd3!|^>30M52SRy9 z+z$iU3*6I#Pjfvt>)2_-JSg#A7qEl>`U~(Mxtd+^$j@_EhXL!yAbY|4D_$}7m^--f z{ner@_7wnoymBp`!aa3*y?m7DDb)k(vqoQ#KYH%-(tH4X2e_}&e1ZQ0RskRR@cpGS z%k%NRk9PZ8FB!^@o#{eM$%8r%8o*sF3-A-@lM+D3I%5E^rkd|ACh*$><+lLyYZ4`W z`R+p=fWP_t0M&W}`@r_@`~cxSWqWOU0)L(o_2bFBd&>R+;2$^Kx%I^PcXF71_9hMV zK| zxrEqXM0>inPb*aN&wMK7elC=IwWl-44%r>W^#b!=82%K&J;1tB>_?m2^NuyE!xJg> z(L;|$_IL@<7b(?$f@`YKSL$B&$vSHDY&dt5k5OoE2tNAk*^bV<_HfHn!}lM5Ez}SB zT^hWeCjr>6Q<1mb`0&l{_V^tqhNa-^NQrrqi1VkOh%M=2C;L&!UI?EuKFsm47hP25 zk%PrIq7~HYzxHd-Ahi{%;=M{<2#G47nl#=0RBZ|dWS6g zmfU*}_zvlwQF_rR@A^|f`vEHb(Kk=9kN$M_$~S%NPv3R8@iO!33HcSRSA4mw;`^6g z79pT|`|+>>2;rl`eiZY^M0+{wE%P2kKpO1;u@JxKV2McHgnfnCa~ck^@ZPHkKLtA= zlus_e>hT){{4CJ-2dV87*Iy@`A8P@?Pmi$xFm#Vst%W^)vF6DEe3|`mtqJ493)P7H zVHm^`KeXr%NTI`bMlJvf==)&a zfHndBjvp+ceV#@56^qo9t3KP;@%<&=i^cUN0CqN*x6*n9=$TcXO1|FI?`xbdg)m>; zvl}Qo_JDW0kVlWr#-3gG?WzD>612 z=CQYKy-TgqM@zuN!wgejYWZ$%3k$tz+RnBRXx_P5-Or-9yad(R1dXcUCQPJ)yusR- zwf5Bk6*sO|oAQd(UueRJGdFa&q-P2p(uqo~T~{5DF6-{-T7vRvXAx=cfs)lxQ@}+? z`PvSopg9XWBNX%ZBmQjd{T=r&HE87sxl|1;q-E+Xg2aKY{OUVbp>CNizdSUcz%P<9@1|Vo z07Z(+mPXxC;oAW?#4UyY_db>T1`3UW8Osp9T?edicsja!2{wc{FmwPhjF@Bybb-7t zLX4#gm)yJQ9svZ@-`>L%u6xk8TJBpbzoid1TR;r(Fc82nzt`)FJ-()w?fX4lviKUW zI$C~l=hyssop&K63*YmZedX~59%)$o*1nqGy2s63V&A#dJ1Ut|hp=|v-AnLg*+E)^ zmgZ)a_$_)CzFO-$^ArSFVZyJ!{A%o(Ts3RvX8Wq^_!Sth_{~@D)hRRXWZfuRIe7$9 z=}WI^T}!5Q{MizkOx;y?rDE|>@}6c_I(~6y^o$04Pi5<5=uVfvvTDvmfzE7_Y-=Py z@f)L;jzqOc>JiF3*(^!9O$B_5RCd#y(1NP?I!M>2`WCyh$YjQ5VSJSsmZ(rkqh(WS|zKFKq79DW^*^O)BWPiYa>znX^)b zj7T`b2Ac!JNJ(F%b{nTY?!XtT5&9qX#Y@q|luu}Er2Das2QrXohLmnH^~7zIJlsba zzUI{NHK+B1yWTJ5N=3~5WvAUKrc)I7B_??ndA~^frNhu_|2WIu;g^*=kE_d06dPKL%eIZ!#s;qkIRby>u5BDc%J;N5Vtc}b1I}WS7&53!pzlmRd=?)aNA@G)L zyZdR$s@ph4!tKEhgxV;c*6S})udcB6Hzog8L^`cIFlf#=?f!U){iWylm!Vv6`kT+_ zafHnNe_Xw5kR8>TEm(W+%skI}s-BWckCKE$AyhI1!5!4pDV30{D_<122^26QxT3{K z(evoO(KjYKVt!!k>4}+%>6p0@H)1AY=Fk0iBj(4<0PPp}8VtAr+qjJzFkk~V;$;we z)#JQR&syu-d8%kKbLZaQ-bWmzGV|=UzV&?@?6&{QF|ykJFBBNYRa`tW$LdFpOR(bC z3lU%xvZbbNQ_>js=l){#&%c!o{1fIfw<)whbjC)c0r(Jx8=%P3{ujPh!)qiQE-y3> zE6x9cTWB$+Am;kVDgg_Riqm4F-JD~tU_(j!ts*vo6t5)Zgrl7rp8 z+5yQ6C552=!B+FrDt>Co;14$A0MB#=kKdBbcW$1)c|OcHZk|6df8geu=ly(dbCI%H zoNwMdk6|~@JLD<+9}naU`;8oE#O#E&uwNCT2}>`?7hi3Avhz>I`N93Dqti~?Vk zBup4}5EBg;4YKd1z=Z$%kb(eW2(cc>E4YajLFfVm4$wa=N5TSc(LIj!J$v@1mP-(U z343TxaEQ+csQ9_|9*7p^Cg*y4<`Nb==G^YWTyOVWKqa*bS~9u0_U^g4-E-O8((X!M z$FPwwAlbEUFJiuorFJOeExp8a|9l5yb6lN|90qrnug2%`o;L0Rss|0UK?S_Qo4fG7 z#_lY}KyU2GW*}K%WHd-+6u!!Cq&s@EJBD_&XLszF-BDm;a7Skr_0o2+VTAPbBYRWR&OWy^RiB)inw-Ef6{dQVQ^l#~BzGz?UBcLq_-`UCt>ZSZK-Ra~`Ksg|mpO0`;HtH}zsKzjiLhEOTHGK!?^ zD`~l1DVHndlm=IJm89IMpsiCGe?q5RiNhZ+*Gt7>xmYZfORm(y2~}5Wvt4w>HrnN) zFE%l#q#;Ssmy%+y1Y@UMDis%tq2Pb;ULH z6|(|1pWP^As$~VX3mA~KWlA$wXu*WtW!Kcn0jdxpuBqP#sqZsI5&otL+`vBH+# z*p`4wY8A9(a$DMCTeggC$+j$wRYrSbqk)ARZH|qOjbcwm*Lk)hVPARFjo~_tw(!Jl zJam2p513;S@KmNFZFPFo3$Y%b&Z8r4xIHpFGJ=PzG}0MH6{CjnJP&^?GYa;`jVIRK z2>zeL;NpjH9-ON72CJS$sI~{IRi0&OsDXBsPuZPSe4v7(j1QH@_ypX{Vi9+h5_lC+bdtrPD4tdnN!KrjBs zxP?2smqXgJJ02YgdVS*3F79h@-KZrVM~@cI4<_x9VDsTd5>yKb+lfnDXv>ram$YEQ z?!q;7a)2sHSPJ;7`=yJ$3m4tR@`c{T3*kcR;)M$rFJAB$)-PUQm5p%0qZ>UiJeEAx zJO5b5!m&tMcoqc~*<>vVK=O4xSygT1xQFyd>{!z~& zJlastqvy{popX=4bB#yNffYdE9`P{vbIBvUbB}oRtgGi7x*-+dP>g^hm*7IhtEFzFv0}7IJ4~^_OP?Rc(%cC0`AZTJbZTXZ0{j{=%M6n_o1_(7rJNJ zUVG>)&e&yhXRouN)P5!<9bQisFnYY3lQ1K_)Cr_L>d1CSe63Hhl zcARj>3nzNVPXts_tDq&5JAp{@6UR?vFduWrvg5sD@Oz_3j(3h7KL&?6>R8~7_2MQ| zdb~x`@#XSz?-F@3x zxqImD_K~~qK5}<~jlsJ+M^G=_?e1zHxeMBOcUS31=Pp$J5qDRD<71gous3czvF;F* zUi%kIcQ)?0^UgbX_&eaWR*~G%rskY{II|61{;U9--Z+Xw6`5j4kzks!(bzOhe5*da|@H3a86q|Czz!VdWsA6 zTNf5?J%|HXsCVl^aiMuD>evB)ZuJWtwg8oGT_~`JT?ZErCiC#n&L5o5aCY;(gY$*? z=0UWvSJ-s(bv9)v&btF6^SuM}35${W_JMi8loTN3iT$;G-MPJUZmzQzr(_QPP#u16 z6K!s>E6f25pv%wIVa>aGd*>GC#&-AScHg|aKDTprb|*JzkFDZ&HfDFifE|guW_M;g z-E7KAU?CF{e}P63pMs*KP+v>bm12hNY{H%O2l&~rwYPoimaSN&iTzHe;72>-m=!FJhGERMhdZOg!?X>L4&!rf zIE>a=#m;tbn9~XlaKr3%qigCL)g0Up-H6k2O~X0s#spg?Ot3Xk$2d21MKgDPL=_CE zE>?Y|SE^){^-`r$Dpgows!Lm7;qwl5`id)sN}Hofi=~}~Ua2s{!WAf7!`(*eG3Ftt zuvTRu`k>0R1Qbjxf;a$Gz(*wHon1isFG5NrL?cM)P!q5=<~KBHh1!(|#}k+?9cl^noocxDh=;)*!_J zl6C)ni66iYaTkTS3K&N(lW#2mYp|eaFZJkZ$#RFLU7m@&-7mWX=PG0mW~=};JJ;C_rXU!Bpn3{IVHGy)wpHi4sML(c9R*V$ z{x+b}2B90HB=WTh;>v=@B2i#V6M*H#@STmwE|AvUq>XUss-ugX0@;SP>K-u>q<+DTqu$%uU!~ z(8OY&Nm!mHK|p2zFDGLPbP7{pIsv`VB()H!hSF49^(7)Cyw=b^nM|##&bJ=kZ=w+< z_=-~pjogA*g&G(H&;$WsCycM>uxMgj)|6esK&q+8NrW%R2w*p6fK?jOkuFYdl!&Fr z3b_U^qX(R6*|^6?io;F4fA~TwZ=wi97Jv)jYs(vdAckQWtbrTs3=IzA1I8L8FeH7t zpfuDZe}MkdP#5RL!6iZ$u?Ijnm1@c&fG_BRJdimhsjzuSr8)w_pbNI3L>2)X-gU+! zh)k%o}vEhf7ythkBExWQz1b!eW#~In=g;1R!+)rhtrs%gF># z1>3h3SvX@65<7u%*t#vSsIaKA5SdWj*4j!QVe8`}5!xaL+>D!Ufjqz#z#f1gAhoc@ zwsx8W#<-b=nP#SslSH@^B*J&}O!&#J?s^bt!JY{`AE4z(s@bzMq$dKeVv+^L$rj0j ziOJE)4*3K(S(|8yNds3v#*O@d7`J~`gz>f*H*ds3nCOj9I2L|_&(2Rl9)vBOF^~np zI0V&Gm{(!LZd-Lr7nRzWh=fs)1$dxHD`;8)Mj;XmZO$q1oOMVvK)|Rc&y6(nZ0dnD z3PGGgYQ)02J-RG6_=ck+y{5)1x9 zhirno=yHa^(OhKV*mv4R&Es+c77r`xnO(1I!&Q~Ax=L30! z#79daPDCm(sSt}ZS=9L*DpMC z4}dX1_`%s07zJzrltLhX0IGm(?KC4G0Zc=~Ob?&E2du(_XVbGyatGK!O`!G=27*z* zIg$!EV?{rb4cG$H{ym5!0uLl-TK7KyHsMSlc@Rh*V0!@MfvAPHxP&1L^kNexNgNcJ zW}mA5(L9SD^5KyuaHrGz*2NZpFgOi)11JL>T{PY4wx|YU7LY06^g7!h3$P`9*1#ES zAnG75!o6*c(&Bmdo(@#-LkGdt^pFRC@f*iT7?3&er!JFGAi02(O4hJaym*Ei?92y{ zc)&*-Nr+P?Dx?@@tiW1zCpyPLCj?^|R8PS=tip!fw(5!N$4`(wI0gbi2Z@KK_ym#- z?pTwZ*u1d}k&^J}@!}wsk&*7$@+e3JEDTTj!5wRZcjyxlZP5^JxkKAAM&^T4=z@0u zQNhA3H6jrKyKxR){Sf6Z)zM|lgIj8=?w3d`xLQLb1K5Y0Zx8{I8Vj*bTB3%W`kToK zq|=YY!*&u6Gk5pyIs#!niH4T(3RsE$t|J)>#PS;fQICWJsRo!Fsfi?8YeQ6b^^m63`G$QV@}n zC>?IIPDpcjq0B;#)y)v8w6Jb)itbmp&=!M`fLTZu)>Xfi{DL#m;ot)F9qOQwTM!3P z1K$8rA%JWM^XnoNL@vk*=jK5=JP(3W$cu3@4H=6Q_=5Za*#+?ktg@2M zb#Wra0Gz-InF%kW-iM>uBO^ybA$xLAm$X6WQ2y4z<#E`Vt$Z*|*yTQPk}P#K}6xOIuVf=CC? zTFL@bjj1j;2boiH5u1lpW=6m>bTP{=k$k{L7hXo2aMN20-vt)WeK12;Ds#?~+(w`j6Jexjjn*fF&j zA3ZtR2F*a8p-(@Ej!=6PvvK$}*xN8hg4Ncrs0Sk#z$Wtq^?m~fSq*ui$Cv{afR^^mX_Wl`>v5c#@? zSYP4r*Tz^>zP1F~L9_y%5Yco6V<9L~aNr^EWKkCt2Z?*Ak&?r3PzpSQQ=nAAE!1H^ zibN3&p!UQ@P|N^9L|?9;rUT7D&H+1B3y9U|OR5yN5%Rc=JWrx&7>=4|K@ux1n}8x` zAN;|U$UYeV5DsSGC@5{fFCv%+unUo5a5pzVIY^Cw$Mj~16F@phxM1W%GT%601CW{F ze=-IFG(-k%!mb0P7wlLi>i|kY8+8drMUZ3wbI_vwru`Y^3t@lSS0lK6>tGhNQKUdE z0uO~|M3I*UL+oWF#p1ptFb;S%h*$7p5I`~n7{FNWX@O(F4pA873XPnKz*}U`-mteb zw`b4XJc)*pIdBGK7*u%H65|~pW!R1PPcBzTcKf*&Nr$=Ji$*sLnRKkTdzWe>Y#zm` z1zsr+qi8G-qX4HVT7kkcWdPC*&-G&yyc+BvS~HYfy%TKJ8H0(cL+_ zqew0Ri5h0RkR~7yV|K^h9XI&0;@eCoyvzJ{)1xtMV!huLX0KwP?KX0D`%+{U?w;vx zota()t8hu2L0_DpmgyBxB`9KX#rkb1?Z!w%y}}~jXcKy|Wt&9_q#*d_qhx`M1MrnV zRx(u+Y!a~`P9Y<^pkkPi36P2`1l+LL4vzP>j1T8}1t1kaS_fP)k^un4s*z>zqhF#A z{DVfY%9?Zw(fT;tqHgHg2IpxtF$^YE!21a{)|25iBS*m{3=Rb{3{oY8p*jsxERcKX zqM8n+g9Qi`Y9LU>G9<(`s>LSc3KA@&)i!FRAzUS`u2!mG5vWAKNfg5tG72To4{C{D zPz`SM@^$eEkRX6*zzzoC25Y1iC_|tgK_Ufl4r}BXv~gfP)=I@?V-XBYc~S`6fVX&Z zg>{sKQ?iBF1a|=TAnO&fgoR__GY^FVD#2RNDnOEua}J)y8eF_SX$d3&i@{3^1_8d< zs6n7zD9X5m6!KfJ2W_eiKmfEQIsg&CMqiJhmM8=iAPqJbqubiz3q&Z48KHm|A;^R8 zfF~enfSsa)4%q@{%mM1t_!UpB1$hFx(kMyz28n|$EIf-U3yKlG)4!Q{Yd7#CuHSLp z{zWMiHn@ajs=?oV4#1n$2nDbT*`%A;HF>lzHgM$JY@-IjI0ZMcCM^Q(;}fYK3HFk8 zSj02&0=NQ4?m&fSEipzRVLk0@5t1>#rA0bn%h;k32}34J0F$8F2%CVg5c)uJupu{y z&zBo%@?pZ06(Xa6>Lzlbz)vlX+#p>*eSuCO3w)F&gkd*Q?{|fftENIQc0q&j|L$`K zrD`)01X3a3Q5vikSwMs^2ni6LtRr9y{+~*(Tp8}m5uh(96-QZYXK@eiy#rrk*$Lepw0j%u?1U58I-^zs3odEHIOT5$FM(mGu*@y)aY#T zgC?0WY%W=gHKWezvhpMN-$gM()~C-LYd(005l{)%f-C{*gPc3?H0C@(n_>eH1#M{# zys-v-If6I@kp(CMJ2#5tLN&?`%H#^hSX3Tc0!bhez(ro5V4T5us16)h126^%PRIv^ zN4w`A1y#_avY<_wfl&YyAGmXMvIpP?ND0&<$0)KV|tbo)84;np?K7=vp*$&N*gD3zQ z5E%jYKm!bc1O^$+#WUatKp1$QVNT98?zaK=KYoAF=mKyCNG*8Zu5adifIGcPO#zh! z+$c0K-hhk%P<@Nm)Awc6BZT|XzP!NQyDlO?`UB_{pfcdGMoj{D2op3IXE6%k2A04L zP+>p@zzgex4hS$HUEj$TjIw*E4;ndXAYLGx>YO-v^29tTfe~PTM=n5xXDu;?0Lp~p zC(^$9AUW<&d`WuXM1=k%2C)0%HzF?(PyYB7)~uD+%1|j7Cop761T<9}Ve{y6Sn|sN z@i?$XUd{!0m<{_UIJaSEk68lr220C;^L?oAYb}HKuGS>R2hb0Z5I}VkH&Cc`myX$jmO zBU=zP2M`cM#4ntZ5WEyw2*wlu=z9mHEyy;D3T|CU;4&rt2jT$-AaH{sS*S^9AgC{t z54=F#0gB=IwzvTD0&NijrFnl~n6SUd0>k%%06?uTHUPr+d+UP##Qd0N8?29@-|zhr zR?q<&!76JK9YpKmTt?_W%3?cvO5 z7dTJ0X|%Y2E*j}WyP~d`n3$TnZ+4DB?cfN1E5-LHVOZ+r!#HT zcy?3i%<9w(e*HNI&~z9;e+$um3Dkhd0AYXCj!R>ZjE9^EAQ_NNLfboe!}JA|7;qEr z$JtF@7r2MO0I(lB;QS5Ni1Vo^ptL}G0}%jg@)FR>fpwgX3|KZu58w}hfhV5lmeN{) zz+fE>QUbDt*yQAxGveVPc;D#1L*a)!X3nT!y37`22bfOh1b`pv4x7lpms%1RpaAes z0Pg#00+`ZDBO-lt)k^qzj)DS!_ZW!a4=XG|0SZ_ffvX@aA3FRYb(=ZdDLsfbQNR5!fl@e=CMy0C)hRez^G^ zkh`sephzj=`;CC9LX1VAm_QL!98WO;xHTN>0P8U!V-Rxp2u{%A2#iMX7G}jN|34Of zkj(p=q4$x7fRNtJCpSam12iva?=3nZo9`ZAUyI}Bf$IN;sJ_DD05t_%*&v1)sN(mp z5XBp|2aqS^@9Nx-J)nug0;Xov5(tLmlbd`L54hYceF2fa?j0`s9S6_EcEX1PTL?`sjqGr)jg z!i|K{4siv1m_+stkp@X$IKVbNlo5F#lj=wqUg7&h+pv@YP!A{sp@`QGNkt$Hy@L4S zArINbDH%!zvnUn_Lya7~Gu8kN1M)*rEO3H zd;o(Yo_FQ0bOYsbsBED40?~cgBqeabFW)Y^(sheEkV1e^-Ip$vi$#eB5Po3F0aFi{ z&6Qkn9U~&hUoy==D6R|6qu|*W27z-JZ_q@GwuDYnf%K4d2;uA2>GwBj0Ky9Z_J#Nz z5kG24KjuIUDF3!KY}D3mG1|ayRRDs*4yK6n(->*+1i-xmmS>a!p?;@-ig>>ekv;VR zA^-v^|B|?VoJE0!Vh+$rtglEOygR3${$pGYH|%((s2!Y~66B8|IT@ZNKh?4!_zbar zg~fqMJY5q@=KnWjpCF>Z)B#ci^aTV&{?ZGO18d=-!tN*d+`MGy9Bf$Kh8-%=0FX9 zF$>b4l)3XBsgyvKOx310OR{dSJ5wV07{_z4lz8% z0Cj+RKzkgBBzSn;s?Y)`^gz)D>=YO9Fos|Nm;v$t0O}4{9xz@|ztcZOykCgOo@xLQ z033f0;tbr`Yg~bVVgMC#_&3pjm4E0%SI$1jJ~j>Y(~K1e7-f*0@ejB&b;$sT=iQm) zfi@8R17`&H{h1ECvNPTL+1Fy@43K_8q+elifTDoY_aTRY#S*CE?^_|VH@pvUPgy`$ z=YD_zO^i7}9zpZ(8@f*rM_}H6dHf0ZB?}<>mmc8sz2V-L!GC|c_Kg~#Jz+hZ|Cylw zb=A>~TLAKx-`|1|{HYWMX#jxkPo6+L!O0VYCpsq)d>}mlV+sKGQAFU6nj`u@0VjVK zSYHtc=?Q;4J5f5`rWgQt-ydrW-6zM_b9f*9{{{K~5swfE_vQH?g6F?Pj9+9SRX~BY z`_q_!jRG|W(5+K?( zA`lh`@DWaM1m1qZd&UuX^XFqRx+5?G_M;m>KSA4q4ALV6{tPBKdS{1No)8`(0`87g z0eJcE4a67e%?H-UD2phtAOZvz)DT8cK$Ji?#s@SE&EIx7(8GWDHe{eboZ>(*fmfhG z>o%g6g$hx7Id?!t2*VEgBAW~F+(!>RfIg83LYz@5V8HzL=AqQEXjdSCf(jT#(6Ru5 zGJ^$#-=iZRvgCfvAU@oN5%dGpGl;+)XhFY>eRp)~Ghl!i-ObVG2IL;-#rKFB@OzWK zYoG0PA$%40z$FI^KeM}qSvfYrdIEe*s#hYI=ZPMCc=;7zPy~?2^m!I;wpmQQXWKyZ z?C;`~U|yWB^c`j!IjHA^84YuL4JoK#YXG7bn0Ey80{IFlkS%sJ6ztO4T=Jh48kq5v@E;5exPFbH!2&KH~%hkmMz+4ct5NBqFlB#=J)Rk*38 zNk3i!+*jNIW)lzqL`6V5qW*Q!0BA5gfuRUt@`_+SBMq<{zV~C7(3YzoAqTmgVBc&M z5w6Eiz_?_d{xG^O+>e5=-wzK0`ePP8+>G!)8STQ+&xv-RGB;xW2>gZjo1Ben;*H>5 z1O5TSISZ#ljgEc<3;^QCm;!4A;Qw9)UaBPaW-vIB02oVQK+XL7`}$0P0t)2KAB!s= zCLjQiHxa}DAEUG%LI6KM8G){zBgGo^Rt6CuwjfVP;PSN)n;OJnOyH=-DNCc` z6hAE>sLj&vp{r`3_ zA%VgSW=IE=4`|UtWDEe@XWD@t!wd}jBS~Z4VCI2@4u6saJWLdj+|-DkeS{SB34r9L z0PIivZybRLf$KNH!_QsYP5Vd%G!6Sh{v+?bihAS$=+2jcX$iV8njRnyMfQO`bK6)b zGr>4XfFTx{IrF36KhH`apMR7CAcSCUH(o!ByEDT7z#K7360>U~Z-MB5JiI`uWR{pV z23Z-2^c{e|(#Rma0oyXMW}T!6G+I^skd8~Z}8PH0>nB2`n$*yteX#?OaYAm zI(`~7K=k*AsTTXm7r?ZXa|H^p25T^Z&loL`P6aWTWHFuL9t%6LNUDh%0tw*QPsY0f zF}YMa5(g<<{NBg|5eQ-ln8e^u=<)ZFDFC(r3vgX501=Dr2&ZNhh7|qKDigmX=DUop zO7uNGk`53( z0)Nzr?tAp=3*zSq2B23VEfA)g8pL4@7}PjpPXQi<3D(MR@;?zB`IQY=PvE~PdVn|n zHBtfw`HkRb<^jbG%nx2D7*#(I8H$@Lz*Mp$ZLjHvPFA(+*vB=DW zAD#Vqo&h=fqXqyG0b9o4_v{dO+|W zD86ni0PL2601QH&N5F?VfWF~V)+@y7PtY z^W*|@@-qscsX-iuYydZed7BjaX;WZKKUTx0mulTCynU`Zjc+`rX7&q9QYN9 z`~~QV?EM2>K>J{xfA_#OG{6DS#czjY7f?LF3}Juyo51@X*qyMRIRtw2>l?<8!2xUp z^dxlloBux#5=c%r6bOKVuwVLqcW-i<`2>jbjSCR)zm}&TkP_fD_5Za~O#}H*^DBRV zih2b6Oei1&Qw(%rG%-LNicA70Pi$kMECb^N`a>+T6D#Q-x9wB-0dUI4^Do*rK{3IK!v_R{f;*gh;bB=287wp_6`j6(hJ;;TpL2$m@I zPnJ6b^u+hz3s`BgEfZ_jNhUypSJb6qnNEEm{2>+s{7f`ZVsZia`^zE(?#_t&r&uJG zQef=v<`Hw`JAnIeWYt{$vYC*d68{7}qyixBPc06>)&HKfn}h4W(#nU3s^VrJ^26{kvIVP8OZ~P3?bSGy)8QN3H{~Q z2f~LQnI23)mo0sC1E(UPNg5sa2LVz{^PesNd%(4CN6X~>3|*1>yskgrGz^0Iir^0Qm?Gs8x-+e>MDkYd^Sx1N$FG!U3lY z1J{NB$rIS_m~lYfegyvRMIbQU{zyK?ikoX<5ZHyenq?aZBn?XD{YPzdce*o)JV4lK z?*4>|e-$y=fSJ=s8o-;6338Sv^y+XHoro~dM4Geb1oLaU_rVQxwj;*?h5zCOqA|8a z8YD>bC7_>!{|c#s%mM;XQYAIr0cIx*FWE90J>j`Vu1Zh zYYGWK_}a38{~XyDH&WI1w@HJI551weG~Wqc@R|Lqz*t0sRi&-g6Myp5&_#u1@u_}Wg3t&K>UCT*|a9Z zE~pBQZs339HKF34Sq88hHK3%je&?DDMhXOd0_g?%F#=jnAu0rlTxAi`0p^}9fEMVn zQYBMRAsKKxLjds3ipf(d()N?z)P};=UQlvcF2G>#v&|X-xcb2W018cYf$a-)Y!ddz zQwuHZ91wFCS~7uScr!(6(t?%D{TUse()36DfF&ZFoT?~`3iJU^{>_PT;(P@@-!NbU zWTUN2Zh!zNm7f!~JfXl1;0|JY(+J>A1c?Cskn~C-$lNslOerAgfFE6Fx9#w|0fYeJ zS6QUYb;9=%@~PkzgcFPdVmYLt*b0OI~Eiujd}P?-dx&R;Y@6|(+g#3gd}t3XII zbo+gqpudOmKY=WOLVjfpz&QZ?6Y38U;oDXu7w~wQPEG8-)CG1A#jKPS(-QLr^vWex z8UXcF1Vl{#_05|7B5;kV^abw;a#kX`r}vYHzEe=F{~91VYb@pCrzgMC_vSa)$35hp z{u)6NK@18cV~e%WuhGp(`M!%@{-X7maK8)SKD@0-{Ej`w_*a47gCToBdU@>S#NWTo zZ3V7p;6Kn7B>GRx+%PPjLvq4)`0tr4(2@k=-XwmfJs0Tb&|X6QAtHD9{-uB!7lPj) z;E{0?Ke%~33dAj?epbz#&&s=r;QyB3UAY2PAP$eA|MKPssP_GMh@t{+{{cHowEB;?DeZhPc zvLy`u=Z`wzXU>Yrt6!viU(XZM2VyLro+{z~v5_uddUWbnq|2vxUmFT@?)NwX&!5nJ z@|DDaLhmDh@#f<9sNrc~ynkyD5&r$a|HZF(_Qwh0M_BylG3P~wdzu84V)_-`A0_+Z zsj4wypd9v!=?6#;fb<0LL&WX2;dV6eo-0(61RWxwN+6;dg6k-9kp2X;_C8r7`UV7N z8MXH=sr23P4Z(AcAag}2e(s*{0;dmOXwoN-J%;610n0;QZ=YP~BD$)B8X_bQfaD#<-@8Xv7nH&q-GgD6SxUrv6*AhOz z{Hee)eoO->Yxw5yeQmko0jz=8JH+BxY!9XYB|dOGt z^8VfcM?I7Nb%D#-fp~vHdckwRZ>*N5EPw8w>FhINclqW6-1R9Ai`BWrQR~+_l(QOZjfDKeZRCOgMsy+PxvL@ z7wm^k4*DXs`S9G=@Fx`j*L$o~Il$FQgz2|akq=BBz4H}G?WxDthQhq`J&tg|=)~!X zT=X@7Y=qq-{@!3X)A|95N2h!sZVz`PcV5lGxy~F)4ZiSs4d~l4{lP&96v&6^w1*s+AiPe9p*<$~ljuIW z@-5Tft&jk~oljRi=Ig-ajIoFJZyvtD8|JXLEPn^*%LMQ6^c_s+TV?~CXZU@GdB%jo z2d%CwfFzDXNTT-<@zfDdnat~L?$fLvSyf@Qf#jjfQrD6dVB%$>g*WtxA3-* zcY?fsa@Y5r_vzMSU)9_f*ev&Lg5a1gOeOHj6HzPf9_OGaRHOt{xE=|6B=*77cG0kO z%kZ^>^1g1E8&I1W|IF`RKrNZXAVk{3H?^iSpfz?s)`i?YjAkI+Q|Rv8SZ{oc6_D?6 zwqrMt{q@ZX`(j^HFT4%i+~`V#+d$yh@KbIRSB0ROPaPg~3G3Z(rSDR=1l*Y1N^eik ze65;UEIpue+OH{lUj*2uwa*>#MH{nOW#0m_#y(8P`e7>Tfvr6YN$yM5tjVLEA?-i1 z?g>`oZa0ETH-4^L=}>MT{q1D1q6dBn+5C~`-yI4JLouhjJn8`I1m%R=hx{Srw=jJ^ z0_~;Hhx;8ES}t`D-+G-<^m3nPG-g2A@R*|`Y?9zF5jk6|JYhA(^wGNxG=9qp8WC}$ zh8TV8hi?BC%ivc;_C9*h=RJJsVV<&jM8W_~Gvb9UL2WwPd#1B*E5HB90c8Bw>L)32 z_QV0f=7L}~|dryQdj4j_gN6FbPe2t-T z8317JS?cv6w`YtH*}<5|kGQ&#TQUgrJ8We1$m5MBB)-|{UYo8HV$+ch$U=ZEcYBV& z-8AL+a^}s9v5@hJ~xR_oNia{oH{Y=86x=LdAJTgWUZa_(5L2MNKzAIjIs&6d zlxY-LyEME`h>Z=qH~gIue~%9%jvn$Ms_>cbzr?uuN<`ujfm=+yQ#`#1?cp+v-t}@n z@jt+Z#o8;QAB4r0;J-wKU1l*V>3x6~@o&o4=sL6wh#NH|&!gj<=-ZY4NDn%j=um}{ zd!cV6Fb5__hI8c_19~>^I$DA%DKz#mUth6!RoVD}rwOVBtbtHAWb`-GV;vX+&n01Z z$XfgWgXN{D-xHyAVmZkORwS)o1nj77@6csllDB-5bY6mby3*x5Pm!npx`A+F@vTgu zH%y=T*3FsvrJyenf%}NSzir9%2)EsIolsk@@r;r22qTfS-XXW1K)F2YN~||U*saj` zVPNk6ITwO3GM@1zBT+44ZAF!cQ=&k=9kcTzPe1PK31;TysWwrU@SO!jZi3Lt&7XVA zaU1i-brXXzUxaRNg`W^$ODfOY{DV-#I}3H) zIYjLO+(6gq7j&9)ho3xF)Q1H9O3!~?Fg#*$oT#Ah>*H_0fK6Z=XP3yC9zsjzepGyi zp7RngxT8AWkom<664EmSUOx2ng1>k@@45(lj(wPp^YfeY^Gi8jB4GEA)#NeH9u4Q+ z23BV@yl2xkHak?!M}NA*SSItwcCLoR`#qxZfDC$u%G+|7Ck&N0%nlS^pzGa1X3wa3 z^R*L{BW)gmx;tAZNSAj!qcHr2Gn2!fX?p3s|{eCB}k53SHo4xBD` zIsEG_LiUHwLVo{{<Cwjy7j=?8+cq9@d(hPA00Y+0Cf4!mCZjTPCw4fcgL1XbdV!2 zzZxo%l-$8MCUoO``c*zb^vi($~^eptpQ6Z z525!KLJy!W$GQVjcQpmcR|wKQivo+P6-4WZj|VvC5iBX5yQ`5~GDOmM*vRN`2bLF{ z&r{|#BNR{WxC}h5-9S3gxdqh6%=}HW0Om8&J_V*%mi{_nI$hICU>sAFw+p?G&2 zW%b_t>P-utKTM#%Zl6JkICr?wqhnoKU@VPDlk6&lSw~DQ3(^a}3(#Z1 zF@Srj@cWMSRr8Y*(dWtY+}@S|JN)TwInf;y`3SSe2sJrW`_VbM)Dwbv&tm`XTwPz< zxgOQ{lG+Q?X9W7=J0UR*=0<16{t{DJS0cKPsNNvHn^pXLp0Ph{l~Y}?UM}cr%i#Ot;jJ{NLFcpc9Dym-lY zT}_(O$;&*^Gj1F3J8GEb(l1Ud@5cVfNcz5;92KhQh2>Krd?f0JBfJC{4{05}<#~}p zHXnWE#nG!ui^oZ@5IjyuUX38tsEog%9!!w!?1mLgj|f%Gn~{!xAARH#RcYn-^o8@$ zWe(veMe~cB!s8<}2U6cpkWNLsUr3QcpJDO5-H{v8T;Np1i^3ve=b=XdDiSWs$0hl& z6MuJDvqkz2ag{!|uyTlUcMVV*E_4pO(f6PuEdCHFuY*XZPyaagg9W1V0`uRojkPRb zbeod#_f_yqx!xV|^0%}Mm*@WP06@+p{Ekua!t8|gc}jkF5Kg39D16W61Ki*K=1PAB zKG0t`SK;ygcSPcaue!Y}2Pk##r`I>#J00B;!o#DD-eN@UjDBwzGSBtz2!y9wyDJ=? z?t|wW8w-OP7ql6S_$o1I|XV-#HCNWKvKNEyR9aJ2Of7jT3o1)qIfr3+7XlJGs=fJooDce<{e*?N8I3eOsXIC@PTW$Zn(sc z5w@5jKAWlwrW11uk$dFVM=x@Q0k&h0M#$k$0?}9?u&I3u%mg;b0%InpB$7{&;EvwE z7zyv-;nq@P#4NT`<}S!0-#1~-JyGu-Mprf?YP^&H$fF}17~75BP`A8!EEppXHLr{b z5_mm6uWA3|hKgE73 zv_3Rgv()hhAu8akQp&YNaFtF9^K-KLCp^pg8>i=J~P)ORQW-j(!@ z&hx+)sD(09+Yy&5S-$dvJFLU!id~>~lfDbn7m*LotAjD}ws@aHiBB5HQPzC=$r%}+ z^O0oV7w*aW5$|%EM<063 z7PjG3XPz*6(VdjT1WYMX=@Z~PfOl{Ha@^Y+dGDe=K8oa}lxKg~K4K6aIDS8CxhVcS z!0zcm<62wHt*^TfgZ?sGg*ntP11Sp zCaZ9h|2qP9$k{gg$}7O{5PZ-BZqPlAD^HSna^nr2_#nt7jw(ap9sK4N=Ki8!J2T_U z4~`;_RGk3Xs=90d2h{;W@`M*-vaIfR3mUy2iQm zM1Rda)RZP)HmFE6K!E?AuQ<*JiQTv3rY3_0DymdG#*7cVo2@`Hd^MEyuH3)9Q{nky zp{7K4b-K%?b(cduz>)6L77fgZ@3IO(y}IaM$Bj&<`hSm`6^ZY8-(p3*l!a+Q4By)w zjZ}}kl^OG4x3_uP^%2Iz#~64l%onZ@$s2YTvPa=y$XDV+i>dN`a!-tfcL41YzXyPO zpJ&DQLi*ClZ=|?mf4G=kh~OOvem`M6HSzw@^t{yY!0F-9_4D+zYbTWN5xWz%Gcq2H zR5|Wyc{+Uh**jFZ+lqUSaLGM)ZbdkqW(K-Ddg?jwM&D5n^CL=ykHq)%xpN2J27WIv z^&Q*rmc!^CZVTKK(D!BVOS#}33i^jy=5V(~_dHVLQ~Zuq`+(=+wtuKDP*1q;`w8^j zS=@jRQvIHt32?Ui2P*wF^-2KoRb|2-CT1_N*l@B}zDbFDKN-G>-!1n&)bQ}1qqi9G zy@wFKVHiFa!9(>2g#q_~r_-16MzNSz2h zI^9vpti{-O4r>Y6Ba9uGJzr_hv!UhoimX*nxZ{I3;CN@eyD+>-;MLe{mZu$PT^@Bu zPdi2LT3L@P@WoJf)GRnYH4FNWL;NoMo-O-hNtC@8;yFW5d1vkyt})_ zHTT$ZM@n~R#O;ZM?*#O8uq&}W8*B2v8*YzD?^`q-xhvcSXFOx%Yg$LIct<05y1QH* zA86yo(D*z@9s=s=dzVHYDE=7r@1~5;4%Y?diO+@Gu@D%0gTOuTeKcCcJW)z6@r|BE zScG7d4_~%?4KZOF1l7X_kM!J%q36@631)@Xks)e?#aT0N!@$ z--Y7kbEkJ6YwsPH-;l%$rBBkP8ZvPKtP87$y(_@oedrD6I*>aJ2F9^;mbz^S%#gvt zAK#WS*;|_%BtG9B!&WN{pQpE74)`Kh-Lnk~TA3SOs9g#1^8z`mImW`~{p@<*V>Mft zMf!WWuaqFKY>QKEM3QdbTB28e7IJu1GRuRX&a@N}Kcgggg73bsfA{QU0z7Xr`D|uQ zIq)EU2Z|T}Z&QjE4a|P;rXa4{6bY}m_+yiW@f11jf!49(6NrbWBOda1sQFN#!H+j= z(Y=_y9!cw11M@%H1&BBAyTCnByjKU~-)%8I3Fl7c;&$Y{qx&5{>wQ3vfH|t%k)z#wSNOZ&{;sc?CqB<)A0LPa?<37cj349R^_XltA)o~o7cRr| zz5t-llF%Ma%Hjq7{X!SkfPD7uP#4{n0^joDru%&Nz4Z=v71yJQ)Ck_oj=Fi~yza;b4b0Oet<@0q|o-PD;q%A$bqV zju%7W3zjAi{bbkhG_oHnLz4l>SonUi=K#~!V^2-p&h_(x65oHhUs>^3+6a_>{|zt% zH1Gi0C$M+>*5rjZ27rO@eqWM%?61=bE&!!y0e4eV7P{(d=92G+y%+Z)^Bvsrh=_^fTF~rFxT9J#CX^d48LctOeVn31-R=~$$0^MU^hDD z!dE@A1;z*n?aNo6GC)2756~13z=V0m5yTbuB#S?utKlQ`x4ie{0upcmZWp59&7-gI z_ymdcTk`J{*pnP!$~-0=b8e&~r13@?8@ij+0UE-jJ&v8Smw*2WU4m1S}t8 z-ytj}mgj0<%n9ZY_b!<43SmPiFHJn%^I9CwT=SWLJxa>td2})i;~-}O`EG%3d#1u$ z&H&~W*bWaUAP?XU)7%67Ah7r=OOq@AJweeTZchk^cZH+dsgzH*tx_j%YI(}#f$8Id zcnEc82)sApJlKG^GM>EkyP~)LJ2M#r*|jAzO;lJ9@ZqnT)htL{KLa?gk+A$Z@8vN8 zdrRU#7k!N5;E%Af*G*0pEZ3dx;Diw!yod4LB+^GH+XR=ZiN&7DB}w54;@$XKPI1X~ zPX&BpH@u&?9&tbJdfxcJ|4msemHjKk`+iHXeD+Flw~^GoY(D%AYk~)ZxL=rF;9tM` zNC7d{6xwIbJ7WSs#*^jO?>saIc!rSEo||VTd)wrvA0Me3(FUW|$6FN6Uzayt1(HqB zM?V63Ex=vhG?_eH_qm2%oWN$lzu}-SY^1wO`|0h4@N?i_K@>b5qFpT9S5W^LZ*95l ztE}CRTYA$Pkx?j*TU={6NIcK<_L1CzfIeRpuLazXUGN@SlPn$K@_4YQ5pn)^a4QSj zqmX1?cQvYp^O2q&O8U5@gOf%D1F)hU?|qEIrx^UsOm=kNYqdLb-NO=h*aHr%mS=-v zdb}>!0Ly{5EX;rOA_ey8g;o0TO=M4%J#cynw!P-36ij7!xnZuR&TE@BDe>y}Jx2J)ynS^{Ajp8NOHmf3AzZ0AIa(q>BL- zCU2SW*w+!`W4{nnh0j#74rhc(!4Fd#1IQJ6{Ey_2$fX z5BalSqDntLz`uF<(I8VElv<`=C^KF(STTIq}ecKXTl6=;!MG-WZhz1;Wx;P>JwB)Ws^J^}Jyp?@En?aYHOk^luV;uH15Pftbw zMTte>M3Z^&up=b?Y4G6DX07aRdy{;-ze z`!5}@OM?$9v;Z(d$e*Kq(E=%Pe*zw$2{YaM#mEiBHSrw#kLT+92>mVTJ-L7cT!33f zH2jjB|0Dxo3rj8X0?S|pkPDxiX9j%RpD%L2kbg{zC%wHS_ZIYiXSkD5@zmv0d++XOg9V@tA7cFM zj+<_W?0)k0rQ6`Y7vQJ19zpMHQDncu3gA9H`gH9xdR`=f3NDnF2geKO3*8h!Br2?fjw+y0W51C#r1w}4cI>?0Q}xY&Ifn^ zf4cQW53uwKhv(o$3x}86zDh=5cy|FJdo6hfXByJu<+<;3*k6ej^?(4dG*OrkTfk^k z-wW)ckRqRpLcn)&cG zc{cw&EVg6ftYz;MTS`a2^!koD@9{@61s?L>SBI*9QiU{Mq3vaA@42!ali?%JU4iXT zfrH<&-YH8y8;hmw41V7w5@6xBsjp4NxD#{VgZ*M`^2$24a0eG|!To$Q!1F2=uveHl$^*9_FpYa+K$^03 z>DQC}O@ZX7L|53iQ`k2o>C^jW>BHvc_pS=hqPiDIb*JG_D$6^Nl<%J0JwezxyQ@Lq zNa%>DXvo8nB@IPtpmBD`_zsD*Bi@Uvtqbf;ZlBt=uyvSFFU7=I&<(h?(ha zyj&3dPEA*)?;v{RO7coiK{#+n6z$@nB@~##3|~3k6wFPZA1c%1f10mr?Z!F?U>%zr zo8TjQc(g)STWXhV#+HQAELwtCS6^G zNlrLz>E*{JADcSghHEJI3H7YfS9qSA#=NhtIpM>Y7P?225y?N&efSahbFOG|7acyF z{XKkk+u1cON<=43jFOC@k_!)}oS`LWR zHPxao1qw)oEINfOb}*0n#^g6<=@a4RPpxWpgz72Gdv@k40lVWJ%x;b=NU&t{8cb)F z$2kR=U2br0!D$8}_MkdMW*ro%CKy;bXZ&s{|Ki*r(1YO3YcP4&)W6t@pZpkyM_nJb zgqpn}{_v-_Pus6Kba?v7*RMGQdfbrH2CuOUzs~$k7APj8|FjgGZv#&S@4Q2q;Q+eA(ZdN9 zcuV~X24LeT4eVmHb8URY!x!7*M>{N1)`Jb{ygczU68I;8vX$1``fZ726?li}* z&w;@q)%w7a`iqe z;oIOg;Wtx(e`?!OeO15tYqqS%xpfTZcSVVqgZ_2JKND|CTptuJ)a|n3(&66@myj1S zZuv#$5Pl>;0_1(2mlYS1*Rsh1d$8Mi*iO#n7qxn|*Wz+T2?aHgCjd!nIX%5jn8_KwV_5QDk56J4TpO%>Qe>hM6U*iQV zx;C#0zW7yrpZp8*YbLJ4z69S~m%6EuyoC9!8GO$!Uwq&jcDZnQ>~_RA;RxJ$;bUEC z^|Qdvt7X?NiMJr{(GE=&w-USUqVgIi{Y&Wv8vL5PfJwXwQMWH!8CcO@f#08(*W=57 z%N@r>4N1=~XnfWib_sbi>aN7);|P#I*~h$)c7ZtvyKzLkkV(7~@egfOQ6;RzZoAC* zo8kUl;&s&)c=y@2<&(~P0AJlYQHl4mJy{tZ`=99ctnnSn+t7tCb;GH^m5Ub-J-YPq zU289fj&4rfyLg$FaW(Zqt?;dy^{;XMxa0jCFX^{k1>Z@pp9-I!tbbMQ5$Ah=7qQ0o zBp7JikzeXwQ-MolSBW>Hu5o;y+KZsW3&;=Aikp)x8!Ae;n)NRx-mkcPd-`rgo;O`m8aXn)3<4^AH|1CHD6T~-KCxa`$KIh30-?u#yL5-9xFLD1 zoB4g|zU1?aTahI;6@e#S%u@UT!-K(JFR23`~lO;DuI=c z{ZU+^7OxfVwnP`rUZo%~(PKYbxI*qxd1mC1{WjW16&Fr4=Uf2(*IH3^V~{~AkK zThB0Gptx@_J+bS{yHRcaIBmXT?dJ-2E&d+3a0%a>x|X;v&}4%@Q|RLF6)&ZJynMj# zlsoEHb@lWe`OWn1zQ9>Tx-5d9gr3-u%=eHz`UA0+`stC%c?n+?p?hSzIuK6y$bel*#plDs%J3%>T8On zTQ|;8aM5{*YuQTv@ar{+@Hb7VF7v|2S0_(;opOSu*8>V%NnE~y{mk35&uc1aUHtLi zSfVCQvUru3d0BaN@$CYyxGt%k_)6eCVN*g5T8sT0#TnI$dbOU2(5@K)B4LR=1{ISN;U8>F(vLfcLU)XkAlF{cAV& z;#aN_{(`)01DXBfXfZ(IDY(7^rLjIYl!??CVV zdf>3VwDAX)d9(d@15??$-6pzu_*V(Ieex{CWqxN5%%lJ1O8#YZ_U>QuvH2%l#e)L_ z1CQb0fB!p<+f*AE_y*21vAz%Is@462IKP4e{|*BafA;A4|LWpj{pElA7mpqKH-B;Q z)^Ggfzxnh3g5xqA366(w6l0wqD8)KX1KT&%%bV(zO?CcH`RDMzK^%E~7#edZvs9ZaOT4Xe|7Uf>DvD} zP#*oa1LfNm2Fic>?+42N_Rj{&|NfPM@_+d2f%5ZD43s~3aiIK9?+sLT4GvUlKOd<4 z>lX(qPyE$DqV3$PH9J{f`5c zFJ=cSS6<|I_Fz3v12QHP!r@TlH&gncb>WZFMc-8T}5uZY{-#u;Ny` zE2}F!w{c!waVtR@KfaTZ0{p?hAjp{l({>fAP8hyu~vsy)Qm5vT&c*)kSNS z291H~kBfh7efH^R*pttCpMI8o*7+3m!e{Q>ROnk0bf44*Msceu6#yq{EuaPiu!l?T!|tUIFS$!iv}s8$r5`$2d9cS{YOBY6 z*t_(h`!HPUefS6e2NXkpXn**JKd@2%5GU13>`4CLK1|U0``>@?`_k`wAN+otMaH7^ z`_2cjy5F}yc>jYB-gob(ANcpP4?5Ope(*jk9~3`G-*+D*@B0txY`@1Xyw}Cazwh4b zu<73Gz5kwnul@deXn5M*!=D!3a~Oc05{lpL{9W(8-wm@U|E}=c-n+j|Sh(N1ch`RV zF1Fvk%QB!{z)|m8ybbg3bl!dEop;^4>+kT);~n=-@7;Hjcl^7ms7T*&?>6EH40dR? zIr?q?cJH0H-P`SV-hTU?w-Yv0cv{|h=k4%zLp|smLg#M^zv;d8n;{lO79+oDzxA8n zy!9LZoA#S;z4_)_{;l;l-(rPFc+0=(-U@FfZ@D);&hD)y2BGUMryhOe4fjU-%{Sh7 z^Nk|UZ@&44f5W}$;?bl5eY6$csH=|VKmN%-c7OekzjnXwz5eTrMe^&;>%ac>>+W^; z>ozMesEYul>^fviaICU;8EY^p_phie^Pu^2>F0y!MM<;PAglUUR>2 zul0WMntQEH3(hT`St+a6zt;JM?BVC`7X=*d=j*@t`7eHMyZ?Fb7e60@HT--H*0}$y z`m^4vKO6j6=hdIR`m-YI1*>O2E4+I7l~-SVg$MWi>|X7#5ngq#baD1X%@zwf>L_gh$G zf1m!e`O;5+`VzMEr(K-fOYWx~Hr-F%OTnszpY~q*X+XORyZ@b}wGhP#Mt85icY!^j>_yu}EI%zW4$LbzgY#1^+?^b(~<| zDB{@mi!Z1z{fT=%`APfvpZw(cpM;c@9R^!}Hzep8v7?aql@4_>@&vpZoETpZoE1*s*!o6CCcxZJFJV z8#J@qo9RC-{-@5fKYI2@*^hAg(X;N^bu_d;`y=kr${{P2g* z{Lr!Cepvm1|92_f8be!AKakr``^Q+-F>g{egD1Q_rDju*OC<# zwA}aH_g6T`2BLKA$N9VNyS?vyx5y%4QDzao+y35nzx%!KW^5GL_`9e7F8#aS(|y<(^8OuE#pbm@~J1W z+v%w%{gdG-|D=B^!8v=f_tcZ_$%Y1CgnQCG<({nle>U^0V{*qy9cOhNR$Mo*((RRd ztD5b_0*C^GILvpwP3SPR<2bY91dt7n*x{DH z+C@zNDxB@bd7o>#7SKzp!>ONdrZy48bg$8L%?{^xPP022JZsurV~vKmX*5pE}Fu2uxD)^sq zDwtevUc(F!yXJr1W`h5GS{SZ(ILcjba44(%9HxBOF{8WYIO)Tjuw`>ZXEQ{1wRi1m zdX)n?Qd35bU$;-9yi&Z<*2K`qbHWMZxMqo{_$w{W9OFE(^krL9#K8Gt+Ak_!y2~(q zaXD~am|`C2E_=)c-50EPIFodk7;^GRqeg|xbxt%fSN!7h@VWbE)4FAE?OC^ z{&W9D2}h5!#PF&6%zs+qq);=(@@L7X_0K-#Y*CX(_i6ZS4f8@x1~EPKHuuv65PkV% z5pDlTi}S}%KS@4um@V2-juXScS>hOrghi2s|2TZq)|@c?sKwdgrH?dMbZmWeiIYam z9NkAv^g-+9+_mZ&M>yPPt*canEIH28O#(dsd4 z>{(v}V>DxQ@3vI4nPK1@(ZB8932%4a!KBf@lhRUoyTumglA2Irt{6B&9A}ZT$ofU$ zH%*%dzKN;cTaA3i$c9ZL6P_`|ztxL{!yGbst3#`Mv;Gz)g_;Z|Z*(wee53S6&nAgo zG&p(WIne0(#s5#%dv@z_UE7)7dsn^T1O$>GDN3^JE6Mil8{BQTJv@fn(ogHZM34jt zf?xtjfEfUD00>4fBAy)G^O4Q8y@_*($v z3Ygep0VTzQ;zHqIy}B$$&bs`L;l6&%Ffj*SFh+d1r`g- zN?_x`)hi-Kb7BW4cJJ+(9SaYW_jUouci)@F7wx_BZt(~2yjuZBFD($#99iBueD|F$ zsIr45gEZT_2ilf*`2Owo_BI3wM>YtuyuD|1>_w9G?Jd(~yxisX&fC>53MlzFivhD^ z=d;OMP-8E8gdmIl`Zo{Xcr&T8({mSqS>N1+Ga=1j!vkSueWPC92QSOzEwd#1$>nX` z5-szsPB?-up8zM1U)sNX>GGvxm&)tgVn~B!0Uqn?)$r(trPufTrUN|}-7`uU9ssinM2(QEAY@~CWQ0lMrm?bIsITt4B7W5G z@mKfR86vw^>nl|tIn|kQ5s(x^YTpEvQ*q>u&~e1a^2!c-)8rO+J{3uJ14-_KihYt} z4>{^tpLBpGWZ6uXe|@n6gi9>b^Yw)w!%c9}=qM(v=MVHyUpO#A?rLvlJTL-|?ATt2 zgFW+O5ihoLyI>-qct1A$LjW=`aRMu@ur!t>meuDE&OLwj;j?=N$9i@jTr7c(;z#eM zfs^{=xwGQJHG?EL$<;r%r`xm7HDTp}p6heFe%6OPmgf%i*gd3yiNgDlOMW!zO>jXc@-`1IxgkWZcN zPH!bKK6SbmGFJcXb5@^fdN>eEw$qP3d3gHC|6rKh4pKxjG$(2)>*wJ@AxAFBX zRYozQWkUGTYGME+0%I?b41yGya!~enEIhI+fMvf|_7C><_a=bk5{q{;cJsYKOcX~V zCvI^|HPJ_9_do|_r-?sL2r{?Z_Ez%a&UUvg4E3MPCrOJ3BFMU}kK&M*h>#Po(d@V+ zpe(?o$Z;B+iPktA^;=6LLmp?>Sh_eXa3svwQbz0p8V6MowA|lV2PX9)PqIMP?`)V8 zLo|dL*ViVZ%3brK&`}7wwj20pjszohe6YUupr9$9V)?^^-L(e~M20O8(!jWSe;~UO?*7E2SVfHMP+;hW_wLra8-9qRc6XnbL6uG4nlL3HPC{1v8(OTQ#V*bZ^f+Oe z-Z3B^hY}xRDJ*r|Er4-0vSIzD6F3%xMy{rZ``&p0Af+0LE|CwrpF?VFKkvhk78)m` z+Sq>H(P^lQhd=&Mewc-%#>t9Ek- zCKM|cc80}vb1zS-#sZ-?%=7CI64>mLSXL^^3sC^2G)sBxI@83Z?1N`eXfKAF>28_r;f?JPHjz zzindF5>$hj!@K5C(P-Rt>-iTS{N;o4{=qdg$zAD@g3f|Q$<2af!sjYaQVQ}j8?m4eUSxakidQeow=Sf})(cZSaP_JPn6 zL6bjyxf-8Wu<}nV2FwY&_Lgz7ys>9`M3&sKC<$aPP7j2Z@aI-Qrc}$CJi6|GmvmVq zPqxdOkYrp9@v^00S@m|47xAv@xLp2E@a8m@t;?4#2_(gv5+`4qBx`nwnP$%R`dYAN zik&qi&BNC&b{Dsdm|$jq@j!b?pci+8KdWF=@}v&AcM>|Q{^l1XdS2MMc;OOzuIP zFP!hr-v<-9Qg3Y5AP-^5-FOj-WPM?FZp*~UhvnQcAaefv*16}o^s^w@b2?+G&+ngm z{<*WWv+LF&O_|S~?MJ}0N_pyRNs6qZ$okwN+=wJup6#A%&;A5oT8s>`6iVvHH&t9I zY%FIs%#PaX*Ur>u&6QIK)0jCqvkPA4Q#o@W!LmY_jb$NR&VxF4pO&2YG++FkEcsNA zJ~?Iau06e$nkFhI@@08y>-1A%PN*`7GB6Vs-GfDSTBKoavh|lj&UL=lkAo+V!=L~B zpmhZr2^{yOjXwSO?e^>Xn za=(Aj4t4~cd;9x)zZZTMSku~OH}0C=?(OXC_M@Mz2VH4uTiM)^>uu{>%WCZE&Zf0Y zktUcK#&#Wi?>>C*0)yK8Mh30h9mitWtpm|!+3rN4HSBJKX`I>x??+TMwAQWu2!V=4 z|FpRYVoI{KYN>^tpHk=C)=|D~B2b~IIdwzgWEGEgAyyhPC2X#*&9Sx1+Mf37Yg3_V z37s?72Xg?lV>!;^$JGbr{@OjOm4edy5-lxodTAkg>TKbB8(S@Z_V*9uYL~n9Uc1}t zPb_-(UVraEMB47|0;Xu07B?YN&IkQdV&>p_Pr=ZYJN53aPV}4cymV)Rh$*YPyS-sC z^R}NFr`k>d)TP_o@hT-vtCv9--36dlshOE(U_ym9?c*UOEEuM1(zb+c)Y{MjYN_99d~_30OzMp?sb z|HYagkU+UQx#HGC@y_pZ>SNyWT7Zf++I@cDhr`cBq1HN0txM^l+fdI%HalOWWQxcB zaj2q?!K*DX)xv1UNs%!6$tN&qLHV2tL=W^(4Wv5g5lh9a`q@m^JIHm-cm5e>`9k-s zAnN49dmri4zuc4$tL*gpk?eJjKlq;??7siO2k&E>Z;Mf{;h>vSYuf46C|j@fL8{p5 z04(>})%ydsid;i7E!V2N_C4cjAG&GksP)>8K0z9_ncgR}K6J&TYIN>$bx-7`=W{~W)3S@-(- z=4-E8yR<)kpl!P-_AIgdloIBkWLoglH^uZR6y@UE3%|c00u50!Eb%53^+A7O4qjH4 zhgg2|Dqx9pSzp;jsl@i)=7v<+zak_2m6t2t_$x2>FYj5uw8SY>{NAyS$syO%aY zyM#jRm-oDFER&aZ{qDsR@a8|gI5~gI9GNJI{T)n+DQ^3Fc7BVKq)=PoQa9ys-wE0@ zawc_Rd5bo6B=xhNt1ootM4*6YWm95#;`!}!kfweof$mcDqJ zpn(F5$MC`H>AR-KTyzX#?g5ulJe!0~DV$F|`7l&@g5{AX|IO(D%PfeDV#$51a0{6f zNY3=f!90}B`|x6-VxZ%L07+K8vL%kN8RDj;PcPOpkAaX$lx5#$c@bmw98dRL5jlqy zT{`JyBJENxIe6J&m`6pWK(OvKsh1jV8hlu`mn>%r7MnI8rhjGof&6hqOS|CPTXO-V z^~`Q-AN{hWq?wPMAae8fn|m9A#m!{^a&=Q27-f&%M4co|I1QFGG$L;1e1x9 zllw@MvbYlw>%DEEU|QXSzG+&77x#FlPYb1#{!C?(6KR*n0mB*srnO5KO-}FZ*ciVf zQVcelpk)|GYq0)8U$CN!AUse+g3A0g}hM>Bo=u4Vb3VI1Q&mw=vROyIBMrFO={c>rON z(rMlkznKD|KESYkv%yY}tp2FEdXk5RL(Vrwz(P=v7ljAQneB@z zpIlXuUjTiSOE$@v)JbmWdH7YcUpEMs6~hQps$(_sNIl)8#?1 zED3KHTsc3gt&1cc7wqv3z_W1T_EE4>vwalU$4! zs%0S+v8s9 zds{fE+IM zsw-z{kBbpI!J@G6kC*l?Uy>Vs>e5^YI0XU25eE~q#8SwZ@}zc4B1p-RQZT{7nzCcs z+=!AYyEGzFa@hwua-_2hlh7aQ#r{Hop$+ecdC=QNon;hE9)}HGIysRt+3{4v1)slq z-`b;%Y(rvaa1wP)nckq-!K|sW0rJOoERh$K=T@sBGsu!C)3iHsRqu5LwRcSfT_Kb#U&5 z=?mL1WW3{(=cmt?bMt^jiINsA>+^f(V955o)ySF__#`ZFpLJlQ#K~uGTXIBmTnLc} zl>v=AK8+$uOPA6gB~5ayD?ILHv`8-oM5~gGvwjpQ>zP%c(30dND6!+fs92tviV=(c z`ls7dSuiPqQCL`>-Zn%E6?uP%18(zMGNr^tElbo&#x>)4Zs&VPCmhz(2Tz@T^1s7| zy>0J0reZ{Zy+|-L$dDT6(j(LFh7ilaU4h=HA|?!CXmeUf(dZsnSdbWfIX@HBSoWug zju_oq^jipy-sRhF4@Wz^?*%MQz(!Y6I&m}EZ6P*-fmBhd7*aflpopvOj7{0u02A}7 zlvnaCs|;=@Y8FcrQTFoBU0=Di%m-E+%a)TgE}rZ+_cz3iv$(mavTPa#Q(;tz%lM$~G zM|Qs+=&by*6~w5u5v>UoQOsyNy!*KmTw;}nOFpBpQp~s>^^(*~A|#HCXl5)w?%Es| z1S*jOK7y5Dl&8*UlGHLrY!WRgqOAJs0n0h1mY}4BNK233yQZ@He#gn1Ti;KPmRc%* zbF2S;+pi3o^}DUK#WBsxw+8~t~RpY$SK!zz4@zolYgOKu;bs8B#|70WDUnWLOMP%eRlBW_5TV_&digu@WfL|G*J@J z*%eXh*S^?GdC{b~gLY_2yzfms#R*n<`An6TaVXavGhAw;9B}`W@W@qf83COWAvmso zCLesb?1x)t>^~hO#fqA^^vV9m%H|aIlSEB>y8f4dN~qt>#BEjt%r`pmDcUNK=l8eA#3^y9J~-vby2suV`1CB71x zqVduqV}j-{hR7sIZsx}(Ezu@Y^CS6-uF5gX)T*{<>Z(ORVj@BRl3k8>#28OJ!9UDt3n`K8)1YB0dZK$41Y^;fy)AJjlMOuw4dD@+CFSPS5SQ6iRE8+2lbA9NK z{R@+UG!r;?4V=&0=XP1;!@%W~W%YS`+LN;zh><9d`c*mHuBChgF_Po%>{jrh*7h7l zlI7VwpJ0181jtY;JA))|!Vz~`r$4j(EME7s*xmbxkH*Tzaa<1TEDA17lKMixRg)RUfhLEe<PcbcNA1oYuOI(^En1L45?qYzwUcJ3ri(H{>1B~%;C!* zQH6XE9G3@odr2GDRW7{l5+qs#t$Q-EEj(I}bcClMNMd+Li2L!dx9^G^lO}yj8=F!~ zCwKDQGz3efy`fF9tb*RsAyJPrI>=a0!DVRhBQYw{?Aa{Vy`CT}Qc` zG<_rk00EKc?xO~A@d=5$1P{t0+I`9W4qmTN2T?_9&4zV`kSVkI85 zF|v>9%J%;B>Xu0nU@TWRb1#0BVSV+1AE8OhmV!+vvcD?gyy9TD70UifQHEzAM;|?L zWdb{TS!K5(2f8}~j^(}WkQ~85XLIU1dxAy;%0}K>SF;H%=UOKYdOPn;G~DW=b;yo% zj+k+l_xARVpDD1EiLE#OwtL%^lg{TnJkn!(Yey+?#Kith*ss2^&r^A$;#qf+Ae;XD z08#F`s#4!}fs2ytzUiZ!3dV7%l@Jh zIa83)0iE8JluqsVxw|k*lYK~&qef}j(s7-tq|W?Rq)1btjclH9x*Y>&qexC(?dA1h z%e`95d?iInGC0ShaB})mdl_!rv^43Z@#ZDzlU5*!ce{R4T4Z-#R5>BQo#;p^M^|xz zhg>&L_56CMjsi&i>bZ@vQWBL?VV}TAtnG5{Knz)4I1I1b_O^U(>6GS4Cv;FP9r-p` zy0UT!)$-iAvnP-yO_WPQ%YBHlY14;#+4=S?66HRMWr>Wp3MsRUGAb&E72eDkc=z0W zzB!{`g5>PXPVSAEsaNXEr#{Olk8G0_Q zn`X@UUUH;FOYdAsDW05=;xSp~3%_0Rk@&G2;^Cxqnq*9R2I&{Yl#4roo7x6tv`pDd1{mFxA75wJGud7j0bm|w`qGrnBqkw%%sn< zaUdi7m?|W#Q39EDi0-ryH7TAX1FnB)%iJ}U>kp>Tra|-J2YYtJ9};j1B-IFLkTv(= z&USwn?&O8ucK5e*7zf3C%GaA57$;R3IaEkeGMT|}wGN8hxV}m8Gd|}OD)kezDuvUH zg!0x&=G=!y3H>ax<08|k4*EOt#TR{rN(!L!G4*2w^rVdQTjt7GMi8~uDGIe;E_RJg zvlPmyrOl<3G}Ai=L`9?>OQe3nM+ZgSgb3M_;>cs7%IS}4E`+RU>64^SmQ!}Btpq|3 zf0VQ-Ue(_|;$>#YOZw*R)&tw+RyA4p7$EI#Ei}!wLCW;q6uLClPcBF4WS=g2$@@$? zCt9a-)eL9mj1^4x}?mNaXDpC^H{>C`wz7*+UJWZ%IO!|pQCLGAA2d56LF^N zB->{|XPDyUvmL=^XqR^fLDOVPXQ4$MWD#ciiI|V;B*#SgaZZ9bRR`(DLe?wiBZXSa zT>bA~u22t!k8zzG4~rA=SPLja&1$f32JIB*Y>$j7qX@G%ZfKny`-Kp z>%;Q^pMj!E`w%Ht)#TMHzrQjQdQRSpqDU*6#CZ zKpLAAHVaG&t|DjFch?7;g!5p&79=L z<$)_=V>)eb+&8Og$ve?;X_&IZdtyC0MV558p?0|!q6UxpfQRf%1gT2mtSsH--$J7+ zEDv285p*K_q+-&QlnvREI8R8IyTB-7=Dk6@48$a}Q(l|tZ(ZCB ze;xs|!JGEOt#0~pCg7Z_rn2bX&Td=R6g<{vH>_i7tE-PRPlUvWm=6*s?zze<35Q2W zygi$mrCjlPras%Nx^f>7HFCPuP->kOBz;zZ`OFmBJQ>KT-LYrj$^(I9aOIIcLuj1w zVl>ZdYMkhu6J^H-)a+a`DX@e%1(z$@teqBW>hhxa_Rqcm+e9+bM^OE=B5Ny1xxt0+@wF4qNTrZjwlN-QSJFXi5p4ZU=W}uxGD{ zGne+-{&5se_aACeSLrlPuWHJLf@#w1bd*UQdFqxlQ|-hwANvsnO*YeJTIve(goX)< z66WEq|Gxi`G|UnOmKny0;5hK<_>Px0;HL{Ry_ATNmw?WH-fWPgm@w1fLd*4yi8V|V z$+0P=O>}=)M34H$DyUX*rZYNPmc*A$2-;_x%1OMj*6B_~>j9dkO?Rejp?8){i%)H- zM{?XSDZqp%C2ATuO{MNc1ZF~&2Fj*}l|y8_>TvxCT-ry6KnHPCXxT8%ZJPH1nLhY~ zB09pz6_($y__5xOmO&v%wxO7kK1186lw4IooIQVld0u{*|2p}3Jw;02+=+5J0%ZGT zAFWbuJE*8js2GA|#BK)bJH$)4v5&aTqRLV`v7-pET8VU|1}x;jxf?;EulB>T4&Ax( z*7vTgbZ4QH-|HWbbm)#lO${mQwpUV2> zfpBpqQk*LZ&R-hdv=!nZ?)x{*gqxNc1%u$9V&QBSL}=ez->JgVjfPMtV{_f@DVIRS z#?rCWG{I$WPyQwyK1qbk&O=oQS&7CvN!&2*9@tD5Z#AF zj)V1`sOYSXiP5^HTU5I0a#+lkRZh5+OE;=qVP}r?nRU%dze>?!{rLXCk9vY6$=VS7 z{-{c^T=&ad+?+^nx{s-{?8ER*HB*}9qU`b`n38KEt{f55b(h(?65@NmDLrFww18QM zc(@8BC2G2@r7d$&CS)lwa|4tNn$(kyx~uo$OD@<_H%plcgs=IC6P;f<9`Tylm0bvu zbU2NL1c|P-ToN`GAtV^-B21y=3SBMbk7v)J^6n;V7}z+9mlL5P@f(H3gC`ZRY46Zi z$c*@CG#5%aQ>a+p{w+jU-`W!qzWLTk9`L#Y`NGq-YDt43lc4T3NYGR`*x(9=`@4I3 zZ$uO&TAa)-l^vPxw6ukV(X_H%-buH6>_GHip~Sg53W*21*Y9V3TP$cG49U`{C`3e} z%p8ut)}jwYf@x`^Rqld>h?IL!p-8b_yr1M~Tij=ED-@>*hAyyl(k6w=!S$XYW-i$f zfAwT&mg2_WzPgRW&9A)bMnNj8EF!=@L`(IR%m$T2z$H+oj6iY-Avv0o)(L)~OP zXA?@!9Kp#bxE?zxn4^06(u;q7QM~x5c+o6qXiRdnBd%p8wQ{t?qsLG?pZzC8uv`NU z^xd??Hbw zve1#}*)z|~KuG6v=17;c9@4?_XEwBV49C(Wf+e+<4tqnI8O%xL@<@g!`&d>7aZW$g zKUK%{ca?}~tSs6@PG=&(ET>hE312?>80zH{PnOfu(JZ%s*{Sk!+nS{oQ)ULi_t7z1 z>X_9%D^-(;8@}jb4qfK6(6RGJYJX|ubV$cmxGOFn6-=ITxuucNm}q>gf<~uxnxi>5 z;dw4WlWynJ-P?hRSy0JCG^<3eWFMU^y(>F=J}lvkN8WqeI5-HEO9+@|Mgrhucskfo zsh&+c;x4sxv!RyRo#EceD-VR+*;qFjmUZt0hvr8emUv@q)F&ZbmJQS~qr;^{?;Lwttj9kshB5iVEo?EI6;*9fE zw<{Wi%c$WLi}s^!UflhhjzvHG*k{?Mv`gb?mS*1PU1oj=cDo}{q`fPBi*yEw9c;e^l+IyZNOURNXEe|6VMv&$OdH`>v|1 zx*D7l0h&q9jPB{7Xqrv&)xp(_2)QrjKg zF!c`)S@!e|cLQvbw%LxD?vv{#)V&+muM-8*!^78|@WE8~Wdh(npikAc zNK|UE^Jx4#x~V%IUR1p)9-XK%v(Y#mCe_oB2(M0k8%_PD;3%QYvs=>l96YIMg` z4dZxpOdIXxN`h&5cQ$0b2jjhEv&>8gt&33MPW|xKaoaK9N3nD0 zf*F0+>pS=L`p7Rd%sTq*WA{su>N2M)*6iNCYkXCWrhoKU{|(rBjAi1w%;-843|rpZ zLADf}+FfT?A}+qEGhdMbk{w^({FxKwe#EYUt8q853SZ0R_C|ks4YW1i@(ulp23x*f zp24qwipH2O+?<5siMF}Dz*wWIIo8o2i=t*;2-i%Tg`7k0_2zs~6WUJn=fIGbrY9wa zYPj(bEbyC=ae?YAMNmthOHw_JuZgt(l%3TlU@TH6R9d2adXI7$Y)WY+e*2wdRz3@W z6`~G3Gi}EYIwxH*Gbs+VsuiX}KnB+Rz*ygqMgK~*=ENpCVV}@5-6-9bdcvyRcT+7= zXSBr}8x%EaG`;jf(t4yj(O9(U56TN}NMx1sQQ)+TW%=x87HJAT7qK5JoQ{=yDQ8p2 zOwkg{T-Z4@xEy+4x@wbR&PiyS&?()BRJsXscG?gauTitPNC7ztbevDhnHA8}{V&~) z$ax^=$KA0=Z)rYcV_hg3#yT-_6RN8>UEZlTqYR4GKG^H-xh}t4jWMNX`nQOS$Dw7; z$c-yML~l%qpcX|>Ke-H?7QyL(#82*j&bI49P>01!^jgo`2_#R@)EzXDI&s(oJUv>6 zhaR;PV7;STD4ndjVAjkK?H$W}5L)N&&^i0C(^tj}klEB-hbBX*rabapYm@>{-1BZ* zlBMp2IXlp($}IgNl+F-8D}3t2k5x>Ee(a?iMg5r4rP>y4ZBC*%Qx%#|s4ml4pSnIt zk#hPs6L#804&HJ7raE4(DgPZ;w{|yJGoN{AgIk{TnX0D7Izf_v{pB5{!XgqrqI*8NG+y_^zKiC*`24uqs;7VID}}af=#`r&7~B}%3`Uq zsJjrpcQ-asAF4Cug!5re){%(mLQFNi{PniRP$~(OX&{$ow%bz?<1}h6K}QeVDZh5g zm3L~QP8>*SAt{ty#CsOQbbkJ&`=wJgCV%OOtMesVrlR68ghwFBfF0ptgCb3di|~(* zmk~Al4EjubC%xnH%c9KqE(&IN-6)+l#(Ss5o1Ah!!~icpoPZrqvaGPorDM7wx)9NI zmtNc}=%~$YPVB#{iH71pI$Fx>t}#NVe7h|~l&Fc$*(_`>ljxCpnLW`3hZzMgi|&_p zxATL$CPt_+{=SZAcyorw2@E^V_LdIgc@;jD@u1&eaR zk9{E|M9=i_oeCgE`DL^QDxa^52ZI;2aj3_D=(5ZmUP-uIlHHw*=nPM)nqoxL;;Lv- zgs7}HsAyjFVqUZ*E)_FP@-6O$!A`tLJbYJ=`n8DiI7IF}@7DP=E(A|Wp@Ky_-MUY6 z9*>nx0^-8R9n?(yi1#$nqK4^QBM|O!JI?fw?V;Iaa-nc?I;6*|$?<(jmZC+vRod*f zoEf^MBuZt&$%pHYGRa z1&0psoI>gpGtS@29_ivfTj9|@-AVAM&om#xi@}F@YudL@M-1M(8(reh>mOgEPBYaY(+0^5bgn8QkJ(hZ0Fh~!C~X49W+ z^mFH)Pl$qdrzThuJ*YVtsn{8>%n)sXo)ok&O;$ zZK)fXpFVpQu^)W&{!BAT&KHRvZv~h7%?zB9Hl<{iXZEPYeC9ttv+lbD%>f(TOa1Am z+Eeam898#^$|@Ho>OOgT-FN*1P;nNS*~!odk(pgdZvDMP%-r@k>-(*`7k@DetbqNBK~m2v&|5h@+NXzGfKjWD4T7l z(i}{4eO*J_+$Y;pOqA|OwDl;$X3ydwj@{NV7}Cq{Hy_!!CkYY_a%e0hy-`m1#(EQ0 z;(yEc=B4_gtJYF~WpumkZcoUD3m;1kV`RTRbWnfrD1hkBl&gV?QW%Gt%6iWu7o8yQ zquz3Gwyj+X{F(?*MUlvhK%n%;Tw>-vMMS6n}bIK zA&-pHF&fT=g)5;+E_kurb_^V8k>rl%>2M1flF~`KP>wShd7xA>V?Hj6bb%yka3GQ9 zLUO&wRzrg-Id&dm?#>@+K>3@WgoU##jAR5Oi5)5_+mF&3BOq>?91=T_pyb8yyD6e< zNBX5lb0N=X>~7r+H4>@}VKSPfPovk6>G724(K_N}Xeims1a~xNHntwBI$Bl<<=}a5 zL5jLC!o`s7+wPk!p9DQxW31oYjZZk!MU7+~Y;Y{^71YKh7Hf@qUA~?xlJj4`DqsGB zY^W<2!yh^#-GubZ+>Yn!ct5>4xZr4wa3AC+%UpMl*5Q$#-V6nk<;HpvqF!*E%_gs( zye>j~;`;uL>)9i{TrXc7NHkR8qgb#L7jmC}p(#5kiPjgVk?1L3hy>xi_#>f%pcsXd zDzVi54li}_1wU;r+C)Q) zzw*)SxXkCb{bT; zfW#ipn_)XKV8_WF3x}?WbcV;18hN4mMp@wRY>w)n4({n8)J1N3;EVF^UGL*0R^mRv zhBP-Pq4N+CZ!XtM8XJv?mBCI3C}4~lNKe)A+dN>jjs-;epdTM8aFv~VOg?l;Eq_FQIgyd3l9Wo$Na^5u zrx`8*vAr&b8`7%^cMrYGmo92d&fsUep=1h!%xct|3l}oOok4CVa%gkjk_xk4(&sLe ztQTAr`Gj)aj}!QQS>qQ|DpbRBltG$L-Zj_->N*J6bD)m6yAYyVBXS^ z^2dqrZ$cN!;Uid2;AW&NF}j=!lkx~ZJR4R^mFC23v&`|7zpJKRHw z+aGF`jYI6ze(5gf5A+e)tw!&kV}!a@!l;b7b6&Zbq%VfwdbClYDXt? zO}R9iA#epm^p5`Oom`6X^?_QmqDg(NEdSpgOfx&>xFi)Pd$^R zk7-vU6G|IQTiPmNp;M5IXkoXwvD-s?I%FNO4lJ*NQxEF4n=i)CcaKk7_uVw zX#iqzDP-H8A}dA&hwhTHv9s|cIULVpG!N2>a_BBJ@zBPt%_%VWe{agKwh!GO$$D2@ zY+pHmkq}OwT4RM8`8S%lE#!;pMu~``M_g$tAvHyZtufKfg%Tabk-*>y7R!h(hkW9} z6bPI;bW`rFhGp#&EzaKDyLVp^?J-NEKn%p!!G&~}bk$=7Xy_hPNAXbaX*VSND|a@C zaFe6aZGPuKPxY=$Y5nQ7hUwr(J1k=^v`XluK!>+wJ)b~IoJvZJ_CRs7`)p9ms>mZY z^;*Pi^foMN1-uIZ?_W_1?M~B?vSo4PiuRylah@AR(FKyxUx-M}o^?b|W3&N15gcY` za@RzbAKZ*mIc_V2@|uwe3v+W)ON>^OD2fMyz;PyHroNr)*0*X2WE0xelI~4e#_mBe z$y?!?6c}?RqpKsebfQDYxY-PSD}|HBzOgM(`}U_$7voq+2p9%1Vod9E9xO_xO>H-N z=sUjh^_m&d8lxmeN-Aw%_d~e*af&6sx_R@fClDChS5-BV&2^IQ5%lFvXJ`5$={?{j zpLLkg(J^SQTn&cvmp3Li9&if{F%a<(m63c+3$MD|m|YhvF0=fH>*FNvg~u%X3ovxJ zhKb&I(5pk4q-!9(&=<&R;gPxBoq2KFm?%!HLP^u1<-%NY3ZrK-u3BK+`AjJ3#^c)& zs>xA|Q%Q@vk+NBaA|3OtpVW`DjV(=o^$OR|HcDP5QpodWr>o^Wa4=Evz{eiM0xwtWX=6{xYIMhhGi)1f# zPzQ34D~9u^8ZwG8nv^p%Cwq-sC;XcTH_;i4WOW_aRW9Fi?W8G@gEuZM!-f+UyUM1- zmA~mrUM>yu72E4uiHKxsqAK#Wld;LC=F-X#2oVOck|iW|7k^33Frd<8sn<x*D<}6+SOh*;5s*3^v+K%4nX! zk8LQ4>?9l>NG2?C010l24fW${&)#9L?U7s1qr7FKC11zOn9^-Oxi}_+9%yPT*)vGb zOCrhHnTpYT;u)o~**q4ua~Ba1+ZA6{C426TM>ujv4X@ zO2(}3sEHJ8iX=^yWotd@Qa<+f=3h2<0}Yw4=>9h)NCGnc0^^j>xn)VO6i%_`&T znw=;S66ZfYurHlei?k{ag^_zz2OxK!bVggbZNFZ?q^lRbC1d%1}-Frps=VRc~|1_iYiRo#)Yi&hT|MH7AxWr1ZSToYT1EMnJAc*jgro57obw}zVV`g#+@9=I1Fk$t~36&rA2;$qKLTYn09uUL|vSI{$NxZtu_wc zG?Js<>p%S&*!U2FqYdd9yZNW;7^#+#F}>zdL1G`xCA&{Xs01Bl59==)0aZKE46JeO zA{~GfoDn2?na><`A00Ac(V^=(6BW5WRE?rHSkuxjM_ZcdivNJ>_;|Dh%H&MT zCgo1kFrOwjwxSUwp+}-M%wrTX)}zgV;6h3kgB9Jbd>pvQ8-&SvbvwusNpd+NH5~*A zH(r^sOjW(eWEzd53DdbMsU7a~yUTjBs=wkwM9YXKMf(3pc^q^_CfN)IbOy2|P*w?) z6gG++O^i9Bro3|^>}fxfyh)g}5A7B~k0CU=T(Q0t-6G+|w4n*qxJ3|tvxLSj#@6sf zWVD%N;{l6QDhd_DdZsCPAx@Jz;~2nZgBsb1FP+HV<%{eUWT5o&7-%U05@K{oBJu2f zw8lkkqJ8L0=9nY3^nsP%4|-$3WUQt~l*fIZC1L8u*lsd8`ZaB1$T!Cx<2}o!lj2F+ z(T`l{q&7NdGfgAfJlQeR8{8CBT8nH#L^YBGCv8I0L^5V8T85;JxfRPvocb%#BrSjr z$KF8u;A0tt$6(1k(?_tU!#DbfCgv_koCA+jg=nG!&;lpR3)2BNQ53Twd5DYquw{WN z>p5X3n{sX)+|&zGCy==Zf=X&E=iFwpd%niT85=JP$js!8waW5*wwOEyVAFQNOz|G)FUCXm4jR5yS93_yrHom|*hG^t21g>L$#a&2zNvSd zoH_N3h!MJUTKf@p<(W1%{;8PEsX>Z1`(eO0x}=3kBWQZn^{KU|9g}$qLwXqGE%F>X z0G$Rr%aGu`Y zyXF`Th-nwPT9!0G61CA-M!mQ4_?s}Js|3hXdyDqv1ZrHWXo0Z~4JSEV_mRpNQ5^e0 zTHLbk$eHHjAVn@fvTny@#fa5pg(J7AVl%XwB60Hw^k^Y+dDJCWqYqHFHV>?{T|Ez?B(3cNN5D;PzL^!GW?ebMUNY-typcjeBelu0iAme3#P+H^u{gACqJwOFrrafri?M)NS7Y@t+N{4 z%^L&3seg1>#u4u0;!5|M^l}`TQY85>6lvc&4Q*rPAV&@jfZBH-L_^9jr3cdFp3r%8 zJ0!fdncN~w|GvELV7Hj^ z#&siR&u-;<{bI*ejyW2~>ZPDEC+7?kbIKPq1@ebOS!DfU=v%%c{>0W+*<=K}Eox2% z9`#e_-a(uCQu-t!rFpXp@~kS291!%OPq%rk&(zG^J-fAHS` z(#Z$ML#?!AsikLNPLOBnn2R@S@doKqzjC!xC-aQZG;?T|cKFe`-!{ZMUSBbKu0Wm3 zIYsdp51FFbzp8r3CD_vdYSX*p(WZCO=^a!~bWVgyU^FDkDowMz`+%e2_?>sN9P&V4 z<*0B_c9(zkw(xYvhW9&fTi!H;=G^aXjPPiDo?53$BeA`OrAOJ6Vc&1Qp`karn?>ms zwCzJJw!v$Pp01^A*+VEJ-}Q}+pizZnh{*(asvXdgmO(X;RbMp5Fj5*R4*l~b-0nU= z6!SZyF)3a;8bhF_$s#YWs~l2r*)rkX9-|e@%NzE(4VkY@u)f)%QEIdzMU8vudo!UMV5Cyf5IKW1 zXDMJ-RX19^EZXdeC=n`!l9Mb}Oqbiqm;U@3I_3E5#txK9@ zn%v2l+dlRi6wEz75_%rKpzyZb?0;lgc^(0BOk+>s#c9ftO-+85La=m6<5?K60F5)L zkF+VTqh2W;5=l}$DUl>GzWe8Ptz4cNBQR#fMHXi3SxJ&YN}y5%Sp|(IOU!Tm8#GES zISVI>8JC5WJLbcj#2sCKYE&{>!y^g!H`Z36S!ym1-HQ8(rAgf;3CCo%DzP zq7Hp11(-x(kSzy;yHMk(U%a{F2cF_k_s*ZU9l+9_HqxVP@1X^dJn&(hH{H#}nN&Y& zl6UysMowqBAKRpZq-fHCDWKp2;A#@;99KPFycB8AnQ`s8aSdu68$`q*AFM%@C#5_-tK>lM%XBjD{0pGzgbF z=E``YtvQ0Fw9jw8{rcacMp}}be61dmVQh;T=ZKJ0LGA;F)0;mnP!U!{i}cUQYqlLEjJm>narN(qZEezXRz@$}A+nPW8NJK7I~Oxr(|ZYo zeX5S}Jdp}1U9z)K*@ty)1e}Hv>GH=F)j=}pMil6tZZDUlI`sdwp(Q}<(*=e({0pp z9NRhp@2B*xLy-(08*S2nXXHbm#M3tAb;I!NO{B|h>m^UR5%Iaov1IvUcVRiz#yNMH+6@w1q+({yztmxrjfxuE zD+H!-tp$KWL>E5J;)Cf>FNuXsabu$iBr>AZM)>d$Ff=4)3m_eIFKxv)FQ0$$f4%6k z#)*vVDS~4?uSSn#UHV~DT8A+nu& zjHZw0Vl-k#UBrjZxs0c#bZAd|K*1Cg4Tf0+IX|=UY_=y?38caB2@vtokQSHR=&?nA9`RrN@TV1QQ8+qe zMIIh39>BloFRng3Pw`|#oH9Bw@X*i6jRM3P4SP=KWSfPJ?YTE5N7X*6Go);4F6UY- zGG;c~`$gmhrj+XFw&PYBEhGvMxk?^~0@?f4h3-gQq~|IQWGF}a?+4r1#r87U6HM+L zhz|P!0_of1I>%vIi8m90qvzG2B9eEB+>2r@q+#yCe*Pr_*6l++eBeiOAu|)_Q&A?HJmf znxYZV5@hO-)*!Q$583Ms%GlrD9Ds4{f#cPN!{28A9_x@rkjP}4?*15ynS}0G1}8fM zGa2>4 z=#0h3<=fl8X2+iS?O}oqT-g137h*(%1O>g&Fmjg%${*S2x0rjQRCd_baAGgnaTjj< z<>zH2$&?)3g?y^|$U_iuI`Uxm!zK_I3G4F1jvjcLp)Vpmrs8Nk9Ooxe61f&>m@uzz z^|v;>vv)0WT#rrYlJ!P!L%b=A{K^zL z1tpKOxTmDMOwPVyNOvE&SR^Yhn)4JUahom0f{V$DMYC{55wa+f-QBo9s*RyK7CG3P zg8)gGrV1wQe|Irs@ylo>*bDf`c8CFyrvH1cs+yVEBmtc8|Z+%&Ri_|1U`v(w!N`RYJ)>DH0% zB*uYP(J+0pdAJ|=D?ewvt;09a3#waJ#|ID~n3W^tlzT$x};^O~{ zZz8wb`5U1oO?=Q3^S6sgiyD{#vGjE zzU1yVnFe`uprq!~zW9nZ?Q(XXoQ0_^AN=*#>T5Q@nbxhajji#isI;em(muAmZ0!&h z=}I0olP1)h;jI&1_WGOXPZ^wx0io_P>25qH-BI#_I zXA8SXe(Xlt9D>y4?67nG#s;^(6lV@O8)K&+GwLRv?e)rSc>v5`jG-fpo`EMZua%kB zesHv#Qr51W^fC=3b+x4X^LRIlhLJThI73mT=+iZnYM=A;y;LfXuqd4)2Qqb3hoZ_Y z#7b~g-FD51LbESt0Vvdmd5J-U_8s;VV7HzYEK{)p_jEeu6~)X~73)k@@sa@A#H z)0(6|QRi}LjS!deUc_ zbR}hnEGbP==VHpJegr8~T~t)NW&j(utP0!6?IxFZXr8<)4_+(-W;T ziY^J-kk2+{W)Rzy*~g4UOP4<_;cx$6KW&*SAxo6U4!T^Va25$1k6bj4@>v+zgA5rN zoHiKQMLH!BoZiKiINi)%bf?F%7|iWdwhz5nmUJ?wd9F8D=>|Vzuk!S~T|=22A${v2 z*WJEjEH}2bC=#Ybf^6eXBHm;!5&6*~+VVupVS{@=}9#t^)51qEb*A7K;R57ywq}xHd zyt0Q!<0Id{yzb&jd))sT-5^(@!_S@-OZnEHZ-#V`FQEFpO0bF?)K7R;9~n| zJ2(=y{7xJRN)Eq}E`0`BwvT!jRx+g-^RK_N-lOvy(YkRz;d@^F(f0yO9 zq-g{#SKOgox~3iNUYNN8Zw^GcO5EGr*oG_3y&7UUi7OT(p6GGO8&r zUbqk`oD-vnSpp>8%g3tbF%4N%w8lI+2$|*OEkMz7CXw$Pz~}_Gdh{@JQ7`!-lHigo zMU)d3@0QplJeHT|QaVvLU!1;pc>cxm;@TjW+tv14o#Gi!Py5c zDV03rIUD=nc`L?kDbopVOPR+;>)X7TV{HzFho?Zt(F+gSbZT42hX-v@4oM%MD48UU zW3|%cs1b|C$l%UV>vTi%vgq1lXFD5|3n!JtIW^E`bRbWEJB)8XG?iAa(za)mHVvIM zD=Hzzy4{N!(JS$=<4?0}qKvZBgl=_? z%(tPfPkyt7)@b$7DeM42$7`Y?WOXk{eN<&!_t`s~SEj*bgvi-~7(M02wUGVyiH5t# zk4He!!5f33t0JA&1|ShK>vv5N9Q_IT#D)0N?%Xr#lT=1}%8l-lBcVlx+UUzoZv4L1 z2g_eOs_ha;8Qb$QYvcHh%Ob!2%Do>?0EliM{?J!71jzKYo5aO?5+KEkURWhn4$XaB z6X_SIkOv~ZsDIRe8?L%g9YOGkV1sS?~=CG76` z=yY3i%=*Xr$wm%H&*geMmP*w|+ED4*elVhO}N;uI(5BNz~M9 z+lE0cO*$nc*+<97a;+zN6K|E#k@9FFr0b8%9hbD>bSbRweL6v!4<}CI$iapY-Th~o z9TOCbCMcpWYBaa+M7gW}LT}7A;$zI4ZT3hN9~le-Zwf0RtMn z&nbw}CR~za=Dwv%LXWb;PX!|i6i=`_erb~9w+)N4*Y6BHf5uQZ%qN(%Fd37(2STj0 zeH1LRr18yD1Z|Ke-8&p(+~gR}IfatDp-3(UC0dGfcgQC$WQJSg45dEW3V$f%$npyL zo9N`ojGI75RYJmq3fy#vkszcV`@t>eBTdE;4{p7L2hLJnJ_apjo?E-j>r8a~LyhB22@6GbBe8$*76c;6z=D zYEQF^PquipBx%EYAxX~mrUJrwD2>aKA(vo7%a0Nz6|rHoAuy<=(##PFQiYB_8X0la zC7a+8Bb*+OTPs-D$60J<9}7p@kN*W4T-p@N3GgU9L_$>S?{#IeVZJPN^r0F%`l0}` zs@PHEXTUqq&nb~D4Sfj8 z?A{k7LWc+9#cUQ%e1=9n!bQ2#%E*`zJu181YcL@1aII_1rHKQjsi#4G?%AVdQO)So zOb(k-dE>9QSLIBDgumXN4ZXi}`b^6y+1+jQLYFNDELuE-11C}vZ(2D77UQKcl0A}r=YHuv$7fH;`gu7A`QMnzNP&k_=)CNBNd1b@Oo7caUGcu6W;q-QdEEs~@h za)Xb5bXDW6$*rw4t<8Yh+sA`vl!TFvi0~MVAKR^y(cIBV8I$3Pbwf>-`923APC^)T z;(CSDjG@s?XgciZ1o@qzZz>qv2-(n}*T-|9p-UV^jP3ym7rGMC(qOSU-O(moJgB4k z^}Y0|dD4#tK7)W5Uze{ZU&)Rhvod1Dg=xJQiD*l^mxqmDxR!RcZ(quDmQ(F&$nNGn z(9!&u8%vG?ONi40WG3NEN5^n!jm;@+svvD#&&MG%^~Un`m@o6M>I6yABif+{$n;`B znn6a#pD#XF%bqZ*o4Q1j+BPoosWd}S(8^+LbIwc*NE}V;JT0GX86pECcf--H8lWAT z;^QeNT3c+N%CgpfzkRy>nMTckh?W$+xIEJ15S^Gg{s}db<)dkM)51etWVGcq1B1@c zz>Ee*v`5EhBDT$dnQ_3O+xv85x5$1Z$OTIRkE)V%GP_b7Y13MONM3t~C+!!xZ6e*9 zI>{G3BNFNU_>)j@6c1BNcI*6Mowcu70pUKpWJ4^Z-H3ibKCoSqTQ6&1J zFwtXX5+FfEms|Ey9Xoo7d+rPf8U4fEL-KLxaaE%e>uWPP)I?|&xtSSE>609sxcvHE z4q(DjTx>^V$MrmiC10C?i(N3I!Er~p$YowcbWAJT9V6*6?icNX8~=EjLyR6ZGY|2x z#;v)+lH^QBgeGbz=?u=FBHZC+LyV}CQD)h_>|r(18u!G9$c?%SBg3T5qRJzECFv<% zUz#91{`VItkF;s+n#z^Iv9_7*g?x=oY{D|u5laMYrssY{Of*%R7>6hF+enTZL6Cg# z{CwobS#%6PnKt+Qd2?dL(H1_YdmY7;)*P3K+IZ&=8LwT=4rqwRxRYg+fTjIyKTl&0 z;!r|K)g@dhPCO%PJG0s=!bK~M%h74_n8e0t{o@MCso>EPV|{9zDYF}Mja*p=nJS=A#8xCv~Ly8~O?uSRq zjKaiDt$sLy7%fL;!yiAoDtXh z`$)7;SvAZ@3(19~vtSn4F*@RyoRd4U!0lzO)pE zYWtpTe#+41hop?U_Iyjf@xVs=(OKr$(Y#0hnmWjSR2#>3km*^2l|o6I(iR?9d0Z80 z%5u(Tkm5wEiXdhA(p`O~LwzJrsg96_$@=9~664a}4b(o6npnpng{YFo%XZ^8$clfA z4nDI13nd}Kg;UbhLwZK`Gg~W;J!UlWVK=bix^3oBQbcNWuSk#D4wP()U5hv6F3Ws2 z6V^{RKZ}(0r?m5N%_D@U&52JZk*c=BXc5snmph*LB&EWRA%wo73FOBzpgUl&DLlip z8E0qI?#G}QgJ;Ue9+OSYVkIF1PkyNG{%(PmGTXGCId^@y67cqMI9r~i1i`TcXo+BKsZakvBH9e=-oM6#4ZxnWNHM0zh$xgY$IAZo3WXX-DvXD^C)UmTv`v= z-NzM<9fgjhss(!t;smVOfxAE|#(H4z?216A&hApkN#^h{z`Jh9>jS!`U#u@dEV#Id( zfk4sQSjlWVag%MxGn(_{KLsrc8S7|H*TW4{p=i-Cs7y7R>b=g2pX#9 zu>pzF6QhB6qM#dy8x6^3H$`ojF#|xl)la9&M%Ou7V>BsFXbI`2kX?2bpYD;v*3TV( z+(&$ee&XiBbgApkal6!Yp+4d`uS12whs15Xg%o2BP1&YQGKI;u)1tMw6V){bWQ!R^ zkHLq6#DN$)AaZj<^^KeV03SXgG!*?!JS@9L!o~unZR_6CW=w}3Ysl;wJ-PKbQsyCJ zWoB#NrU`9Y3=I-)OgVCFL#~WXT{~FMF5@XrmbDd;qq$HS7#r6Jl1<#W=cg7gL!)%r z;{clFJ}P9NYGm5e)I1_zf{?Cn)W!z3c#~+2dzA-J_8_Ea(uwPu4=Z^;+8?qTJC(DIt_2^YPVlDb?R4~F{7sb+|*)R{`G`q7H+j*Q=rO0 zTS%CH{b?~|CY)=)aWMy*wDo=%+F@h=ed(hBQ$cID^@;h{K%^LyWtyzHgn*e6CZw4) zk0MJ;l74&`(0thW>-4Axip-+M^~_|OE$wBSEwQm-#Qq2?3KwNdV`w{!&EpWL`SzRc zn+M<~pP)l#1bbQai#ujaVA3%fN3*GYT>jb}BL4(9PE&aty74G5W0|p&A>9iUfyK}! zk4&pkyGX8fbK+J#mKTdVKvJ&ApQux>_IYp`+e2#nkRMM|fpn9|hXKp^^@%vrCbW6d zU^y&CNk?eh#V5lwQ={Xqc+%!{@5lDJ{+pzzQk8BmgDkwy4uQL!dqqA`|_eu5`mhTj3#tHo(IfBPy%GVXI7N)t7LFyz8+m`OubW8`4W zjcAH=^9e$9m0}ePZU_eB_p)L!p(!p}jPLrT{!h`T=QqfR{}9w@JTxs*o2Uv#Hxp<4 z%(hPbW9Z=6cYGXP;00+-DjD*`M7M7sgMJs6IV7(p!1-oN_X|!HW3PCdsT{%p%5F)F%oe z**F=430*Tg({tLN*#aWDp80pU(#Kim&-`p2TwtMhfsD;YG+JX78K(Ley&*$@d^)}7 zNkp=d6g%-Dq3r|h-B-LD-ci+Pc~OgdiM!{ciyAwtj8nPOOZr1g%(;Z7MLcV52*f-< zc?1=8iTV=##(C8%iGQK8E?+X9lJODDLbI zp_sCJOX8uWK`%jj2ErZHC`JONORcbMQ>{osyV6fb@}i?Kgk7Q~I8}>f0ftS-ZX}*+ zA)n2l!Bv)1EGwH^HmA`Fr=q~E@TIk87f7bg2nEfG?g3dPEDHIu%cG3zcoOf45$EGX z!hAtMW@A9bwY3L-q{=4mDzwcJj7DaEJ%N5-ZJSJLGws-?c5wbh_$J2NW><#f1)hN0$(URhP zG^5>)$%)-U#4pqL)CY-gWHIqW^3P|bF}a7!L>Tj+u$ zR2F53=!TRkX1Yb5^pwUsURr2}lR+t*f4cSAr*70pKzdLWfxhn3K65J%A3?qz+Ozen zd4%FQm=069eEdV=oS-g#qPzgS-UP_&s7J(t{?J6nKpC1ZwkSdzZ{&`x%IONZTRH9Y__P(v!z4H zgUE%I>4Srin3)y$j>1LmwCf2!8MI>(At4oKSx$`VqAlkk!lJvTQ@d5qA97Un^eso4 z-+ZIVC$@9Ugc&Z(eGe3^&sxyb(|d#2e;PTQ!9L9FC@GPeLFF zc=(XpO2*#6PxPxDyH@??s1uE=g>9X&tdQ zFvcjuF21*R=rD`3)6TZIXt7h$W&XSe6MFWH9cOW1J}5rzq>CRdE2{gW*-vcPGpjHd zG1Wx_qQDSgaq8MdiH8P6ZqfgH2jJ5$g~NRwGc~PYh58IoTsAB^JG}}O{^K*{>AMMn zBE1m_i)s_?MR)XZE21r$QSH4@a5qj%2B~qW>*@cP z-m@D#MkjDfh9%O{&>wSU3c}(Df{wrNq$)*|p#??=((S-yHK)PcViL8fu?H2!g%c05 z?%5yg9gmVlh7&TQTJh;qdo9Gl72t4~E0brvbi!l18wiRhfl~i)iof?moW$v%8dAhK z5h;oW^>?^4jGC4j*x4W!6ru*24Tr%onQNJD83k=2)BW3?v4PJ{vM{jV7jsz=tf?P1 zzT8;vG^bi>U$>U^i*3zpm_{<@u>vgcVV*=0 zi#D_ZS8?J{#CQyLbZFYd*zO>#i;=&`(o;u?_@Bcrt z-uv5*>%Q{z2~{_|_Yg@?Vo-4Gw%V=nYOkK*KN`|t(hhNb*T35$BU%^h z2b>fiy~X~7d_R9r369)|wsPsRC`LkJ$_bZ-Shez)sEX=8pv&=5Y)i|%h@lUXMWCZ} zLpGbq)Qu)pSF^D-pPS-ZC~0w6PtsecfGMh9%-hJF z>1f-}W6~2LDJD_T|E$G%dagSY9Ik<{%yREC-t}0^!kan zz??&Gb>VNMd(ktAhwW!PH>-u7xEV+kS~fORYZ`4?yMMYT;7ohf+{I>nw9>z=Mn+jY zZLAuVMikAoe)Lb=sGdT|9H6}s)T#PuRmA)VeG1Ww5M-HCWpudFBu}gII8H2;aX<7h zH@Y9ZPsRjjV&p`Fw<;J50moMM0QR#1oDdBq8`{3LA|Ly8FcB!KPBjTk^)h?a6dB-& zBSW!u9oZ6_$O)IDK=VU&(;q6Q#!HNWq=Qi!;sYWjxiYyOYg~>KOJ&%}@XbfxYzQvF zmnr^x0)hr}JXYRJ8sdb0*uu_06lF)ll+@$D*W`{iPC+Uf+VoR-a&cxrXpa(l$e`?3 zgCI$QDKItV_qe}nDfJtDvD3D-E`z|)&V(%RQ7Mu3X85jZs0YhR8O<_OQ-^QoMMaGN z_C1nl!cJT{Gv$$6-BxuSSanTZ(OH?a>5NH^R6&gY)@Ug~rm%1blK2m?$l7sd8@Jzl zk~sSDZyIc7wtEd-(t$E7(j$|(QBbP19&Tb+{$~wHSz{D9*^=4dNqnR}N!Y{_Tm6PA zUIsvzb=#sIO8pHj{IS1Pttyxy@}==+fFcQe3}2@PsndPEXlt0IMPR62Dnh~?*QHou zoq6G%k}Mg-lc7CLgPYX0T68{y;+gD@Rcv0X3yqtoFhYeCZzc?-Fo~QUiIfs3!~d24 z)tIOv#3FxuVdSTC1qAr^^3Nm zCt+!d6~s8JeKy2IxK2ccOWJuhiy}*3if%I$AL&yBeDc)3bBA16B!6d;Auatemi5w7 zC@b8b#%y#<@}qEbkn&>&<{UA;v}=`OBa3FbLe7*0nb=cX(rIrxf zMaRe;Xlz}(WNx&i9vU6_M{c+_b_t_Ska%2a9X$d{_ zI=WOpopz%mcAO&?Q&a3zMWd)k5v5c{1v~ad#~D^eXr|I zjM!u*F!l|MERwOv9jKAUD?aAd>65u0kwWXM!J4Fe;9GO?pejjszoqjsqUlvxS{ ze;3VVK$A1xNjuHvYm~{ch;}U6%M>CefYZt1NX62NB#X9%OyqI@{C?ufBt`13FI&2z z9gRKy6*^u2^REVz=TVYnKB1^4Ep*X!CPnYKUyK`0Vw_YK)KrW79l<8(ktH(xpj{zN zk7{#>8S)<^_OJ7ayVZbK#`UmJM!`FlzyswHY?&viGT1J4rQe6fCEWb!fBb2OIEf-@ zGA2745(5)Tf*O;#QJIe1upSUyPAsUwyTC$rmcgDhA+pE8zuh28@?Z<>dzvNDk{1Kd z-rGM3Kl$CafRajh9JHB)%!r+8^f8p%k#k2^BnfjeWzMu!-6A$p!GwB=X^s+W|4+>X zmWXV`%>8)WimWvkVa9*TP*yGdQuH`zN+zJP_Mvf;cv* z|Jy%SxHrv%F^&~_9m#}F#WGm^2MCu*%`|c}Vgwdezi+{g>TT`5KO%HAdGhbk3=txx zGx3sBez1paT?;2Cx8tLKQcoZNBc|IlSP_?wrh~&zoNvi&Z$vZ5h1_i3*#gPX;#-Wm+u=F;P z3AI06FDqXZxFdg~9(Yt%OPDN1dK#G*9{#$+B~HTMuvsCF0BnQ+sv9Km*#0y^dZa;W zle!LQ^w_{oon*%T8v3NM;iINSLXf1nQ3Wq}ppESKf2)H`DwpPFHcMV`@{s3|7t7@l zUSe?E!sc>|Z+;z>F&!&{=`npr4CxagI);ez!0A|EtpSzn~&yM~f%mwg{JXc!3^TEe8{OL!k`mm1+YI3gU3Il9ge4;8Xw53Ol7p7SNDmS1oQ4uy^m z)TqsA-D&eW7FE<*uYX~n+^YWg=bPj}9CRu|hAit|9NQK0fhm$fVdLm^t=IV;=n#JZ zGoxA#GF2RRR41uYQi#M!0aCD-;u{q*4!rzxXVeIERHs61SZhs#iIh|$``$*&4Y!T# z)M!RlM@pC4*bY4;kW+oE^aC7SCv~1tt+go9TGCjwxO|a>O4%E$2_ySCkFq`vp!2n? zj8lP*2_!9Ae25(BksZ!$ZPl?*v&0FnB5)C<)#OPuGvtBOH`|!;nn06_v^ToO2;@l~ z8dPPS+1_=kec|3U_Oi$EDjLs>g|QC3jf!=wkR(6BA&yNleq>mqF=S+mYYCX6;J#Ff zEuOUeaFtuv*s?IH!dg^0u~DIwz)08hcq5P}Az3QCaneIKtd@n79(IQxqGt?5$)rcV ze_x@EMRFtH(R#|T0y}MT<7Jj$t8WZSC&@wZ%(leDOSr%0U zLpnsYwhW7e$1#IPN{|z?cq-sbHk1($Y6n|IF>E5Ep4SR(RJR$uQTr8QetzYCIZEMC z;^Qw+J_AWoGNo!|tcpfbrT+!m&ae!ILS&ls8_if;BdO8-d>jOc6Nmq~^(n-h;3)fI zR4ilBXGTk@gr>j7Oh??spQ$oNEwDdI9a4%Ugh*DvKwGSEn9%wfTiQj9OzCfIP`89G zDRKk{($*-&k=x*1@L_67*aa(wpNwKb@*_sYgyZ^F1t8qa*{Ex8#}ag5pzQjX+q zH>j~Jz*Zd%Aku2EaZSx>QZwwV=q>XuSdwIkK`d!k%98%aR3zCzn{3HCn%<9pG>ENk zRnpW6l8O%3KDd&?@!)^`@DU1<;fF9Za-u;(r0kFxk{qQuTDL*l`Zo9Dxag5jJ-m>% zNshW|uBL7^^hk;lDLziv2Ny_2?0rj+OcgZ%%qntu6u`p@8QC5sJ*wnda0!Y`36F&s znd)o;3vbe{dE&*)+7hy8rbz2iZ)~cPMVfem&a&(pFJia*`)^K}SV*JIa1HLw7Wc@x z1wI0?(W+~pI93v7N~&arI4f#~kRUFh5qsL;OK6L>2cBcgyTK=hqVE5>Z#Nw6EH}l8 zmGQeRYec9GasSF(PDR3~e;M{eq57nktu3fI}P(0wa>`_tca-x@-g zqv?;jVvn1oDeRoUo)RU8xm&KPYi@;Sc+C}V$oTbtPRwc8X^Lc0 zCjHlXU;nEAYD3Hjid5?wX_DL*XFEgo#qNqP8S)7HWKvI4Aj!HJ#X5FKk_-nIXNoH| zXqTK&S!{*-zY3+$h%SfP?N(Whk!InQOrs<0VuIG_@r@NW2^XUXm)c z)2&X}E>&PxthJ$)V~4-A!f92;SeXoVw?UMpQmH-if;b^z)BhfPDexttHIJ1ol<;jRw_-X|{$lm9`iqsnoA1eh^IxMD5 zZ8Je~L%aLgzxeaV%hH&fv&bRH@d#@4D0jgn@nJws44MjJp-Zh1*BS@k8>23X51^6a zBR?Jutsz2!B5_(V45dfhrJ`yok9K2PC2l>drc>FpN@TJGlFAB~9n!Z)P%>lgN3deG z6`EDMfzPrpJnyVIU=`(zMHnW#Z%j!hGClK&TJnf3i|~ z1b9lGM8FU7z_lgrQFhJJYoz}WKC|ISXQW`V2C5vilItO>naTTYXksBxP9&KO0}oq?zeJ( zK$!29DUYhTmPm=>7`i7PzV|+#8d|bA1_YV0<4ggW>{P?Y#7(2jj7<)CsQkK$s+cx+ zyshyJYhwy=QskM$$?m(qdFPW9CLc$;!lD!=wY65557^T5NM`*AFqIdcRLoHlC=Tvp zyMJj2F2R}MUm`G4DUbn5tH7QC8Cu?v_YNEQCM4PC}PC-6>sLWF# zc7G#$+Aq7XPp0~6<@WerV0T;m$USiClGe5m(T-TvgTPR59%@trK{EVY<<{Y6BUC7* zQWm@^!jxpGBJH9)oBE~y8HLE7&fuU%G%CrS`ctHNLxPldk{jC(h>*}Ii=#@&EFsjV z$R9k_2DVI;sbU78Nvf=9@0(&J%CS#ZFrYCj|%z$ zPlf_FjSidQN9vL&q#-v7G+8f`*$PXy-H%5K(qt^SpfPzX<)u_>L*b<=Y94{7QjM;a z)}wCM-08+$NrdhPKQ}_0pg&?kr!EGPMa4}JSRxG)UP)uf?EgjWwH-;G1UZT!HRMI! z6{QlnuOXMLza&sGP3_1Bgo7$vE z+ur7T{`)GzHpe8?NaZ|+1d-XBCOvX*dfwPikZh<76tr9{TQ>u_Lm=@g9nD{`NahCi~k_pJw-$rh>d82|0uc(91z(Uvyfgvx0RtW|CO zI3>xf9O5?{-})vGQXtSU0tMC9Fa?5ZF;fmWNm0AkyfxP(cdYpV9ts3uk<|7z`^e}O zEWDd$c5ko!jn!Y%E?J{q8NOjA%B)zZf1^rea$+b?HT69lKpyxA8XpP_DKnM<;1=>2 zn81Uh2D_xqiehYboB<7f?W>CaP9-+}Yx!5T{8gxx0ejm?vCO`LTABe4|NbijW&KPA z^)uSSmNc1L=pzYI%t#Up=7yx8_~rQJd(s(;L84^%@_X8@1|J$Za!ds?k{kzMjvOK@ z*1yE2*Xl2WdYQ;vM^K_b(K30YKsqv_2AU)(JdVmA39V6LV-+i?vZgv|W7_HtCQxDx zrbze2Dn6Md?`S8R_i8$W=anyXv3>o2SfPyjENPI8$aqGc?z3ZtjU4^wN@}b^a7;!? z*&CrQhQIjV&=R3DV$@mOPx1SJ?lR|T}x$G$e)`2xvI(qj56<2k?^tYFG-4su13SWMt7x)gC^TZ-n=u zi-AtkAPmVMdzEz@OnkA1$o}2xZytLmCa_=tp#SxFi3~KpL4~3Y#nHOj!RqFX1F~Pa zU&!62{m7td3H{s6f6Ad6;oolVJ_hVO;@@fMKdEbn{<-!)d+etv^Muu`{MVL62|ZJB z+}mCJ8(OG0n7^e?Z8d8MT-PQZ|E1)=gjOX`d1V2}W8VZLWPF07CAO4Wh zAhyRg#@#fW5htwtlf|2Hr`VSH%8aidot8g|GyK*K_^5CYUriH!ySD7B z%6{UeioT&Qwd~7CKUftN#sN-hecA9=Cl6R*XrB4v|M&uf8}J*{^*1(Py#Bn*)>Dw} z6+iosUmyK15~MKTi=bk@751FMPska6{@?%gzP<$XU0^>3bC{yjw?KyP0Q|P&Ey59K zT%HW$NxL=8T+945W=)!klmaRF)$s?Vl&@c0WBPqHZH^nZD3-x={+Y21De;s5dp zreStYa`uC0kiUxD!?ymWxtP z@xo<`-yZT(1Z$zrz)=+CW8e=B#)v-lj_Rz;4U3YjU;j(QPY6yB(S+K1`eXG4psxUv zob?kxzX~WTgZ>5n6}k?7Nt5a?^*?>`s`k4{q7M+h1aMCM*(2=HL$_&8bAW_fU^hF6 z(-Z37zSoy$wYiVX^!i(!x8I^js{-?!zEBV{PLB#CYNw!M@v*nwiwrADvZ*Vap81cI zG^8SNQ0@;#_&NudDOVdAQtn8hDFW;NYO+@x}8spDl@I6EGuN*?TvzZu_b zvTvb9h11}eAd;mwW^;saSg3}Why)#_Ldb#kF{x0^uT!MAV!rY1!0!fgBZBCgjbm0yu7-Qs%_zu9SA^*a3;)Fv!3$`$|k7<#8p8J*f8lY*-{lzG*o1$YKf!8p|+{QuwWM8=^64q0aszX{|rL*F;s0mQ%mx$O~>1)$T= zD~4|a?fT)?HAkh;7!1@{q6#@?Y{pnq-w3I`G>$SC8DA0Fi$my092pk@V0bu*-vrsi z96M&RYq^e24-Z99Kzvz?T9V zPhh8uAs*Iw!WB(%Q%;BbO^u4fz&rPq2Fyg@w4dcvjfq>pK#~YV1tu7<@mTW1JKC5E ztJ1Vz+V=o|=3{Tk{X7F8u(>S04%Dw9Z4jwgF<_&v?&Gkz-+tzrF7djA{yIM@}! z5A4d@X?thJTiB=Jq1_k8;xgr|^nN2sg8M7@#f;y~R!}$@F=WNQN$rQ$w}5^docO_t z?}PBahA~h7|KnSLMSp6A%Ac(>{*i|3BDx`2WMrlJ2UO}IjDZKc4CLRROd!b6Ny`0Q z{d@1q>a1M^06=jvGy#0z{w~MQJN*D86ny8=@6=KWT#>iR|GV+)t%ZO6aq_2`>f7ik zQLp)_Y&ri;{M+y03>E~8+yC@X*_m*K1bxx~P9_!r0(_BXir-I#abvO?CMl+h?u?@C zd&KV{87=gGdXzlg`x^fJcNQrc|7w&!WYSL=ICCu$%Kp#q;*vqk9{iI3az#Cf6#t~3 z9ob(Nozd~ne^mXIE$Cl+46-mtcgAlC_l3tkPvb&cJ_X0F=RElDzPzI(8H8yE`q-Z@b&QI~e#*lh1L+JK3$y?OV5Rg+weK`VDu(UvJ&Ge&af)xDU42!O9JPqs0rK zw6EXL)myCE?>EVY2|Wcl{DlkW&tGsCI_EL<7uvY++B<(CTo~a)9=y8qjr8()f4;-DaKW9=UiarK zuQ$%WZinX-bDTr3_s+i_ULWTJp6BZ4+}S~V!nxkrb5CI6&hS$|>(8{$p21N&+v4TS z+3MLgE@#fhv*TyZo;`i$^qDiK>!*8XPP^0NXHMgu3;uKm53uCUba)^6LO4C9xB1Sg zfm6NHr)JpDIdSsD$?D18iIX{p$ibIGIO$J>3G4ZBe&Kh8o;ZH|ggfDn z+oyZ{#Bt7hT-0z6D|qL%*K}%dul0_6JtNP=}XzsKuweZ|LQNuN-s-{VN_Hcd+-$L4VM_(meReD_F-L zN8D-I9qe$4Pe=LaAYVCnARcHRJb>$@1HFR>{DBT`aKSv|F!?}be|z8l{rmRU_V@Pf z4;U(;7_hcL#2fwF-Wn3_id)!_v27BDzyJyedm;b}dz1=TQe!07Q*Y1Jc zja|6x-nDCY*zI<4$){_!JJY?}-Qg>4cW>8Dw{vXQ&Yin8 z-p*ZqR~IjMd*XJr^zM%8j^554Ifoht$06e|ZAbH^?K<+8dfQ)e9KuU2F1@tfZTBzv z?Uk20y!9`+?ExSEQghpO9NX~NZFAeZn8$5#dyfkm+_pAf*zWZ#$2(eXZ`;0o+t%7v zx9zE|&23w^ZN(Y3_O@-UY@NKNvw6$rEq+Uj7f#x^Y~GS>>22PUZ;6}pEpD^hG`4xu z=FOY<*&H^vH*p#^_cm?jGpB{ko7|=j9&hrSx_YaFIi5Fh*|>4j#%yD6)5fr|y$N%? z>UbMBx=mf&J6r=7DjUc3GS)U^8#?PZtl#h_8{E3MzPE0@U(bnO=j^7vZvDD->%;mE zuIsfH*1yX+R{6oXcHKIjacx-FSi5emZrwV+)~}0eTfASp&Tie>5zOsaw|0$R(_6bH z;}AG}a!qIL8ZNliy;ZBL94a=rRbjPTq`Et>w#>E?u_N zE$uARrMGNpT6)H2mpqR}KE_OQ|9pqD@Vr~>m$=29C5sm?aZ5Ujmn>NvvEmoExw1H4++2bSPLI{F zIP#Uni~OSY;zf%VFB)9r7FQSb7B8B{!7XYn#^R#qs?YTnVi;Zc91jgz8svI-uCwsD z1q=Pc(FF?^;MCz3Hf*wRfm;w3_7*J6IK+jm1q=CBSU7Hz1q%4qkZ|=MrhXD?C4uL~;USrPOIdkUDoin#Gx5KNO)5gu5 zxo&P#%belCoVk2cEL<7eHo zo!QUMel~lyjmzw32cGTCezwjVEPCG5o^8&4`k7~*e)^gGncl=z=R>q0rkaPI0nelGt&fPnA^SkXkz~8$WVV4M82k;KyLlhRkdQxn- zJtDeIkn3)DNV9ySW%L_v`#Zhccfy^n*tklRjkm^c-vaA$aJT&}ce|~-M=%Sw#IrTB zYOryV1p(zb>S$fm^%a*PPfI zIf!B%mi@Ih$d;>55@tW~Mq9+16-b+ywQ|*6iAJ`9*Tl2&YWK?3%U;Cl60rs&*2K0W zq*u%}WO%Vhrget8{&MrmrAwD_{4Uvb+NGMYX@>wi>7p)udW{#|1;J}}(O<~VKOlOF zNHqbgy7Qj+NwAv!m%v!&J~|ZVWrj1rV!o zYyiM^F|XLerT~|C`vl2#h{M!V;bi;7NfE3&kr7ng$%YoNR!PqsAhvRHn50S+)#;CS zc=ab*c3%OscKF;lw$15rcf5fM-!oG6#MK0@f;*!)yMSJL-&9+eeQ9U1u1< zx?^o}>Jh_KymO>RR1KK8qvK*!qN+RE&}+a~ccg_s!0C>50jTkC2he)h5m9p+j9dc= z_6QgK5j_ou|L9eBNa$L7RU9f{b%)$5;oop+Aj!rG!n-sM)?AF%*N}H*PE{E^XNyaBI-&ftyv5X4vz9q?u0QsQ5JQ346!2%q6$yBLvYY z90G@&gMay zkttD>TpG6d?HQkBcqMR>+bi3*5Wx>MdchFYZJc=LH}uwT_%9oTo(^0Z)&ohwp8=ej7HuIGj4D(O>stn>Vf}bP zoWRh0ty@>Y$FJ=QOWoSIF0Ad4Qau(&MW<_kp=;MnBa~J-404DZ!kQscs9!A#^{d0G zXwyLeYUCrg`ksK3&2ehKQtpz#+cih?-(ex5R-)Nv7TqRh){U z-d}P{vggC%23S+D=@thdD1o!Zd(tO(RBYPg%{zL_h9y8w!6-(z$S`D9M$0PO2kT{O+SB} zv`G$~AAp>G{zFozeYog|seHrUA(p!Ne@65qd(N2~=DImeTzTgU41G_q$-z$D#<^qs z1I>Y$;LI?)DGViX`Z--7rpQxQVW^uEW{)H?C6La}W_NIT_P5EJfy4A?3$CmaE-M^r z9Ktg|N=_mN_Y9yD6zQLC8BDra?rD!t7q@O!7mwW2!NAjjGYO-;f=V&L3Te`F806q) zhFMMH(3u0kPB*hj6vY{42Gdg`&=Rbfi7^G80?~l`j4%o*HmBf9QPth?;L6JFQG-k> ztPoh=wL890sIGXDkx8ky4goJIpn^ZsB)b)F*8!Z38#iz2%nUTc4MC`|Q);V*{AL$W z8HAIL7@ARFC2zVL1WzR{n{I!c_RmS5=pOu8XLM|@K|*Cv}_^2+M&wI6$z~FYE!!FmCFRqhOy@rNUi=# z7dP?p2x*i|$|IVldLMIJ29933eECuiUA2q(CoVO)fFoWSAfHM$4dqOLnh{nnf;ROq zzc_k<9BK#}F8K45NP8D13Z}z&!|0WhU`ndH5Y7h!(6-dlirolF`-4C4Uw7xL_%uya zJvW88>NpH=m~OXYXU_pZah^GwpY4%Up`#{NjS^i0gjaudEUn@K2E`lxtRsbbAgLzq zbX%kvj7@O^P%U2}wIl-JHN#*sYdGmnM-N@LBysWrQ=oNPgt6@vCo z91nm~R{*P2Rd>A2r=V4L94~-IA;D6XmFh~3ws+#S_*#>qD$dEKvnF~Kv`z-ELQkE} zA?HxzFu=hb%Z>tB#h?C2lW>Z;hlm=EzRNi|6{iM`eFYXQDp-}C>JB#vt{_z14v<~l z;c=}|d957ozDj8o`syEZaIZdubUJ}ZeInANy^h&K3a=hWt5tC<@7nxBKJ2cf)YUq%^|v_$sH=WoB&mACRm?KZrehsU3ysw8 zYZQzM^>bg?+ab|LOnK|^iNw{27a#CNs;G%nMX9+dr&3WJh*nCf2C1lFPYZvrchAeU zmpi*(Ca{89vzOiO!I#H(zl;SQH7>fB-LCNo)C%MjwMN2fFk;0lgWxKD4ZDX;iY3|# zO+S#@Do)ivTr~w&w@$LFG}r=X>kwXdd}fDqRQFO_=$cVf%_*vqS&6g~R44Jm`wo}= z_7MOpB~o38t9ZhDp>5bUDoxd1aof7MbK9f6vu&#*y*kONgxQ?8gIn8}^9E`w7D>F_ zZJW2u0LMyT70V`w-4cnjiD`{f160+P@vAeHwM8+Na_X=q0$dEBR=nE9ldySAhzqgR zU^N=QLR1yW`cirA;)OczaLqP$@cxGBMA_*Z+Uqw!aLsv>Z}7xfh^Ru@Z2hP~tCZEm zx5XPdiI#^qv|SHk?NDqD;Mpb^*XF%-Ydsd6K9FCz?!dTiU4~Da_iJ)PTj{NR71nX} zF_T}z>LztoNv)vTx=}A?g4{tP-L@dNq*@BHVPzL&n?sXzt3D_hIAJhQ7ThZ+hPn!g zRjlg)yWfm&!@?{xFSa&T` zWl769MO*M}0Q*XZ#o(59u@Y@1Upj^x>a{qciMB!B+w=h##(r;t+ncNV?bbOv z*Gc$PjEI57zcq8ql;8T-N#x$D!Efm(AoaRCM!)h22y!8p-U$?5eQ6zV)K}x32tUDl zMBn1HIei2&79`~rwgVz?&LP}tQG&gFtHwKbD?o3RuOPqKA`aiUDH(Rk&8Fa(R)eup zSOwR5aIL#ONDqNr28EZv^i}|Jyxs-?(=8y#)!?oVnS~&cuZgaDqYC78*D|WFk3xNY zafi2;d(k$!+RA4>uSK=!fI9!JP z0KDriw{(SZA#jREmp(!*_Aw5%OBtL2W+o8kN`9?f9GAV|;)P@;s6dzv7aQ^&fMuz^ zHgTnA0Nw&}FPGB9U&zjn@)!$yUw* z(e4xHOlhTs0Dv3iC6F9Tq;<(ZP&N1!0sk}k7Y~KJ<{{w7!<~lcDm|8F0^#j|0k1nV z>Gb4N?bD}Doj&!4r(6yK2AcJ)F465}Q6xC1g2&|u(>B#l=e!_2lD;Ref#&(GEn6VS6T$Xw3EwQC+7e~SSYCS0svcO18DLNb4N3Q z;jGL8QM3!U0DT2S->xKAw|9i_O5o)vwYEawt(k2gxdoiDZJih^ZC1lzc{}hjOaZ&G z*`L!LzzNZoSCK4{*6+T@mHtVpb-TmPHf2}12dXd(G|ft|Gw$@e0%%sgfgBU~24FGR z@!Qm9Ke}TQ33kx1)xiVM1+hlbEfm`c26Z5X=W4 zI{=XtPiYe%rAEBvHYaBQoda%D0~kut6(#_(C}>p|M<;+7l}kpe9yS5Dv1O|u=xKtF|G=K$iHi~s9weSM))1%57*H($4l3&AG;A+l+fa>5N z0D@M{2q06yr`Gt@4-$#uA|ef>&x}J@-PVd60}c{w7r>gW9@XM%8Uf%5h*VuOlU}92 zl2HAs2ZbrXBwDa3Ed_8A;IE8MopI<8kTk=`t!NQT%`{M1;RvD`hZ=`^Im_(<;WW@L zuzZ|ow0oB?nsFd&e>-2}t{NT4=Nx(6n?GxaWbPat!oHq@Yo;rWD$+c+}WbfC?7^ zDAa*a-Qq4@a!cCc(zv*{WRU<=gG)Ywy0NJ|1Q;eQ%1f12nCj>mpiZmH0A$=yzTkkc z^byb`&=!}vg>iwQD}fcuaY4h7m9~No*WAMHf`t<~w6EBbSE~|fM+@B+bSh-6fLrH% zjDY&oJP#_(Al(W^9f{PCJ8j$m4J={sI`SD0Db`42V@nHhk!l-P-~S4t5xtTmDwn01(ebm z&?2&uQ#*FwqpI7)oVSjT`2vyGyaAP&ZHcwn%<&}K&YD>(+yrh`b>?r)ig&}ENQQl^ zbPo7CwYv?oyxzH80iVh;;AD`Eq*nR_=$7?&I(!Yg|5)H6nf6w8t7#qqAZ!wB<%t!$ z*2{*sZ$}%`4D1xsC`_DaztZVl51@H?9YPDt!c$cxj!5!GLNk46yVB zR6wR9oJgkatGA_6>tP^hW1U*=3<7M8!&DCW8{>df!%|PWablG!*oKM4W#-C}BNBN;1o1JY6PRXzSO!e%0NwL)n=|yu1 zL}2QGP?c`ibY!O=N}km%aQ0NB3_G2}Cr?c>z17>r*+nK;4RRA0%Mw{>jD^@r7B%%X zP?hD9X|FUB;K&LqK}G`sSNKYc#CrTSk!s&O5MFCzHCbWhhpo{6dJKF2lVK=G(yTi= z-oE-LBv*N1J?sT?z`9q&uK|pj$z4EJg_sHv)*XU<0IWKOxm>aY)}*+a z*xDe(5?xJqP0!rHrjaXNryNW1R%Tdtuu5nZ!)7$X!u%fPF+OWU7 zZ~tBo7i%#4Du5M&EuE}l+6e0aCNV+v@g=6J@30W_!lU zxj73eWL9zPBQK8=L2=S5;6SQ`R9I(f8Yw6J@p)Oy6uK*7KthBOrg|Sdx9V`V|S}>^<4%b4F z%{F%7a{UarRpwUksc@AxSBFoF=xVOlu>L&>tbLr-Be%Y=3N%ekU2EEEp|Mg}rFS4m zgykz_*$AjLbE{j^UQ0)Tcvfnw`~-3qRMs@`3^FTOwoq3^vwn4p6idO?trG0Y|C&fw z>?=oshtw*nCB1qYV!84G5SK?%TLrw7WGT^Ruo#SiV^^BzpkBzX0WJe+uyVgvS5(Nc zk8;pVlq((9y(q3-w!CI;*lO|}jFD^2ZeU0&2drEEK=P~5RRk;L)kIfX5E@#9=<1d= z@e&V`FD70{Awn+bzqP5`QU zxDFg02LxH(La)_a0P6_20$z@#Wj4^60C|;G14oRlVd57_avdnKw!Y|N1Xj5agswx+ z$>r+IR^S&nI0||+zkZNlR_LmEkO9FhbPKv7SNI1OdiY=6{FYn=CaeljO>3o&ivLm$ zi0uMhrMtTMZK0vDM%g zz9C1L|uQgeS`jm+{_EA z_R1m5(i}@+)&pmUjd^W^E<45txRY*6^TI>UX;H94W?2X7l~FcGn3Z24UK=eKSg;G@ z0*^PO+Wu!0fVIWCuDl9!gx)gJhJpn!hz;Wg31O63YR=TUySa-#zvM;K% zMG08AJZdDYw^amI2)kFVT=K+U)q|-FOt(*PEdr3?l%+KxRe|Ls;)KA-t57XmwNP+H zz8C*rupI0wAtnV;>LbV~^4iibhL47RvUDlf~z=v8R(!bzk-0mPe~j#dShoepRG z=`Ns{*9IRFkm;$)Z?`e+Nr z^~VDgT|-__uZJrkxfMjoW@DgJ!Jh#1f`#i8XLAmj$+VcMFiaMOU=O;W;gF=-Nz`ql znG`U4?3il4D8RM=yf_KS_l<>Zfe=j3tt%}J;mEjNEuPR~>)@dEkZvo&--O5{VF9oz z#InMLJDD^*z~SRo6c!J?XPyN*5c(&K2O#2_R9uyZk%r@;B+p9DO`Zc;X;b+ZU^tk? zmc?QP5oe&`aL^xUvhWLMBoI$cMPZ_2iM!!IV;{9#oDy@ZlyGrVEtxp542(twbK|l~ zOx0hsD0p+!-cRZ-tG~p^8PHh{T^bnRV&J=}WGs+<4TD+vrBW~zd|54+l-(MaGdURc z?0I<_*?0=93mGTQ)ZJuikhM0jJj|Hc?`~@<^Mcz=q1P|`UJ^cfUqYu%YQqKvwW*BRbCkqYP@yOfgj@} zqDTy!OWuV~0f#BLT})|1NIG!X)Fun#VUI(Ap`xfEa}SdaI(LOM>gD;?Ld+RN+)e(iW-<#lUo zg}t_mhjHy=bQvTA0%mcITU%YzTDyj}F1=Xxvq3cQei$FQn%+0wjNE0{P^<5dmW z(icASLL~aC^ea5ymd;BDE?ow!4dc~KSg$@mN)ed4El$>hePC1It_}>MTwLinu!=Fa z1R5|QSyJyJp>D;nSJs1sz<~&?q0Z_s7K2IKmEE9TIt{?awRGb^#;NKnRfUm)iN8gW z7zA9{Zwbh>&<*i__YEsIChgFOug5l5(vTmb{kAgNtlof#TOiWJ4GK5 zcW+HeWUL!Hc!j$%?8>aGSv8@sq7by4WZ?wG$U#6(LK1o7^(@67_#5Mf${=FWatfme1dUT zG~AYt*Inx1deMt}+fs`IT@GMmvT@;e5G5-=Aq6IYc`yD(f-q6I(1yvw7l6{|Bak?V zn*I5%)?8VlhIi!Vm0BDUB-hea_jsPm>|n)d0%~{mQIhiSoogE90)B%`yWXr0vNLG7 zCkiG#1ChzhbnwdR%M~-~R%mXJChSi8Gr2zS{w8BD4G%3o<=a!q$8zvKa;kG0mWOzX zHiy2iVUP|7r6#2Ev(olZ;gd`?X&_=2oT>7xRFSbL2d_V#!{iW8G)*lAeHW1m!J-l1 z*F9dPRt#(&AQ*dd_a1-E?7Q(;6QVH_6J&5uSc1f3-TK7OR-;w`G6>lYccd*Orrm)A zjjFU#G>%GF$V_%djzX$A^M=FpJGjGRM#>b69oHGLAj@wAS7(PmN?3l9Lwt3ds;~|R z6ixy$uo$XxmYfk)VDoSZ(tMes^TBxVLDGk%9t)rwHnn239FW|PR4}QTBJyMA=v5)w z0Kr+l2o2#t%WzrF-u@tauWsvXaucQR}eHsWw?7~m!PAfkeGiD5It#4Z#7d7$k7#0 zvbZ@3$MW)GUB2D0Is%Tv%C@lCuY5qISg33ls>GJApacchtAV%q2JI2ljWMB5m!^m+ zqZC$P3HS_AnNRttn5f)a350GhfAPiTFaDn|7I_KDl zTC#_8qAN)l_!|nbm?Vt*OtoovDoHu#Qf`v*3;&T4@z4uds!?0=d?oQPtJI8@6_1b^ zNVm)@A-#ApC1AR7jeVU8)3}l?&haVL;|wo^LJ_B7gY%@1}oEZEOmZnWI+d|=Pz{*sv1VdnX8=fh# zmbQj5a;|->I*nCoiFR3{R+OxXmXj+Ne+JC$7pG|k(RY?g)sh@+mR*&gCC#@aTv{EJ zi;&BqYqe?qZWmmPs4&5-;cgJ%W_Q{^T4o?jH0@#yxXU$nXM}Uf3QaBsh9gMw&B(XH+bdVItEedxqH5$U zE3FRRs?HemWkF!UA`vQ=72`ETNKKJm!dek61l`ioFmTadsPIk|XBq^m^@~QIj6H}5 zM6mb+7>^M@^&^qcRM^gHf&U>@*Z! z)}r~uu%{v6ni=;$Co*l4K*gvZ=-PMVo;VpMxEvC+st7HE-a9TzrDq`|ORhzF$@f4X zu0K&^e8KEMqJ`yG5NihB0#TE83r>}%L4MpCQWILh*Vm3C%gY^ow6xzs$(12@@-aGU z?}Kx5aYr%>`wBEafK@fXyD*wK)Ic;bs6aFopuxsVahGO?-yx5F6n=)n zwUA7=b{o@jg{<6}epk z)UYcgfmS?9Q1!c`vbyTfXyLk%gWn@nk zoF#`tajUAc8VwIyw{6=3(%igdk}0+ppb%1!LQ!S5ncm%O%lPIk#+}e_spwMKrH(s7 z`m`sMc6+ApQsd2d69us%(uXwT_CEnUn$w}ngEwY{0G4V6Zifnx(p$t1pL()`y7X@7z8T`thlQk2Oa>BD*V4@eaT-=j7CEjZA zD;gxv6<|+QmOWzLUB-H;Vodt2EDRL_>2gMTLDmf~HbAGcEl8$ime+klQVF$S&?Mky zu<4H4nqJRTNVeR_v+yZY$)o1t1&Vs2DBQUfQ+dUx$@}mm=~Pw*l$0^Ft0DeW)fnz; z6Dq~SpjYv==RgfGZsJhN~X~zAD?xBSV2+TS6*9G=eud4}#X>>Y4L}_i)nj;27&Ap?1Oi zM~R?-Q#lp_qq{6n!2v=Os8;8-X;_%|$MfzJLQQnFF&P*r$F{64Oe>LMZ4N?>f>lFJ z@n+w@Kv6cI;Afz>K_z3z@B)z1q5JHAA&}aa8*KrhCJDDDb~ts zo59DR!Y_!d4^4w5o03rLRvN~%1R^RVSY|8WnSp41CVUC-Ck&Y{fhufaQ~;}HGZTZt zd_d?_QbG-rtcakE=1Y)efeu!ioyh`n$@m_>n**7Gz2NDB2EmhuoqBajV7!nFipOLNZFN`>aXUg?|P^2 zHGrs_gI|DC0xVnauBF0tVSMv z6l&?Yb5ucTo`t!V>>1At;%3DB1g4L=vky%zmFeJYk#*2A7p=2Q*#I|zgOMP!Dli4l zJ}?!P?@=u^W34QuAWpgn{BZ%Ldu>GCS^8(el_E{#a3P+{bkgJ^pn*W1foOqTnyVHp0z;ENDh5Rz z7%Rah8C{whin=cnCgZx&R3%~Q-b@522Qy2pe6@YxoEUZxpp>c>OpaTox7#ZI^i9RH4S z2#=CKWf+i;Kv4&tYuN!tbV>J2MgcKsRu+rNbZcs;+m~DgmT$le1jZY{ODoN@3X1?E zW0~odJOjej?%sW76mW%Ymh|b(Pm39Yya1+*^%9agv)?KUA=?vPZYb{ndgu&z2;{LH zQmU-H1G(91gZXQdZ2;0}ROGBM)dIUPb$ChPT;6O0Rr+bGP(-DAM&8L);8i9Tb}2-5 z3MupxJ5c=v-ee^hKhimQG3S6Mc4|nW2bskn5K!T|<=+4`Wh3x~TflE4b|T5kcr*v1 za;fwZ;BEfN)?o6dNHmw703a$MG~iBb%1M3eAd16k_*4m_Qbc9DWu6zK3+^Y*q|!M^s_vYsrr+%D0moVA&7R90GY>F5ONB%>-<7Hi6!jjP5Jk0#K*q&^p!82e+s-UFMiBziWfn=SHrifDe z6k}#)3Q&M-pk-DMknkliWq7QcE8FZ`VCdX=mC{RFn^%Jmz=1To0MbARgSGEK%>_e{$i2274EQVT$!jwV{;REen+P)E}OA0+EQHtP{{4H%fpD-a7@ zE&T!vG@v7Z$px~*-nlzu23eV9#i_Ii@EwV$?yfaKpdUcgih+)RCYY5HX`K5R=NpYn#GUct)196hg7I85T; zFSWGae^Ge_iA}YuFR^r*?ewEeEP`a+d01IZ3iThRQa&?9Rj3M&EMF{JBQSFo603#C zD!`yx+F>P`vQ*5yK8b9aLn~E0*JGFznOh*$8U)fVt{HVx#l_;r&N9_N0xD26oCcy& zB1N4TsZ_aJnpGu`b_k~Kw936ir61*HevZrnGq?&uz2dfjs`~D`)-Ft4a*3*rRx*&MwR@@GO)hQRnNt)_17eWPBKiL zMw_cJH9uw>@+47i(04eg9&M6Kah4GUtPSIHM0V-m{qRwlSg|F~b0T)Cx=U;Bhzh`J zhg;@%4eXNe>PIM$PUSH9)y$F!f()(Pwx`jR`|=LuU&EZ14%qBa4rw%1e02ezBu-R` zB}G)yY1!i-qr(-)0a{>5pwl@hK-L{V;THDMweQ~FnfGR3b^FKmC8sN25{i};VSfL6 zgit7;@~@@~Qs?Gc^^0=~zoAVxQDoAyy5=@|) zO#oHk?9Ls7L{vn}rdeeN%&iGmQS4QeedXA{*Cu_x1%hrXWL3OwgeteI7mQl?mble# zdjOrZU{zjIYL`d!EY(%Nt%d!1oFAoZ%3N7vRDo*MZl#iDz)|hg3r=N$l~C%pKnMkK z+KDau-sVl261U9m$_g=~*1`_UeSDKqgMtkba#95qi)zpoe(HaKUHdAywMQ^Dt+a^Y z`h9NLyOb%L@?;T{CBZb^>ILrMD_C|a?XWnEOUI;WjFsLA0*%;;XNFcXy^>5V zaMp6T7(6?ow;8adO?lHlQ{w}L&0?8GR}WB?6dEwe@M-A*cJ&pk^`DR}Ypz!A*7u%Y z5|^~3f}*wy&?1|~WEsubVSxX~u%9<6xMl-T9ufPhx_=aCHs)xq1biCsE zqNgB%QU;CqgtUtv7BwK53Q|?rmBdhBs*1hx~qYSK})qp#$GXCRca$V-)A`7>x?po0TWilqwFlCd?IyR|6j@-n|Nd$z5YsjBwq zWX;Mdu37AG5R$SQnO%SsE*_z@zh*P@yD52=Rb3UzsKnF6snT1U*x0AvHQZ%4gLr$~ zz!ZGS&3jdOmB5O{!q#fL`b1d0nIg9Usnuj1wRt5qxDf~GpqkE9*C`OXdE ztLdjj*%q&6WI7Li?~ zU3cv>w(alO4T1Q2sP!h903$y?Oscf zM4O3qS-Dl9tO~hAwtm9qzgK_(zc#&!=N;U8))qu$BRnrAyKx+1tm2DIVvrI0C2ZuQ&+G=zMTfC=GTOhHcfNO7$ zrMF&Tu7UWs#X7HjXy=7`E^=-%1NcI! zeS*ZR9$$6N26m``Ym(tCYrG)JD*xa^1zt&}g_*Qyh)`5Vk0EmR417a;%isf`^kA zzzhR)vi2z7mVc7Lw5qHwIsqubsYjdhLML;Ai^{Y z`-*G)TD1a8m0@MWFHy1N+pxM(B!Ep_J+f*w5tp1xE|zs5lbL|&UT)P`X&Zn*t3og= zN3~%93qO=vE3Ya|164B%NUt4$W5Cp zT*|e^#zj*GGP1&FCFRo5>X$xLwO1OFiB-PV8i-i+UaAbU?hJoS%fP@3R^UYgD+|4( z=0@DKMZeBke#O%CDpJm&@mfX~uWre(Aukx1!mV38%$ZJJkWku@|z{bSB z-oQ4%qRMF5RL)hjTFA)14$o?M#^>n60C5(Efhim`Q*W=w>1UNqKyk8D%)%-j78!e* z1{6vFfmML(8Nns;mEy|BUS3vO3P{5pKDMRg9hgFpRWa8PXY#J9W~(xj4Z9|Yu$~oK z2C~UNklV^#6mvPt>=Msrg*zZiF$Ex4a&Esuiy^epv|2Xzg8Dkfpe}Q;N`|f73LslO zLc}bOXeqSWtpV5to`MQnyZPV-x%Q@{Rt^l4jUVb#%OKedRa!}prA&)HUG@sfvT&cw z0>975%sjzafR?Vxbv;rZmxYb{&(=HGgC(Bp@!l`Mj z?4XctjAYxmy$rl{m&ZziMOcBq(y>25Q8IW zi^d8FGT?zY+HuLZThU1&+d8JXhDp?JGm))~1xhKfrGZ2x`bt$O{Vpm z8cC*w=xY4Rn1c68p;r?syGgZ`p`dQTU0ncc+|;4zwFZD4*a)3={e~GEir!z+U`4OW z^`gDNuOBgb#WGHh*sk?3uOgn7YAsLDr$MUPCKRq#wf`cSHi)HUQ^%lM#(K$Q;H=96 z9I zswaMBhu5%5;xAJS)YO)`tr{fAWFWf%(rDE}!LLAS0jjgAj4}Y;vY%LYMUr%r`xW%6 znQ{zrG!auhR)ndbNqhyK(gd3_YKIfnS}m69vnrNT8_Q@e8V%gC-_VU9OM)r6ybsl=OrI8%_Q4UYOptvrj7*w7gDKuPO*(|JBNV=|D5}8eFeQepjmN3gtg4I6P3T%O| zW``y524vD=K?w$v&_K)-U9}XOz9^G(A2PxHX5pI39*7#T3kV}qf%=j*vj?l=2VIl17_9ENNxjV69{K>WELpC zD??HN$W}v&CZJK)o4Fu0v8`amF|dnW#E{C>dwNy|eU_i1VFDk3d{aBy9_LY#u4ma6 zX_o4(!U)u@R)aGUS3<7CV+g)997wj6MOH1s3~Ak+K{Fmu*yV>2Ug#fI=(7wYu+9op zXuSdo)WDX@NF$JH%it~+Y4L-!0<=m%@&L&;ac&V&E80!S3k$+6*bUr`JjD^Hje_C` z9=RE>H^92mYb}U?GHtlgkmUeRGTg#QD>>I>U8CY_YE(-xmN~P8Usw<{qwgR9d(jn_ zrUVJQNy9Cyv~N5?==}sVTsmOEwyMseaEsHjx+u~{Ja$)6iB*(v;Z3ZW!=OXJ)!ZfP zLArrj8&Xu4913_X@K5wO5{yy5B?E$7v{H1X<4&cvt8%U+>*6+E%3(qf^Hwf);Z^V# zN{M#{AQ=#QA<`?G6&_lNxEKo7wU!HHVAAe{BrjBcnON)9ER23yaj%NHROaQZ)=Ld> zt5A8#y))oY$jQD{4g=?qSbGkT3mKQJi)aB{d6e z6vd@!f?jtfm*tX8%fLwMJzM=0d}!Ksv2o15nWzQ6^x86k03(zRd&}XRtx9C0Q6f$cF9=lUK^*80Z-ZeR*3`{ z0zlZ>DkyzGIRw6FgbPE1I^*=R`NSQ@r|Yq zA0-!~D2xHNj^2eM0m?4oy8z5MD|ja3LmK#|B5kW>Glt}>wf?sz^~P6vOtFp9eFLs4 z1VeW*0I!L<%IR`NMy>p|rRg@kMdw2qek)HdGi{;mX4H8_%bGpOTUl@;x{Vciwz57{ zQ2;h75tx1lW_h`N1Nfjwiv?hu(0R+yfr65W$~vj{!f*@c17A_kh3Xzwd=ZR=y-XsA zwgj~|v9gEH7T}wjjiuVJe&eyIBm%;2DC)h8e?`U`auRV0CumXy1_#pz;dcE--ExJv zW$XYZ1GF(Pk07D2$5cwMX}vpk?A-ALQTVAHbS?yZDld)FsUQz7Lk6Vxs_j@^u)K}U z#DYq?#J#5ET0R#bH>tQX)*_vYbzjtT^%k++t190$^j98Z?xy+llF3>4{krU{^4oFRdpBM1^GtUX$8iN zvK0-RP|GqeNV&m$3~Ta?-9pslBQVle1X~D1_aN8fI^^fX8S&Yo$o zfXs@>2*&W?+6_QD6^y7r@;e~Q05>WxbMQs@qP#5CncammmziDR&c7(K0Malh z*-l+ckftGmIx)#viCzN$=Sng@e8j|HZwm1wvptO8pv!@haD@wE=4g$`ZbBPw-IP$A zth$nmB@3Hw>@2}cT#Y7Xg-GnKSD1-#1BC3a*FebLdXFjoAYQYkykzSBymuY2+`0P3 z)i>18-MU@-?Y`YMG>UCO}5^g9g5?;wZ6@mTb5DAQ__9Ly%VvOh53Af%4F zOt>xT%Tj19yX+Ez<>7T;&ka5hxoaHz@cz%CCCcP*8jAr-+h$JSUl zDAE>l%EGDRgG>$@MkpQ&j`fB$;C1N!AdOg!90bG3+ADvrqPdQsL$97-&}x#3>r{y! z;(5uW-4zk#bz6l)T59lSLA;oDMSos!iDt zlDYuB^fOp%GT1-w_xa5{rv@57ig%8Uv%v1AsX0Y?(Stdjr*Dip3&k zzL?}+ zY%g-TQui|E*aDq{gyikC_^NeTN=A?cBK3oSP^?)5KKHi5(*S+A!a?G&W)-B`!VXck z;Kjp;#4gCcEK!rIfoPm<>SB#@F@PBgFm1j*wT2ky+d9lC0mDPbip?rTQ?q7@KnNR0 z(~Jb|QfITU(IOJii?>MYTEAg{3|-w&w^}s1AWCK~{lJlN3Q1NjjSy`5P?#IU)ZpY4 z&qW7B&Ny5mbQL!O>kJaIdhTY4q5se_5>h)|urZ6!6yqf>PD7F271-DeDPE#ucpRYZ z0)2_a(20$Jvz3{7fSomk;7gA2U1VjqO0is7(Z@{99+Y+~GGK#;{T7V z_x`t}thT++6RK9)sdI*Ipb3qn2Ab5;AWhDpL7F58B3Xi7us-eZ5^GE*B(vfd%_F)NTc(9*}PnjSJ-4sxcXjzXV}aumO5$0-hk?%F|GCHpxNBT|vHI z>A>EP4D-t2D{WZt8{PA(gMnZ5-PbmCfZcF(7%7{TJ>BcSHnw9%TQO?{~u;P=aqKvzpt1y~xtH7s$N4T8_r? z_IW;rEk@eP)0-;?gf}C*tx2^QBy67q7*>-qq0H-B1Kn61QNfGzYL?F0vJ*Z@-lv|=TCWlaVjAx6P%4%Snb zzK74H84H44KejMi3dV&5V;CSlg8Uo**1(r~@PZYoxhCAG`tGWcL-VgYK8)t(r3hR% zX6Rx;e{cMApED@MvO-|+lv?F}=^2!r)wy0IR&s0j(*eP&7XKy;<5=F_NE-IWx|=>& zU87?>gL)EKn2xN`&LqOG~>j6~CC3RlU`;ttYem}$m z39$>rSOP09Gw@32Eg1ME^5r=jGoX8edm!G9Z=XQrxAPYA5x}orwFU=7lQxI|9au?T zR=myqwU5;Xhsf#m!*WCs=Y#mG`C9Y|Q|W-S`p#Rm@soQuSj9RS}zY zVOahm_!OpvtZKY5^v#|vZm#FF9vH-Mt_87bum{KVld6SmBLFOuRb25;F_-j z7J$4z?~r0AIjrQ+_<07W3Sy<;f=?y!QoL2p7x7lIE*-n(-lZ=BWDD^Y3U2A8X=ZjP zAmK{ZbtkWqW({MRukd_9Y)u@Fe}0}?uHqExxCE7^RjCSeHQ*Ulvau(Fm!>I3f!PUF zeOh>S4v0#41<+;)z!G=S1Pz!=m?g7H+!dXwHf@5g1`XxY-DjSLLH9|t;B_{YL08^f zG3&%rXw9p(3bH8(OuhfElDx2yp4>|$C8ml@ja|{20pST_?1ZE{J zN$cv)E@dh)^GY+C8s?2UVP4?MT(~`==no%nXoJ}Yg3F8vWn6CTqmcm@=@U0^%)qn# z+Q69~$hy#>onexLEDr6*hG@qyMYlrhQNSnU-0$16wl_3jMu^{EFcPgT3PC&Cv|umy zHJD!8MRx4NFeMZ;Ct%75S<$De)HEcaDV}1$tmN5aNuUh`B@i+VI^%;0(Xk4N)zFtm zq0ke1G^Zp>^8uCD61=HOGYYN?*wm4+r~#AB@+pJ|mXb)r{euVYrvwWy^{TT3 zkrk^dNmdpGcP$7=9brM6(pjw^ZlL!d-808c`;2SQ!oB};B-PD+(B;<%pMug9B)>Mm zrQzD@%nu}@F8|k`6`)`g0ENf&1cQT->FCYzf=I?$%Xmv8xjp&;1{Kw*5 z?wcM9*_J`G_ylk&b7Z|}t@zG;?aHx0mDVU$9LvLbSfVX8=TdY#AJ3e>WB7UFMa#kSbSN6Ia77(}kq z7RI1N1{S8FXibEx4OFJl*GolK)GC&hLt%0e%O)K6A=v&JdhNKYnCrF)F5dkWuaa63 z97d>3um98JTAp#6cR*{XaJEzxn)F-Avf278$kWh)V*Bcu)_S*tFtDCEc^j%wgMqh7 zu$8M9=?r#^ZRLGs%CaTZSY3v(?nOX+1Gu)YlZ+Wi7aFXwt75^FQd?4vEtL(n9285W z<(FAubR#6MN>gOmi!YQ_s)EsKLr#VNhXSlbSOTj}w4*t<2&#;o9fDmYw0b73+J|M? z6spoRJb?f(DX=B6Rb9l?D?xix@aiH$A*orID$2N+r3Zl@@hi^hMuau8p0FL#JW40)`3AAhjlSFJVWVK1!+qNxHL1E z_<9Is*wT6v4mo3iOjD}FeRAbKq4y16%}JtSE-~@N>%S&f~>2oz*-(G%3}BdD$JrRyYYt< zQQ^vEaha?O!di3l29=yG81fQwhp-`tWT#wb&1Bmph+8NgqnJvbjdbSXvK-pt(Rq&s zV~a6>Z?qf+_96p}Vn`BOGu72>;7iRlL2oq~)@WGlYrb8}`;xMnoOo%+9Vdg4e?2Vh zz$418MKBm#2U1>-eP~uGwQ?bVQ0L8mpyDwD-teHBiGAvI1pRsT^zxcD{(einwT}+jUZ=maFZ~shP#K|&yq8WureVCiZyFeimiPK z+K-TOjcMt>og>*A07uBP3$`K_TP&>@IaW^IeD4hPSfzx)j8NR2?rB=vv7#8=F3#}GPYgUACyW0rhE$9MAc~uo=5%TK4 z6`gqwm{^uc3z&r4h9FkSt?Q_{Hr%lrVb2}VTp)}MAXk2CJ+|!|?%~ywav%hNEj3rh zDA0RgU33+;0G`d(ILsp$ygUuP7sSB8*oI{;h-76#C~umxs0EHHOOqB$JA!3@#n0lW z&B~t(qLt@BfnY3Li=WMZ3MT?YT57A-Cd`v+Rfh(Ot+iR}usbIuR&<*n%uj>K_(hLlD~%TA2x!bw&{=PtzZP3q1y^*iL~?@>=)4*;QkR> ztyP~%k9B2MDs4g1m1%bh4us+l@*N0j6?waw3T=E>rd)6=bHpl${rCHqU%qxK(}TdA_N zAQbhn9%R)Tk@&v^tt48nO>>eJ;bxF;A4g+b0z((%(`6@KGzJU5kqaSO(=hc8dlTH; z#zFaJ=Z$&B+v*bFPSA(jfa@T>f#_SNFT^*{91MUeNVQvHLtQd!_4)PdB~)3Gu8Fy| zIRP$QNU)$<54H`jFHo|zZVa^@S1#N_o?La<#S^stzGhitY7^FZmAPW>z?BWx4Y~^| zVuLv$SqU46mz^4zOU50Gz4hv}hS?FcyU2yR&oG!$Ex*>DVVZ;ct1a;$1{JME79;1;k)zBCo>6z`8*>3y^(H=9R3Ap@DSVl3U3-G@J?(0{E5&U4bqu*0j^WgdD88 zHQ97aG;koxf^iF!rtOzvYij~-es+ijO}vn8@!Ye|WIPq0DaSrs!L7vHNfNGd7mBB4 z#FgJb+AZtQm^0SI*P%B&)uqOZPw|wDxx`K8`SuKfsLS1}5zRa++P#g;c@5Z=zf zXTUG5y54q|?pvB`-8tZaV)2m@JH5(@i`ZKj3HXWP;o|#lB2e$IhaVxvdNW-aa4Xww zMZoIKkRG0(s;err_|Sj`TX}C+GJ%XXTk5>1Dti!WuIsrK{nmwP;d}og@>b_IApoP? zOy&bqe+9J3x-vWfqk>v80C*WYmLA}{{SUwsrM3GFrh^UeazxeHwiSk;IGjy;N=o3`L^C`ts=#Mz~Q6+66KoOYj|8yF!OS$Essn?}}eZwCVE++0RNV{t} zs6GvQv>hN~0X$2yZE0dInYMHh*Th`3a`5#4qAqk>28uzpO>Zw4>2lBeKgfs>EudjS zx3|uG3wT?=c#wYwzpZM{u;ETyYxqDnLgl(82%`v%P6HSZ2(<}51Q~2A=K;?c(tu6( z&9} zh9ChKgG$a&Xy&mC8S-ua#TRfXVOK6&ac=SA=P#(NyN$6jEitXZ%(n%UUR0q;{S8ojEnJr9mix_VB|tz+!##?Eta4?M-}(EDD0KfmSh~BBho5a%uNbJ zUJ))ZSmkIuw(sZ}6t)qAiM}X5dj{?WR-z?nyKpLC{mj$K(^cmO%orXPp3Jq+cG?pe z6x5Jy0YJ6vg(1XVdKVmb8LCSGSXF3(-hXIqyBMnph!%WHtWEBsokHeVqskHswUvSL z@0n12*-cDVS(AGa7dCClxzc&7SO$r~>G6aJF?Ji1bvDsX3)=NIw_svRU~nI;vcufD z5PvX1O;>~)DpRdQUJUUurRGw+Mg3Vduqo~kw98Ip`B8QyOXy0L1-;{{G6HbMSJ4aM z`v*k3Bwkr&aoVF(^{kZ44FshKH4{E4&>KB45e8Dm=>%$<9zdh@Q8u z-7+mqn`}EK?N_XuutGA!gWqEph&&c@0nD=4S{0euZR7XU4>vrpmyBC|zW2cWnSw5C z4XiPvrxsXSSZ)|g$Sg>#iO4Xeh{@g{MK^(k>8_=jHq+DKop5Qhb?a>dX$%=(ME0__ zj142mys9Qs{g}+QTFTVm-diT^0&0u9x?)|=U`TfzQ2PfWF+LfBZ+TZjC$cIsEPNM^ zTe2;AH{SUrgkC%sW05w{*d;_yBRK?o#DN zgcx_sv;dy9)4jYAcHP}z&ohxhX$v$dz^xD@?~-`&seHK0*bIV~c^5oX3|$^*Q&?14 zMuFF17ty+c;PSJ*(r+InZ4hB{{St#Bww;`{NtSNqTYZ6U6h_V3or695SLY&hG*G50(@H{Z_MzQWoF?I z3x>jO+V%Vfmx3#e7|TDryNMjE%-s?hW$GS*Aq#_HSKv;;p)vOB`eX5(eG+sf@zRD{ zD0ji!4&j}i`E}WJ7gNf>j4UhOo}}R0;-HA#gWviB0xxM;Ixh!mzzbSqrF;h5CHYbX zW@rPY-~2oF&xN`7H(e>cwQT|7?LO0jrTa?yB``CzVV|}{$jEYdbIBragW?$EXQ0QS z$&==rUBEC!-VTvjR6Jb=F_hqABsP$a>A6*rS$cgA?5pebUl-aBY|rcLTnoZM9$w14 ztS$phO9DoJ3GI-LxkEmVuT@tPAy;8YLZ zptqzNTz5cISed9?cT(C$ofNx<1XGGx=8gIat4SRX1{Ol*sFChkJ4u?LEsxxsfJ-n4;S4wYsu4+F4 zDozy@y9XjZI{+*{6P}rO04DB+XL=_05`wM#?3ubUtpNeJ!ecmofx^&zn@{(jAq&g+ zpv(qsN7x_o)9cB?l7!{oRZbb@U_$W7jSi6!WU@Zk4I`HY$(X9Jo=jCmTJK3nxfaME z@W#9V;sP45R&qs5^ZW%^AjBZ~lz*K^D#HfYJ=*!Ip;5SqOx)t6Ec=Nai@8>$89Bc|N{k)>z z4(a!Z`wOzKj0-e1G%9VE3n6bdEN){>sI6pNFf9wu!ae1^|HNDu2hKEF-C1!j4Zx1S zP55_Lx_hBAHVBE+-4R49X}3jvS4wZfmh@i*D`*z(O2AynxEgAam%^?}&$yGI_gbK? z`tCaRN)849L*R|TU9Tv!Uc6x5=#3`;ztDx5s1Sk0?eBEW!h6S3vhXGj#T^+cuM7@^ zVfl1DR=3XEHC$F6S+wx7aC=hln2EW!p&rc&$t-*fyXCjb+pH5WT@JUFMBjA)Vc~DE zwrA;91&cw!T~a5JW!M+8adEDa$RhSeB;LeeT@fuS%e2*|Y#VM~FzjWjF18bym`^Uw z9a~A>owNqLqShtUX^TbHh0K%$Uh%V8+<>&wW+mCiPkV;6V~Di?1)6dVW6hu|J43Wo z-T6;xT%b+CoVhjHR#jvjO1#lh6kyr)QZglsxj{FugM?k`Eumo;$@T zA@i~kuQfummT1f+<5ATI&Gg@b)&!95;+!!CDBx#Rb_N531YNsl=W__i;dcLJ`5KNVuy0%lx8T1AT)*QP&rP2z=vmu_8i=zg#s`MM%l zr`mZ>AN%0_rUNVN3k`6r?6*ta?+RjR&;{Sd_dlmnE43C_${DWOg7|BuYzGkk%`btwi2}?b5`$D^! zN{n7Xfte_^8mRe(mkYD-R_O`0iATcARk#AT3X*E^(mbOR@Nt#bvlIlcl@Od>$~5aj z%cb&51Fztj)HMayow^n=Ca#@xuEkA(yBYXOZ0%IeT%{*4VQl%+iW7q+VHtt7blq0W3OV=3 zPd133Sv~|>6x^>MORmL+kyr6ZcyiwHRVfLOVC(k0=||`mA=S3B>&l%VuL6b^1zeCV zB-<$Wfthv9iNG-3stMY->qANFf*iU4SNInIq}7*&XLW}Mnh?);43X3 zsN7Ox4G+xAQDAfL%A4w_Tr#WmM3XTgJkT{lg#mZ;JqyJxiKf#W8VLWY@(^18-q@jp zR2#W@AB0v%Sctbo(0iKdfR^r6E8lR>JekzF6j`~{a0}|U*<{;$?@o7n(3gNzsR+d) zn}X`h;@vGTG83SB;Fls2w2{fK(6I$Ju9%7);!1eTSFVbu@~&sOFIkHLj5 zOHgHX8R)c$DS1@Qdjm6aZD0Aie1LJfcgUc-!5Fm-pDkPn_{gSfQ)kf#e$C9zC8nAk zfj~P8%~rxKv{**rT354VNeAD)t$;h1Z6EgX8S^LTd1|`1Dj;_TxatC~bX(8O)mih_ zSzv2rKxkD?U8%GlhtRf;X#&!C%L2KT7o7&?&;_WXX__c?L{ya-D=!Y-vlLGo<8sZkLtwONJds*Y@|(hi8T&}n78&Ga5b$gtFCskI7kF$8vqFuaX_ zuRHTH^h=%vX5}s}BMaV<7dR_tu9VsAwPzoFd*dQNUZGVO3!&mnQh*6}RW-U3;0 zm4m=iGxy?VdT{};7M}}Vbv~uG3XwH<%ieKRu4dzW+=)NYhv3pH`$9mHZL?o5uVo)J zCAWf0!~6Rwu)Ys;6|S}kt8yj4i}2noAeEfTE40W9F(czL_gLh@AmA^nS2%33UvmWzrSr!Hn^!HtZPv9s`2U1xzNLtNytb-vQWIBRy zvw)V$Lb52ofdIAk8)SM6sJ$)#Ky_5G_nD#S3d*fZ-|B-jsi-mncVJW9Tw6`%u=S)| zCM3XKYlZ{44(j-WaVc33;E9i(O1~+7`-Rq~$Lhoqz22N8hpL>cVHvmz`x30S-i(f% z4S=Y+_eyAO`mWpxJgRfP81GfUO$!EfT~)}5IX)RutYso3QmZ9*nVedAZRJFm=E<}K z)S`e%18jc6h;C^NZAM!av*zbP^*XZ(*r!#Ljp}ScQ9iC6jY@iD7H&I z%bKvwvV((hW~5F^1ZB!CL{Pyei^hi7j?d0UNUJxp=7<}o@5GBDH&#Mq~-i!BzFDKUXI+TzjwtlN_rl|m#Hs|r!8Vlv99p4!7=h~Jx+jevy9GzCQ0S}$L4ZhTi)jUO@I^&PPNv(WNy!)s_Z% z3>uJIDZ7pnzr44DipF}h6Od*5V%D$9tO-aPDk&C9t5`RDr)>xet#z8SB^>e{$nP?W zO~6;?+y*Z%3#PDom6`O|3}Kd!FxCq}2`nsQc9-hMyogNw%TP?F(z1kOS`bLM-rP5t zf^5<|K`8et zf6Po;(CWBTuhduM*@9+aDxl3)ifq*vLWbIeFxvuro7wctuGU|Xj6k_n7J?R8*IS~| zMSJFl8M10~9P*X_k}V0r8$86nu-8KZ%|dXMU0`0;SXu?L0j!sdSu$<3Km;6zMY9Dr z79a@0vTA1tv8y>0A5`LMBCkqe4OPcp1|nD(LX(H-eYScK)w06uJ(y)HwH4Z`{IZZ+ z=@?KjEtOe4p}PaGPOu?2E@0LLR+?<-tfkSmFwHgoVILLP%3i>(X9C=*cL%?JmziJT z_k~ScuEFMa_X~YZa$QWjG8Q=e8VP&vGa3*0-2?h1DdQugrN4H+W`taFZcu|?Hunwh z%o_LVJh>Mkq6{sPez&3EEWBkMAE2I6nMKkUCRRjd##?NLDg#&2Exo$`{4Lr+sI*Kb z0uDl6rh>s&fVl$0^aSh)SSK0~>W5KfHt}Yacq`;hFc>tf-b_+$<4v~jl?FRQRd(`? zg|9eqg}}7q%46U*C8c zY1ke6b4kkb85G{ZV2lxf) zwX;O%7uDw%c3atTh0NiX`*bHl`-XcB1l4MZ=G}e)*oEn^ZL2sdDvJSY(d00YemnV# z^@|1CfP7oHZ<%k)EL){4j4RAY&}xZhitU2QwS4Sz*K&Da+AU24fMx|OfQ?(WFlR2$ zg86O1$<+i=x46)P?I+JqQ>$HRg000Ukdzk*wvcaKq;)$ky}9%pI0CaT8W!V0o&z#+ zezqllL3*~t6J{?c42xB4CbO;F1triK{e2JQfd)VYgev(1r2lH4!B(fF9b%};s>92{E35T zI#>r8ceZj6fPf3;Ea2{Z?2%$s^;zrjJ%0K)(O7V-c4t$v;8LvZy3@8aZ!YZz;RmkP zf`pe5hh--a9k=1ZRV7*czHGM|$~rwPs&}s_nSd-CZFppk)N5o+7XGqY3Wl){%7)*d zFEXxw$ZFB7n}Z`T7};C)sv0dlJWF=ghbL_4t3otPNW~ov>mLdaCVbh`7lmOo+-3xe zFx-DQdm?r^?gTrD!LW17gq+KDCqjdp!SzJ~Ed#%WfsH{RW@G%?;-u5h7{#f^3 zl%3%dg~BRA6CXG3USL7m-7qQ$m4hk28IQ%g(b(7Rw-S7%^hP6P?~`WbX`R}-n>dr% zmUe}NR56LXa^@!UDWp3;*8|*hg93<}Ze7Z}RYM10Ga%Y}qB$E&7dDnI@Z_xdbwSSG zxnm6fQg>x50IsAE$Ca22)vB5mRF|H(%VFr2(*1D-MuT2c7n#XW=w48*g4v! zN#q?6lM89U=Hz7&8uehD7B@FwaX|Zr26f&RbYJ#(aG!3txiArU68qt%WWEKr-%92! zki?LQ%Oy>^urHWl_X|qEruRAt1D_EuCJp%0{AmLQhJ@}8p;CV1-;u$9bCsQml3n11 zS3#gWLDz!n(I!7>LF^rT4BM?d2?Awhim4_o!hTC@t*97vVibLG;p4X40>NRm^vfgk zhtN6TnTXtCjDlPUAmS1m?3q?4G3<9q+OZ>#3O)+UXP zTxq~n7l;N;3M?Mu*ZQEb-vcCKq>4E`hdJz!x;zApZfndMZVI2_*F=l%wq@~PrI3vK{8 zXW%lM|B{g@3Y*zhUWT9}+gSDo1uw|DprW*KY_1<15cZOMaYd-c5hZHY1LF7Zl;+G; zh%OOXebp>WcZhZnY7A^fT&TjNWQxY9RAVkfBpPc|QMssBBTD=62C}k%S-}iT-!j9k z>}*)e9S7fx>LkBjsX^4ip%A1%)d)}t~nqq zUyKn9ef3crw@QA#>KUYf?w!xR{al7mgQB|vb)vNnCvDido-8rzb ztx70s{klnwSeAu0XJh@?G8-b)1v2*tQGV1fj(XOoqsp*yhlsV&2>>(3N*OoPkE@#*-C%F&jB zklV)2#nbZLrl%Hw%lb$=PVRDOBUCq*<=A(V02xLH#qHvd_S^${ zNNTe4Fq{q2k`>Odl(ej+2HA8$$}&30af=~8{@-F{17=}zHAqe_9`BiUEN=wz#b)JO zu<+c%*ce8JX!U7@d=O3cd*H>!!jB<>Ot36l!=aDOqeGhV#;Rno5*XaZH$6J%$z#H2 z^?JaJMQUjKOQ~3uXx8{_jyv`x8@qTMzrV*Y+PN9L{BV94nPOyMjt0SkWwv(cJiOoK zWt}~g$ivc$Rg*>{erSrr48s{%h*k^4*-f}IM_9oc;h1gCEV!ZZ;Qw!y2H-CV*al8& z{I$BZtggWfBO?;a{~%YcnxDx8aq#`@^`MX1J{b0vG~?b^cRov%F~WixnqX$>!SS9w znsCqthOFW=>k2F?M!@d93;qD;7q(rHFgrlV2+^X3EWwy9gZnu0%Cx*FcZ}s|O{&Q< z>6X>WfMt_?WQ~DahHk=JA?_yme;`O26!BTHGGhuTmpCJ3A(U5 zu-{p@^FY1Agxnmyvx*G7oWqnrScuz{O*g~ykfjQ%ey#TIB5)TGx|YTOF}S5XT}A1V zcK=uHbs+Lq*h>ITw;fEvUCyDrHNkQ_VfwFHp0N^5NV~|CtDa{TOBR`=VB82a!!@Nd_U74S-DZMCBKJEF-USqWN|yMR3lA4?x)XuuA!jiQrb?tUvRGfxA2xnH`y0f(IZ38se6 zL9rgMP*;?xrvu#&3LumF!R@*Dk;Hgi6`iT5EKwSs;?Uwyx@;`+%^xr;cgl2NT67(J z!ygxCjrD}9U|E!zIlKNbfoF-Y8Fxl8a6Mva0cK@>D81GU;_r%g zr2)%|J45vs!YyT7^eY3S!o(ncHC}~e>wV1f@8@(b(53EMSt5Y3Myi&tAZQ(%ZA&oi zfaJ;vtIUpgYw-44Z@)D`g3SudmEBf(Vr`dsd#o;F2!qt!DuqEAxk^!BP;M0$hOk|= zc=*k4v-&^SexWwMZ+cVT@SFW|GF1J3*BP=}x`M#8GBn6vA!u8FqZwV~h$&<;8*aTG|_)R$B*^ zgEb96wc*VktetPkgQuU8MBA*`6Y8y;4$^UfsI)Hx z5f3v?0ivvwTl_F2TjhZj$Vli>pZzhya|b5eR#*!6!{d-`)w_4i6QEHu;HDq8Xkti@ z&yz~^f#m9OVSI!QAHw6UN{=Zj+nzyc6%*!OT=acV zeVFuG4B_GUh^oTm#SK;~7QA$<0aDEmi%rL_hNZ+(#V0hO-FoirgWoHedJEHn^1pcJ z#UZIFFIO##PB7GjFuT~Zz$`F+(F8)AieXwthLg;P3!5$L#1`K?HUWa{O4}ER-sgp3 za4rZi1H0=IK*gf;EaZDx@ERWFEmSI^QE>h5C}l<+0(w)$`a{!~#J9~y8aXRiIo7m{5GocI7dF?tM` zgjwfP$+R>xl0&yeFx4gpbg%yys8L5z~hGbGzBa~RQ zY@iZLVHRkbfTqM#S{deR2{zq^CJ(nF5*MEdGc8P#NtKWr#`^2G;7f4nH4{`N#Rba87drwG)q7Yf1j<<)O@>$Pw+Do zqv1y_6G3erJTU<|xT-j_1cj`kmv^x0npFcQ=M5-pckiIw;op|!sM9-B81Iy2J_ta}3$)`*R4e%xp$`Hyq59msl6Qdot+ z8cCmKMu_i8Wu*YCNr{x(@cZTOq9jcDwo+K*yAgyrAcJz!n~b;>t#;n%S`yeBG9pM-ZN9NrAqcN~`WL&Akye=iwwPbT>s_)dcC%E}@Olq( z?8ILeWK|8`q@lqk4!k_~HlYIBbP zFkgY$41D3_y3e#zJSY`4;pq&Dov_xqqE(5f3NZk(l3QC8NgE6%fOTMT*jM5~=f!MIPOQZC_AK#YLF4=vucRppygke`b+g4N$1^YYj=ssOmmu zBpWMWrNJQTQ3KT}f~u?qGtV=_woa^VkWNeA@z^iPrXW$bEigQlXeTH*Xf{zVRTjofFQ}ie4zMjQlWU`aYUQhi z@4x`IPPFA?;m2PJU*#|$j@fXA zgNRy=&9{khUo_eB3X5{Stj_~6HUL{^WiyaE`a`Ovu-8gUrBq6HZv4R)6~%IC80(GA z+^oBt#Ck9Xv1^bMmziGPlq}R425E5#S@wl`>7t%Ye5#DwX#NAUARrnHCyX~pC>W#d z0O~8lzJ#^~)>p$Qmr>b_wU{A#khNY~7Aa z+kqrjg&ok7+XSt89s+8{gsyemX!Wba)EkO6qE}0cq{4R36Og$CE z)+J@LcFu>)L68|^AlcO2wz2NBi{M*f?bv1@#I0fvN}}w^cciLXZ0sBG$OB-j`sjjY zSx@G{2rYzz$y%Fer=^Z+`s$uaM72B6mVN>xih*b|zQ9(6+O9WIwJ)e`-s;U7Ago#i znJUHP#$9UEy02DAfu)N>Y)3cW(u73-23)DAz|<`BK)R(}J8eax3a-^q5L_EKq3nwY zD)E#_wN0R{11*%q226EI!&plwAfthL1 z-9fNR+)aSo0x`FG^-q6*tN`=|VAA5C{95&Lptic-*jEVr&RZ%cn=!d{iFIudsg|Hr zhurwP9%0uxRxs<~2dFsvoh$)lQ#2_f)Uu3BAqTF+!k1e$+l|Jwa@U&gR#{%CDD%#` z>bxt9fO}~{wJmfc>kDM3%ugGB?Ts4(!wCqf#8UYIG_at|-~8j4d@Gl2X^vajZb7iB zCKJZeAG?=fTq0I;0MHI#(FSr2Fs$pfR}W@w+4@%tL)P%hEUf^_;9^Gxajdms_|i8~ zWoGVy#9n2ufNt4AS8^)_S>i4AS1T?f$*N-+`|K*$KwerT8w?m~AeZb4*I@~@*nMeH zenBvk?28IF0M*79!KPdRc$IR{d2tB%r35=5xuzFpVG*c41YQIhhHW=@YIvInokYEyg*d)uZB9$t6}j6vI5W7MvS@c);k^D3=*t;RhLoXrmuKwu*EsPwf)UnvX@` zt)grTIR%i^rIk=uvxrKx1YK5E0a9xQz1HEi#93~JNBXh`q(>IaKJfiV3ifm$$`%0F zt|M*qK4oQP5Fq6Obz4TW1N?w>YLE;&E;yTTFD?3o(Mua1WE_IYvH5#UuvJeo8f+2a z1)o4&g=S7$uN`AanRx+vW+8wR7iimf0C-H?%~oP22$o-9E!Ejg_Ya7)g++NG+JKV+ zZrPDFZZKbf90LZvb>5dNv~Cy(_h3S4)-_uZFJTwzEH`1S<#o@!<-I*Vb}xw5kayh6 zLG600bg%Mmp=?QWKnp6ZCX=$ql7i)*4N!5(v4LPKat(K;@4^hARk(9bu&a~s-9p7? z8A>f`(m<+7Z~aYp?Ng$KJd3lcrp%18EiS)v$5CY2rFZD~k z*n&~EIN>oVRn}J_Ei(|1-KE{Ca~avSn#bD?NV3I1lPs0nTUQ#-0&f+DAb)HDORPHa z7PmIPwZB4Jg(SEa)~e3pxBh1woKPnyU_t{eq}hl9vk1nu4rZb96ZFhAAb$a?&<==z z#le(!rNs($r(KYhl|TcGwAO`8Mgis|$Sx~=R&fbg?5j=jF`Jf=aS6DL_agKXaA&n? z@XHFc@aeqmU@ML5&;I^NyEe!!&ZWSicHO8dU5#jIh2#M&=J zxujUJEGMipQ*3P-cXVLSGJr8t5?UN&AkcE6UOMpVl)CLY>b9yZTMo%qk+c{hxRo?J z@5(G$_JHORYRSMb8(^bUoR@(u0X8gd(Ez+n)~CTdvR&t31TDO+x!kd!VME-dcH8up z`ZmZu;IRqew-Z&l82dNuqnhiIF8r_2`Z6fV%K~VvJdAEwnhDGq8-BY-b1cm=i&cbZ z3txd6lPQT-WnpBcVAjv7m`8V z^=dK&fV~9&f>@dKJ9a6kw5;X+*r^wL;9$V5h?k)XbjDKit)$!%^9Ay+J=Nt6m$?_* z8Y`%Uz>A-^t`0!n!jU(bdsfZN+_aEbX!eVryFmf?dw9*j`R9aL;4MZ7wGe;Vnm7Lp zv9{Qj(5tRxswm@@e6vX?JWi!$eywMHA;cCWta33!;_y>dm=zY;@YA0>|25+8^z-4_ z^0^EFm(^p2zRX(~yJTgWjna2nKo*|u6MP}u4yf^>01P$+a4jyb(zA$Q@D)|xJ`VUW z3w!H^u&o&cAJc=rF`40OU5!)-eA%$ z4)BSO@53XKc{Sh^HU%#plMPlOwHnxumw{jrY>Vz?6m%^oY*a_tV&;jo5_31!vaX8B znvWF}b1Uh#8293Rw2B9FwfBFRv*5PZ>Mk8uZF`wqOFM%27qd(b#vOHF;Nk+gU_e^h z5Fpk{#HFIEtlEqT1z#QIhgVX!UBjXM5QAy=(_28s)ny88cyNeSA_4~+;)Ap1LeOKZ zBn!-@$mm(d#tfFGU?pRMTTYE>!MTZ#L{ybg2(G-clQbl(}zcv$uRpo`b4M zS;0nRG8eF<3t)SI1Y7C0W=N3ffMkmg({1-U&&n8k%Plu8feUu&O%df@vdD^NCGQpr zO&CI|WlIJ$SfFg1P0EE93+1*&)t0)f0uPpb(ueH&(Z|wNRisrwQiYuAYH73vxPc9D zfr?7kQj0H6{hj`*59h6(3bZE()WA3OkT+Q>W!VCy;oT6g+$Shi7U(=cD`>l}bBgcH zyVS@kChCp?Hohje@_v@^TVCpv^#G_WoAtYLFc(F^hDgf7Pc6P*r9ukwcUX#+dfFxz zQRiDA4^iHW=u<@svYuAnZneWSD3A_cK&xh_jdGpzdj54E0K(2vc%@9S4z)M_!r0qL znYy-=Xc1n8_T@F{ePBaH69r!JLLAG#Dj}cMmE%)LTvEbP!FRhaQ0n3vUHqdAiHrZh zvt!ejm_o#|*Rd3$RKyZ2LcYa+hV`#<0ToJ(LcJ`ddbz4QY<=$Wt7^6-G=-n6dY+*~ z;n^=eqzGD)S@)u~_q2Zk3zU)YpG&;ibXy+&F?<@mj8HAYf8nMn?mYD*|IuREM;HIx z@g*g@6M4yo`e#C`Y5Zu_69H{8QI4#ChgjEa|CT^hsbm%ZM@PcRHPb2oFE_kcuEsC`?NkhU9e*>dC_tNc=8V+IWfA zZQ+3#{#MTSBdt+y+4$m3GDE)45*f3iX)@|`_4`vr1)0^gcUR4Atd6#E7pett@vWED z4YaG7}_>ILsyQ5p6I`}i&9r%rJjlK3m z{0hnN-di9x%14*oq?*vEcRt9s=5LCK*g*Vo%U=)wvOj057dlws?Tn1?dSjzFluzt$ zP<0s8U#P6>gyH9-zW=#te}cx3&^iTGnsTe_Z_QO7j(<_as`VfKT;!jK<)2tTsVwLZ zI{ZbUmmLwmE%XOocptq-@N1*abjIIyUoC*F+&K&bY4Gp1f&*gjypLXbw+qKNLhjzB z(+2h#{`M&J`ayq_*(-(c_H1RO(O+3ZShkn5F<#~y16~SDhT+9J*~!eJzof!%*zg0P zbTqO=uMJd1V18472O+W;otj@y){2hi6MU?q52C*%Uc{;)9sZ!P7Idp#{er(V?1Pzp zgIGh2S6-I#9$%itUmWAVEix5#*_$JNnMgOsAYVu!!JJXQUr+?suMr#2siH(u#JEZ> z{z8V1R@HGBz}~RmCh=$c$l?a%ocS*fD%7xAcu)h}6^-Y*{M{jB?o~`3o}DFAi=>|h zV@LDzMtUR~_PawC-VPLEgWxCwE`E(<{2Gzc3%_k&x^Kzf9p+RZvW}g!V%ssUmOog~ z?-EpOxw8P;Yv(cbM9B5;5GbuuQu$lNdxIsmz%@i9IPw<=xY1ts>zzM_@}VyK+kyaF z_IpIXOsd!Tg{Q$1@bDXC1%8b{yR_ma0GnG*!+mg(N^_)dlyO+1xNy&kyN4*yZ!9W5 zV#&RHoL*~>ntDX7LMoi4KB$W;Fcf-Rt-Pa8`hmh{RYH{i^t%IsAF74)dj-}@@zAYH z`THc?I{B?VL>QOs$S9-{ylQ4yI@6y5(o1hbrH*2pWP?P}P4$}uFO>s|apGqv&#Byr zrM)tiaz-gkarb}cTi^cQDRVi7s6A=;7zH!_d&F8V*}GkTXDb--K%^N8Qu)KaN>~Ep zkbojs{d2VOy4MHdD3HhSL+O{fehj)+Aj2_1^0^i)lYN`?!$-lKBp zrhIy{d}FDXmUV0plQUVptT&Yn>#vCrAp90l!SV!NNAAaO5z}G#>%^RO3{2+>M`c*j z8He8^{C$F7B#h=EgN$D!;x~-@;o8(!5?Z}W4Ph-te0?v;gcp9Nn1wA?aptd15lD(x zb5(BPRTzBh#~KR?7iykUAg4UMyy_({gJ85ciylTV;vL)QMNmjp*brHN%QtP59P}DZ zD3X5BCWW*CAn~VtNdYu~>5qP+j|nR^P-M)i=MPlzhfL(|ps~3BIj&3>{=Kb<-Z8bF z?mhGLGcfw=FKYd&XyI1{x_*-|^!BJ@{`_%_id^}to&aCy4_)vDdvf#rPM zzZ>)$W9A{e{J?LCg1;r2bhd*q@M)V-ovC`6kTLD4dV0x$q5c)&sd(&0?HAMV`1jr2 zYO}}kMi2NPJYE;BP1g-~UArsnn!gseYdM5n@!GU&aP6*L*T!q(u6%8;+7H); zUE$g;mv}DkT5?U;)xvjh&8}V7OkOkEbxr4*aP{b_s~a39IB1zUi&rmPb+uiI>wI;( zDqWec>Rx%(mH1p0t{QQ`QtNP~&#??w{u2-QrJIH;(iNjCuV`F>M>&a@46oq5f8`Zd zUU@~nVjlM^@UgQJ4_!Wa`RKCCS8-T=d2!k3(#u*Lip%n);j)Nt(6@nmvhOAF(%vPP zUUKQBmxN13mtK-C377V{z9eGS#5drHcuALA_~c85mtJ!5@{328Ts*^}$)R!a!X+19 zbm2w#%D5%JhL>72pNb9SB+svEY-bJEUu4kq!Ouyb_I&aiWE&Q3mgVCOk_6c;|< zIP)9n?9o|g=d=4~oqhIMXQ#6doP`@cXYt-SJDnBIj`-r4aCVm`XYDvEoHg2U7EX4I z&e{=oEMP~U$aUBec^c2^?Ko=(K7r@eG@doU^vrzbXvdiahv_p1JI>s3<}%v|UmtD% zdi?re``5RBJ=+``1!oQ0ZF63Hy@w~ZpRqk{ADyv1Y)@zOvDkh_I3w7`@C+`7*eth) z`mo%7fKQ!%`WgJ59!?KuH1Lh5kIpzfpWfGI#>Nnby!*lE)YDd+hGFw*{Zmdo^^{ZN zso|7->gbeHCr-7E#;FHR!M+YL25z6idpc#b?UZ;*+%`I8n})Ehf6BH~PT7{Wg;R#x zPT3ahq<6|T?uKn$oo+iNox+ooXHFh%J2{^`*mm-^lN+4JljF7_7QyyfCx?^5iRq-# zi6^yB8k~62i6@nuYePf9WjN{o<&i#HPdst!iCZ=DrYmg?C-&?hZjC2~t)mmShOI*! zh7(6yPb^OC^05=Qo)AwMZ9SoL!eHwOTTjR*^tYaX`^*VpYvY72w@x_j_;mc}xZ@*- ze0(@=nv-yR|G49iKkoRpEws}+_BdYQxGrus3&)1z(y`6s_Hk(+&f;+k)iNA6%cqYy zMxXXEecg*=M#ml#jtR$hvCkd+hmKjOo@ySm`D>$NzP9RXUmm?h*U?+H9IZ(@I&29? zkG34GA!B7b5xaB#3?7U)h)0Dj`;OYO<*1{!;L;~<8E_G|{KIw@pW%ruM}?#Ok+5ZW zG09!!}H(;Jb+h4PG&6>4o?P$%~Hiwc!UK`eQSFc&U2A{ZQ&S$GvuSsjd>c*OKb;fk;j3Lfvtwm%`HJN$RxA(8yDOG+vZA#jERQP& zoJUN;@*y_UinKhe*sllp{IccCm!)M3%a`$5hMx}0x}0DcF(3MUw0v1uhW`Q{%6wXn zC=mds2~&)}M#2{U23 zoEdR~p}=Z^2RuqMae6c}ou-Fa2Q8-ZRG1D^z3C}lacVS;odM?^&Wouy-JV)MIhvR( zIjowD6Qj;V;t(f7Cr`vq6ZhUkX9Ay4ALfZ}XQIQ+&`Ir4rybg%6Kt~3Y1?Ic11@4a zcKYoOrU{Sn(bO4Y|3BL4#?cKoW)6`<^Tu$)3QpQL5=(Wv8UzP=AADq{k!20rhdARIgvS{P-{$-b6e%a;C z%Z)z=mtA`4Wszi>K&8XWF1;*pK|Jm8h|?}_n1{>uUV0g(oa+lR1*nEg_6tZg2v_5! zgjZ2)&}6T$7G&$}T3pg0t(F%LF1h%Ui$jgCk+W&;UJP^%7xqA<_y}6Z15bPfKD}r_ zb}e|j2>hBZieT2!g%`vNhTzi#mR)dQ#c5Lu!7I_Wut)j)`2|RIg22oh*gwya72G;$@QPW7vxZ?TpfsO50GxteL9x1nV&l0$&~tYtpe!hsSJVWvurpOZ zAX?2t)^N@ca2j{^1gTgOWqX2F0BqRV7r^@Sc=Q|VzA-xI8#5dxzY(2f1+8rm?4CtL zOR6R2f@ceZT9Iws5s0S5*<=%k+jQ0t#F}=@fpCGb9K()*-NIQ3KUR`mvvF)ZGwhhC z;np!1M0{rcde|Pxt}8f96JSBGAXzdiP;>hNkW~;HD~RoDCu7;Ovjor$+q~~Zkw;x;@bAM1s)pE&e;@;la~=-I~=Asv`!wg>f}jX66%TM*S<)#IBB0V zD>ezAdFx&QtB6%Y*g7D;hOPfCI@O+L+!f}=tr-9dY&BFZiLV8RHiv3Ef?%^jZ8%|h zO{o){31u~Ojt|GC6gc&WBO}qXIHE@qJ^To* zrnnV2qA|WMyCMSO@z`$jjr*KzV^}~dE)^`P~C*8 z#MFg~L(`@%$rP8)GR`u1_TW`y(k6$QgTu!1;CN7%9EwlrD7t}E`VVeaWU48)aZhAf zOw&PO{Sa^(HY|WuiK_tA2ny|S%0=4X*gDU(KA>LzA;N4hENzfT6Nj*_4=i;?4eRjV z^NpfD7_N0l6|)B7YIAK|Gr@_rnrla^*OUO%p|I5XG}l0z*5JIcCK6!Rtd6TA9__7J zwQBXM)umCYqiTOOH^ZuVoN&5&Ra&(VbGx(|NVs7Ykd}nHd_`U{TE3z*k`2qX$}5Ds z%U1+&?TFM{`p&WvoH`(%l4Zm41#vBaH7-xfdWNq@dACPCZ-r>7+yJT{eUX#1b8-FKg2Zdp0`7c z&Fkat9pCIyLj}-A!fcB}wgCdHI9DR8P?sWWxGrA%x$&z3u85bM+ZWabVs7$9Hs%%h zN`)orif0pfcGooxN8UunjnH44wx5lD`LJNObM=6F>-5#(sv+(oUMaHyJL$^4KsH=C ztGg6c=&!S+TP>)@LXu5a#VZHgDz3_)-AJ63-s4fV3B0RDw+MPfa1qXL5>JZ(LkO012u zdxoR&>~Pi)8&hWwj7}-EQZ$V_`YxhMX~k36%&w>cLbLQ#h^=WyKC6#2(&-8V&;f;2 zA!r*^n$GO)I1?L{yc#M!)w#4|>j@*%GqxAo=Z!}JrHkk^pYg@^Gen?vijn6aRGW;U zvmkkf(+38oJ(FDvmst}Jo50o4>8G^`qca>1I;~6LR2phBC>3>6bpO;1Xt}zS6xK_ZF5K*mYfWgw1AR2Nx8JdWa^|I zG4#ZfB6s3R@x;FPGEy5YA(BEbk7hi7LJNR7c|wm8=~gWQ z@iSG($%CyY9KZDV?s1Sw@mWLiY#vYA92iG-37wc`QfGrhIu05tcLP^J#7VW3GHNuU zG=0>_G73dAD6PyURX$-n08>Wg?<53ewhNGmA1|sNktXiCeBfHW? zT@(d@b|{WkBwE%aS{|{VOi3jaAM$6|JZI~QK*QntbnMTVWJ;x!a5@L}Bz{U44Vw!9 zXJ0#nPY`M2u)TT$=X!|psr1q#4;>zIDAZ8$CW%uFnh%Ma2DZwZMpQ<#QEAvjK{Xwc zHZ2^oagz`;np7&#OaRT%#!Yckb7Rl=v)EWjK?QQIA$LxKJ2NMpgNGo>l9H#BCS*)% zq=|!OMNOw#20JBq=CS+<9h4wC&zmmNYGHO z)Jzi~l#m+Nj8@@k!DKR`1fxGXO3OF8vzsj3+mI-vTRmbI4-mWe;(vWcZ# zPVre%Z5m~c3AA+X3?R&~q(|O#xiwXCs~#?wQ|hEdZxO zY)Wm-Cb`B=fD}vB)rfPE+zRnEfi`nnLTiH1I%;=fr>Jq1YAe&xK%J9@o|vt_u?GtP zSh6IBN=P*jHRQxw`gp@=_YMBaKLSSe+MiG243aG1Qm_9T*QebJSU^(sodo8LR9&gN z)_ag*Bk}Z`r}5o?{N@ly+PrQ~@B1}0YA)5A|G=f*`%{KZP*Cm7zZjFR{6UO-5p61WgJn zsjU)4iJK9C2_4lCG+s65Q=ny{V0tCaujtmV^yhqrq6|b# zJLKj9d6O^Y#gOrwe{o@Ir=U}6X9GmpI4AC$wO98X@%H`zun`|YCEn2g%U0Ub33+n- za$k5Ese7ig7x<`2p7E@RxAxRP>sR=aKf{jyAyWR|pRi+y*Z84UIuGWo!J#}eY;Syh zemmq%UFqw6O!)pDx~D_ugt2D)`do#We3ege6R-0VrBGwcu$}6sUh!jy&`EtIX%z41 z1(8D#W;i3A?uZ#Hr52jo2YWUR(m08g{@Q-237|MmCk+9X zVQVSG)YN58zL~E$G?7Eo)~4_i@9zner2(Y`&at+MgW`lC5HoBghYo{B3rmln5@9~d`eD!Yc=mX`V|{b_8NcDJAI0x1kY%c2_CiA^_uYId|pox)IJbC*MH8(?ZaKYuAd-&ib3^` zeyVzVPYMNH0x`vzEzZ_)2y4G2h#Ga;dwA)bV9GVCjW}zOQ%D+DA0VLOqesH((He-F zU8l)Huj=*kUO*XF7bMGwZPBQhlh3Z)zH+!~B|uUNXIRm9@iQ*(=uLiCYnMZXpn;kv z-^CXYGx;@PQ^=`7TuBu)kwZDLSNh`@_e&{rM!db(JNpJ#C5PrxXGdt7EqiGnX4cz; za;CkOr%X8=^gcdeYy4gwEXf!7Szo}XxN>Uhs|XpU`Vcs$r_$6A_c-mZ@7EF|6NfeA z%1O}UfE<~@jbg?ykum8DFUK&6H6ChA^!O~_$oD%GFGFY0hDI6Mb6RqaJ@TdYOH4w0 zsLjmx`?38WooqJJEzmGq_-3#ooS}SMPt`K)o)=0QDss&)-{yxJDLJRQ2}UeUzl_)S z^`2f(S(>pl>>iLH^)f$R-!sUh%o%oXAVn_c(EKL#$~g*|_<$;KaP!Y5+2^N!r8~bj@AYNWs)lLe>;& zrb^%Bd;FoE!T;r&tA&;6>Yh1Dvq_!ptNT}7EvXaxxDy!7R!Tc5s565)`9fcB?XSYI z5Y(U1IrL9aC-uyNQs$HmL{F>)n&eKrvjbGwe9?jOZ-LC*xfS0`?U zb0A#;G@Fzzo0WGo>>T2Z0;XJ};7vnO@umnexL(=DeG*?lC^^$U#o6&JKHvn&Njxjl z8k+EKeV)WApp;FNr~5^r*z>pR;VivE??AO|ow)!NQ^KWQvp3GvOLRFxJ0wa+NxVnj z-Xe8MkEHpt$JgoZ5Y5{Gn*MqncF_tb9XBK1zWce^LII9KM}K#2hEOf&3JnBJUM00m zSV0HgVaFxaRI1*-(+F2gc18E9D^@hC~ z^q&3yu=Soza$H%`=G=QDGPAN2Pz4Q;00Dv|s6ed=P|I#=+2jx34{Nq&yYuc$&#*~C zl&*rUKYmU`7Ux~dtjJKn=g0iG`(fm6(Np*tF#7a4u=AR*6Dz2Vq2USL6Ik-;Li03< z7J=rj@9I$eFY1#Arb`(}WA5(Z`#o3`?bE+?L7===p4{<25uQHLft@V|@0LM*Fp)MM z%{f4+gH3c8!<$R{#}9Fk8boJL#H*+ z`@Unj6N6~jNRK;5izL#Dll1DfHOTYnYv~I`-xP#4OQM0ECeSW4(t$-g0jI>!D+gxI zGJs|nMkRt8F5yiZN>NFD(mm366;3TzZor#{&h^W7gf7I-bs0iy&f6`2c97?cF+K2z z_j4~nl5x8g&LiC}wvD297RK?s+8!Do_ysAb7pD*=I%i|NuHUtb_pN>U0^6u(@*>S9 zN2sUk&tE{X3=I>GM8ot%J+)5e=~X(XX6&OkG@lRi^u)>zw(QQz39lkVfb|Y^3a(QD{n&;-mM*2i&;HD3KN}V%gPCi&m z1)lqZP+5ee_KY%Bw@b9cD%uTi0IYkaRsLB$v9h+a?yv)5?7VH#c2qhc1-E3oO> z`dV4r6`S%|Jq?mBak#L0B*d)Cb!7+Gw9G00C)}y2dZ!tCT|Nn*nnMkh5;pzOTps?M z%(=mxhOT)GQwu&)eY&|#qbn$yH-5cuA81a=%P;qjOPUqmsOU2x)exEoR8H`;{^P+f zA!k;xC4U7R6?^*Elqvxe@T@=IH>c_zpORQA6X}4Np0ZoM^jQ;~I^vXarq#`EL{gMZ zVoO_tVN|@?e%Q_{tC=#63Plq>jjUKnbDl0i^CbG_!hC!jH+D>-KSuv6HY$VFpf^+CmD4~&d+s|X$?9>>f9G#>PwOdVKc%_ zz^KzpRyG;8SCeRHqC7(r9dOi{rfwX)Tl@5(eXLo%|NKh%_~?_5KdR8FZK3AW`Cl*V zM@JuiBowvki3(aotG&t85hr}2hq^6msD|z^tOeAG-sl9SGf&ok4<_ZPuMc*PHYud{ z2V&Lj_cj^5Kk?o)^v)Qsmv{DpRh48~d3PIf@%f>1lWrXhQd-A~=xV+60Sg;E3T{8j^7&92TeEn-d%Kdrr8-#P?V>zfb2 z*xgVo;A~oTgg3@H$=Vq>)x;= zx(d5WA%#YN2TJ1`XRp4N^LJ=gNhf2gUn#E%W`W!CTK|edOnmAqhx|aXyS|~T^6GCs z1!y_p1%0b%w~=#NdwK5_>81Lf2VGZQUPliFOkbJ>vX&R^7WFv3iaFzGJ#G6m9vyJImZUt4_KNfg49m8ByH}4m!V|#Fm=L3gY5RDwt z)U)?;;|yqd5+Sn}KOP`&iZmmO*F<&Xzp6^yqGYXZk*uA3eJM=(P*rumrufzv&-d*nX++pS_vel zj8HEphDvednNh}+ozsJL7*p_g2vd$f49!E`M8=#7DJ5m<1$0aNoOX9|M5yXy)6!eK0y~XU*|;YG3aJVd{4Ix|am%gp)zl^LGJg`TD^( z5;%wSlJ(B|)ghdjKSd9T*cT%9`t85!08vGmmO==X8tQQUx+GB|^vWC=HRbh$I{ibq zKI2S6(w0aHpDMJZ0lYY~j|7uwl*zhoB!41{^1RvVDZw@G`v(+ta%l}LT?&a*XKSAy zY0TbpU|k13})eP7nB-3HHe zd0H?1XE;MGp{6kut7!13eV|fHKm2I&(ZPpA?t3o!j}Aqn3N{%zd&rZKe2S`;PFpCc zzF$7L1DbkzAII*)LNp2HTzdaM2Ou4OeP4XqzrP*t@Ln;(-hQu!-*cvn5;E$68FXok zFDWgrJMw#O2`pm>k0o>zPGe+WM4`R@?&TX1Q!2MF#B^S^Pd@8hb+^`_&n2m)rcDp! z6G#=he>ft-xn`xwYeFzq#--?4-WfG#1JzWEk%Y37WU-`F)@iZZQ~+6U@GLngIc zYMFGR>H7w_xy-@fSznK&QF(12;(YZrkK{dp&qGM6Ig=NqqN*PFRh@}64`IsgmHWm@ z!(~`L+bc)LPwbx+i)etSj=X*e*u3)c6rMCxS|9D?|2!~zIzsPz)0Z4aGLxc~YMLJT z_}$b)g`E4rpH*sT*g_#tE2GTw71P&DUrBl~W+v$!r}tqd0G)*}Izu__IF#q7BY2+X ztmm*M#q?Mt-V*7vDUue_^8=mo>y%P|^S}IpfK}g{mPDTo52$bxIBkN{DIi*(+4GYe z${SdPu%6kANny}wFzD`+%-i*~Q#AEbru5v)JW`8<656NpNSd-g>5)9jXSs1dn3dUl z29yS&v<>O{l4JNPNPP$a)S_q~Drv9qyl{0gdP2)%)<28*m8re+NzT-l$J_NCfhtlb zfU2lG(kGL6+eCF+pAv~Btf2|A?fRadTz|BGv|V4p1sa-Y`anx$=@CPdY!y#>5_X+& zm|WXSCba>yWdx~SJrc7rhVLHP_Y3lk2EPhl^{LzrE>%>?XM$AJP;u*wL%XuY4L{2J z`Tv>uz*Eo3mz9gDm5yJ{vmorLVAKu0?3eYCJ_B^&q>cdp|q_Bhrt41(M%vx}S z`qvqxOA|4y)KDp;jw^NGZ$j!Bk?SGCsLi5qY$t4;+AkVW`3*?54(g~W7MvBp+SiF0 z^hneyRWpAhl~e;-D_&6PsfeZe2Tf$VtqTw6&nla=FND>%o^9uvlu=8jp2-uWKhl%# z>^_*w33}loG70k#rhT9W!8^o`ftmR zid?54>)NkP$hyVco{zpYgGUcYA=O{DlViZ+z+?k{Q;AUcS(heNd*< z5`*|IX7tSG0n|Y(<@!i~nvPKmr<&r&JYF)X&wOwuX=K*n5zSD}Eup9Ko`Uuw)s^w! zS-wTo{!8cTMXN1n)w(GerNV4qBYUV5Efwjs#yi2dx>vqPGiVq>>)*&I>7QCh)oEmu zvPx>0?T>6*MTs8p6-LHeMFW%72JNxfhj-2s(`h9vhQE% zrOfgPD`6P@EuLCrjiM;MRryEy{kWHCxI(xgwSxiD0MtC$F!8tp-@<=Qmz=LWvN4Pv29h zp^=zRc=fB zr8^$vhiJ;FtDiK`fAL5c-Ovujto{Je^o6;QbW6X!kY<{b|MKE?-qK63Z2w}V-pSb7 zJ%2aPfLMJ!(w~Z6JN+ff3R3MYbr+No)@O4YOeLrKs*9mNk)`(RHe_lQRm1$M5-8=8 zh&9!lzX#6-xk9gu`&(K_i8eKd=H(|n0lyv_`7_kN{dE7-UqKZm)pT01lnPZ7U?-`x z7Tl%SOv{ZXR;^DSiB9?TOrH}X_Vvad%qo6`VW3l#@=6h+y3a)dRKVa-SIMG`$Em zxiu`MzA(o~sLFQv?na-wt2NmM!}g;(>r2o4&9VK)FyL=WE}evF z=Q=skpr81BIk{s8DFCaxqE{4Ga`T*R8FM-Cv@rH3*CfcLD(XNF!V#cRzC_pcwP{ZF zdjL1@wSZkew+nS9@RbC6MPVjS{MS~eVL~NYKSO|>GW$(R|51-F3}oAGgQ%V zUnfJ8>2AR8KYs3&QvzEpsWRH{16J!l?s>GY=|A7m0#JUw?bMR9^UFWp&*&1q(;qAI ze4SZ~Em2xphhEb=KUzLTOZADRS7cR5vftkaS^d>Xli`&7ye+AO?Z*t$QiSGf{CM{7 zzNpcK8og58cQw4FFsr$C2`RSe>JSgWGFSE6Z5sl_vJCerj5_2LtLdJ8xew5eH+L<( z`ei|GBJlnYU8Y@dDH`k~t*ORW z__clE$h;+1vi0dV<-{}od1|oXMRlZpdfayEaFdIo9I~G%;Z#1ge(J;gvwah(aP+Rh zG~iTx>c^wLdLZNkK7&ow3^k7Vb-!XhEvdS8`kl}P)dEh$=L4ixsFp~S?W)pLVOmkCZs{)@Om$}htj;;C zaVYO!d+*@=_ug~3o;*`|hv|J6rB3)#Ct_D6og8X86a_^Zzuf4t#1K zs!ghu$-j}cj>uYW9Z63uf>n?9*LdL3ey`-yk=axd>nb{Hd)uO_3QC2r)><I9T)P+y0Q0t6?b5XOGEw9dkOmn7hn$5&L+`1lotLi6X>VT`|g}dXm9ub9V zRFyK^mvyz`3`k+Ny=X>FtyNk3=QZbV=}7IK-xbJmPnA&Rh2eI2zN55MmedR30C15h zji#z}GJ+n$rJCvw&61q{_JQ9g0TA#Kpx~9T9+D`2k z*acg8lKz2xo$KqQl!e;8cq}kg3V$gRP~@U~!v`0cCbVXFs(W4Xt6=HI;qCkygRvK z0roMdb>%WRx~Pj&>0l=qYESFKM?%q1TV*iN;9r)4U2qnfg<=mu)gGAb z9@-CVO%JOAQRUJqz@8d2it9$}H3yj_2!ZlA*5oZYu9rV`t zMT4z@uKAo7v8M4rAzE^01jCHo{? zoeEATk%Y2ywJzffaJ|!671%ZwZWo2TB;qYR$1*%wYVymz*Kx{U@g2k{iOzk zrdoPn2KBw{c`~F9uGEs{rlQmc*UwN_yO3B(*jr3(KgtQ93(fS2N@W4mmVp&`1zTOg z->7}c4?l6>#)z$O_SaVG|7Wi}m9}bP;r-tPE&DTvsf3vBxP@KAVUr&ffZ5v&`VbgG?E3KAX zhpwlB5mg_kbtvQqSi?Nfe7B}qHO&ETgEF$7MPi+Qz7~4wl0*6B(+y*++xI_H3sk51 zzm2HXC(6-FLZ$zyQmn5Jrd8n8UI5>TkW?oSbsT>{R6_WReEv*V{l|_>Sxq&k&I4R+ zP+d2vP6VM4Ycq&?_79DMsg_wYL6ya(e(sKbQK(SVsr~l;)-d3gl3FFaX2{ASYqJm= z$*N8&>omLc-+iYVTgy-dm8GQRotd=P-)so5In@|yGR1-_S@w3IEV^qZt?mb_T4jw| z{?J>CN~Cb1o;}vJKNGepldo*Ph;<2Ab+Bp@XK4?x{MxyWalZ=s#i^+bAVDbiCU_VaWsDkY_#Au4)Dnen@3KLb*)&0o70 z%BhGHC_N^r+J;(R-M6GF3?)TXUcJv{3ahX7G5{c{R#{F%THQ*1)mu_i^$%cESMJYV zmQD(KE*tyz*YOo8s8vIL^QXN4QU4?wRgh}@NUD|aTI0377tv9%18f7K zaWQ51Z(i*aQ&+|SRSO=iI01ZE=&LJ3jLJ5F`dn(LJwj@CQ@}b#`=iJ-71kiycJp3R zssdD+_)pTRI?a&O&|lgQa;xL}whO?nPrvo_{xeTMr4pztjyjUEiqbl&tx{5jrGt?* ztD;1*_w}N_I*3W}1(;OJjV)s;)QZSTvWmy%)aWzSEt4%kyLfkqQ3*{?r+2``QcWb= z>+j$R^7?iLoB&bJb8vzRh82fW2xSK=wpQt;Z7>DUR;uTcTrska9)eyqrs|5)#~_N5 zI*O}9Y-$VJKw1az3LvMV!rJP3^;&mz8yJO1p;5)95Kcv?x`9g|%1p;{so@sSZ^;B~dEdg!{LLAyT4M+%uI{tgz^&shCo$Z~JOCLP=W1A%G`< zyndHhiX|Y*^cAG1`Uz7&mACbN@TbI6eaq>8@tcBbaA~|OqX6;#kWNjX;b{HMw?bJB z!Bxj1RK4pfJ>>=1mV#4x0z{~0RqLtt1^CR8>xqpl+YceNvydu=Jt>tmt*=?i&!e_J zJJE-BO3l8RHBhz=87g;0MQN1u*mX<->uakZt0hxnRIB>4u8N?#I$PbN7E0f?x^FA1 zPK2z>98O4Dm5iDPLi_rnpe}CO;fml|SN;Rv0H0J`<4Tv3Sqbo?sn+GYVpxOhF`VVu zKG#?YsOjI;OMY&oT@H{v`)e$aTG6FtRZ95hi`2g{SMVy79SyJP4A@6ZCFT#y#`-5x zRPqHR%Hk5($xIb$D~+YFYtgVjel_`@{^$}@-A+ruF5HUb8Y%ylui}DDa&RUYRC$ry2rpm!8i>>8v`^HtRhr+WOA*%K_OQ{Y~ znM*CIt^=p#b7ub1Pd_{M#R!mPQHP)ELG`up^t1b1@@CBbt)2P+oyrLSor16J(?d>s zua1IYaS9wsPfa{+DvOGieYZ$urs|J$uhMWDpBfdWhFGhsKHCV0tsmK|ih_Dfas^z| z!)jP1YGs@at+fhwd;Qgj{P)sXNmiLuhfaXw1_Ax6;M_u(>dp650`RV6)Fa_(wdPs{ zv5Ri0KF)J#l>7CE1B$GA00tR$XgA#lT3IRO4t}qUav6Z`P_e&dY1XgYT0(FwfwHTr z2w1;$U?EoW>urarOs!`FWP@*QD6sTe-yWpbyD7N_vI<8Xx57kF-_kR=2K?aw=N@wD zm+G5$LvFRiDzxo!5|FxLnbhhVgWFa5s=WgCxt^2jwY_d9KqE92csm+i+iQ1^p{<_| zrTbW7^(A99imTnLXsfgURJy42J2Uc*FL>XSItM^oil+p&>n|U?Lc(e?td7z>?UbV3 zw7iJtwY;?JCIHEwcNi~qV|wuN5x)hE8hw!%#y|G+Yq*F^=uh5)uc zVR(gBg|T6D^+osiF7hk(fMwl}0GFSDw<6b3T#Zw!u75(ah_Iopy4pD) zh1Ho* zMOW2#9v%Ylb}yX+)CPq8HBGTl@L(TM7e5G_+N%hbO;W4)1q`_Suw$y8{%@Uf#gA## zAufd2Y(;IyOHdca0Xtz)Vtd*J6y+KUP#1*jvw8XoRyR%!rKi?my={s`Q*#NjGO?n> zrW>F?F}1;!m=;3qWLPa_zW`aSDHuz~{*jorr9+@wyJO1LEq{6Aa9uciQS54xJpsvf zS}NswQ^DyVnM#MmVcg~Qoj-BUG5qwDmSrYcj(t`XJRwF}nCEk(2VRMBkx<&L=3UxO@C zE3Ny3=haHBQPxI+?6LZ(A;P|2Aob zNUmE!uEy4059{|^ei(!+=9RD28mlV+oX*-D90D1$vaUJ}Q@~0VPNhe{YU}h{ck7E# z$9gGtobo|jy%zu*=&DzS?o*!0AArj>vi-vz(?#WJx?2UaPG9NhJLRig^j1V#+XTv2 zZ@{wk%m1+$N@{5rwWmOBp`9v4*f6lt#ec{na$77t)z|nsRPav}zq(^SRG!dbIu zull=Msjh&o=Re;wjI#V`JE-)tPcf&eld295R`cuVXVofG39is60smc2`72=pxE~LI zSR~i*vm(kGY#~?@Td^O?z56l)ocws(9aSb*^w$R;>%YqmD~GI{y)w3L2DVymJja&vQk)av+83ood%t11uFJd6RaL}!^S8~ z1MeAK-%#XAo%<-NrqfhgWA|zI?jP}pCIBn1Q3#+A7My#e+cgUTRC#3@wM&3ZR@UN1Ufc4q% z3RsHu!l;vAt59?fdJ0DuIW4&LaICIMHP))_?4<^0CV`e0?}<76^-pMNF#phhduUHn`>AAlg zt3jqw6fjXqfFf6ctb_Wh7aaV5reb5Ir~d&ZsgvkXE#VQ6(AuT-6{o;$=dmofCUj=* z3J5i%x+!(Ev?9Q+JoV26PT?lt33-}5r_mMA@q^?lSnce7g((Ru)$UMuPta4RbG z=`XW?+XJ=?q`Ie%ny9#0h;r%LzrO8GffmZ^2lfv1g7HE?&19iY$3%iq07z&ah46H%48Wurw{cKX%T8$R9A&xAUaeZ zJR}!@h}42>xwvKhHB{KtV9TW?UQoM$T&(UBm;{+pE3gcm&MttZPF*$WtwTENFKzvX z(CKe#ywGCcWtWfZ<`0FHa0gm=BTWTx+;sVz8nm$D;r0fCr)nS z1y~Y(u5r>h45j?y%OQ=$)>5F;ZB;&$6MJbHu)gX`n*~a=@#|p(vX+ugH`fHQ;?cCO zE>T#uS^&{a4yzoN1gMo0gjYn<6=$qcJVkY-N>zSQax?}= zK(%9l>U|_tw^%iit55dy_Mou-oBaaDQ8!qf158hGsK43>FcEax4A6cVi4+iRl2@I+ zT2kX`=YoB6X>;_-7>bK^;SE@%vFbk#&|amuE`g<+KOO%3)6YK@&d&-_$yx2#2@qz0 zMf1MSoW7cm@+(#W38@W_>P#*G*9Z7uy>$|X)&SJ+f0+ER96V}DeF!;q`MW1Dp`N4+ zRhCpyW+&h29SBp*9xabfGbg26BU(}nP`|nZdm1%k_AmML ziwbS_92~1U&)L$KEkaUJIOP=8(iitqHU%;XQu&?!m_yZ2iA0L;RI+P@E-BZKZh$uE zs8b38x=~1d2=eSWxOLx$)z8hG^q@xmKYm*<=rnH+l>qItEjTmLbMnbxPpzL!H076= zTER@F|MsRzDgAWUYN!qlM@_O8RS4PyoQI_SsiB(2PAJ}0M{4=ddg%$B6zQZ>{i-uH zJf~Z5r|}Z*jPZY`bXC{@vWQB{oDfc>lwu4BwY0wf$B>s2hz5psp`E&cDy34n0r>EJ zuSYh0<-H=UG&277q?UKLf;3H==26orH|<b?(-VRZ^+|oV;3(Ce<|ssYIO=sg}3S2t!SnZ_0Ffh%(v+SrkcB*ZGwa zN<9F;$-nSdj#c4h#f@sj1U6TYM*GpAiUEL_Rk%;Rgw#IN)1c;N@FpDT$$sjjb&yNt zPVMz!Usn(&*CY30UQfj{guwOLC`G((}*ZP6aQ!#R^j2(RR@QPPI}2ruve! z(t0V>QGpawpWg?|KsWJJtIdxetODg59A}$p%>T7~r5?c=4RlecF+{Ec3{#UNK>a+}< z`{hDOYC|gQry4cr+zRL9b{*vEO^=U%muvU~`V*Ftu)x+-6cD%%qr~AIdI+|NX zQ{2?$1*w}8=b@G^?9;1H!G28a?JrGFso4-(9N3V;)Bk0nge!wA7sqqwdnuZ#-H%W| zR?(`-{m@c14S_+^I1LWQw9Gy;qDeceDv+Z zaeByQJ^iQdL6&s!`%pxc4o`K~JtO^}@L4wR=8L>6snf>OrVjp#(M~li6g5erXx6Mh z?ZboGP7c{8=Jq6<)=yBs-;gzZU`kK-K7P}B;Z| zGqdJSj^(kTvhL~Mme+yCRi&XUa2h6iuXU3ANvCN_o2hTMU;aHr&9I`{jyedQ);ukZ zW?8Dn@QabWhxJbKGo0TKH8fDu>Hft*8LXc#LY2nM(?9Lxj6SFKsd>^xm5$T;!=7KE zrXN)_t{CdK@B7q4S?(m*BpF4LpSr4S=m$f$p;2T~-q`bu9^a`~j~Y6cvJG{O)IXo; zcY8#kG`V*`imgv}qb`l=sPNR=NWb+I->y>NGlu$7Ky`}#38T8Xip#i7gFlb77RtC9 zl-e0aN%iZW&;wAuk{Ojx$5jfY#HXmQ`tmYJdg;sZ#XliWLny!TWs>jlezc;Nzwd@B zI{H#I`7ahmLk%riLgiGyb&s?Co%q7OvTPJS~ zhE=;(o7i>zZDp;-4O0svz*L2*sHpl*?N+5)mGS_6>YICUlJaSI10Y+s3t&?1a-KgI zy5eJT*5+N$avRja6r zv#BirIm6FwrY*3wi(FL<@molz&?>Y1vEa(dE5OuQD|FLwxKA;Fw*jmid#*Rz^G$iG zLqb+3b>dP0*9MqmbqS#k0I8+*$?ZW&eey=9<^W0p z!k}8ktz$W>Zb>_>(5n^GDQB(>FwC|m_HO98zG@IK!anYXE3+-FR&D~&gB6IE3V`;~^vmt9fR8sRGNH)>l4byN{7ssP+$)icqb8F#7qPl2_2v|c;Z zy3=b8RH+XDx9YI@)MR@iv{60i{)a#siL0wZS=&{0C|BsV8YR+Y4e#&!8sgR0jsK5C zjgCL~~$^k!-f9{pE zx$0E%sI^gt{+GnKG_OweOC_tTST)vPtTHA2KSL*-bI{efsrWTA|A*jhIsdQV++bWI zFL#xv%5$|{9dK$VYB{?Ny&7iE_2<&Ax-*8US~OTu$6901U$e?8`&i4VI|Iu47rwQ8 zs>6)44k49Kq5JBzQI!h+a%x37sz>>~6jIdGE`n7FzH9<0r(9ZPne^NktLg_wYCYkS zs>>mgYOkpy`U`Ee3?V6k{AK-52scGiqpZXKy4jJU(okNr`(+NK_7B(A(~$a?;1sYs zT|JN=b!GKosiQO4cCspvtt-C;yk-McFza$#zB3|iJa+}FPW25AHvUQ_70`N+kGm9h z?OjKOQtFLT{d^a+4GZl9PH6vMLu%)Bf`J zE#OlhI5bQCC7q7Je_(bAF*VcwJjlS5ov4;lmGS>u52!BbqznkF(-`UtNaviL7^43j zS1of@^)rh6x6PzxQ>UtY=)|H#sunF(WkgmnYVxbxse3uiFXI-f)Lb>|pGQMg8AR7A zZlz20(pUiCSXGhK`rsoC?ne+csFn{TyGnHxrRw9Bst@lOSwoHe;6v9}30vXP8j&g^ zX_Tfmx~f7|HKP)%^75*lk}Iu4jY&SeRji@fk6!;7e$;nmSfx%?KZe*&>Z_E|u$kWO zZ&ThMS5*7E*56xB^*%SK518JzPt`07hMG@}rj5(~|My5WQVcMAV>?w? zJ#n#BdBYn4JmY`u^&NTuUT01}R`_p64*>Tj^ssVCt5tn%&jbB%toFJiC+n-pq0ap4 z&MSK{+<%2FsM`>!KKhn%Rq9|@y$G3};~-b+i!Uvavg!wmDJE21mzVZKbe+V5iSV?& zgmW}JrT9z>Tu zE9c&7`qUycwg04Y{8ajn1AN;@?>_mIRZ&ms>6$7}PQ{nKe)|~OXQ^tFuo~egVWrdh zD*)z9w$Wi;Uv+NaoIUaT4YE)OrTI+})oG{}Lwlch0hxM_p}rWA-c(DZA)Ed$E2cuY++5KeCiLDfcA3NT(T*s7 zP+3Ks6AsPQsw@U5(MM%fRj)V*r6j-6vWPXiXiybsGNUh}zUrj`yg+g4A*)Q!F_ZU# z(>?^(hgX=tckDH_xSEzyr}~|n!a|A)+s63*k=U|#KdN&2JHeVd4zg-cXrgF~E-I$_ z0ib40XQ%4L15fU|i@ASlgFyd9$*rEx>);~Q<(S-CcdhE8syuGm3LxcmVsoj1v!1^T zdIps88zofAOK}k?s+jU;ovL$F%?W;$rb>=I1WzM8<&nN;_N)%*mFX8@P9(D)7%>~b zSx?7~fVef)!-!Z}V&z>L|H(9R==r^)ksR2M9ZcZn&!w}v;4~d z=`5sH2cf#i^efP>Z#}u=UsC?IlopGxMD`WqA8e(wduRTKTGd|kO4{G!dN)!%q=f%(=&JUM4!=UxjwMw3&BFwn zsi*hBvwPeZZnmHAnMif&UxR8FD^b^eKH%<8F9D>}pQeGE?MD}(>cHThfRrcn7F44l zmHYR83|%y6v&cL8&je5I|MeMh{@i#9l2-j~h(gy>I9&xo`|s{K>SWziXZDURr(){( zlTkDS{6~V(K317VVHBz;!6*Kf6R)J&0)#B^>3or{hE)7_DYg{*i)Fdc|Bh#P^Bn zm%laNJ&fW0V$;;N$Nh#@(Wr>iZKyANe)ySur2S`mpOT9D^s^;R@O^8plW6x>cweu` zOIo9*zlDQTDA{RV9}?-m51Q!+q@kE{se%21G;@2D2-B=-^z@+%2wgn)U4^AS{3y%( z|Duv!`bhd_OzfH3i#n$tI;t z3O{e%R^DmIHx0z;1eA5qjtx_{u#IN1ln~SLCnuhoIJ1T>ktvF$JF{P$l+t(;{d*(tjf)Z4Njs1xPyd)TcOFvix7LS&wn%Da24u`Q1Qu;dNsD zp>?`y>R6FKN@#@shbObw{u{N_?o$1lK@mD@D4-%wn?{-Mhqp9kP>TL-CiNPsdTb|v z2Kv_nJR=D;d)YFmEu(WG<~rEPr)rMBuV0^PSt_7O%&81(PKQX7R!%{kaAyqmDF7e= z^}>t2-qwl$kLsUajPLCuosxWdUMA8F8%V8vP9xdh#gAkCOTei2PIb=_TiO#~^2y|W zAnJpgB2Bc<>EI{@SZDfUem@NK7Yb+-jIts1@EIka##N~9pnB%nr%y53e_VWe0El><;vB~kP|oa%a1 zpVcRZy;Js&TI}?UU*w7Oxz1tw_(VwQbyPLhnT~s?h&l;Dd&!-@#VFrA8hAR4^OH7V z&{6x`$$5V;DM=>->g3ULIXjSBiWytXg+L@xZ&Smdh+uAmCKCyGuBj<*cYAJ0AL&GqISynya<)E zOOs&%l@w7brc@t=2HLA)>Ja$sE^TpbT{M#Yx}A;wOOiqtSUTpMVp)|e0rQCQ+eeC_ zbOi!kPE)`{H>`+uG)xIK&kH5n`8z%j8s!Lp?!;rgkv3Db(R%K{!e~2g21vW8>;c|} znnqI>|5GrPp{6L5nhu*#O9JVH8|a|=!ACmxwJ_9mQscOP?DD4|RiS=tr6HJN2Iw~( zUh*NgQ~{<5r!6y0la2k&jSZ3Ks;2%;f6Kty^|=3Gz!Z?N&Y8Uyg1m#M8Rm zV+ZJB-Ra8A{s6`KSHzb1L(6<0>O>92Q##E1iS1VxpQbk;71ZLl{%zhekbeS^)Jc7t z08aiY%xoENQa9?cYN_R4U`{yIGU?Dx#u>PO}dBg;I8((tYZaN#3QlzZ;yWUwqktrB8F1etGzXrubce zsH~>C7^xKXGYzKZ)FHEvOv+y_F^w?(LK>ZjO&`!K<-?vw`M6JAj@sFn8vXp1MoY}^ zHO}`y|1_7%@iU@V$muf$mL72M)ZSx#n^IdSr9AO9(|xmSQHTHJlYd$#)gj0EB2+_a zVIx(Z4K-BJld7#$<S_WMX4{CY;&qP&;p)s@1aeZ#0>WBA~d|JhQ`s{nl z$5Q!K!~0%aro|0kJlv;1!OLY`xgCzC%mrROYIvE5{b@}VV0NyoIN;BztXqw94y(l&Nr3wt{ z%)LRh(198n={|XK&Osr+NgKB-Fzku{T0%}Y@y z;?h`-nv1B2GsTUq0aV4R^|772LPssv_sBBoI}6QJ!fD6Zq6)P<>ZU0Ss{^^FzN)oU zJ>2Ta{mpBCX_SIpC8Pqep6E;Zj1%pXy1I+fIzEiJ6K`sRmGJeRq|Nw`=w0<$+Z#oYt7_=ZN3ICp1=r>pVhFYC3I7IC;Pik4jIC z#Z$ViXH{2zb|i#t5ABL$^?m4K%BME37Ekeu5SCoNF}6=(wdNw$i}JAc!cjWV^9;n~K((l<+*IPJ zscniDe>75-PPvAv#t45XM*YoB)kO%(!}>mC)Bn;1Zq-pJ)wxIcLuYDqObK^)c(S>< zfo3Y<)CE&+^0%UTntQzxP#Y(`RCTbb7H0dN@-GWxY4i71RVw<;tS~8Y{Hgw`BT&ev z#mlOQ?(-FzqM{zI%eKmiCj3dRI&<-qNB>~$gSAz+p?a+E!xUU?4EW8b6HKWXS$$>E zG_0)mJkghSYWcJizS^ofW2)cfUa4s4kKTU3)g%9@>Gj{1*Md;@$>cN7ufLS&luByd z)ooN#siQ2UvJ2HYC}V21kJUF3PCK5{=CA)nY-%?ud^+x#GRDqAKXqd&bNwT|G5Psf zn04i+{hxmVOlQ!l=(PXwcmFcr=i1cjihpEO{>G(K<@+7sDsI!`b*Mu<>XfJXSWZ+V zR=r!k_g+*xQNh-gA)YU6O@#HcPOGTOH?HS5!^X}jp=%jN{^gsV-hNbAs1%p!p?<}w z04-9gs!^Nxb|+}n@6g=uOEOlxN$fuTrjk;#Q_4$I7vuc;)lN+K^)8lG_NYGiD&kTu zNL`gT_2|nlzTEsWhx`Ut2$qh2u-1C2&V3rB8-7)>ZAFyT($rXv$_Rhk6Mql=`wZyo z0_Gi){p?b$!z^FadLc{x>*xExZ04!j=Pm50IqPTbl<3wAsn*BwzpAM;<99Nus%2^@ zO1(R3tj5;tq9RKbMXIz>NplskN2ptQ(I4!nCfD*?}yxDtZ#|+3O%wwYl^(<9utaRbzaxb{3UW;|QN1zAyeGru!kZ8d;H5xtthm z!&y3{@#n_`RuA+owX%Ckj7vwVWmUII&FS=ijaL7uwbjYUvclB#pH@xlM`g9s#b7j@ zC|D&{?NcEYp#B9>-6chEN@1z?>W*Xl95Q2t)%01^)Rw(cT^LO*pt?$mGxGXJ`lVqi z(o)HN7a;5b5?3Q^h4(awmUI;zs!gtd`=eXnF>0n_5tX&sRb3nc zy!BBvsrt8Fw>34X_AE$y{#v=ZMDPkxm0JIB1Y{D6veZ0P+Dec+xk`i|&%j8%tfGn! z1*wgomFZP4>DrD}r}cSBUvAyLycf%*l&r#s8Xi;vS0u1ndQ&@a_F;*uS}v6`Y9}iy zXiB6?oLYUcD4g<5GW>M;>x(X16|Pc4>cvZZQhzPX{V!cahCO}JJEyF$+6QnNUR}K~ zg;39*BdV`4zf@KWtxzaUQL$CE(?dL}VL_Gglxcnr_3v9_%_!CAF#XFCsu>U)2GrYU zCSfVnn4cnl0#-Je8d&S;J@i$5i=ejJcPcJ5c_yV&eQ|0At#sB!!pij8;RFzeBA|8#Rl1{yR%yi)xhnM5 zZ2Z?ddPWG#>!)(8Pf5SU)O9JVvH@(bJAj3>$|Jf`)$uQueHvI*lRwk@AyPG@rzR_R z!*n{>QEN)AowV0jp&I|r>ZO2-s;Z~IN(weJL%MW*gRg-8TRfqWpr>c}% ztN>2-<7N$$s=cVmt^IfP`(8+E_v&01N8f$(?TNvF`ae9XHU?xRl^mxwp@OkVtvCR} zBOs>bE*FO**1FpN8@;bPUky5|fpy73Yx+_3&!YB!b}VG|RfPS|h*=%@-_VKVQpW!~ z@F~D5Cbj5#EdEc)lwJ~~>T&sEc%l|6s8+xVxVmr3#i@?|2gWY0d2jTw;vtC2e^;-5 zZo_K-`L2~%0jwMUXP@tU_UUKX0zUn0GW1W0V7t!_eA}Yz1d^p+)mK7YN>b}5s-Cji zY9%)81Ej2g*FsNKJncWZ!&zLZBsJ?)%g6T#QgO{+!mThDr>c?Ghn473WuL0()~l-? zrV*fhsKh@Xl-cs(pXm%psEykP`9h0q)u`I4q$>E;meqvY?)?MZWMNhOraY@xKwkdJ z`%l8S8|hgcHvlXPVcicf+6jv-QQq7Av;idW5l`6nJAC#-J z?qzmTv2L}EMW}S9`h!Uof)YtF^&DyM4O z+)KWtfJzPl0j+M=xUEO>$6B9_hAPzupu-w!T}ri#_IhgLa8ufB-B>#LXBby;uAcDe zJZRSa{-MVfno&b(bww)7+GwXTzk;+;C*b|5z^)qw?iqslckt`+WmFyNEIU=R_n$I> zg27cc1-+BC)o`#r7UqG0Y26@@N~|XSBHL)B61_TKB|AY`J}~gAX;uCISrj0mcCA8XjraD%?W6AcXD`4*`hvlEh zTP^*ldz0Gs~Qk%wykOs82DD5S32Jgm6jv6S7UB$ zQuR)&lx?lm%9?=J;P}ETUDodmx+}O0x{zuYsEgykS9t(yCDsaJN7m&ukr!`lvop*oy@L_>Cti?W%HBoU6Q+F!rP{ z*4ph%C#O+rKmKSFUDmY#E?ITI>YRI>@K#2H$%i|G5G$_51smo902ay>hw9I@0Y=*j z1f$m$;b{e2eV&?a|Ngdb_+Y&CQ#`Z0$5yLIU)6VasIamm>*j!|yHkgK0ioT_aUQ_3 z<@WzQ7+>R;zN%1W^{Wr*tK4ukYP+3aSNGMYitoVdS535b8yIVy&Fb~S&aJo7bg%){ z(iLzZwKgrY{V3y>x4V#Q6~;oOZGu@`u?N1iluI+fAQ+<10p9HzxuMfuw2CI&8;h7aL2)01H1yu7%Y)D zHC#0U2zssXwkRFYWlI4#-;s4zl*^*lsSOP2spUp22pD2rneB>>0a)4A*`<_tXA*Wt zOM|4}6A6AJr=FqpFoS2ZK2?hDAu{h=(40#Ioa1t^YeiIyB>_xsU~;Du zT}aODpkf8DSa*f)%XWauz3$xIh4jm_$@y#(z>LsiK$z&n^oth(HinKTF4H;z2|s;i z&uDw<^cq60%d)x=eQm9+Q6YfDJ7MpY`~-x#cEee~1qRvS!F{0EFWHdbuvdby+4q?s z-hN}IqW=t>l{!m6YlL9qZ8m46c@{r{p%#JGeuH!# z#Bu;u0GN2?L6~Dn0D0{_I1l5_R?V=u38qn#rIuC91^ki8Qbj=A7r>@~JFE{7ze2X# z=2)9%EzEiu>j=BN30h3c5$QpTK};-;B3QVAif2G z>C@`ad*h*#TxnL|@qKab=$mCX033_$AZ@jhdlPiMDZtWhC;j&))v&&)239dFgd6Lw zq`I=JwNHnF{j`>O{a$AZjyiz@aVu;q5mtW@-ZIkSQQ*O!JdcYoC*Z2};#R}#W=6ac zc4KpEKNMvp{Tgt6SRTaPX$HT}e0w)lS+rb%ZT))Rzu(kIe~m&e>#ic~)umrS%U5No zHMn9kP{hk0ytDUxmIs?|4Z91Q0<~FRJ`dT>zic#M8EECKJp^oRL*PVoE31L72mMy< zuY%d~_bCxD=rw({HPc-IvE}bhdAVbNT37DY=yK&>Ama*L-GJqFSSR7wg};{m0jvsw z*Pdlp`sJgqq}oqETj5|IEmZqiXuYu?tHUwtk0W87_FwV2oda?u zEQcY%Aum&|^;>&thqzY+!#?uj03S|f6_&Agk{3%KO+TE3KI_U>bM6-8Yr;jy9XPl^ z$F?YNcP(+Zd;ecaxh{82xb%FH@Zr9%%{Rmlp)x}m1(Sjx*kuE= z@Y(K~i_NsE8%WM=A=_5l6>h&%;_}|0>k5Ur8D`t^j^)~{9x&rhGat4nBVZF2=7n%1 zSjNqotS~BMCak=@1qPdrvzeg0J$Y+4rQY!2wztTFwL$WA4Xj`Aq4QI3$3o>*#Q>dI zM>cm+vU+B=K>}$`02^cp63h>Jnj6Ka^Pl5I^tZhb+SFxzo$Wq@1nqUy$5oY?5VztOW1W~=BHW3Il>Ps64#kZ$Ko zX}c_Sbp^CTQI^|agMr~-m2G)#7Kn@d zYu25nGdF&*Gg$Ant6}y`!LV?G!xK-dj37L?-a4>YJz(;kjT$hu2ATMh-`2^m@weu6 zz0pUMtcYvx+%N}2y6|q4X5mgS(z-p%H3u{tWF4&7)_#QS%#t8HioK7!Olt`tcp}!t zj_`YkyD)EjP<(4FdG!VYbm4s-BmZhkf!{BWZClwD@Mb`)J^DX(4e0*>cNcZBG#msb zOAUSk>UCwTUAgsIGvK*ytHyIaHTKHutNV8`ZmEZ zriOSru7focO9XDwYcNUlH4(et;35xbHi*RoX~U&uYnBd&!Rdg@!FZfB+%CCOR-9WO z`rX(YgzNMe)dyAht5M8Ct9#kQO{aE7k{p1Fc)Sm02^{BQ8oB7Wg)S zFB-9J4_p|BdnL@m^pO7C36%%U9)~?1=rcL5Gkx$u=%EY{Ah6?O>C8P=G23z-Y;?=+ z4pOn3wuIRA>>7OhY{bj5cS{+vQ>Ul6AxhXDBo80MV^rf_da~@jl7u@!@-!r2>%v-* zg$BH&N3ld`51EsTUHgqWErv?7NF5j3_)k*-mU(0p<{qv4r7zqElzA zfVe-gJ?Onb%h+pWcu3k6@?H?~c58cZu*?gc>a<>feXCAvMNkEn(014Qt-#m7+lE$H zzacMeyb2EA5%B6WeoqXMu|ezI8ea$t12tOM73Kl8cHo+TE$%M=x)T`uD=h`K*1Dab z{c<-HTZCMi3!vWrms{g)-?yl1v<<{9Klkp~N~ksJ>Ys{k(RXLq9?H*Wk#Vp8T03wiaiXCDqQxIs>(A(f!(e`_Ht`Cf*LPt7k5LMZLYxE3hv|7s7JVD@m5+ z1w(OGs&f628n_vfN z)(|`UtPXjxuqaGE`xmgAdMyGhDs21Yk3d`Vt#w&7y+-3}uet+?xw7tlvZH@y8-j|m zY_v7fE{#P8E;s0g%a8i-A)NSVS9XLCG2C9njZkt0R%|LjpoLz!63TMI*Zx}s+^WCs z!|cPdmA2Y%tDOeYWgDK`1-eSx)nIqU} zLd~84n89!k!L&RQbeWk574W*dR-`3$UNJAs@9 zzCQ8rlmJ(ID6Ovxr0gD|ofTI><&E;tpSp}Y9G&h;gv?fgQ9Ly_`j8lDO!VT_bHjS8 z-2g*S@W5PHq8BT05mXfD2_b7ptGs8IL5nr5_y=Ynq&adea$Q3;fw8jC%PMVFtp(@U zL?AN6RKWfLeZxgiP95r8c7Wd11E){Ik8B{2a+*w-R#?lXuEDZm+66prOHZ|^N>?j; zSScvjScy2-!RE$Bnr3H$$6kMKDVT07pWF*>j3TUw)wPK$%lcN- zwAxF+YJ$6F%;6x<>;3WE?5Rt?%W;hu*S>gC--fR0LpOM4Ax*n#Eygy7!d91|PT6K1 z^kVCk&Q>JAT6wj{z?3-wHGQuJgeImQIP@jw0sdI`U`=Hnh=A9Tkh3n|ADL7qI16Ge zmYuXZm^oLFSI=-*`<2vIAFf#-lY)Zdrh?H>3wu6@m>2TvkzsW7!wM(&LvF3Z$uw!x z>M9o6L)2JI1@_YZbV~+;`op%M)A=qO1LjQYuF>dPvc6y*Vr(k1cL(_tAr%6JN<%`G zKlYE*R^uU3D;@$JO0*aNuxEhLTu5M@bvv*5EvNrm%d~kDC_u)328J|%u6?=M< z0R92LR@_?9)R8qRVVHxP-7JgY_B_##f$~s}qz~pu>n`?%9TGDmDTQmBSEGCv%Ix6VkfuKLtwi=7QE_JX`}V6 z+#tXvtl!fvU_n$CSp9ZZM1`hU2P*aeSqs`5UXC>fN-w=G%w*}65fmD&uTC@fH4P4p zMFM75p(p~XS+j;_+6a6trv0W*w-=;OmWNGzI(@~W=m{{?XJ=lXj<(lq%9@Ua1vMYd zxnnq0Z5GTqbY+!eXYGX@Jqs?aLeyiLDXv%kCK2cojh+lWG;~na7DQu#npJ5?UM3a0|XHNNNsv;n#bstFO!=YP@{*Ni3#e;ldDPgX1<1V^qD4-mf z`@2JHa${?_EjMg(=rHU*{lqq5t{Yw>u=>O}<n+1Rp*j8 zGpQxfGp^kFN}JXml&X6WD`ZNkYkh1tb||ZmEtkMPdhG(3 zY9qk$rA6{EBI|PepX>(-P^D)v95D zP5BdAbt^%OPOWlj*Tia;&72oEoCCYKK1!XYRc~zd_JQS#Cna&7(O(T}UDnm);7QBt5atI=H` ze?UHHQdnDt#WK=$U(D(+KV^PzIon=+uN`;G9_p5eS3lSZ{w+Y(3$`YJY}y8FAz1ij zwW>g@dSJ1%kom$GKnQFi@4yfYgMh2A?UBV`@PLYfKkOc`Vwy!;)N}1gKn(+}#8d;V z#8lTB;4CW6TQXJ zA02uZ#CkJ92g%L`yFio1dZhhYzPlQ}SU1D2f~{#FDBrApTZgS#?dw0SoLW2$WVLT# zlA8?K~Hm4*9Rn)dZGPwIvqObgnzOkl+kr=}8V7U#1``os^0KNVTAxI`c)$ z>kwe|&MdqF`%+IWpjuZI^~xJyS(FNb`$4L4E`iS`|AO0{X~mO+!2t zuclzCvlZ@nIf2&`)Q^uo`S_!ikJL(l<<&J?_65v!YxdE-5nK^VVO4JyC?DQQD}bK+ z6&}UkuJ7n(L1slG)dz^RrfMCvPnUqj)(OgL6XJFYxm8N)D3n?@b%8;y$g1xg*%06j zSuMH@CX~oZYt;j!(Oza(b1ZSKf~dOqWKluvB8UP5o=Plb-oGFFU6XKE#|DEn`~d^g z+V1Mstuz5xRJBE5MPv)rM)#{rxX@2Q++0TxYq8!8UDYqdYAs2vMY%9lYyqny!Md`b zy>WK|xx~C`D6sp`E?4%wE-DSyU#Eo23aXb7;A_o0lTl?2{N3_<@ivgV_gZMJS;d9% zRl;gNxL>`bz;BdS`*C4GY$$l;<+EV!TIRmiUOsx|<#4t3@T@V{r3R$BkX$jkE=z`O z`l~My0<)Yd#{m7W7FD%QYbb*SX`|r4PePIPDatDf?hDiBcVD2SKwqQpN%+@A2J9%< zd*0T7=d8YZ^l$E6jQ%e?w8h)`rhj zP#dso^wseH8U1ISiVcRPS*fm$^{`RRW!bfR@&MQs>2B+XGkq&o0M%<{)(eac!ewig zwb|~;!(d%s-3tOv25vW+#MdLy?V^FJMGVa4Tr*@@H*~cpCYV}{xfuf!+^$Q8J%hpZ z`eQW5?uO!eU8}QDWqlt}R*dT>NVMsO73xOtOC$ZM@HY1tWP@x3!jf>C>Gv?40gu4A zC$E*O(`&imHRk%c2G;X_9Xd(11NK%i?$T9__hkYId#mU7K<a15Pvsz^!DHbi3O|doEwkiRJzN)HNkLrb~u89S1 zy}AGyHUwMUX_xN$ZEkXM3}>ytmWu}->USRM4{@*zeo4QjaZ8hZw;8y|;3Ah;UU#g} z#>n3G)^q2l=YJRf>cib2;e~*kgMHO+owdK!_&OEWy09SDYYhr*8B1jMg~8HipJAfE zCerl)e>h-j4Yl@uHW~ObNB-KQg(-G&IvNZ{!FFM6ZJT#Oq4hENSFiEOWe3=0Q@PfEWxO%m-SwK6s)}hyl zR>tPoUssfK*%z?3_6LtqX!)+PVyl3(uZU*%oab6zX~(v~?nc=b#sJC=Q~--ppf2y& zD9b-6`SyxL*Th%>E412{4}4a%Yik-YFO#hw#u_f10Vde0aMvMzSYItewmy`RaXY)_P^YT8@L7xufx(<5iGvgAX&E|V3PIntz%Mc;O);+ zY#r(ny$IQ; zt-EJ)A=gxu26oBX2W#oJ5BGr2{Yd=>-#$8$L;W`25ZgQxBz00CnT(SE8kG@&x zw~+wjk&iXJuw$}oD@+0Q1+>52kE=N18Q2fU0A_(D{kg@!oa;1~&pNPo<8#CAqObsI z_UJSGuUmG##sT$qU#C)G?RiC$ZTf?3DT_7smQQw5oK?{Q@-Do~FZEqV@(Wm%T~@N| zKo>08Ixyo}zfIY;4y5bVuM?QpyWdCM^f7VierN~E1hkp*Ml_3i`fwrP;f0bBY2x6T2#4|6ED%&wMieV9kaOF=JI9&ARs ztG)GK_yKHLO>cmIgss%embd;d#M|Mc6VCnhIiQ+FvO9v=AXjXwzB%-E)i>^0!Hvh` zzp&q+qTHHvr<^(P^|0Ipl5&Y^9SUNd-%7u0RCHA(yjT>=4lZlH0%BPY-7h&-EvKrgIt4-+Xd&A7fxYg z-Q-}ZHRMj8W3xf}Q+t8zAJ2lcK0vtolF!l`G7boAyPKw939<{M?d2NS4~f?Lt)F-q zY_h5~)FhfAi@;a1uT{GefHO&vGa zAlRsCgk|eiHf~w8wUZTj*PCL!I-xzL%2_`07hotD3T1tkJzM4aJfvJ#8iam(eR5sv z5v~;o_fgTU7Inq0F8U0@UeID00)1TG*(JeNAusXnM10%KxO8$|m6^3&sbM8Rtv!;K zn|urCR+L<;y5ixj;q;M5!gSy$mp37-U&c)$b+PFqpwU3S)RxU$7cs0(!pOSt9Qc(=v`ak(v6{;< zF4L{gyqU{q(-3xRI#^4T9H;~#ZL93&GS|*^g1XzMdsQ-TLN2;3J7Yu675avewT^@< zNmvlvhSn~MzRomo_fDHHYea}b<%5PNP0=hwb;AnqS*!3mJ_E#yfp#0T$X^SyLVC)YusI8 zZ4MkV-Yd~)F&c<5`hV=sAx_%^v4agPCdP7&%bm_jm2X-M87k3$gWqQ2??P{4U zDsFXOflaRR7l>tNE8<=K8f$ew-LMr&7dcnE62!yOYpbmXI1K9iF0`vpe05_7`1J>W z!UJU6t$cw8bv_L*toJ3j??CY{TN9?g#RH{xDa&7tSGw=FM-5k^t(04vVEeSmhC^0PTAqNuvMk^^FwVM? zwxm7QsB7rm{MJ=oqu+pt7e%wx`mI1VKH22e8G0A{7Y-cwO0Pe1oTJnV;<8uY*ZP2z6(n=@b*cn!X10N?KOeNnEy%TjWUzZv$@Dlmn;Xzr5q zJHe>%**(2z{{iBz02rp-;nX*Sgd_5066Tftg?AxpY5SpewtRA41U$zaoAetUv<`S# zjF}(!574R@S#u@zO_362JS{1?R2mxe0U(N1$dXnWnWo8du+9=6<=O5 z^LFpMi*}AdVQzJI{;q5VVMY-4b~47oz`oOTG0|T>{vFDNb>qN^h1-B%3%WHnDD={M zv&#$8m9_SrFvYsu;G860Z&&cu^wuFRh5l{-n0q_X?!NBn>&h=vN%|<``@No zldx$wFqe&BD7}9s_41M*XML?ZZ0nm&bY*oxd2<;V*IMq{8{RC7?EB!2*C+5T4F>C+ ztmYvNv9xu?&IN;SZdlN{N!Y7sb0LE*wp1M474Rm}hB0B-r4UiBSONz6%4i$%Z#V9o zy_|CH$pBs#6V#XPn{IXB*Z1t7T|~Wn=6(a28brDXw6@W5id{AoUR(-NX1QrDmXMoj zuF<#*3mAqC3%T=aB01MAJcE1<#I)2-#L8Nz@Z2he1=_1{S*F{`b6)u++y!r4Ol$b1 zmR92J&1buia&w^q&$@WvS}(mI4vrEpH_*DhK&tNK8L>2mhKVCy{jdz|_S1cmZ^b;@ zaj=bc7H4~@<4%Rc6}PPo3g+FZ0GAp9ed~?A(QeXM zAj3j?VjtJSjVJ7{&3y~8R3X-Zxx*`s4DR=OTtO{{yFj z+17e4h&x_Fyu%}{2^*ky3H&wrP9)==;%X9Q1xs+Q!&(=v(MKSP^#MyllYEPv)~asv z4b~?3o9RS$W6SRatl$FI?qCvhdDZ@4PcI(ny+T z&kXs#@0|O0%R5)9f>RZB-@otw7|qN#=keWh&pnq(3fOT*w!Qk zTdBp6E@^NB8hm)4mFV(FE~sw{GVGbPGsSgld{?Zu!CmeK7Vm8>s2ff$u`}TzA*L(7 zdy31i?%8Jyw?nc5_f|kxG&jr(8K8&(>JwPDEuX-KvnW-|cy0~ePCUbwJ1N-SU{NIv zxEmNVZ&yij|70!Bb8h4D8qrk(3lIo&kn}V^NK}6uT3^}!)Sc~ObptPj0O12$pcIkFn7M)c_tuhm|r$P^QTN$kK zX*1a?nXM9Q$!E8m>gpN_*YZ?hU6It8Sgd8(qK{XYG~~8=O~y@)=U8(L=>#Wri6G&Z|n9#m?02&*s;_Le)u*tSr@G@Q8AIvvc@XQ3pbge$E^IbAv)baY(w@}r<>T%6EE;wyr z+?FqHt)NJ*4%xT7eeH`3;cC^_>TBe;#&xxti`153yjOAjtgxcX(&R!*H zWu02qH(<7c)$Q^SZc&1+73j)d03p1hiU#7fDr=zA>C3D(8@`SmYmM42)-XUc*Dm6y z+}wg^k--28`{GNDC~dHvZB>PqZ-*!^yUo}3NMo6eyF)f>T`zDj zHT&(L`;@ zX|0$6ZsVpgUeMdw<)J3%e%P7 zGzX>Z#>+#6+Hh&Zb942f;swmtwV7ZP zBDwK#&}0t-7fa+7?^W2hJiaoN=Tc>ZTF77xzf{kckYFd6_P;-J%`D$Z$n@r;PpbH6C^rV|qgb z&z*tflI9{u!8f%9;T89dk7OQB?~N%7hG6`Sixad?_!Qa_@eD? z%WT|Ad85@Zz%sKbuW6%&?x8(qGMqbmANj9#4_iN*$$oWc8R6xt*6!-LqU=GC+q&m% z#1o*fOl7dJZrxs?d0<^!&xpOYgK=XvEBRbKSP>Zw6lV0 z(Tcy?{u}(Fxz&Yq*ryiz)(S7!BjnZD;A5>0s=chTfTRh zl=UbN%iyiusV#C;Yc;=irXcOz!CK3_jmB;ZmzIIMGH{Eq>pkl|*y}DkAgGC}$I3eM z5Ae$D7;TMKeL#!Gb}Yq!Nd~rb4B^w z``hY1b8c&KF5LjFwLAHkvJsSndt~6YTtc>g^w`n5G;EJ-mbC|vXWN0#CQ#X#F7>=b zW}9u_1TGpApVh_+pt0n!cxP1P!WrYYzQ+Qo@Qn8ml*Jwj;ITb;mWa^l+Z7y zrP&mj2}@&yYrni?C1qJ)mU#z>aS$W#_S5K z&*I&Q%&H4&o4roK34~y;AzhX{6f3rrPY~YT6eDG5m2`79Pk{AE>sxDYp%egDYc))2 z6}>E+!6$j-5-gPj2+k@kH&sM%`3;q8?U0Dd)KcyVi#TSoidC4J2RB;=H)Bevt-RO1 z0r|DMxm9{Kfm@MqG19olu%Xq3VhE9E3L(ZZE24$~6LWOKjrFvqf zn=D8LO_8LzYMGi?W3=`{O}8|DLI#6fPmx_Y@E`+vr&ThH0#*YlWPgMuf#e0 zbJUwx)T&0Jw5U5>PKZBY_anG&MW8w*4Z;y$>@ka~8bakF69HB7J#rS6sewD@WjRB- zXI_)xBkl^%*b(=H*L{8JrotUbI~_u@dFco1CPS2jd6c`O)?B z0lsK)g)^cr@>cLj*xrY)-ZJV|m=#wZ_U(GS?)jG(N;y}sb^~>37%uSz9S`+h-n=|? zfI2m6XSJO-6|ip$);OlzI)-@5O9U$Kid zydHnR1={Iln@2+e*uAI5w6dBLV-^?&?g|ZX>PzLlwXy+cL%|mHeD-S{mAbcm!ix1* z?#N)e6=U`7O%2{<@QyBP8LZ4z;&K6*s<65xVlWNkFK%MEq_;**NPWSW==j&xHMU95C*)phIHQ!5q0%rDQoND{_JVV>pcE5}wI??T zW)o~sp?%(97cjUmdvFBH`V=8ojtDYCe1KqPfCGR+>wsEiGZ}?Wk?f~dX$+z^RBN5P zhoKIym^jJ!e7E0(fb+$RTqHNU#7{E{8QBxIUBrT2@DvK={4#zVEtdj&nHe`$!fys= zUaaz^REXwJKLrn#b#Nnr6WR&n_k!a_vRPUQM2fBZ*m;%cI(ps`aYN;^T#SY=ulOpf z$r>}Y)nzPqFps{f%2s)JVkrh(N~TQ{27}?{3%D>$G?ELxJSUbcQTuL1#6yF16Fxxh z`pWdsf8fkrRj3jonulksW`GI@EFNeqY8tATo|8Dt(A(x%U~P&$a+;n=rX8(GQ#WqS zWMZgDMq|M^kebY{tjp4xZ4VVD*J(vaNot#dnfSg=;v_AJ$%MRRQ`pxy%@~in9JvSZ_qu6q@0o#$eN2X3+ zRx$NR>%sIp)SC=1ZHi&=z%rAz{|{GZk1Tq8~6fSEEQ?>)cX&4fkj_nJN zVr0yhGNO_O&F9xJ4-_8ZaM!1R%sSM3eVgQA!qdY?tP~3C=?yDPNhxeeD8{2LcmG4B z6J{5k1HFjncGzXIkmgicOs60>UehJv3)2T1)^27xkPQZDBG&302dG4^+H_#$90ei zC_ic)lSEi%j19IThe=X=a*%^HBDAIoE5Y?B$eG$|WDBJkB8nc6uI;=MZB$j47rN z4{%KQ+`=cW@*N_c#?=rv{bJ&yX%E-zcECg701w7k!al!X5_b7vuW%Sc<{{W2#4Dpy z#4qu1x<3GJ0%_cZ7Tq=u_+^~!;Wpl}0xZ17%{Fntn-G3NWb7@J?0RfwIOBcbTpX8* z4-m^`k03Q=jWA6B82}O_DbG>{U^*dM87*e~*0^Dq5U4Rx3qav_1Y&rhgSP|3j-I-M z9uPv~J8rX&R}6)yM8i#eAdP3Y_#AJ9(N{)K5UH-;!vRpFF1ESbfxHQsL82A}PY^D3 zdx8bx6Cg_a6d-nY9jvy^E@=`yiW(&`LhzQWIkY%0ow<0 z^w~0@JIce!UQ*UTrYLgB1S$d~oKbZn@Z1!nIMovy5+@3guxVo(0VJD*ik&^&!8e)Ra1=8-}i36-utV@E|mCL@(n=Q4D7Dh^oXhhM69)2VH%oxf8-FMV8Ab zeUYxp(W7Oc?_8TL;JiG_^K)l!)>f3LUKAj^q%CweD|)sk|)2 z&b#meLMUVhiy$JBFduSJ-a*rHKC_MmQdc zUWofNd2R?Jv`z{ww;SB;659nVBe0D9{unZOl1NT@D0bMV6LDz_?Re1LX;tl3O56dF z(B2v>p#+dj1^7}^DR~0bH9gi_Tf~^U4QZ=BWf!3Ff}qo3Y-$XDOt;&b;fq95hbeo( zu7-R7Jm^wVhN+}XQcb0=GIj(cIet_jSPxQa3Zf)e@J=YV zWHG)p)afmWqoDge6&K-?cm_jP@WR{~ z_Y-ImNH>5GM8TU)t)iTLjfd&#|=l)qDv-*a?;ib4Fs~j2rbNNfk_3aDVBGP3 zf#d;98B4+H@+yO=3psB(T7<2I6pIb=WboU>Fp6*M66xiP=mAqw=|$2L!v^IMUqQUt zlT?n;yE6LnfLq(&Hi@I)L8}`WNoUQ&M;(~RKMP^QbOEG6b;$#z-lR0Q4N4r@$;~bG zBxh7*$zn^K=GZO6^?=^g;EQ#yrg=G>cow!vA4nTt7K)A>fX$|?BYx2AY03?Dj z=4wOCS7D>X$rslPf|OTpR>=eicn?D}S66o@q=OQ+f@3d1qp}JvH-7RbZ68|_ z>YXZi9I=sI3n7VJ$1%r(H8jd7e*&)|8^G?I|XXv*wk;ZP)k%LLCy!Gh67dK5$sUyW6Z=12f3XhwV>estUL)O`6N zQ2vBH{uHD*Y|-e9?4e{ZhXr;mLnx{f>6b-q6jBhZS)<{V`B)d?M{dKx9=eXJyOf0? zonRqyctgmYcSk8(R4Ey8ej{(H7C42=&dxk zhdx7IfJ8BcrE!B}O`pycx#E^3mx2js@ol0O@Z~sLWQKAbX;E)0!J+CdQEQw20JJ8I zIUZT*W!)%WE^)$){o7TI^;vWu?6GU!R(gFOIix+*PsX?k<2H`8`xgiuE-~6gCk#1+lv41-35Dp~6DQnE zw3gsAA!(!nPjVmnG4GOPQ$oq`go~L&!eS~6c&MFd$*GP-bLdCpuqBN|QNSsLMMw#? zOCcGLX?lGG5AcyIBkAus3gs%IG(rl8C5TrKdD9gRkpoZy0vfdGQzXS$v^xliU~;h| zyABJ(X8$6JzK`<|H_!#UI1J?);-ax!f~L9$8Nvr1Foo+KOq_>mZdSWzw_N-T zKyqjgx-c%YiL3QphR|Ph7!m)I*MMLNIe85MDIdFH`kaf%NC$zEVRO{_I`jZ|hDwdH z;8f`FoS4}_gAc)9BpynoC?*r~!k5(m>KadeCG+QmUH4@P?Ewyh z@s80zY??j|H~9#uYS+6yEbGj0tu*(46Xt<)9qW?XSOLO(JqV$mC8n;OMlV`i*~2AWr;?L{;17li!w~FBZ&6~RBk5uwewY*TF)A>k9&ZC! zz6n$Z3&#M8UKB19xgbYSuakAyb$|f&@4gk@K3Mcu3u%N>NuqB`-KM*1jTKxLd%poFAR0`aKOCp4j6!#Q-r4;j+ zh6SY#P6dn5Q;{{nX4B3g5n7^!-Y*sE9C=9yjfjTl)xyZ{DD4?(q`{k@E-Uf{n&xcVqQmQ&g zgGHnO3|~CpNc1}ArcU}1E2=L(2dETKYiXPW(HAVVCM!>%YM_#&f==@e@#Bl19WIy~ zz!(ZZ@XZ(bg;eUMUo!y%Fbf7OTPUus8o zp8%HAHL~Nd3gI%TMf%a4LT@U?pOz(xZ7C@ta%Dh!@+p;RFx=G5{Ho60>?X z(+Gq?fd{@JAafl!NxbmmL=Bht9i?|YG!fqwzv&9TF{FdPD;YUD`ptco9)H(i&NwO< zt)GF+ny#t-05xWy8Q>OwS)0f3*$WD_MsD~i^!LCR-~JjlOR+ZkVD$6PMx$_kG#Y&m zjsN})?FscpqnGh{();J|IkkR#3!ndm7SQ_Vzx)07zW>14CwZ?y%V_+f&ii`E`}i20 zUg(b(`jdq|zY+f*{#!z;{T1~2|JHvkI=pub?YQ@^;?w?3Hu$~pZ}@@pXtMF_XmaOW zw0}nX7qox9>zT>Mbv&~_n%tVAokly4_B7fnXxGr*Mf)MzkI;UM_7k+9q5Tr=*J#(# z_D7T3Q?%1)=h2=o_t8E?`v~o0w4b8=0_|65zd^e_nrtVuV`yj5o0WHy@wx yIsWP}85hv5qETf&-=F9AzyJTWpAQlYvvC3Ks+*6`_k8qo@{0>-SJD1pp7GyL8nw9q literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_horizontal_center.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_horizontal_center.png new file mode 100644 index 0000000000000000000000000000000000000000..4fae7c545c4241e0ccd1c812245828a358c231b4 GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^j6kf%!3HE>zAw%KQfx`y?k)`fL2$v|<&%LToCO|{ z#Xud`L734=V|E2lkiEpy*OmP~kARq=sgb~<=|CY7PZ!4!j_b(@7m9wJKXTx}0fRZ5 Z3|aY%$2MA|iU8#qJYD@<);T3K0RXL7C3FA) literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_horizontal_left.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_horizontal_left.png new file mode 100644 index 0000000000000000000000000000000000000000..dabb85a283cd266b374bbfb05b4be437a19e13c8 GIT binary patch literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^>_Du?!3HFsd}P)IQfx`y?k)`fL2$v|<&%LToCO|{ z#Xud`L734=V|E2lkiEpy*OmPNr+}!LrFZThEuc`jr;B5V#p$C;$Ke literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_horizontal_right.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_horizontal_right.png new file mode 100644 index 0000000000000000000000000000000000000000..03f6a8770dd4f65f0ca501518b750e11580dfa59 GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^>_Du?!3HFsd}P)IQfx`y?k)`fL2$v|<&%LToCO|{ z#Xud`L734=V|E2lkiEpy*OmPNr+}!r-1i!g2t$sii(`nz>9dWepRlW~{lrc>VRiUYBLhndwNZdCYS#v{!EOvTA41 z*kf-i*#2`@EETvVbImejL&Cw`52qbgZMANj(9mBxNB-ln_sM^lwwsFA?+o-i4zk$O L)z4*}Q$iB}1-nkg literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_vertical_bottom.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_vertical_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..7b7809f2ca693cd7e308b7dbb057e34d6242a8a3 GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^dO*z1!3HGDrRq-tDYhhUcNd2LAh=-f^2tCE&H|6f zVxW%eAk65bF}nh!r^M6OmHh#yfT#smmfeO;K%o>*7sn8b(^oHT6m$s?ak%)no3qS~ zF(~zgt7z1PcC|8Bqn#lR%a|E&-6+?#IC$3Nt#X3r1);u%cgl&J9PvVIcN#C4x82$K zcGWMZZXJeQ+x?RH&gF((jjst<@Mrt-kIw0H-6|M=h>NfHD5;+aw2#5l)z4*}Q$iB} D7iCE( literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_vertical_center.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/background_vertical_center.png new file mode 100644 index 0000000000000000000000000000000000000000..3e4ef97effe8e60299f86b6efafada7d5a9f0c04 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^dO*y`!3HE3I?uTRq}Y|gW!U_%O?XxI14-? zi-9_>gD|6$#_S59AbW|YuPgg~9sw~!QO>%ic%YDgr;B3<$Mxit3q`-q8?Z1kFzsW^ UEmqUa1DIuQOCG0HD2{#rNCn(JL6~)uXE5K4*@#OC82hX4Xe~^A7zwkguSlqmbf=zMn z0+wEIKO+%v+P_zB^0Mk`+dM_nTGx;^{<5jdtd}qI7TnO8d3mX1a+~`fcWKdA++ueH V3a>N&KMr&NgQu&X%Q~loCIB&aQgZ+R literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_horizontal.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..01af977a9ae9430d633f891c2dac2ab39f3f37cf GIT binary patch literal 1198 zcmV;f1X25mP)s;azO(k1ez?VZ<=7Zg@5^;&rWoTmgz)wH`ug?R z+1Y2mzrQ~k35Sv|d$J4aa*=&N;Hyk}-yux!y+y5$rrrQ?T@er6SE*TietH z1nun_zspu;mdK;BEGdLQMNv?UacygB>&QFj_QbHNs_M?(d-C4b5y{NSbe?8_On`=> zCqo>@ad49KBw~tLYa8d|6s)~xDKzPK=RCSPp5e*!gRKzQZ;2CGgDPnwXG6;axGOB{H!x2K*brI zmNIK-0K&prBFm*p#v{PC*QKkgD|+zk6%i4IFCXaU=B9C7ryl~W;lCQ-|JX9X3@`)C05ibcGQbQl1Iz$(%K$UL3@`)CEd$H|Gr$Zmw+t`?%m6dM z+%mv_3ZVW0yRYzZ(JduLT6FV^G**N?&bh{q>TOctWx-0RXqVM`1Jn{Wl!=KHPD9fg z7TKSs#cqpct?m*@2TJB}JQO{J5E^#MR%9T36%6L$;$kAkh(Wl;{WdABR&|O|T2qYz zAaq_wP@~4c_-*jo^fxK7q-^E7o*)bW^rubD<)=lPbjI1jb6UKpb=6wqCm`AYXz3c- zg4a>mh36#NP~)fN%P3##@}Wxu-kHKWJ+(19Iy5)|Aorz&l%haQ75KKuN~vM-c>+;j zp)Hsd2vGWAfMArj*;oT)=1b??eGLu;i3kqUr%mOail{bSo{pln)e{x6T5hYKpZf1d zpW2RNiNE--1<1%tA3``^U0r>OvR8~aUCVxF1x-1X+NM^~X;2m|rPJk0qESFvYbk_q z?na~0x82>{=gd4Pih_!wXy}C7R=}>$BHtzZMA5tx*)^GZ@qp<3d2({{!kwO;7Mq)! z?}x+TjxpvQ5#7ZUNxfn$6X2g{eF0)=Vdfv>@%Yp6@$t)}qoeP?0Dj6vS}ha#IsgCw M07*qoM6N<$f=T8#00000 literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_horizontal_warn.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_horizontal_warn.png new file mode 100644 index 0000000000000000000000000000000000000000..b690555457cb00745d29f17d5ac627db9b7bdcd6 GIT binary patch literal 1212 zcmV;t1Vj6YP)p4pkxt^m;`t#)?1XS(X^s+zItcXz*4=jZ3Ai^bv>RedfZ;=Pv;LJta#+qM6wW|Jfa&q!jwOlTL5RvCi(}?$8+P0Ods(K(*RlN7zu2=v$ho%ISgJm}? zmucSn&ZjXT?C;q6W4@{?GEdg)wZs^uu4_ptJv%x&x~f75ui;qJG~MK?syb&w2%@UJ z;BqBBlr1>dI&I5{=VBb^*-hjU!^ZR2l(sSkRaHACP18tS*9=coRYC}_E9cxxJ{bW3 zCWEx)Yav=V{DMQO`lN4SP3|{eXeELDzd)Tb=|>Ruh)`Nl9Uqr__7*W z41oe<2%*bH1js?>pkXJSbGgNzYzUv1QtFLkJzM6v3}eundLFNb>T8;&d&e=L+O|+l zO=|!}Ft#kwE3lx2#k~XoER?aVL8juE`yZFEbFOz2w583lq?88XvA<)PUCr$ex$FW6 zfJ54I*|GBs&^|8V^Zot3e0cRrL_|K1QC6!}Z`_t21GPPr*G5+HrgMAY&r@zhvj9%X zQuGTr$)N4y2WRozM-O{V4jAJi(zP60{&Su)$82i=8460k-dyHB=FDB&uEnLjEA!h9 z;$#0p*oEaDhX2(7|BqGx7Jvm{0aySQD*y|?0$Zv12%+~+^_=!)m#}gw+FjXffL6lBGBHTu zMC6HL%BrO>XDVPj&Y~trI#@Et>#^u5#@O@I`a4&_FbklSg)va;3>>mLJpkn3Z3c;m z93LOc@%#7Uy_eN$^#nGb&zcyP;j!Whf+xIV+-}e&P0uWVtkz`b_&cjJMqrS&S&bh+ z3<0pxHTH$aS=q(kA=_BvXXVQ*UmJ^IOand&(3Yolrn>`?K$5`$0M5$^DWO2kHTWD@ zo58%1-uq4n2@N3o%bCSV!5PX4!dX7#V+T-G--QrfSa7V+WN=(QLn`-NMzIchsQKH> zW5>BPZ3T?|wqsKU<&uWiLcEYTn|AT8iZQ<3-{1d+`x|E*E^eFi$5<|?p`SVo1vms{ z+fq8)zL1Ro>Aja2I&yq`d=?;ShLZz7^eZNo{!(^+ zxVgFcF5KSU)`y3OKQEWd6X)D#BJwHsj@%>0KLK!8n=u6-w*3+y*X!%+H&<6z afBp@J%`v!+;z^SL000069pUr@VcB^E@*ib#!!uhlhuco2GdW056nMkD#Iz zLipvqzqq`-e0Okg@Izf)T|EsUoDJz&TjsOgdoOn{6AAUWEtjdkPf&UyBjVMe<^j2DWwnxT-GH@ z45xyhl4TD3EW@R3;g^I%#JfDoFsF2j#R9snn-nl24oVSANr!U|R;v|QYbOgpr4$em z?Ck9Hzi;IdAq242iX=cR2YziOiP#UPbP0AW4!9g)IOc^C*2V!h8g%hiNHCl`2U54d z^bc{xAptf$uu>gk$qS{}O)H>{Lv85LkV=03`)D3YKBin+>v12h0c@I+$2*OsQb-zb z2^Ve~0Fsi}V7Rm;l4~^iNl!P)sv{V+CR!YIm&5a1y{r&x-G;!!0fAZ>$2;iHGkK2_)|OVzy2fKUR}2>=QB&Y$eYj>x z#HVh5Pgi4FnO5+lK3ogvqJ~)xahp>*0VI1`jU!01;v8UyPeoA(&wONN41T1l>2msJ^9dun+mPF2+;{lYZ~_6!)mpv0x-)W4L*#6&05UJw>2J0s)b#)Hbh**gp8ikx|)Xe zS;w4@E2RLv_X{H0YumQZLu*^c7%;|wbM6l=m&@-&^sH^$YP6dJjEFFw&%fc_-Q8QI z)Mp}UY6QztFviSIPftG>0Jz@W-Tl-w%|oTseE?|EoK*7VocleW&(BXzPF^1$AOHLd Xv;VoXt1ssq00000NkvXXu0mjf!#*Dr literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_vertical_warn.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/fader/handle_vertical_warn.png new file mode 100644 index 0000000000000000000000000000000000000000..2fb40e9c17437e5b3761a239ec911a74d0d5d4d6 GIT binary patch literal 1194 zcmV;b1XcTqP)UQQX*2I z99QJ-bbGsNk7<~d&u8x*&5l=ECCc6@Z=c^h&x}W%ot@$7>FLv|s@?;@E2Y#EDCqLu z|8~xOyS~1DcXD#_Q{CL$JonyzA)@)bK+}DKN$qq2UlLv)ePw==mpqKnggoFv}sldJQu;W?2$&7(*eU zI~q=Dxg5VB?%TTxraeLehY9 zxNzS9kd(v@!?`7qyhf9scz2VmIzqx2*G?lP)t@Lq$MI_Gp%SHp-dfhy;ySJ*4Tsn; zWHa<&r4-EPa}lsdM@Ly{;?O?6JU$iy{BSuZ}yGZW`L>Qnl81Tz$keSqA(t+-aoCdt=PbpvjhoopaDMO%Z@u z7IE-l7;IhF^#NDH!AuC43G8i+hmuNRm#qyEw=f~2=d`Y*p?%gd=i^E#Klq& z2Lk}J!^6XmRaHG!N<9F8D$YqIZ)@%CYPI@uadGkH{QUgaUpQZ>4tm%P8UO$Q07*qo IM6N<$f|#W&oB#j- literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/blue_in.jpg b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/blue_in.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c159c3e468114eaa893ff436618dc8cc6dd92725 GIT binary patch literal 1823 zcmbtUT}<0n6!s6!9LJ>&tw&w)fZ>4>SEI}5Bul8IiJGzrg+B|%)Pd*{(a@JI(bOU# z1XIYStVBjo0Z~NQKuu^F)7rhzAv8f*4J=EgXw$}C8kB~Khbh1w!qh6eP6nj3UEA&W z%HQ$%zH@AybFY)XB=1Ai+s3zy5DX(w9=M?7Z;$~(5!vKB3M@;JB^$0#U4WBP=+BWtON{0rI<_#R_D~T$W)riq!1WYyaK3P_5N~@8fKF#5Q)Oz zt*9C*hqks(F&ppyzHxv3jfn46DiCsZyrUlju@>S=qIJQc`Jdsh8%fuv6SOGl$LkF& z(bq^iG-u3O>c51znXo6!_Ek)7`%hjoRvway~D2+j92S3mQirwD}ASlCl*tCjIBLZ*mjdSO4uhi znWJ!HR9lMNlFS#dR#Cb~gxJ1Odc;tuaI2scnwz|akNjWa4)*_)f8re?R<2^4zAgLNuAU8Dcb7C`<((o{u`a7E54`t=|EGW5?EIp+LD zZu3TanfCGr6N{1l1#^5ca$?s5?2c}%$Q|8ie1>i>W6#UT-K+BN-tCeSXLByq-QEE@ zj%+6E9r?SyCH^t?Aif;QQY}XY=kVOY`4R^u>+$@iW^$I&&CD{p^6kXapjO#urzopC~lIC4c=JhDgjf literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/blue_in.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/blue_in.png new file mode 100644 index 0000000000000000000000000000000000000000..34c7810911cfd4e6b623a50c9e36e0b74699d261 GIT binary patch literal 8083 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 loBh?aFvGhym#N|Y2#6aWB#5{;670ssJsKY;*p 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 literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_in.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_in.png new file mode 100644 index 0000000000000000000000000000000000000000..f37cdc68324aa780281cd2e15e07a8a22f07986a GIT binary patch literal 7083 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 literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_out.jpg b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_out.jpg new file mode 100644 index 0000000000000000000000000000000000000000..42783a5854b2b73fef09a59053c6cd3c7d76070b GIT binary patch literal 1761 zcmex=>ukC3pCfH06P05XITq?4J21E^7eo0A(TN+S4wfI*Oh zk%5tcnNf*>Nsy6Qkn#T!1__{3fq(%CFf%c70%fEGKuS@h7zKqA1BHQdA}GROy&zFR z6mvkr|8FtyFf#(JV-{qvXZXtWD_UG`x?FSlrC&#nFFn5C?w5TV3ttw#FoOzT?C$sO zcijClC#EW^aBvVb_}HBJ}*%AAy`|Z{&N21 zjDdtm-t!GBreAKq3?kc}9|-Y&dH5w!WCF}-AbalsorvaQyj+)ez8)ItD@_#^%v*A$ zsi|sm5EthwkWf*Uqob|oN>-MyYD+|fugsbd;2#7N!mh_cb=|BNTB_@I0)svZ82*dE zgw^X^Ai-HLG+rV2kE%fOR-i}%3!)RRXT8t?=`k=aifMK>S3SSr!r7BWygYY)fw?D4 z(8Z@KKouYuc0i4G(?O&vM151Z-88Pwm|ro!xH=Auwv@@1z>g~NrR0wEJ2+;ipK?Z>|=ty3fHBHZZLVi$I#7;fO zy`C$Rp6q1%s|dN{@%n6`2Yt_uRwSv0ET zmQ08e4`8a)T_^R(YH|>FcH2a-LX$pVn!WXKoiZ@}uA7TZH`M64jw_O5Ob_G-WJz4- zEs(w<)n~IgrV$5!W3bvH8Eszo3p~vWG-Sj|&GKT#Bn*g^U%P9Z= literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_out.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/gradients/grey_out.png new file mode 100644 index 0000000000000000000000000000000000000000..97b8c046b64e21ce4ad2523f76a6d8787d23e098 GIT binary patch literal 6009 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_soft_horizontal.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_soft_horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..dfb5c95ba78a538b315805fb954d62083b38381a GIT binary patch literal 258 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI_!3HER7z;E4DYhhUcNZWH1V5d3*8?fe0*}aI z1_o|n5N2eUHAey{$X?><>&kwYnUP;o#n60{08ogd#5JPCIX^cyHLrx>+nH(mfnwF3 zE{-7_*Im0d=CU~Q9MSMfXYbp$Qvcvz=Db56GQ;KX$j{+vo0FKfkM&mKG{cKRbEg09 z=k>m+Td{t&YTWbEoy%@7iu#vvbN|^hHC*<*{!DWH$|wIY{1Ec6?3?uYjCtC$4S6w3 wympx{ossu#uT!k9!rYEYlC2t?_ap-JmE}zs*Pna-8t4oLPgg&ebxsLQ0CKZdQUCw| literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_soft_vertical.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_soft_vertical.png new file mode 100644 index 0000000000000000000000000000000000000000..a370a6292aae87bc0d7a5d1b6a424b2fff35c710 GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^20+Zn!3HE}sOa_sDYhhUcNZWH1V5d3*8?fe0*}aI z1_o|n5N2eUHAey{$X?><>&kwYnUPiB1I3Cx zT^vI=uA81X&3V{BfbHSU_JiT(6Ur_*&hnF7c~D?U{BFw*88*9PyMD8-c$u1eXhy?7 zQJv*1`|39qsBK{2)bunI_$vBb^T&yK{f}x@lUcsYIuyv9oO4@{GrL;wor~zp3X!e$ oDgta5(>%8~tGZo!%l4Dm_knqs#oB_2K<>&kwYnUP=8Fy>X*Z=euIiEBiObAE1aYF-J$w=>iB1I1cB zT^vI=u7`FR<}o=69I&~!`);rRtM9xi$3N-*V=!kd3u17U@}A}!I+yj*qO!$GsyjIK zmmGN45|YLhcRz8`qy6s>D#u3TeLAgI?7!-jMvPEhMuAnWX_PfX4R=Z8b;a-1_1W38 zKm2(yd7aUm$?GiUobhk{=;W|n$!uj*%MBHYpsi8{{}{p)Omd_gEiM5a#Ng@b=d#Wz Gp$P!g-dQXF literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_strong_vertical.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/meterbase/bevel_strong_vertical.png new file mode 100644 index 0000000000000000000000000000000000000000..ad6eabfd84c4f8f1a93812aa1a9f000258fd2272 GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^20+Zn!3HE}sOa_sDYhhUcNZWH1V5d3*8?fe0*}aI z1_o|n5N2eUHAey{$X?><>&kwYnUP<@=*|x_kaCU^*N775{M_8syb^|QXQu53idA^J zIEHXscRhKK_mBaP!^QmVoz-ud)*PO}b421yfT~~cZ0A;snwOsOMxR+LCh2MRU1;R9 zc)+->rP(Y&z~a~c$uG6DlOMRq=R`N3pYS~S)4g6pt>b}>PMXIm@5PBVzy9VLdQ?fH qD@s80M=F!5YtI?cBLY2Vlmhl=%rc+j7216eQL70(Y)*K0-AbW|YuPggqCK+)B-P4|zO@Km*o-U3d9M_XOy1I@i%qUz&n}<}22WQ%mvv4FO#r&XcT2|xayI5LCL&Dd}O b;~^#neo2<;B8iDHKs5}Wu6{1-oD!M9I_m*YI&YP!WTttDnm{r-UW|f-fh! literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/select/arrow_down.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/select/arrow_down.png new file mode 100644 index 0000000000000000000000000000000000000000..674dee8b1a0591cfe2499f43f6ac2fa53e26cc19 GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^AT~P(8;~?yb0--{u@pObhHwBu4M$1`kk47*5m^jW ze;tGwoit`w00kvWTq8Op3BPJ$N%y` z(4;vTFZ6@ly^F*=HYL{zUf&q}PT+TLUCaNZNp^7-?%NL@nDV4>^);*h2W8L2>!0e| UzMNyW6lfoVr>mdKI;Vst048Tq$p8QV literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/circle.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..c2c7f31f0ce16a5ac2c7af0ad0ce4ba101e8de76 GIT binary patch literal 7594 zcmV;b9aZ9qP)GTC4%`C?lNwCc=-M!pB-MvB7h$%CcySKXw zWQTi`6eU^AHiO(wsas4CXpCXmf$XInR{J6XFtJ6Y8R z$g9e#Dyqt>Dnv%EW!)VBA~T1mf$mWymo(zv#+%uqG2NJ?CCMET^}LGY9%B^Avr2 znUz;ZnyqXvl>pGpBvR&%+`DGXQA%_P2&lW&WSjNuZUo8gW=XU4IAN2P?pEfWZl2~| z;lACi-J(6bBBK19bVo#oM|*giS(_OmLNs=Gz|8?dX4ax$2%153QgsXkfT}tU5z*ZO zfXZZc!@bQj*%9b&%PKRb$=x_wnakUZRV4zd?jjIX1$780iTGb%VP_UUJ80{3I{=~EPjm}N2$CqF%=BLlaC*|2 zX10xw@qUaqiyR};Xc=R+5AWfg;iKg2pK-NK>#*7G>@gr<==$byyGvg&APh zB4L0?p#fL}FhC+Cu`1ALa1!>_F_HIzMU0iTMWF>)kwN;h6Y@pcKH;t$W2Ad*ckkDn3A2jup7T{PqlbHs zG1|j5Vsu2v=j>sI;V!!_O$S7i!DMFi;X(suCU?i_1CQwdRi%Lj8mNjBqS?xqh+5jW zaYrCCi*%M`02ywzGtJ#)7T|&sl=BqdtR$|9y|&ynP!1Z|D2BNgfgNsXC+9O_B*2WY zzOI?(QEsd`Uo|2+V$_@`+^_3WL?|LK#%LRc=~M0wcZV6^J}`z0k0Z=XFc2^p8hu_> zRgfrA=xk8LXMJ#?vA4s$RA24KPP(#>1dg^L6~L|1E!31EFo zAzQ5gv?E5Ec}7@8j12ekh??_C_edD}x_g zBeRBCAi^LKV+;&;wLNvO4Wgc4HkdUUK(EBjA}LrHs!HyOy*J%mWeeV}%w%SQr1stb zX(TGGD5}?5$)NZnz@VL*;w09BC=LyVn>WdH^D^@!E&b$pMtB*luWQtGT^$~ZIW*_g zn6rjE#*B{Z2h&~djz6zIf>XI^BTDA(ZDs@sBnsw;chu1c@pIL6T{Xt&F-FCld|goyFU%P|BQ#zw3@668dW=Cc!*yK>3;6h^LVy$^ zp4@(-;`=2Moe5wfGl6+A;~E1N6F$xfpY$*~9gS1RzpaoWIA7Q*T;0md(-vzZJ8|C|7J;g*MS{NQE&!|st8%!*x--MwD`vD=bolA>BcjF_6^}E<82T2w*XyM@ zXU{n?#=z?}74wA|q4}C{cMKo6t_v21!GUo(B_iUqMgVa46ZUqB-9dulJjIZii35dQ zUK{QMyEgVs3_5(dwr#@xXnj5afCUr`_W6A9^LiB}s3nEGyK{pOz=#e&4U7tRPlIju z=<78q%qvFJH7>;%HRsf83`KxDUIk8xp)#x zj>iF{Xcng%^l68+3ovk3VfN_?5y9Gy?WNd;u!-d#h&-L;FKGX?Q^L>t_gWaGFUTlC z=tiMIULqi?YK5E2-FpmA$(m#jAML~Yx~Ar9YK*JK7<&2CoD=gkaE(h5lRrN%UGu`Y zE(~|f>w^2>cnm)g;q);cU_=8997Ncu2c2!O#-0Kt%l%tNbdBqr{GUr#49q!kos{4hL*p@ajKL=gIOh~e{)Yc~PqKmOkPM@F5Fpwl zYPp+x9k>g_Mas-Tl4e^X(P&W8-_M8FrB;>kB{jQUZdFm9sfdBh}<%Hj=G~_Ps%O^Gr1xuZC{QvRk;jhYx5LtA=X)pmAf+ zA$QbpRJn2tc{gUsYBWmSk;5{p17I5^x|f-Cgu})eVK1|D+U}Y$dHwvzhvPLT=5lDp z0fGyFs>&p9z~mlPPb0Wb?(56U~m%sIw*-t#-~&^^A! zyASv9VSc`jBh1Vq$Wyne!;C?bB2ZN+l6~f%deY>9%H8zotzB9b8ky(_jxneb_Smor zjd7uBV?C|$6k|g!Vw+&bHZ#(l;lta^U>0yEPfCPnl9?&Y@bmMdh`@|E15E^+)SQ#! z8W_VEaoHG7KULo&%)-LM!#(`h3D1P~n+BqcVn5;U7SRsmg9D2AW{GfN3KCn*t7MAk zMr+q5x1T8R4Mkx4zF+Uyg@?s3hKG~(M82E34T^4ICMw+Qp(bI1K(Q%FV0N{+dp?E}O&ASu z$~`qOtxD}ajJ13C!1j8u%jC_iFVPkja38WLj&0b(z~!{3#fKf900cBJ`j8ocu!%7o zZD)43XOg2%xQ}blA~4K2d{|hJ0K=W6In8{GK|2UA%-jJEcQZ&~<|ffjl%mfl6LpvY zwP+B;Kx|BC3!d5Ijd4hi;dBr4(*pT0z#dQ7lL&K$lL0f=fd#-Jsdb8( zRYPTBR+mf$I;*N=ZtVz4pjS~6ca>}yJO`X9JjAP2fHx6lm=UU7Yyb{F(On0CnOTQf z=u1nPNd{As02*e-X(4OijIqH77l2MXG(YSaZY;v_#I?CQ-OT`ndpvnyCNj({j!bYS zLkTdd0VY~(0~}}sx~jt56mY950}qI*DjV*-PAZp^HrzWc5C%qsJ}|fskRPB8pUWH`VkLLZ!)+f14MV)SG9_O4?+}a#tTUM@NY+Q3iljWfbj*pH8yO-3usEM`m>nkRKWhK>xOJ>OAw8iTa0!G|{NQ zzvPML;pll-3yFN>>myhm@E&IpJ9XhXc@hMTuCM3>h&nxt)S1;zU9fK}98W~pY4I(B z4L=fUW*a~gLAGE1do{q(0xbrCnos)e|u_-NqCew83Y>50bGJYfSDOjKLSaC|B}uB z`_D&GC#W+J|33bgVn6Q%z|aQx7=V4}1)@r#_?@B&(*H|5uG+u9fd7*3D4_v$=f8gb z1lIXn>NlzdP;8Jbs!8bKC0l@`DgZaZB+%V0oX^tn{J)>*S423UMnZxpifm*z|9vp0 zw;IrZp;3Du$5=!5b(j!ekCqCi_+=K*-~p-q>(y`mWHz79jWeP5AvK;Sm!wi7=zUyq z@NRVh@FLk}*b-6Qq$H3DsJpwXbsqM7iU_GQAI=C;H6ft;P-2pSl2+#tT*TkJJG8r@ z`|yI+|8f#}9MBM-$OlVjMI}nA4eEvLM0Vn(i7L^DRP`mX4(VQ9+*LYB=AzU9x~fGr z1h#vm1d`ZQ4N{G!322}jDveea`s%n8O5vp!fw~Fej6s%-rzY##Os|v8jF8z}R z-73J6SRTd{4QNsH=blV%L$#0suL#tC^U`6SE%St)ZS3(`MmC31vEM{Mf7v>rMyVQ48XLoJ1GFzF-uI>hqdv9gduM@hq(CA(Di0ME{UA51l z0MBy+*$YqTqX2d%7V4Y72vTUt)RM_c5`k)%0S(r@(Q}H|@o4Sy_bS=0LR3L}BcCm> zoh`5gpgp{^is4q74R;EpLx7uitDuJ?B=i`pELBt7I8$Lus;cU&R8}dgIy3b(cvnFs z9;a;dBdc{M?z@Nv+8U^J60M2uww5eTsYEx#jctM1IIC&Gd_iYb4Yh^xg2+d-+|mxF zr^*Mq6Tnbc3d2hRE30_6qX6RhnIi%NGCamV3v#V4NzATpNLAfgRkhcagjS<6OQ(O7 zDC#((aMxCq&i;vr(h*iqO>cB2G9^+_l|6t3v8&*jj7q46I#Vlx)~V0>!2N)s?8M-} z%GAN)8<|5x1G{!}xzV#Jv9=LX9k7XikfM9lS^en42K`stl0;Qk6UgfBs;bOtL7A16 zy_H>EySjE^?YE@E_-pv{%X0u0Q zm$W_~gc;UnAuN8=VAqB(w}Y>_U_E3yR-Lh-Fq`L8&AEey&(#_3ZJ!(O%0&H&P3jMoY9T$LP)aJ z-QH~lsIb)-s)DNOfQUz@|9Nb^VI`&ay{yC9uNs7kd^RoUxaYIS91*WTLqR(7iP*2+|AqxL@X`7UH8*7`g} zySS=&_hx1yS6Wu`tlkMxRkkucBDy+JDrF`kHuiRfC4lDM1s|y@KfowcW@jkUn{*L) zLHY<@Kwhcovv;>}fjrb_12hmOkKbwGQO8i#YCw>>_f~c(vpTa>dntG8eJ^#V*5`x! zzV&s;u0m(y-fv_#O5)!8wC`G|y}5UDSF*Fv-N;M}pbcj~*VIx~qe{JZmye?DhW-5j zFH=9T#q2=lqtY5|bbro*24CH}o0v9x>sDb>>o$POavSCnS-IQ7pRG+EculEAeJi^F zXk~WiX@Bor>fT!GgO%C2me$_LwRqpR)@MOmc)$O{wa!vic4F_w?nYxg~5hp#^`?e~33xG21g zVS&%*qcO%AoL*CsxOcaXu!$;{`)E<*=Krbz5BH$+;DxH)y&Joq5MN85wX|wUSy=nV zeSdV{3u~+VHnU-g}gzMg@+E$g; zYSZ3~s$v{s1 z2XDMy(&w*;HrYctRSjL^jGE);f<6L$_b%rc7d=yS+Uk$o$Du4DQ~>(kENyffr0P$=-1oM7+iNzl1^AiCJgKDZ7tk{b-9`uG)3d zWO$!d@ed;c*c<3pcjC)}?!6^ao%xL1splJ=jkP|w?;D+o_xsJv)O~N;_Zw?(R4wNH z#+}=8Z?1J#?laT6VH7Mgy{as#Dx8kYUeL$MOD8>V(cl0|M?~Of^|RzWb@&1C&+8@f zKg7@fsNVlU=Y{KMplYBjfR4uc^Dn3sFJcz6$cD=ZK*c$sqFWC zW2>MP{(e5ZS7NQdws&FejkT72J|AWk*S&bJ#l4r;-o7(^*Y>rSS7tO2QB}UMdT<5+ zdF?o$1#%K)Zob!>-`02EH!G7T z??1fn<&|l>%PZTu+Rq{eMo=7?yMJ%*5Sp;)pH{ zHL3_}B@9tT0QmX2^xZGGaU*shEU=dT%lNZBPUQNBdCG2B1Ml~n4Ipy~4gCFln4Rc5 z`Oby9Z@ar$_iZavdvD)++xvd|zPDwjXQo%CueA*5sH(`^jFrpA7*SndN^&AQSHBT< z0T|TYx6B$-m(;CuC#h{t7Xa^WMKsnZtWHb|zPgXFvNtUPB60apN8)2gerP+C`+qG= zQ^=LL_qi|dErk1k8>=d;DpnP0E#^vl-y2fey%uw)f9{X%D%Lt@+Ry^I>h6MLuE_d%`cOCY5?(Bf7yLj=T{Z67IrpwJ=0w#*1Fl_x{o9# ziMt!hluA}CRtdGAD|9N$m3mTwYc1~+702jG@7A7ivAgZ_Sx%MhDz9CUowoKe zwS4a#RTW+B-94%+x;wgigi6uYeS`*}6G(G7>sR~?3rhFy_6YZ$xY%He@9GL>MEe*v zT3uCL>GUWXt=%`0q}ynjKn58tFXx#Bz8buMa&&Jrn_fV5TS>}JsKv@;Wuc{C!`E7- zmSv^m{JUqSJr}7*W=B;=cl|HpyE{VasXj})3BaG7EY`E_kziSUW&DQF&@vyM&9-io zRdrN$#xN)AUw4NY=CE)Oi{H!BXZ7HmJn1}2;!y&nl3Mm`4>GGz)lj!}%QCm5@}14f zG#s@t-1l*)AsK2gvesv0dtchBgSiyeZ&B*@FUkM!K#@IiJrKv`97 zadxX~?RC0=tI9H)l49@ef=2FSH%3*upixqwG!H$l+1>Gf0T|26vx-HmApigX M07*qoM6N<$f+;ng4*&oF literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/horizontal.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..e4568beaab556f9ec811b24298d34ce2673eb28f GIT binary patch literal 4138 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 literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/over.xcf b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/over.xcf new file mode 100644 index 0000000000000000000000000000000000000000..dba97587c79138c505c429e20e4690de4d1fc9c1 GIT binary patch literal 82092 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 literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/square.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/square.png new file mode 100644 index 0000000000000000000000000000000000000000..9011a78ab4a4c773dcc3adbd7c61000550c54e6a GIT binary patch literal 705 zcmV;y0zUnTP)jPLtC$MFv_LFoHFe~EynFvhyjjjyT- z|1E)+b%7XcPUyNW`wbO9NUv%A0yggyY{)5UFYP8eg}La3@L zT~!Ax$?0?&d^7%hK9kjj&>G>KKYTI%dc7u_D!~nr@bJL%$=d&-%nQo0bk_LW?Iza? zY!%MAi(xO{3`s@;ame#hNgDUQ_;@@fwvE4BF4B1kRVZ*c9IQpc{eIuIJ)~DlrnW6a{E{&KN4s7o3l~JB z8;lo5bTVDN5keLr48tIdImJf(o@Lo2gtl#k(Wtc5#C(bblb*K@;12>D8DCk^!FDe{ zh7ctHTb_4f3D`E?DgYY^&lv!ww>qZ)xZ-&$08Ev)P69B|*f!&I8bO zjO7IY9S4}+08o8&?G*r(A1%BCpz3p#mjG0JsPq=V-cQtC1K2a1;5`7@v6LVH$P6R} z17PbYT2KJC3?T*wVD0#6kO0;U9tR6xY2-X;0CSfOf(I~l%_Ir{pDq|h1K|B?SyTYV nmkOf;@VrhMC4d->2LSj5MWN(83o|M}00000NkvXXu0mjf`qmus literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/vertical.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/state/vertical.png new file mode 100644 index 0000000000000000000000000000000000000000..dc2f7c29ab473e9a7da97458d4bd422d4ab4e8ed GIT binary patch literal 3606 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 literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/window/background.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/window/background.png new file mode 100644 index 0000000000000000000000000000000000000000..94c03e9f69fbb4d13cbdb42eeb7e2ba808806aa6 GIT binary patch literal 81473 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 T_^EJZ00000NkvXXu0mjf9GDbz literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/window/resize.png b/share/web_surfaces/builtin/mixer/ardour-toolkit-theme/images/window/resize.png new file mode 100644 index 0000000000000000000000000000000000000000..02cba4790e90630f4ed14a5c97c1edea50f696aa GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmSQK*5Dp-y;YjHK@;M7UB8!3Q zuY)k7lg8`{prB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&Kt)}iE{-7) ztw+zD6l^dMV0LWxd-lw|@#Dt!C39J-qE^3Mb*$Pn&gjFjr_9QFvNIlTY82Izyz%f$ zqp+Xsh6h_1wf!VNJovz<&nMgQU_+w@pX7-LFB^5{NjW@t$S5Kwi+b0|F;54JCoov9P{fwsnBv%}m->9`G{PAD5;Iw}V + + + + Ardour Mixer + + + + + + + diff --git a/share/web_surfaces/builtin/mixer/js/main.js b/share/web_surfaces/builtin/mixer/js/main.js new file mode 100644 index 0000000000..d0d8bb6918 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/js/main.js @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2020 Luciano Iam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import ArdourClient from '/shared/ardour.js'; +import { createRootContainer, Container, DiscreteKnob, LinearKnob, PanKnob, + StripGainFader, StripMeter, Toggle } from './tkwidget.js'; + +(() => { + + const ardour = new ArdourClient(); + + async function main () { + const root = await createRootContainer(); + + ardour.mixer.on('ready', () => { + if (root.children.length > 0) { + root.removeChild(root.children[0]); + } + + const mixer = new Container(); + mixer.id = 'mixer'; + mixer.appendTo(root); + + mixer.appendChild(new Container()); + + for (const strip of ardour.mixer.strips) { + const container = new Container(); + container.classList = 'strip'; + container.appendTo(mixer); + createStrip(strip, container); + } + + mixer.appendChild(new Container()); + }); + + ardour.connect(); + } + + function createStrip (strip, container) { + const pan = new PanKnob(); + pan.classList += 'pan'; + pan.appendTo(container); + if (strip.isVca) { + // hide pan, keeping layout + pan.element.style.visibility = 'hidden'; + } else { + pan.bindTo(strip, 'pan'); + } + + const meterFader = new Container(); + meterFader.appendTo(container); + meterFader.classList = 'strip-meter-fader'; + + const gain = new StripGainFader(); + gain.appendTo(meterFader); + gain.bindTo(strip, 'gain'); + + const meter = new StripMeter(); + meter.appendTo(meterFader); + meter.bindTo(strip, 'meter'); + + // TO DO + /*for (const plugin of strip.plugins) { + createStripPlugin(plugin, container); + }*/ + } + + function createStripPlugin (plugin, container) { + const enable = new Toggle(); + enable.appendTo(container); + enable.bindTo(plugin, 'enable'); + + for (const param of plugin.parameters) { + createStripPluginParam(param, container); + } + } + + function createStripPluginParam (param, container) { + let widget; + + if (param.valueType.isBoolean) { + widget = new Toggle(); + } else if (param.valueType.isInteger) { + widget = new DiscreteKnob(param.min, param.max); + } else if (param.valueType.isDouble) { + if (param.isLog) { + widget = new LogKnob(param.min, param.max); + } else { + widget = new LinearKnob(param.min, param.max); + } + } + + widget.appendTo(container); + widget.bindTo(param, 'value'); + } + + main(); + +})(); diff --git a/share/web_surfaces/builtin/mixer/js/scale.js b/share/web_surfaces/builtin/mixer/js/scale.js new file mode 100644 index 0000000000..4682df1d9a --- /dev/null +++ b/share/web_surfaces/builtin/mixer/js/scale.js @@ -0,0 +1,82 @@ +/* + * Copyright © 2020 Luciano Iam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// This module is currently unused as 'toolkit' provides its own transformations +// it could be still useful for developing custom widgets + +export class Scale { + + constructor (min, max) { + this.min = min; + this.max = max; + this.scale = this.max - this.min; + } + + fromWidget (val) { + return val; + } + + toWidget (val) { + return val; + } + +} + +export class DbScale extends Scale { + + constructor () { + super(-58.0, 6.0); + } + + fromWidget (val) { + return this.max + this.scale * Math.log10(val); + } + + toWidget (val) { + return Math.pow(10.0, (val - this.max) / this.scale); + } + +} + +export class LinearScale extends Scale { + + fromWidget (val) { + return this.min + this.scale * val; + } + + toWidget (val) { + return (val - this.min) / this.scale; + } + +} + +export class LogScale extends Scale { + + constructor (min, max) { + super(Math.log(min), Math.log(max)); + } + + fromWidget (val) { + return Math.exp(this.min + this.scale * val); + } + + toWidget (val) { + return (Math.log(val) - this.min) / this.scale; + } + +} diff --git a/share/web_surfaces/builtin/mixer/js/tkloader.js b/share/web_surfaces/builtin/mixer/js/tkloader.js new file mode 100644 index 0000000000..f4b29e4c98 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/js/tkloader.js @@ -0,0 +1,90 @@ +/* + * Copyright © 2020 Luciano Iam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// This is a convenience module for loading the 'toolkit' audio widget library +// https://github.com/DeutscheSoft/toolkit +// GPLv3 license, docs at http://docs.deuso.de/Toolkit/ + +const STYLES = []; + +const SCRIPTS = [ + 'G', + 'toolkit', + 'implements/audiomath', + 'implements/base', + 'implements/globalcursor', + 'implements/ranged', + 'implements/gradient', + 'implements/warning', + 'widgets/widget', + 'modules/circular', + 'modules/dragcapture', + 'modules/dragvalue', + 'modules/range', + 'modules/scale', + 'modules/scrollvalue', + 'widgets/icon', + 'widgets/label', + 'widgets/button', + 'widgets/container', + 'widgets/meterbase', + 'widgets/state', + 'widgets/toggle', + 'widgets/levelmeter', + 'widgets/value', + 'widgets/fader', + 'widgets/knob', + 'widgets/root' +]; + +export default async function loadToolkit() { + const tkPath = '../toolkit'; + // stylesheets can be loaded in parallel + const p = STYLES.map((style) => loadStylesheet(import.meta, `${tkPath}/styles/${style}.css`)); + await Promise.all(p); + // scripts need to be loaded sequentially + for (const script of SCRIPTS) { + await loadScript(import.meta, `${tkPath}/${script}.js`); + } +} + +async function loadStylesheet(importMeta, path) { + return new Promise((resolve, reject) => { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = `${getModuleBasename(importMeta)}/${path}`; + link.addEventListener('error', (ev) => reject(Error('Stylesheet failed to load'))); + link.addEventListener('load', resolve); + document.head.appendChild(link); + }); +} + +async function loadScript(importMeta, path) { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = `${getModuleBasename(importMeta)}/${path}`; + script.addEventListener('error', (ev) => reject(Error('Script failed to load'))); + script.addEventListener('load', resolve); + document.body.appendChild(script); + }); +} + +function getModuleBasename(importMeta) { + return importMeta.url.substring(0, importMeta.url.lastIndexOf('/')); +} diff --git a/share/web_surfaces/builtin/mixer/js/tkwidget.js b/share/web_surfaces/builtin/mixer/js/tkwidget.js new file mode 100644 index 0000000000..98365ab482 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/js/tkwidget.js @@ -0,0 +1,185 @@ +/* + * Copyright © 2020 Luciano Iam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import loadToolkit from './tkloader.js'; +import { BaseContainer, BaseControl } from './widget.js'; + +class Control extends BaseControl { + + get element () { + return this.tk.element; + } + +} + +class RangeControl extends Control { + + constructor (tk) { + super(); + + this.tk = tk; + this.lastValue = NaN; + + this.tk.add_event('useraction', (name, value) => { + if ((name == 'value') && (this.lastValue != value)) { + this.lastValue = value; + this.callback(value); + } + }); + } + + get value () { + return this.tk.get('value'); + } + + set value (val) { + this.tk.set('value', val); + } + +} + +class Knob extends RangeControl { + + constructor (options) { + super(new TK.Knob(options)); + } + +} + +export async function createRootContainer () { + await loadToolkit(); + const root = new Container(); + root.tk = new TK.Root({id: 'root'}); + document.body.appendChild(root.element); + return root; +} + +export class Container extends BaseContainer { + + constructor () { + super(); + this.tk = new TK.Container(); + } + + get element () { + return this.tk.element; + } + + appendChild (child) { + super.appendChild(child); + this.tk.append_child(child.tk); + } + +} + +export class Toggle extends Control { + + constructor () { + super(new TK.Toggle()); + this.tk.add_event('toggled', (state) => this.callback(state)); + } + + get value () { + return this.tk.get('state'); + } + + set value (val) { + this.tk.set('state', val); + } + +} + +export class StripGainFader extends RangeControl { + + constructor () { + super(new TK.Fader({ + scale: 'decibel', + min: -58.0, + max: 6.0 + })); + } + +} + +export class StripMeter extends RangeControl { + + constructor () { + super(new TK.LevelMeter({ + show_scale: false, + scale: 'decibel', + min: -58.0, + max: 6.0 + })); + } + + set value (val) { + this.tk.set('value', val); + } + +} + +export class LinearKnob extends Knob { + + constructor (min, max) { + super({ + scale: 'linear', + min: min, + max: max + }); + } + +} + +export class LogKnob extends Knob { + + constructor (min, max) { + super({ + scale: 'log2', + min: min, + max: max + }); + } + +} + +export class DiscreteKnob extends Knob { + + constructor (min, max, step) { + super({ + scale: 'linear', + min: min, + max: max, + snap: step || 1 + }); + } + +} + +// TODO: consider switching to [0-1.0] pan scale in Ardour surface code + +export class PanKnob extends Knob { + + constructor () { + super({ + scale: 'linear', + min: -1.0, + max: 1.0 + }); + } + +} diff --git a/share/web_surfaces/builtin/mixer/js/widget.js b/share/web_surfaces/builtin/mixer/js/widget.js new file mode 100644 index 0000000000..a4541d8677 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/js/widget.js @@ -0,0 +1,91 @@ +/* + * Copyright © 2020 Luciano Iam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +export class BaseWidget { + + get element () { + // empty + } + + appendTo (container) { + container.appendChild(this); + } + + get id () { + return this.element.id; + } + + set id (id) { + this.element.id = id; + } + + get classList () { + return this.element.classList; + } + + set classList (classList) { + this.element.classList = classList; + } + +} + +export class BaseContainer extends BaseWidget { + + constructor (context) { + super(context); + this.children = []; + } + + appendChild (child) { + this.children.push(child); + } + +} + +export class BaseControl extends BaseWidget { + + get value () { + // empty + } + + set value (val) { + // empty + } + + callback (val) { + // empty + } + + bindTo (component, property) { + // ardour → ui + this.value = component[property]; + component.on(property, (value) => this.value = value); + // ui → ardour + this.callback = (value) => component[property] = value; + } + +} + +// Currently unused + +export function createElement (html) { + const t = document.createElement('template'); + t.innerHTML = html; + const elem = t.content.firstChild; + return elem; +} diff --git a/share/web_surfaces/builtin/mixer/main.css b/share/web_surfaces/builtin/mixer/main.css new file mode 100644 index 0000000000..d16fb48e12 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/main.css @@ -0,0 +1,61 @@ +@import "ardour-toolkit-theme/theme.css"; + +html { + height: 100%; +} + +body { + background: rgb(62,61,61); + color: rgb(248,248,242); + font-family: Helvetica, Arial, sans-serif !important; + height: 100%; + width: 100%; + position: fixed; + margin: 0; +} + +div { + box-sizing: border-box; +} + +#root { + display: flex; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; +} + +#mixer { + display: flex; + flex-direction: row; + height: 100%; + max-height: 480px; + width: 100%; + overflow-x: auto; + justify-content: space-between; +} + +.strip { + display: flex; + flex-direction: column; + align-items: center; + height: 100%; + min-width: 120px; +} + +.strip-meter-fader { + height: 100%; +} + +.toolkit-fader { + height: 100%; +} + +.toolkit-level-meter { + height: 100%; +} + +.toolkit-knob { + /* empty */ +} diff --git a/share/web_surfaces/builtin/mixer/manifest.xml b/share/web_surfaces/builtin/mixer/manifest.xml new file mode 100644 index 0000000000..00ce953026 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/manifest.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/share/web_surfaces/builtin/mixer/toolkit/.gitignore b/share/web_surfaces/builtin/mixer/toolkit/.gitignore new file mode 100644 index 0000000000..265908d19c --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/.gitignore @@ -0,0 +1 @@ +styles/css/toolkit.min.css diff --git a/share/web_surfaces/builtin/mixer/toolkit/COPYING b/share/web_surfaces/builtin/mixer/toolkit/COPYING new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/share/web_surfaces/builtin/mixer/toolkit/G.js b/share/web_surfaces/builtin/mixer/toolkit/G.js new file mode 100644 index 0000000000..bdbf1b0747 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/G.js @@ -0,0 +1,218 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w) { +function Scheduler() { + this.Q_next = this.Q = []; + this.Q_tmp = []; + this.tmp = []; + this.debug = 0; + this.after_frame_cbs = []; + this.frame_count = 0; + this.current_priority = -1; + this.current_cycle = 0; +}; +function low_add(Q, o, prio) { + prio = prio << 1; + var q = Q[prio]; + var len = Q[prio+1]; + if (!q) { + Q[prio] = q = []; + len = 0; + } + q[len++] = o; + Q[prio+1] = len; +} +function low_remove(Q, o, prio) { + prio = prio << 1; + var q = Q[prio]; + var len = Q[prio+1] + if (!q) return; + for (var i = 0; i < len; i++) { + if (o !== q[i]) continue; + /* NOTE: this looks odd, but it's correct */ + q[i] = null; + q[i] = q[--len]; + } + Q[prio+1] = len; +} +function low_has(Q, o, prio) { + prio = prio << 1; + var q = Q[prio]; + var len = Q[prio+1]; + if (!q) return false; + for (var i = 0; i < len; i++) { + if (o === q[i]) return true; + } + return false; +} +Scheduler.prototype = { + run : function() { + var Q = this.Q; + this.Q_next = this.Q_tmp; + var empty; + var runs = 0; + var debug = this.debug; + var calls = 0; + var t; + var i; + + if (debug) t = performance.now(); + + while (!empty) { + runs++; + + this.current_cycle = runs; + + if (runs > 20) throw("something is not right"); + + empty = true; + + for (i = 0; i < Q.length; i+=2) { + var q = Q[i]; + var len = Q[i+1]; + + if (!q || !len) continue; + + empty = false; + this.current_priority = i>>1; + + Q[i] = this.tmp; + Q[i+1] = 0; + this.tmp = q; + + for (var j = 0; j < len; j++) { + try { q[j](); } catch (e) { + TK.warn(q[j], "threw an error", e); + } + q[j] = null; + calls++; + } + } + var after_frame_cbs = this.after_frame_cbs; + + if (after_frame_cbs.length) { + this.after_frame_cbs = []; + empty = false; + + for (i = 0; i < after_frame_cbs.length; i++) + after_frame_cbs[i](); + } + } + + this.Q = this.Q_next; + this.Q_tmp = Q; + + if (debug) { + t = performance.now() - t; + if (t > debug) + TK.log("DOMScheduler did %d runs and %d calls: %f ms", runs, calls, t); + } + + this.running = false; + this.current_priority = -1; + + this.frame_count++; + + return runs; + }, + add : function(o, prio) { + low_add(this.Q, o, prio); + }, + add_next : function(o, prio) { + low_add(this.Q_next, o, prio); + }, + remove : function(o, prio) { + low_remove(this.Q, o, prio); + }, + remove_next : function(o, prio) { + low_remove(this.Q_next, o, prio); + }, + has: function(o, prio) { + return low_has(this.Q, o, prio); + }, + has_next: function(o, prio) { + return low_has(this.Q_next, o, prio); + }, + after_frame: function(fun) { + this.after_frame_cbs.push(fun); + }, + get_frame_count: function() { + return this.frame_count; + }, + get_current_priority: function() { + return this.current_priority; + }, + get_current_cycle: function() { + return this.current_cycle; + }, + describe_now: function() { + if (this.running) { + return TK.sprintf("", + this.get_frame_count(), + this.get_current_priority(), + this.get_current_cycle()); + } else { + return ""; + } + }, +}; +function DOMScheduler() { + Scheduler.call(this); + this.will_render = false; + this.running = false; + this.bound_run = this.run.bind(this); +}; +DOMScheduler.prototype = Object.create(Scheduler.prototype); +DOMScheduler.prototype.add_next = function(o, prio) { + Scheduler.prototype.add_next.call(this, o, prio); + + if (this.will_render) return; + this.will_render = true; + if (this.running) return; + + window.requestAnimationFrame(this.bound_run); +}; +DOMScheduler.prototype.add = function(o, prio) { + Scheduler.prototype.add.call(this, o, prio); + + if (this.will_render) return; + if (this.running) return; + this.will_render = true; + + window.requestAnimationFrame(this.bound_run); +}; +DOMScheduler.prototype.run = function() { + this.will_render = false; + this.running = true; + Scheduler.prototype.run.call(this); + this.running = false; + if (this.will_render) + window.requestAnimationFrame(this.bound_run); +}; +DOMScheduler.prototype.after_frame = function(fun) { + Scheduler.prototype.after_frame.call(this, fun); + if (this.will_render) return; + if (this.running) return; + this.will_render = true; + window.requestAnimationFrame(this.bound_run); +}; +w.Scheduler = Scheduler; +w.DOMScheduler = DOMScheduler; +})(this); diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/README b/share/web_surfaces/builtin/mixer/toolkit/implements/README new file mode 100644 index 0000000000..f6437538bf --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/README @@ -0,0 +1 @@ +@category: Implements diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/anchor.js b/share/web_surfaces/builtin/mixer/toolkit/implements/anchor.js new file mode 100644 index 0000000000..3bfbca6dd2 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/anchor.js @@ -0,0 +1,82 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +/** + * TK.Anchor provides a single function translate_anchor + * which returns real x and y values from a relative positioning. + * For example positioning a {@link TK.Window} with anchor center + * needs to subtract half of its width from y and half of its height + * from x to appear at the correct position. + * + * @mixin TK.Anchor + */ +TK.Anchor = TK.class({ + /** + * Returns real x and y values from a relative positioning. + * + * @method TK.Anchor#translate_anchor + * + * @param {string} [anchor="top-left"] - Position of the anchor. + * @param {number} x - X position to translate. + * @param {number} y - Y position to translate. + * @param {number} width - Width of the element. + * @param {number} height - Height of the element. + * + * @returns {object} Object with members x and y as numbers + */ + translate_anchor: function (anchor, x, y, width, height) { + switch (anchor) { + case "top-left": + break; + case "top": + x += width / 2; + break; + case "top-right": + x += width; + break; + case "left": + y += height / 2; + break; + case "center": + x += width / 2; + y += height / 2; + break; + case "right": + x += width; + y += height / 2; + break; + case "bottom-left": + y += height; + break; + case "bottom": + x += width / 2; + y += height; + break; + case "bottom-right": + x += width; + y += height; + break; + default: + throw new Error("Unsupported anchor position"); + } + return {x: Math.round(x), y: Math.round(y)}; + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/audiomath.js b/share/web_surfaces/builtin/mixer/toolkit/implements/audiomath.js new file mode 100644 index 0000000000..a1a153c297 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/audiomath.js @@ -0,0 +1,324 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +/** + * TK.AudioMath provides a couple of functions for turning + * linear values into logarithmic ones and vice versa. If you need + * an easy convertion between dB or Hz and a linear scale mixin + * this class. + * + * @mixin TK.AudioMath + */ +TK.AudioMath = (function() { + var exp = Math.exp; + var log = Math.log; + var pow = Math.pow; + var MAX = Math.max; + var LN2 = Math.LN2; + var LN10 = Math.LN10; + + function log2(value) { + value = +value; + return +log(value) / LN2; + } + + function log10(value) { + value = +value; + return +log(value) / LN10; + } + + function db2gain(value, factor) { + /** + * Calculates 10^(value / factor). + * Transforms a dBFS value to the corresponding gain. + * + * @function TK.AudioMath#db2gain + * + * @param {number} value - A decibel value in dBFS. + * @param {number} [factor=20] - The factor. + */ + value = +value; + factor = +factor; + + if (!(factor >= 0.0)) factor = 20.0; + + value = +(value / factor); + value = +pow(10.0, value); + + return value; + } + + function gain2db(value, factor) { + /** + * Calculates factor * log10(value). + * Transforms a gain value to the corresponding dBFS value. + * + * @function TK.AudioMath#gain2db + * + * @param {number} value - A gain factor. + * @param {number} [factor=20] - The factor. + */ + value = +value; + factor = +factor; + + if (!(factor >= 0.0)) factor = 20.0; + + value = factor * +log10(value); + + return value; + } + + function db2coef(value, min, max, reverse, factor) { + /** + * Calculates a linear value between 0.0 and 1.0 + * from a value and its lower and upper boundaries in decibels. + * + * @function TK.AudioMath#db2coef + * + * @param {number} value - The value in decibels. + * @param {number} min - The minimum value in decibels. + * @param {number} max - The maximum value in decibels. + * @param {boolean} reverse - If the scale is reversed. + * @param {number} factor - Changes the deflection of the logarithm if other than 1.0. + * + * @returns {number} A value between 0.0 (min) and 1.0 (max). + */ + value = +value; + min = +min; + max = +max; + reverse = reverse|0; + factor = +factor; + var logfac = 1.0; + if (factor == 0.0) factor = 1.0; + else logfac = +MAX(1.0, +pow(2.0, factor) - 1.0); + if (reverse) value = max - (value - min); + value = +log2(1.0 + (value - min) / (max - min) * logfac) / factor; + if (reverse) value = -value + 1.0; + return value; + } + + function coef2db(coef, min, max, reverse, factor) { + /** + * Calculates a value in decibels from a value + * between 0.0 and 1.0 and some lower and upper boundaries in decibels. + * + * @function TK.AudioMath#coef2db + * + * @param {number} coef - A value between 0.0 and 1.0. + * @param {number} min - The minimum value in decibels. + * @param {number} max - The maximum value in decibels. + * @param {boolean} reverse - If the scale is reversed. + * @param {number} factor - Changes the deflection of the logarithm if other than 1.0. + * + * @returns {number} The result in decibels. + */ + coef = +coef; + min = +min; + max = +max; + reverse = reverse|0; + factor = +factor; + var logfac = 1.0; + if (factor == 0.0) factor = 1.0; + else logfac = +MAX(1.0, +pow(2.0, factor) - 1.0); + if (reverse) coef = -coef + 1.0; + coef = (+pow(2.0, coef * factor) - 1.0) / logfac * (max - min) + min; + if (reverse) coef = max - coef + min; + return coef; + } + function db2scale(value, min, max, scale, reverse, factor) { + /** + * Calculates a linear value between 0.0 and scale. + * from a value and its lower and upper boundaries in decibels. + * + * @function TK.AudioMath#db2scale + * + * @param {number} value - The value in decibels. + * @param {number} min - The minimum value in decibels. + * @param {number} max - The maximum value in decibels. + * @param {boolean} reverse - If the scale is reversed. + * @param {number} factor - Changes the deflection of the logarithm if other than 1.0. + * + * @returns {number} A value between 0.0 and scale. + */ + value = +value; + min = +min; + max = +max; + scale = +scale; + reverse = reverse|0; + factor = +factor; + var logfac = 1.0; + if (factor == 0.0) factor = 1.0; + else logfac = +MAX(1.0, +pow(2.0, factor) - 1.0); + if (reverse) value = max - (value - min); + value = +log2(1.0 + (value - min) / (max - min) * logfac) / factor; + if (reverse) value = -value + 1.0; + return value * scale; + } + function scale2db(value, min, max, scale, reverse, factor) { + /** + * Calculates a value in decibels from a value + * between 0.0 and scale and some lower and upper boundaries in decibels. + * + * @function TK.AudioMath#scale2db + * + * @param {number} value - A value between 0.0 and scale. + * @param {number} min - The minimum value in decibels. + * @param {number} max - The maximum value in decibels. + * @param {boolean} reverse - If the scale is reversed. + * @param {number} factor - Changes the deflection of the logarithm if other than 1.0. + * + * @returns {number} The result in decibels. + */ + value = +value; + min = +min; + max = +max; + scale = +scale; + reverse = reverse|0; + factor = +factor; + var logfac = 1.0; + if (factor == 0.0) factor = 1.0; + else logfac = +MAX(1.0, +pow(2.0, factor) - 1.0); + value = value / scale; + if (reverse) value = -value + 1.0; + value = (+pow(2.0, value * factor) - 1.0) / logfac * (max - min) + min; + if (reverse) value = max - value + min; + return value; + } + function freq2coef(value, min, max, reverse/*, prescaled, factor*/) { + /** + * Calculates a linear value between 0.0 and 1.0 + * from a value and its lower and upper boundaries in hertz. + * + * @function TK.AudioMath#freq2coef + * + * @param {number} value - The value in hertz. + * @param {number} min - The minimum value in hertz. + * @param {number} max - The maximum value in hertz. + * @param {boolean} reverse - If the scale is reversed. + * + * @returns {number} A value between 0.0 (min) and 1.0 (max). + */ + value = +value; + min = +min; + max = +max; + reverse = reverse|0; + // FIXME: unused + if (reverse) value = max - (value - min); + min = +log10(min); + max = +log10(max); + value = ((+log10(value) - min) / (max - min)); + if (reverse) value = -value + 1.0; + return value; + } + function coef2freq(coef, min, max, reverse) { + /** + * Calculates a value in hertz from a value + * between 0.0 and 1.0 and some lower and upper boundaries in hertz. + * + * @function TK.AudioMath#coef2freq + * + * @param {number} coef - A value between 0.0 and 1.0. + * @param {number} min - The minimum value in hertz. + * @param {number} max - The maximum value in hertz. + * @param {boolean} reverse - If the scale is reversed. + * @param {number} factor - Changes the deflection of the logarithm if other than 1.0. + * + * @returns {number} The result in hertz. + */ + coef = +coef; + min = +min; + max = +max; + reverse = reverse|0; + if (reverse) coef = -coef + 1.0; + min = +log10(min); + max = +log10(max); + coef = +pow(10.0, (coef * (max - min) + min)); + if (reverse) coef = max - coef + min; + return coef; + } + function freq2scale(value, min, max, scale, reverse) { + /** + * Calculates a linear value between 0.0 and scale + * from a value and its lower and upper boundaries in hertz. + * + * @function TK.AudioMath#freq2scale + * + * @param {number} value - The value in hertz. + * @param {number} min - The minimum value in hertz. + * @param {number} max - The maximum value in hertz. + * @param {boolean} reverse - If the scale is reversed. + * + * @returns {number} A value between 0.0 and scale. + */ + value = +value; + min = +min; + max = +max; + scale = +scale; + reverse = reverse|0; + if (reverse) value = max - (value - min); + min = +log10(min); + max = +log10(max); + value = ((+log10(value) - min) / (max - min)); + if (reverse) value = -value + 1.0; + return value * scale; + } + function scale2freq(value, min, max, scale, reverse) { + /** + * Calculates a value in hertz from a value + * between 0.0 and scale and some lower and upper boundaries in hertz. + * + * @function TK.AudioMath#scale2freq + * + * @param {number} value - A value between 0.0 and scale. + * @param {number} min - The minimum value in hertz. + * @param {number} max - The maximum value in hertz. + * @param {boolean} reverse - If the scale is reversed. + * @param {number} factor - Changes the deflection of the logarithm if other than 1.0. + * + * @returns {number} The result in hertz. + */ + value = +value; + min = +min; + max = +max; + scale = +scale; + reverse = reverse|0; + value = value / scale; + if (reverse) value = -value + 1.0; + min = +log10(min); + max = +log10(max); + value = pow(10.0, (value * (max - min) + min)); + if (reverse) value = max - value + min; + return value; + } + + return { + // DECIBEL CALCULATIONS + db2coef: db2coef, + coef2db: coef2db, + db2scale: db2scale, + scale2db: scale2db, + gain2db: gain2db, + db2gain: db2gain, + // FREQUENCY CALCULATIONS + freq2coef: freq2coef, + coef2freq: coef2freq, + freq2scale: freq2scale, + scale2freq: scale2freq + } +})(); diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/base.js b/share/web_surfaces/builtin/mixer/toolkit/implements/base.js new file mode 100644 index 0000000000..83f7aa4ab2 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/base.js @@ -0,0 +1,921 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK) { +var merge = function(dst) { + var key, i, src; + for (i = 1; i < arguments.length; i++) { + src = arguments[i]; + for (key in src) { + dst[key] = src[key]; + } + } + return dst; +}; +var mixin = function(dst, src) { + var fun, key; + for (key in src) if (!dst[key]) { + if (key === "constructor" || + key === "_class" || + key === "Extends" || + key === "Implements" || + key === "options") continue; + if (!src.hasOwnProperty(key)) continue; + + fun = src[key]; + + dst[key] = fun; + } + + return dst; +}; +function call_handler(self, fun, args) { + try { + return fun.apply(self, args); + } catch (e) { + TK.warn("event handler", fun, "threw", e); + } +} +function dispatch_events(self, handlers, args) { + var v; + if (Array.isArray(handlers)) { + for (var i = 0; i < handlers.length; i++) { + v = call_handler(self, handlers[i], args); + if (v !== void(0)) return v; + } + } else return call_handler(self, handlers, args); +} +function add_event(to, event, fun) { + var tmp = to[event]; + + if (!tmp) { + to[event] = fun; + } else if (Array.isArray(tmp)) { + tmp.push(fun); + } else { + to[event] = [ tmp, fun ]; + } +} +function remove_event(from, event, fun) { + var tmp = from[event]; + if (!tmp) return; + if (Array.isArray(tmp)) { + for (var i = 0; i < tmp.length; i++) { + if (tmp[i] === fun) { + tmp[i] = tmp[tmp.length-1]; + tmp.length --; + } + } + if (tmp.length === 1) from[event] = tmp[0]; + else if (tmp.length === 0) delete from[event]; + } else if (tmp === fun) { + delete from[event]; + } +} +function add_static_event(w, event, fun) { + var p = w.prototype, e; + if (!p.hasOwnProperty('static_events')) { + if (p.static_events) { + p.static_events = e = Object.assign({}, p.static_events); + } else { + p.static_events = e = {}; + } + } else e = p.static_events; + add_event(e, event, fun); +} +function arrayify(x) { + if (!Array.isArray(x)) x = [ x ]; + return x; +} +function merge_static_events(a, b) { + var event; + if (!a) return b; + if (!b) return Object.assign({}, a); + for (event in a) { + var tmp = a[event]; + if (b.hasOwnProperty(event)) { + b[event] = arrayify(tmp).concat(arrayify(b[event])); + } else { + b[event] = Array.isArray(tmp) ? tmp.slice(0) : tmp; + } + } + return Object.assign({}, a, b); +} +TK.class = function(o) { + var constructor; + var methods; + var tmp, i, c, key; + + if (tmp = o.Extends) { + if (typeof(tmp) === "function") { + tmp = tmp.prototype; + } + if (typeof(o.options) === "object" && + typeof(tmp.options) === "object") { + o.options = Object.assign(Object.create(tmp.options), o.options); + } + if (o.static_events) o.static_events = merge_static_events(tmp.static_events, o.static_events); + methods = Object.assign(Object.create(tmp), o); + } else { + methods = o; + } + + tmp = o.Implements; + // mixins + if (tmp !== void(0)) { + tmp = arrayify(tmp); + for (i = 0; i < tmp.length; i++) { + if (typeof(tmp[i]) === "function") { + c = tmp[i].prototype; + } else c = tmp[i]; + + if (typeof(c.options) === "object") { + if (!methods.hasOwnProperty("options")) { + methods.options = Object.create(methods.options || null); + } + methods.options = merge({}, c.options, methods.options); + } + if (c.static_events) { + if (methods.static_events) { + methods.static_events = merge_static_events(c.static_events, + Object.assign({}, methods.static_events)); + } else { + methods.static_events = c.static_events; + } + } + + methods = mixin(methods, c, true); + } + } + + var init = methods.initialize; + var post_init = methods.initialized; + + if (post_init) { + constructor = function() { + init.apply(this, arguments); + post_init.call(this); + }; + } else constructor = init || (function() {}); + + constructor.prototype = methods; + methods.constructor = constructor; + return constructor; +}; +var __native_events = { + // mouse + mouseenter : true, + mouseleave : true, + mousedown : true, + mouseup : true, + mousemove : true, + mouseover : true, + + click : true, + dblclick : true, + + startdrag : true, + stopdrag : true, + drag : true, + dragenter : true, + dragleave : true, + dragover : true, + drop : true, + dragend : true, + + // touch + touchstart : true, + touchend : true, + touchmove : true, + touchenter : true, + touchleave : true, + touchcancel: true, + + keydown : true, + keypress : true, + keyup : true, + scroll : true, + focus : true, + blur : true, + + // mousewheel + mousewheel : true, + DOMMouseScroll : true, + wheel : true, + + submit : true, + contextmenu: true, +}; +function is_native_event(type) { + return __native_events[type]; +} + +function remove_native_events(element) { + var type; + var s = this.static_events; + var d = this.__events; + var handler = this.__native_handler; + + for (type in s) if (is_native_event(type)) + TK.remove_event_listener(element, type, handler); + + for (type in d) if (is_native_event(type) && (!s || !s.hasOwnProperty(type))) + TK.remove_event_listener(element, type, handler); +} +function add_native_events(element) { + var type; + var s = this.static_events; + var d = this.__events; + var handler = this.__native_handler; + + for (type in s) if (is_native_event(type)) + TK.add_event_listener(element, type, handler); + + for (type in d) if (is_native_event(type) && (!s || !s.hasOwnProperty(type))) + TK.add_event_listener(element, type, handler); +} +function native_handler(ev) { + /* FIXME: + * * mouseover and error are cancelled with true + * * beforeunload is cancelled with null + */ + if (this.fire_event(ev.type, ev) === false) return false; +} +function has_event_listeners(event) { + var ev = this.__events; + + if (ev.hasOwnProperty(event)) return true; + + ev = this.static_events; + + return ev && ev.hasOwnProperty(event); +} +/** + * This is the base class for all widgets in toolkit. + * It provides an API for event handling and other basic implementations. + * + * @class TK.Base + */ +TK.Base = TK.class({ + initialize : function(options) { + this.__events = {}; + this.__event_target = null; + this.__native_handler = native_handler.bind(this); + this.set_options(options); + this.fire_event("initialize"); + }, + initialized : function() { + /** + * Is fired when an instance is initialized. + * + * @event TK.Base#initialized + */ + this.fire_event("initialized"); + }, + /** + * Destroys all event handlers and the options object. + * + * @method TK.Base#destroy + */ + destroy : function() { + if (this.__event_target) { + remove_native_events.call(this, this.__event_target, this.__events); + } + + this.__events = null; + this.__event_target = null; + this.__native_handler = null; + this.options = null; + }, + /** + * Merges a new options object into the existing one, + * including deep copies of objects. If an option key begins with + * the string "on" it is considered as event handler. In this case + * the value should be the handler function for the event with + * the corresponding name without the first "on" characters. + * + * @method TK.Base#set_options(options) + * + * @param {Object} [options={ }] - An object containing initial options. + */ + set_options : function(o) { + var opt = this.options; + var key, a, b; + if (typeof(o) !== "object") { + delete this.options; + o = {}; + } else if (typeof(opt) === "object") for (key in o) if (o.hasOwnProperty(key)) { + a = o[key]; + b = opt[key]; + if (typeof a === "object" && a && + Object.getPrototypeOf(Object.getPrototypeOf(a)) === null && + typeof b === "object" && b && + Object.getPrototypeOf(Object.getPrototypeOf(b)) === null + ) { + o[key] = merge({}, b, a); + } + } + if (this.hasOwnProperty("options")) { + this.options = merge(opt, o); + } else if (opt) { + this.options = Object.assign(Object.create(opt), o); + } else { + this.options = Object.assign({}, o); + } + for (key in this.options) if (key.startsWith("on")) { + this.add_event(key.substr(2).toLowerCase(), this.options[key]); + delete this.options[key]; + } + }, + /** + * Get the value of an option. + * + * @method TK.Base#get + * + * @param {string} key - The option name. + */ + get: function (key) { + return this.options[key]; + }, + /** + * Sets an option. Fires both the events set with arguments key + * and value; and the event 'set_'+key with arguments value + * and key. + * + * @method TK.Base#set + * + * @param {string} key - The name of the option. + * @param {mixed} value - The value of the option. + * + * @emits TK.Base#set + * @emits TK.Base#set_[option] + */ + set: function (key, value) { + var e; + + this.options[key] = value; + /** + * Is fired when an option is set. + * + * @event TK.Base#set + * + * @param {string} name - The name of the option. + * @param {mixed} value - The value of the option. + */ + if (this.has_event_listeners("set")) + this.fire_event("set", key, value); + /** + * Is fired when an option is set. + * + * @event TK.Base#set_[option] + * + * @param {mixed} value - The value of the option. + */ + e = "set_"+key; + if (this.has_event_listeners(e)) + this.fire_event(e, value, key); + + return value; + }, + /** + * Sets an option by user interaction. Emits the userset + * event. The userset event can be cancelled (if an event handler + * returns false), in which case the option is not set. + * Returns true if the option was set, false + * otherwise. If the option was set, it will emit a useraction event. + * + * @method TK.Base#userset + * + * @param {string} key - The name of the option. + * @param {mixed} value - The value of the option. + * + * @emits TK.Base#userset + * @emits TK.Base#useraction + */ + userset: function(key, value) { + if (false === this.fire_event("userset", key, value)) return false; + value = this.set(key, value); + this.fire_event("useraction", key, value); + return true; + }, + /** + * Delegates all occuring DOM events of a specific DOM node to the widget. + * This way the widget fires e.g. a click event if someone clicks on the + * given DOM node. + * + * @method TK.Base#delegate_events + * + * @param {HTMLElement} element - The element all native events of the widget should be bound to. + * + * @returns {HTMLElement} The element + * + * @emits TK.Base#delegated + */ + delegate_events: function (element) { + var old_target = this.__event_target; + /** + * Is fired when an element is delegated. + * + * @event TK.Base#delegated + * + * @param {HTMLElement|Array} element - The element which receives all + * native DOM events. + * @param {HTMLElement|Array} old_element - The element which previously + * received all native DOM events. + */ + this.fire_event("delegated", element, old_target); + + if (old_target) remove_native_events.call(this, old_target); + if (element) add_native_events.call(this, element); + + this.__event_target = element; + + return element; + }, + /** + * Register an event handler. + * + * @method TK.Base#add_event + * + * @param {string} event - The event descriptor. + * @param {Function} func - The function to call when the event happens. + * @param {boolean} prevent - Set to true if the event should prevent the default behavior. + * @param {boolean} stop - Set to true if the event should stop bubbling up the tree. + */ + add_event: function (event, func) { + var ev, tmp; + + if (typeof event !== "string") + throw new TypeError("Expected string."); + + if (typeof func !== "function") + throw new TypeError("Expected function."); + + if (arguments.length !== 2) + throw new Error("Bad number of arguments."); + + if (is_native_event(event) && (ev = this.__event_target) && !this.has_event_listeners(event)) + TK.add_event_listener(ev, event, this.__native_handler); + ev = this.__events; + add_event(ev, event, func); + }, + /** + * Removes the given function from the event queue. + * If it is a native DOM event, it removes the DOM event listener + * as well. + * + * @method TK.Base#remove_event + * + * @param {string} event - The event descriptor. + * @param {Function} fun - The function to remove. + */ + remove_event: function (event, fun) { + remove_event(this.__events, event, fun); + + // remove native DOM event listener from __event_target + if (is_native_event(event) && !this.has_event_listeners(event)) { + var ev = this.__event_target; + if (ev) TK.remove_event_listener(ev, event, this.__native_handler); + } + }, + /** + * Fires an event. + * + * @method TK.Base#fire_event + * + * @param {string} event - The event descriptor. + * @param {...*} args - Event arguments. + */ + fire_event: function (event) { + var ev; + var args; + var v; + + ev = this.__events; + + if (ev !== void(0) && (event in ev)) { + ev = ev[event]; + + args = Array.prototype.slice.call(arguments, 1); + + v = dispatch_events(this, ev, args); + if (v !== void(0)) return v; + } + + ev = this.static_events; + + if (ev !== void(0) && (event in ev)) { + ev = ev[event]; + + if (args === void(0)) args = Array.prototype.slice.call(arguments, 1); + + v = dispatch_events(this, ev, args); + if (v !== void(0)) return v; + } + }, + /** + * Test if the event descriptor has some handler functions in the queue. + * + * @method TK.Base#has_event_listeners + * + * @param {string} event - The event desriptor. + * + * @returns {boolean} True if the event has some handler functions in the queue, false if not. + */ + has_event_listeners: has_event_listeners, + /** + * Add multiple event handlers at once, either as dedicated event handlers or a list of event + * descriptors with a single handler function. + * + * @method TK.Base#add_events + * + * @param {Object | Array} events - Object with event descriptors as keys and functions as + * values or Array of event descriptors. The latter requires a handler function as the + * second argument. + * @param {Function} func - A function to add as event handler if the first argument is an + * array of event desriptors. + */ + add_events: function (events, func) { + var i; + if (Array.isArray(events)) { + for (i = 0; i < events.length; i++) + this.add_event(events[i], func); + } else { + for (i in events) + if (events.hasOwnProperty(i)) + this.add_event(i, events[i]); + } + }, + /** + * Remove multiple event handlers at once, either as dedicated event handlers or a list of + * event descriptors with a single handler function. + * + * @method TK.Base#remove_events + * + * @param {Object | Array} events - Object with event descriptors as keys and functions as + * values or Array of event descriptors. The latter requires the handler function as the + * second argument. + * @param {Function} func - A function to remove from event handler queue if the first + * argument is an array of event desriptors. + */ + remove_events: function (events, func) { + var i; + if (Array.isArray(events)) { + for (i = 0; i < events.length; i++) + this.remove_event(events[i], func); + } else { + for (i in events) + if (events.hasOwnProperty(i)) + this.remove_event(i, events[i]); + } + }, + /** + * Fires several events. + * + * @method TK.Base#fire_events + * + * @param {Array.} events - A list of event names to fire. + */ + fire_events: function (events) { + for (var i in events) { + if (events.hasOwnProperty(i)) + this.fire_event(i, events[i]); + } + } +}); +function get_child_options(parent, name, options, config) { + var ret = {}; + var key, pref = name+"."; + var tmp; + + var inherit_options = !!config.inherit_options; + var blacklist_options = config.blacklist_options || []; + + if (tmp = config.default_options) + Object.assign(ret, (typeof(tmp) === "function") ? tmp.call(parent) : tmp); + + for (key in options) { + if (key.startsWith(pref)) { + ret[key.substr(pref.length)] = options[key]; + } + + if (inherit_options && blacklist_options.indexOf(tmp) < 0) { + if (key in config.create.prototype._options && !(key in TK["Widget"].prototype._options)) { + ret[key] = options[key]; + } + } + } + + var map_options = config.map_options; + + if (map_options) for (key in map_options) { + if (options[key]) { + ret[map_options[key]] = options[key]; + } + } + + return ret; +} +function ChildWidget(widget, name, config) { + + /** + * Defines a {@link TK.Widget} as a child for another widget. This function + * is used internally to simplify widget definitions. E.g. the {@link TK.Icon} of a + * {@link TK.Button} is defined as a TK.ChildWidget. TK.ChildWidgets + * are created/added after the initialization of the parent widget. + * If not configured explicitly, all options of the child widget can + * be accessed via TK.Widget.options[config.name + "." + option] + * on the parent widget. + * + * @param {TK.Widget} widget - The {@link TK.Widget} to add the TK.ChildWidget to. + * @param {string} name - The identifier of the element inside the parent Element, TK.Widget[config.name]. + * @param {object} config - The configuration of the child element. + * + * @property {TK.Widget} config.create - A TK.Widget class derivate to be used as child widget. + * @property {boolean} [config.fixed] - A fixed child widget cannot be removed after initialization. + * @property {boolean} [config.show=false] - Show/hide a non-fixed child widget on initialization. + * @property {string} [config.option="show_"+config.name] - A custom option of the parent widget + * to determine the visibility of the child element. If this is + * null, TK.Widget.options["show_"+ config.name] + * is used to toggle its visibility. The child element is visible, if + * this options is !== false. + * @property {function} [config.append] - A function overriding the generic + * append mechanism. If not null, this function is + * supposed to take care of adding the child widget to the parent + * widgets DOM. Otherwise the element of the child widget is added + * to the element of the parent widget. + * @property {boolean} [config.inherit_options=false] - Defines if both widgets share the + * same set of options. If true, Setting an option on the + * parent widget also sets the same option on the child widget. If false, + * the options of the child widget can be accessed via options[config.name + "." + option] + * in the parent widget. + * @property {array} [config.map_options=[]] - A list of options to be mapped between + * parent and child. Keys are the options to be added to the parent, values the options + * names in the child widget. + * names in the parent to which the childrens options (defined as values) are mapped to. + * If one of these options is set + * on the parent widget, it also gets set on the child widget. This is + * a fine-grained version of config.inherit-options. + * @property {boolean} [config.userset_ignore=false] - Do not care about the userset + * event of the parent widget, only keep track of set. + * @property {boolean} [config.userset_delegate=false] - Delegates all user interaction from + * the child to the parent element. If the user triggers an event on + * the child widget, the userset function of the parent + * element is called. + * @property {array} [config.static_events=[]] - An array of static events to be + * added to the parent widget. Each entry is a mapping between + * the name of the event and the callback function. + * @property {boolean} [config.toggle_class=false] - Defines if the parent widget + * receives the class toolkit-has-[name] as soon as + * the child element is shown. + * @property {array} [config.blacklist_options] - Array containing options names + * which are skipped on `inherit_options`. + * + * @class TK.ChildWidget + * + */ + + var p = widget.prototype; + var key = config.option || "show_"+name; + var tmp, m; + var static_events = { }; + + if (!config.userset_ignore) + static_events.userset = (config.inherit_options || config.userset_delegate) + ? function(key, value) { this.parent.userset(key, value); return false; } + : function(key, value) { this.parent.userset(name+"."+key, value); return false; }; + + + if (m = config.static_events) + Object.assign(static_events, m); + + if (config.create === void(0)) { + TK.warn("'create' is undefined. Skipping ChildWidget ", name); + return; + } + + var child = TK.class({ + Extends: config.create, + static_events: static_events, + }); + + + /* trigger child widget creation after initialization */ + add_static_event(widget, "initialized", function() { + /* we do not want to trash the class cache */ + if (!this[name]) + { + this[name] = null; + this.set(key, this.options[key]); + } + }); + + /* clean up on destroy */ + add_static_event(widget, "destroy", function() { + if (this[name]) { + this[name].destroy(); + this[name] = null; + } + }); + + var fixed = config.fixed; + var append = config.append; + var toggle_class = !!config.toggle_class; + + if (append === void(0)) append = true; + + /* child widget creation */ + add_static_event(widget, "set_"+key, function(val) { + var C = this[name]; + var show = fixed || !!val; + if (show && !C) { + var O = get_child_options(this, name, this.options, config); + if (append === true) + O.container = this.element; + var w = new child(O); + this.add_child(w); + this[name] = w; + if (typeof(append) === "function") + append.call(this); + } else if (!show && C) { + C.destroy(); + this[name] = null; + } + if (toggle_class) TK.toggle_class(this.element, "toolkit-has-"+name, show); + this.trigger_resize(); + }); + var set_cb = function(val, key) { + if (this[name]) this[name].set(key.substr(name.length+1), val); + }; + + for (tmp in child.prototype._options) { + add_static_event(widget, "set_"+name+"."+tmp, set_cb); + p._options[name+"."+tmp] = child.prototype._options[tmp]; + } + + /* direct option inherit */ + var blacklist_options = config.blacklist_options || []; + if (config.inherit_options) { + set_cb = function(val, key) { + if (this[name]) this[name].set(key, val); + }; + for (tmp in child.prototype._options) { + if (tmp in TK["Widget"].prototype._options) continue; + if (blacklist_options.indexOf(tmp) > -1) continue; + add_static_event(widget, "set_"+tmp, set_cb); + if (!p._options[tmp]) + p._options[tmp] = child.prototype._options[tmp]; + } + } + set_cb = function(key) { + return function(val) { + if (this[name]) this[name].set(key, val); + }; + }; + if (m = config.map_options) { + for (tmp in m) { + p._options[tmp] = child.prototype._options[m[tmp]]; + if (!p.options[tmp]) + p.options[tmp] = child.prototype.options[m[tmp]]; + add_static_event(widget, "set_"+tmp, set_cb(m[tmp])); + } + } + if (!config.options) { + if (!p._options[key]) + p._options[key] = "boolean"; + p.options[key] = fixed || !!config.show; + } +} +TK.add_static_event = add_static_event; +TK.ChildWidget = ChildWidget; + +function ChildElement(widget, name, config) { + /** + * Creates a HTMLElement as a child for a widget. Is used to simplify + * widget definitions. E.g. the tiny marker used to display the back-end + * value is a simple DIV added using TK.ChildElement. The generic element + * is a DIV added to TK.Widget.element with the class + * toolkit-[name]. Default creating and adding can be + * overwritten with custom callback functions. + * + * @param {TK.Widget} widget - The {@link TK.Widget} to add the TK.ChildElement to. + * @param {string} name - The identifier of the element. It will be prefixed + * by an underscore TK.Widget["_" + config.name]. + * @param {object} config - The configuration of the child element. + * + * @property {boolean} [config.show=false] - Show/hide the child element on initialization. + * @property {string} [config.option="show_"+config.name] - A custom option of the parent widget + * to determine the visibility of the child element. If this is + * null, TK.Widget.options["show_"+ config.name] + * is used to toggle its visibility. The child element is visible, if + * this options is !== false. + * @property {function} [config.display_check] - A function overriding the + * generic show_option behavior. If set, this function + * is called with the value of show_option as argument + * as soon as it gets set and is supposed to return a boolean + * defining the visibility of the element. + * @property {function} [config.append] - A function overriding the generic + * append mechanism. If not null, this function is + * supposed to take care of adding the child element to the parent + * widgets DOM. + * @property {function} [config.create] - A function overriding the generic + * creation mechanism. If not null, this function is + * supposed to create and return a DOM element to be added to the + * parent widget. + * @property {boolean} [config.toggle_class=false] - Defines if the parent widget + * receives the class toolkit-has-[name] as soon as + * the child element is shown. + * @property {array} [config.draw_options] - A list of options of the parent + * widget which are supposed to trigger a check if the element has to + * be added or removed. + * @property {function} [config.draw] - A function to be called on redraw. + * + * @class TK.ChildElement + * + */ + var p = widget.prototype; + var show_option = config.option || ("show_" + name); + var index = "_"+name; + + var display_check = config.display_check; + + /* This is done to make sure that the object property is created + * inside of the constructor. Otherwise, if we add the widget later + * might be turned into a generic mapping. + */ + add_static_event(widget, "initialize", function() { + this[index] = null; + }); + + /* trigger child element creation after initialization */ + add_static_event(widget, "initialized", function() { + this.set(show_option, this.options[show_option]); + }); + + /* clean up on destroy */ + add_static_event(widget, "destroy", function() { + if (this[index]) { + this[index].remove(); + this[index] = null; + } + }); + + var append = config.append; + var create = config.create; + var toggle_class = !!config.toggle_class; + + if (create === void(0)) create = function() { return TK.element("div", "toolkit-"+name); } + if (append === void(0)) append = function() { this.element.appendChild(this[index]); } + + add_static_event(widget, "set_"+show_option, function(value) { + var C = this[index]; + var show = display_check ? display_check(value) : (value !== false); + if (show && !C) { + C = create.call(this); + this[index] = C; + append.call(this, this.options); + } else if (C && !show) { + this[index] = null; + C.remove(); + } + if (toggle_class) TK.toggle_class(this.element, "toolkit-has-"+name, value); + this.trigger_resize(); + }); + + if (config.draw) { + var m = config.draw_options; + + if (!m) m = [ show_option ]; + else m.push(show_option); + + for (var i = 0; i < m.length; i++) { + add_static_event(widget, "set_"+m[i], function() { + if (this.options[show_option] !== false) + this.draw_once(config.draw); + }); + } + } + + if (p._options[show_option] === void(0)) { + p._options[show_option] = "boolean"; + p.options[show_option] = !!config.show; + } +} +TK.ChildElement = ChildElement; +})(this, this.TK||{}); diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/globalcursor.js b/share/web_surfaces/builtin/mixer/toolkit/implements/globalcursor.js new file mode 100644 index 0000000000..b54c0076c9 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/globalcursor.js @@ -0,0 +1,71 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +/** + * TK.GlobalCursor adds global cursor classes to ensure + * one of the standard cursors + * is shown in the overall application. + * + * @mixin TK.GlobalCursor + */ +TK.GlobalCursor = TK.class({ + _class: "GlobalCursor", + /** + * Adds a class "toolkit-cursor-" + cursor to the document.body to show a specific cursor. + * + * @method TK.GlobalCursor#global_cursor + * + * @param {string} cursor - The name of the cursor to show. + * + * @emits TK.GlobalCursor#globalcursor + */ + global_cursor: function (cursor) { + TK.add_class(document.body, "toolkit-cursor-" + cursor); + /** + * Is fired when a cursor gets set. + * + * @event TK.GlobalCursor#globalcursor + * + * @param {string} cursor - The name of the cursor to show. + */ + this.fire_event("globalcursor", cursor); + }, + /** + * Removes the class from document.body node. + * + * @method TK.GlobalCursor#remove_cursor + * + * @param {string} cursor - The name of the cursor to remome. + * + * @emits TK.GlobalCursor#cursorremoved + */ + remove_cursor: function (cursor) { + TK.remove_class(document.body, "toolkit-cursor-" + cursor); + /** + * Is fired when a cursor is removed. + * + * @event TK.GlobalCursor#cursorremoved + * + * @param {string} cursor - The name of the cursor to remove. + */ + this.fire_event("cursorremoved", cursor); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/gradient.js b/share/web_surfaces/builtin/mixer/toolkit/implements/gradient.js new file mode 100644 index 0000000000..8cda82827d --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/gradient.js @@ -0,0 +1,211 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +TK.Gradient = TK.class({ + /** + * TK.Gradient provides a function to set the background of a + * DOM element to a CSS gradient according on the users browser and version. + * TK.Gradient needs a {@link TK.Range} to be implemented on. + * + * @mixin TK.Gradient + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Object|Boolean} options.gradient - Gradient definition for the background. + * Keys are ints or floats as string corresponding to the widgets scale. + * Values are valid css color strings like #ff8000 or rgb(0,56,103). + * If set to false the css style color is used. + * @property {String|Boolean} [options.background="#000000"] - Background color if no gradient is used. + * Values are valid css color strings like #ff8000 or rgb(0,56,103). + * If set to false the css style color is used. + */ + _class: "Gradient", + Implements: TK.Ranged, + _options: Object.assign(TK.Ranged.prototype._options, { + gradient: "mixed", + background: "mixed", + }), + options: { + gradient: false, + background: false + }, + draw_gradient: function (element, gradient, fallback, range) { + /** + * This function generates a string from a given + * gradient object to set as a CSS background for a DOM element. + * If element is given, the function automatically sets the + * background. If gradient is omitted, the gradient is taken from + * options. Fallback is used if no gradient can be created. + * If fallback is omitted, options.background is used. if no range + * is set Gradient assumes that the implementing instance has + * {@link TK.Range} functionality. + * + * @method TK.Gradient#draw_gradient + * + * @param {DOMNode} element - The DOM node to apply the gradient to. + * @param {Object} [gradient=this.options.gradient] - Gradient definition for the background, e.g. {"-96": "rgb(30,87,153)", "-0.001": "rgb(41,137,216)", "0": "rgb(32,124,202)", "24": "rgb(125,185,232)"}. + * @param {string} [fallback=this.options.background] - If no gradient can be applied, use this as background color string. + * @param {TK.Range} [range=this] - If a specific range is set, it is used for the calculations. If not, we expect the widget itself provides {@link TK.Ranged} functionality. + * + * @returns {string} A string to be used as background CSS. + * + * @mixes TK.Ranged + * + * @emits TK.Gradient#backgroundchanged + */ + + // {"-96": "rgb(30,87,153)", "-0.001": "rgb(41,137,216)", "0": "rgb(32,124,202)", "24": "rgb(125,185,232)"} + // becomes: +// background: url("data:image/svg+xml;utf8, "); +// background: -moz-linear-gradient(top, rgb(30,87,153) 0%, rgb(41,137,216) 50%, rgb(32,124,202) 51%, rgb(125,185,232) 100%); +// background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgb(30,87,153)), color-stop(50%,rgb(41,137,216)), color-stop(51%,rgb(32,124,202)), color-stop(100%,rgb(125,185,232))); +// background: -webkit-linear-gradient(top, rgb(30,87,153) 0%,rgb(41,137,216) 50%,rgb(32,124,202) 51%,rgb(125,185,232) 100%); +// background: -o-linear-gradient(top, rgb(30,87,153) 0%,rgb(41,137,216) 50%,rgb(32,124,202) 51%,rgb(125,185,232) 100%); +// background: -ms-linear-gradient(top, rgb(30,87,153) 0%,rgb(41,137,216) 50%,rgb(32,124,202) 51%,rgb(125,185,232) 100%); +// background: linear-gradient(to bottom, rgb(30,87,153) 0%,rgb(41,137,216) 50%,rgb(32,124,202) 51%,rgb(125,185,232) 100%); +// filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#1e5799', endColorstr='#7db9e8',GradientType=0 ); + + var bg = ""; + range = range || this; + if (!gradient && !this.options.gradient) + bg = fallback || this.options.background; + else { + gradient = gradient || this.options.gradient; + + var ms_first = ""; + var ms_last = ""; + var m_svg = ""; + var m_regular = ""; + var m_webkit = ""; + var s_ms = "background filter: progid:DXImageTransform." + + "Microsoft.gradient( startColorstr='%s', " + + "endColorstr='%s',GradientType='%d' )"; + var s_svg = "url(\"data:image/svg+xml;utf8," + + "%s" + + "\")"; + var s_regular = "%slinear-gradient(%s, %s)"; + var s_webkit = "-webkit-gradient(linear, %s, %s)"; + var c_svg = ""; + var c_regular = "%s %s%%, "; + var c_webkit = "color-stop(%s%%, %s), "; + + var d_w3c = {}; + d_w3c["s"+"left"] = "to top"; + d_w3c["s"+"right"] = "to top"; + d_w3c["s"+"top"] = "to right"; + d_w3c["s"+"bottom"] = "to right"; + + var d_regular = {}; + d_regular["s"+"left"] = "bottom"; + d_regular["s"+"right"] = "bottom"; + d_regular["s"+"top"] = "left"; + d_regular["s"+"bottom"] = "left"; + + var d_webkit = {}; + d_webkit["s"+"left"] = "left bottom, left top"; + d_webkit["s"+"right"] = "left bottom, left top"; + d_webkit["s"+"top"] = "left top, right top"; + d_webkit["s"+"bottom"] = "left top, right top"; + + var d_ms = {}; + d_ms["s"+"left"] = 'x1="0%" y1="0%" x2="0%" y2="100%"'; + d_ms["s"+"right"] = 'x1="0%" y1="0%" x2="0%" y2="100%"'; + d_ms["s"+"top"] = 'x1="100%" y1="0%" x2="0%" y2="0%"'; + d_ms["s"+"bottom"] = 'x1="100%" y1="0%" x2="0%" y2="0%"'; + + var keys = Object.keys(gradient); + for (var i = 0; i < keys.length; i++) { + keys[i] = parseFloat(keys[i]); + } + keys = keys.sort(this.options.reverse ? + function (a,b) { return b-a } : function (a,b) { return a-b }); + + for (var i = 0; i < keys.length; i++) { + var ps = (100*range.val2coef(range.snap(keys[i]))).toFixed(2); + if (!ms_first) ms_first = gradient[i]; + ms_last = gradient[keys[i] + ""]; + + m_svg += TK.sprintf(c_svg, ps, gradient[keys[i] + ""]); + m_regular += TK.sprintf(c_regular, gradient[keys[i] + ""], ps); + m_webkit += TK.sprintf(c_webkit, ps, gradient[keys[i] + ""]); + } + m_regular = m_regular.substr(0, m_regular.length -2); + m_webkit = m_regular.substr(0, m_webkit.length -2); + + if (TK.browser.name === "IE" && TK.browser.version <= 8) + bg = (TK.sprintf(s_ms, ms_last, ms_first, this._vert() ? 0:1)); + + else if (TK.browser.name === "IE" && TK.browser.version === 9) + bg = (TK.sprintf(s_svg, this.options.id, + d_ms["s"+this.options.layout], + m_svg, this.options.id)); + + else if (TK.browser.name === "IE" && TK.browser.version >= 10) + bg = (TK.sprintf(s_regular, "-ms-", + d_regular["s" + this.options.layout], + m_regular)); + + else if (TK.browser.name=="Firefox") + bg = (TK.sprintf(s_regular, "-moz-", + d_regular["s"+this.options.layout], + m_regular)); + + else if (TK.browser.name === "Opera" && TK.browser.version >= 11) + bg = (TK.sprintf(s_regular, "-o-", + d_regular["s"+this.options.layout], + m_regular)); + + else if (TK.browser.name === "Chrome" && TK.browser.version < 10 + || TK.browser.name === "Safari" && TK.browser.version < 5.1) + bg = (TK.sprintf(s_webkit, + d_webkit["s"+this.options.layout], + m_regular)); + + else if (TK.browser.name === "Chrome" || TK.browser.name === "Safari") + bg = (TK.sprintf(s_regular, "-webkit-", + d_regular["s"+this.options.layout], + m_regular)); + + else + bg = (TK.sprintf(s_regular, "", + d_w3c["s"+this.options.layout], + m_regular)); + } + + if (element) { + element.style.background = bg ? bg : void 0; + /** + * Is fired when the gradient was created. + * + * @event TK.Gradient#backgroundchanged + * + * @param {HTMLElement} element - The element which background has changed. + * @param {string} background - The background of the element as CSS color string. + */ + this.fire_event("backgroundchanged", element, bg); + } + return bg; + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/notes.js b/share/web_surfaces/builtin/mixer/toolkit/implements/notes.js new file mode 100644 index 0000000000..0740de8c8c --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/notes.js @@ -0,0 +1,102 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +var notes = [ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ]; +TK.Notes = TK.class({ + /** + * TK.Notes converts between frequencies, MIDI notes + * and note names. + * + * @mixin TK.Notes + */ + _class: "Notes", + /** + * Returns a note name for a MIDI note number. + * + * @method TK.Notes#midi2note + * + * @param {int} note - The MIDI note to translate. + * + * @returns {string} note - The name of the note. + */ + midi2note: function (num) { + return notes[num % 12] + parseInt(num / 12); + }, + /** + * Returns a frequency of a MIDI note number. + * + * @method TK.Notes#midi2freq + * + * @param {int} note - The MIDI note to translate. + * + * @returns {number} frequency - The frequency of the MIDI number. + */ + midi2freq: function (num, base) { + base |= 440; + return Math.pow(2, (num - 69) / 12) * base; + }, + /** + * Returns a MIDI note number for a frequency. + * + * @method TK.Notes#freq2midi + * + * @param {number} frequency - The frequency to translate. + * @param {number} [base] - The frequency of A440. + * + * @returns {int} number - The MIDI number of the frequency. + */ + freq2midi: function (freq, base) { + base |= 440; + var f2 = Math.log2(freq / base); + return Math.max(0, Math.round(12 * f2 + 69)); + }, + /** + * Returns the percents a frequency misses a real note by. + * + * @method TK.Notes#freq2cents + * + * @param {number} frequency - The frequency to translate. + * @param {number} [base] - The frequency of A440. + * + * @returns {number} cents - The percent of the difference to the next full note. + */ + freq2cents: function (freq, base) { + base |= 440; + var f2 = Math.log2(freq / base); + f2 *= 1200; + f2 %= 100; + return (f2 < -50) ? 100 + f2 : (f2 > 50) ? -(100 - f2) : f2; + }, + /** + * Returns a note name for a frequency. + * + * @method TK.Notes#freq2note + * + * @param {number} frequency - The frequency to translate. + * @param {number} [base] - The frequency of A440. + * + * @returns {string} note - The name of the note. + */ + freq2note: function (freq, base) { + base |= 440; + return this.midi2note(this.freq2midi(freq, base)); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/ranged.js b/share/web_surfaces/builtin/mixer/toolkit/implements/ranged.js new file mode 100644 index 0000000000..f75133bd51 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/ranged.js @@ -0,0 +1,664 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function LinearSnapModule(stdlib, foreign) { + var min = +foreign.min; + var max = +foreign.max; + var step = +foreign.step; + var base = +foreign.base; + + var floor = stdlib.Math.floor; + var ceil = stdlib.Math.ceil; + + function low_snap(v, direction) { + v = +v; + direction = +direction; + var n = 0.0; + var t = 0.0; + + if (!(v > min)) { + v = min; + direction = 1.0; + } else if (!(v < max)) { + v = max; + direction = +1.0; + } + + t = (v - base)/step; + + if (direction > 0.0) n = ceil(t); + else if (direction < 0.0) n = floor(t); + else { + if (t - floor(t) < 0.5) { + n = floor(t); + } else { + n = ceil(t); + } + } + + return base + step * n; + } + + /** + * Returns the nearest value on the grid which is bigger than value. + * + * @method TK.Ranged#snap_up + * + * @param {number} value - The value to snap. + * + * @returns {number} The snapped value. + */ + function snap_up(v) { + v = +v; + return +low_snap(v, 1.0); + } + + /** + * Returns the nearest value on the grid which is smaller than value. + * + * @method TK.Ranged#snap_down + * + * @param {number} value - The value to snap. + * + * @returns {number} The snapped value. + */ + function snap_down(v) { + v = +v; + return +low_snap(v, -1.0); + } + + /** + * Returns the nearest value on the grid. Its rounding behavior is similar to that + * of Math.round. + * + * @method TK.Ranged#snap + * + * @param {number} value - The value to snap. + * + * @returns {number} The snapped value. + */ + function snap(v) { + v = +v; + return +low_snap(v, 0.0); + } + + return { + snap_up : snap_up, + snap_down : snap_down, + snap : snap + }; +} + +function ArraySnapModule(stdlib, foreign, heap) { + var values = new stdlib.Float64Array(heap); + var len = (heap.byteLength>>3)|0; + var min = +(foreign.min !== void 0 ? foreign.min : values[0]); + var max = +(foreign.max !== void 0 ? foreign.max : values[len-1]); + + function low_snap(v, direction) { + v = +v; + direction = +direction; + var a = 0; + var mid = 0; + var b = 0; + var t = 0.0; + + b = len-1; + + if (!(v > min)) v = min; + if (!(v < max)) v = max; + + if (!(v < +values[b << 3 >> 3])) return +values[b << 3 >> 3]; + if (!(v > +values[0])) return +values[0]; + + do { + mid = (a + b) >>> 1; + t = +values[mid << 3 >> 3]; + if (v > t) a = mid; + else if (v < t) b = mid; + else return t; + } while (((b - a)|0) > 1); + + if (direction > 0.0) return +values[b << 3 >> 3]; + else if (direction < 0.0) return +values[a << 3 >> 3]; + + if (values[b << 3 >> 3] - v <= v - values[a << 3 >> 3]) return +values[b << 3 >> 3]; + return +values[a << 3 >> 3]; + } + + function snap_up(v) { + v = +v; + return +low_snap(v, 1.0); + } + + function snap_down(v) { + v = +v; + return +low_snap(v, -1.0); + } + + function snap(v) { + v = +v; + return +low_snap(v, 0.0); + } + + return { + snap_up : snap_up, + snap_down : snap_down, + snap : snap + }; +} +function NullSnapModule(stdlib, foreign, heap) { + var min = +foreign.min; + var max = +foreign.max; + + function snap(v) { + v = +v; + if (!(v < max)) v = max; + if (!(v > min)) v = min; + return v; + } + + return { + snap: snap, + snap_up: snap, + snap_down: snap, + }; +} +function num_sort(a) { + a = a.slice(0); + a.sort(function(a, b) { return a - b; }); + return a; +} +function update_snap() { + var O = this.options; + // Notify that the ranged options have been modified + if (Array.isArray(O.snap)) { + Object.assign(this, ArraySnapModule(window, O, new Float64Array(num_sort(O.snap)).buffer)); + } else if (typeof O.snap === "number" && O.snap > 0.0) { + Object.assign(this, LinearSnapModule(window, { min : Math.min(O.min, O.max), max : Math.max(O.min, O.max), step : O.snap, base: O.base||0 })); + } else if (O.min < Infinity && O.max > -Infinity) { + Object.assign(this, NullSnapModule(window, { min : Math.min(O.min, O.max), max : Math.max(O.min, O.max) })); + } else { + Object.assign(this, { + snap: function(v) { return +v; }, + snap_up: function(v) { return +v; }, + snap_down: function(v) { return +v; }, + }); + } +} +function TRAFO_PIECEWISE(stdlib, foreign, heap) { + var reverse = foreign.reverse|0; + var l = heap.byteLength >> 4; + var X = new Float64Array(heap, 0, l); + var Y = new Float64Array(heap, l*8, l); + var basis = +foreign.basis; + + function val2based(coef, size) { + var a = 0, + b = (l-1)|0, + mid = 0, + t = 0.0; + + coef = +coef; + size = +size; + + if (!(coef > +Y[0])) return +X[0] * size; + if (!(coef < +Y[b << 3 >> 3])) return +X[b << 3 >> 3] * size; + + do { + mid = (a + b) >>> 1; + t = +Y[mid << 3 >> 3]; + if (coef > t) a = mid; + else if (coef < t) b = mid; + else return +X[mid << 3 >> 3] * size; + } while (((b - a)|0) > 1); + + /* value lies between a and b */ + + t = (+X[b << 3 >> 3] - +X[a << 3 >> 3]) / (+Y[b << 3 >> 3] - +Y[a << 3 >> 3]); + + t = +X[a << 3 >> 3] + (coef - +Y[a << 3 >> 3]) * t; + + t *= size; + + if (reverse) t = size - t; + + return t; + } + function based2val(coef, size) { + var a = 0, + b = (l-1)|0, + mid = 0, + t = 0.0; + + coef = +coef; + size = +size; + if (reverse) coef = size - coef; + coef /= size; + + if (!(coef > 0)) return Y[0]; + if (!(coef < 1)) return Y[b << 3 >> 3]; + + do { + mid = (a + b) >>> 1; + t = +X[mid << 3 >> 3]; + if (coef > t) a = mid; + else if (coef < t) b = mid; + else return +Y[mid << 3 >> 3]; + } while (((b - a)|0) > 1); + + /* value lies between a and b */ + + t = (+Y[b << 3 >> 3] - +Y[a << 3 >> 3]) / (+X[b << 3 >> 3] - +X[a << 3 >> 3]); + + return +Y[a << 3 >> 3] + (coef - +X[a << 3 >> 3]) * t; + } + function val2px(n) { return val2based(n, basis || 1); } + function px2val(n) { return based2val(n, basis || 1); } + function val2coef(n) { return val2based(n, 1); } + function coef2val(n) { return based2val(n, 1); } + return { + val2based:val2based, + based2val:based2val, + val2px:val2px, + px2val:px2val, + val2coef:val2coef, + coef2val:coef2val, + }; +}; +function TRAFO_FUNCTION(stdlib, foreign) { + var reverse = foreign.reverse|0; + var min = +foreign.min; + var max = +foreign.max; + var scale = foreign.scale; + var basis = +foreign.basis; + function val2based(value, size) { + value = +value; + size = +size; + value = scale(value, foreign, false) * size; + if (reverse) value = size - value; + return value; + } + function based2val(coef, size) { + coef = +coef; + size = +size; + if (reverse) coef = size - coef; + coef = scale(coef/size, foreign, true); + return coef; + } + function val2px(n) { return val2based(n, basis || 1); } + function px2val(n) { return based2val(n, basis || 1); } + function val2coef(n) { return val2based(n, 1); } + function coef2val(n) { return based2val(n, 1); } + return { + val2based:val2based, + based2val:based2val, + val2px:val2px, + px2val:px2val, + val2coef:val2coef, + coef2val:coef2val, + }; +} +function TRAFO_LINEAR(stdlib, foreign) { + var reverse = foreign.reverse|0; + var min = +foreign.min; + var max = +foreign.max; + var basis = +foreign.basis; + function val2based(value, size) { + value = +value; + size = +size; + value = ((value - min) / (max - min)) * size; + if (reverse) value = size - value; + return value; + } + function based2val(coef, size) { + coef = +coef; + size = +size; + if (reverse) coef = size - coef; + coef = (coef / size) * (max - min) + min; + return coef; + } + // just a wrapper for having understandable code and backward + // compatibility + function val2px(n) { n = +n; if (basis == 0.0) basis = 1.0; return +val2based(n, basis); } + // just a wrapper for having understandable code and backward + // compatibility + function px2val(n) { n = +n; if (basis == 0.0) basis = 1.0; return +based2val(n, basis); } + // calculates a coefficient for the value + function val2coef(n) { n = +n; return +val2based(n, 1.0); } + // calculates a value from a coefficient + function coef2val(n) { n = +n; return +based2val(n, 1.0); } + return { + /** + * Transforms a value from the coordinate system to the interval 0...basis. + * + * @method TK.Ranged#val2based + * + * @param {number} value + * @param {number} [basis=1] + * + * @returns {number} + */ + val2based:val2based, + /** + * Transforms a value from the interval 0...basis to the coordinate system. + * + * @method TK.Ranged#based2val + * + * @param {number} value + * @param {number} [basis=1] + * + * @returns {number} + */ + based2val:based2val, + /** + * This is an alias for {@link TK.Ranged#val2px}. + * + * @method TK.Ranged#val2px + * + * @param {number} value + * + * @returns {number} + */ + val2px:val2px, + /** + * This is an alias for {@link TK.Ranged#px2val}. + * + * @method TK.Ranged#px2val + * + * @param {number} value + * + * @returns {number} + */ + px2val:px2val, + /** + * Calls {@link based2val} with basis = 1. + * + * @method TK.Ranged#val2coef + * + * @param {number} value + * + * @returns {number} + */ + val2coef:val2coef, + /** + * Calls {@link based2val} with basis = 1. + * + * @method TK.Ranged#coef2val + * + * @param {number} value + * + * @returns {number} + */ + coef2val:coef2val, + }; +} +function TRAFO_LOG(stdlib, foreign) { + var db2scale = stdlib.TK.AudioMath.db2scale; + var scale2db = stdlib.TK.AudioMath.scale2db; + var reverse = foreign.reverse|0; + var min = +foreign.min; + var max = +foreign.max; + var log_factor = +foreign.log_factor; + var trafo_reverse = foreign.trafo_reverse|0; + var basis = +foreign.basis; + function val2based(value, size) { + value = +value; + size = +size; + value = +db2scale(value, min, max, size, trafo_reverse, log_factor); + if (reverse) value = size - value; + return value; + } + function based2val(coef, size) { + coef = +coef; + size = +size; + if (reverse) coef = size - coef; + coef = +scale2db(coef, min, max, size, trafo_reverse, log_factor); + return coef; + } + function val2px(n) { return val2based(n, basis || 1); } + function px2val(n) { return based2val(n, basis || 1); } + function val2coef(n) { return val2based(n, 1); } + function coef2val(n) { return based2val(n, 1); } + return { + val2based:val2based, + based2val:based2val, + val2px:val2px, + px2val:px2val, + val2coef:val2coef, + coef2val:coef2val, + }; +} +function TRAFO_FREQ(stdlib, foreign) { + var freq2scale = stdlib.TK.AudioMath.freq2scale; + var scale2freq = stdlib.TK.AudioMath.scale2freq; + var reverse = foreign.reverse|0; + var min = +foreign.min; + var max = +foreign.max; + var trafo_reverse = foreign.trafo_reverse|0; + var basis = +foreign.basis; + function val2based(value, size) { + value = +value; + size = +size; + value = +freq2scale(value, min, max, size, trafo_reverse); + if (reverse) value = size - value; + return value; + } + function based2val(coef, size) { + coef = +coef; + size = +size; + if (reverse) coef = size - coef; + coef = +scale2freq(coef, min, max, size, trafo_reverse); + return coef; + } + function val2px(n) { return val2based(n, basis || 1); } + function px2val(n) { return based2val(n, basis || 1); } + function val2coef(n) { return val2based(n, 1); } + function coef2val(n) { return based2val(n, 1); } + return { + val2based:val2based, + based2val:based2val, + val2px:val2px, + px2val:px2val, + val2coef:val2coef, + coef2val:coef2val, + }; +} +function update_transformation() { + var O = this.options; + var scale = O.scale; + + var module; + + if (typeof scale === "function") { + module = TRAFO_FUNCTION(w, O); + } else if (Array.isArray(scale)) { + var i = 0; + if (scale.length % 2) { + TK.error("Malformed piecewise-linear scale."); + } + + for (i = 0; i < scale.length/2 - 1; i++) { + if (!(scale[i] >= 0 && scale[i] <= 1)) + TK.error("piecewise-linear x value not in [0,1]."); + if (!(scale[i] < scale[i+1])) + TK.error("piecewise-linear array not sorted."); + } + for (i = scale.length/2; i < scale.length - 1; i++) { + if (!(scale[i] < scale[i+1])) + TK.error("piecewise-linear array not sorted."); + } + + module = TRAFO_PIECEWISE(w, O, new Float64Array(scale).buffer); + } else switch (scale) { + case "linear": + module = TRAFO_LINEAR(w, O); + break; + case "decibel": + O.trafo_reverse = 1; + module = TRAFO_LOG(w, O); + break; + case "log2": + O.trafo_reverse = 0; + module = TRAFO_LOG(w, O); + break; + case "frequency": + O.trafo_reverse = 0; + module = TRAFO_FREQ(w, O); + break; + case "frequency-reverse": + O.trafo_reverse = 1; + module = TRAFO_FREQ(w, O); + break; + default: + TK.warn("Unsupported scale", scale); + } + + Object.assign(this, module); +} +function set_cb(key, value) { + switch (key) { + case "min": + case "max": + case "snap": + update_snap.call(this); + /* fall through */ + case "log_factor": + case "scale": + case "reverse": + case "basis": + update_transformation.call(this); + this.fire_event("rangedchanged"); + break; + } +} +/** + * @callback TK.Ranged~scale_cb + * + * @param {number} value - The value to be transformed. + * @param {Object} [options={ }] - An object containing initial options. - The options of the corresponding {@link TK.Ranged} object. + * @param {boolean} [inverse=false] - Determines if the value is to be transformed from or + * to the coordinate system. + * + * @returns {number} The transformed value. + */ +TK.Ranged = TK.class({ + /** + * TK.Ranged combines functionality for two distinct purposes. + * Firstly, TK.Ranged can be used to snap values to a virtual grid. + * This grid is defined by the options snap, + * step, min, max and base. + * The second feature of TK.anged is that it allows transforming values between coordinate systems. + * This can be used to transform values from and to linear scales in which they are displayed on the + * screen. It is used inside of Toolkit to translate values (e.g. in Hz or dB) to pixel positions or + * percentages, for instance in widgets such as {@link TK.Scale}, {@link TK.MeterBase} or + * {@link TK.Graph}. + * + * TK.Ranged features several types of coordinate systems which are often used in audio applications. + * They can be configured using the options.scale option, possible values are: + *
    + *
  • linear for linear coordinates, + *
  • decibel for linear coordinates, + *
  • log2 for linear coordinates, + *
  • frequency for linear coordinates or + *
  • frequency-reverse" for linear coordinates. + *
+ * If options.scale is a function, it is used as the coordinate transformation. + * Its signature is {@link TK.Ranged~scale_cb}. This allows the definition of custom + * coordinate transformations, which go beyond the standard types. + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String|Array|Function} [options.scale="linear"] - + * The type of the scale. Either one of linear, decibel, log2, + * frequency or frequency-reverse; or an array containing a + * piece-wise linear scale; + * or a callback function of type {@link TK.Ranged~scale_cb}. + * @property {Boolean} [options.reverse=false] - Reverse the scale of the range. + * @property {Number} [options.basis=1] - The size of the linear scale. Set to pixel width or height + * if used for drawing purposes or to 100 for percentages. + * @property {Number} [options.min=0] - Minimum value of the range. + * @property {Number} [options.max=1] - Maximum value of the range. + * @property {Number} [options.log_factor=1] - Used to overexpand logarithmic curves. 1 keeps the + * natural curve while values above 1 will overbend. + * @property {Number|Array.} [options.snap=0] - + * Defines a virtual grid. + * If options.snap is a positive number, it is interpreted as the distance of + * grid points. + * Then, inside of the interval options.min ... options.max the grid + * points are options.base + n * options.snap where n is any + * integer. Any values outside of that interval are snapped to the biggest or smallest grid + * point, respectively. + * In order to define grids with non-uniform spacing, set options.snap to an Array + * of grid points. + * @property {Number} [options.base=0] - Base point. Used e.g. to mark 0dB on a fader from -96dB to 12dB. + * @property {Number} [options.step=0] - Step size. Used for instance by {@link TK.ScrollValue} + * as the step size. + * @property {Number} [options.shift_up=4] - Multiplier for increased stepping speed, e.g. used by + * {@link TK.ScrollValue} when simultaneously pressing 'shift'. + * @property {Number} [options.shift_down=0.25] - Multiplier for descresed stepping speed, e.g. used by + * {@link TK.ScrollValue} when simultaneously pressing 'shift' and 'ctrl'. + * + * @mixin TK.Ranged + */ + + _class: "Ranged", + options: { + scale: "linear", + reverse: false, + basis: 1, + min: 0, + max: 1, + base: 0, + step: 0, + shift_up: 4, + shift_down: 0.25, + snap: 0, + round: true, /* default for TK.Range, no dedicated option */ + log_factor: 1, + trafo_reverse: false, /* used internally, no documentation */ + }, + _options: { + scale: "string|array|function", + reverse: "boolean", + basis: "number", + min: "number", + max: "number", + base: "number", + step: "number", + shift_up: "number", + shift_down: "number", + snap: "mixed", + round: "boolean", + log_factor: "number", + trafo_reverse: "boolean", + }, + static_events: { + set: set_cb, + initialized: function() { + var O = this.options; + if (!(O.min <= O.max)) + TK.warn("Ranged needs min <= max. min: ", O.min, ", max:", O.max, ", options:", O); + update_snap.call(this); + update_transformation.call(this); + }, + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/ranges.js b/share/web_surfaces/builtin/mixer/toolkit/implements/ranges.js new file mode 100644 index 0000000000..1f1738c0fc --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/ranges.js @@ -0,0 +1,83 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +/** + * Ranges provides multiple {@link TK.Range}s for a widget. They + * can be used for building coordinate systems. + * + * @mixin TK.Ranges + */ +function range_changed(value, name) { + var range = this[name]; + for (var i in value) { + range.set(i, value[i]); + } +} +TK.Ranges = TK.class({ + _class: "Ranges", + /** + * Add a new {@link TK.Range}. If name is set and this.options[name] + * exists, is an object and from is an object, too, both are merged + * before a range is created. + * + * @method TK.Ranges#add_range + * + * @param {Function|Object} from - A function returning a {@link TK.Range} + * instance or an object containing options for a new {@link TK.Range}. + * @param {string} name - Designator of the {@link TK.Range}. + * If a name is set a new set function is added to the item to + * set the options of the {@link TK.Range}. Use the set function like this: + * this.set("name", {key: value}); + * + * @emits TK.Ranges#rangeadded + * + * @returns {TK.Range} The new {@link TK.Range}. + */ + add_range: function (from, name) { + var r; + if (typeof from === "function") { + r = from(); + } else if (TK.Ranged.prototype.isPrototypeOf(from)) { + r = TK.Range(from.options); + } else if (TK.Range.prototype.isPrototypeOf(from)) { + r = from; + } else { + if (name + && this.options[name] + && typeof this.options[name] === "object") + from = Object.assign({}, this.options[name], from) + r = new TK.Range(from); + } + if (name) { + this[name] = r; + this.add_event("set_"+name, range_changed); + } + /** + * Gets fired when a new range is added + * + * @event TK.Ranges#rangeadded + * + * @param {TK.Range} range - The {@link TK.Range} that was added. + */ + this.fire_event("rangeadded", r); + return r; + } +}) +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/implements/warning.js b/share/web_surfaces/builtin/mixer/toolkit/implements/warning.js new file mode 100644 index 0000000000..bd8c0a8dd7 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/implements/warning.js @@ -0,0 +1,58 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +/** + * Adds the class "toolkit-warn" on this.element for a certain + * period of time. It is used e.g. in {@link TK.ResponseHandle} or {@link TK.Knob} when the value + * exceeds the range. + * + * @mixin TK.Warning + */ +TK.Warning = TK.class({ + _class: "Warning", + /** + * Adds the class toolkit-warn to the given element and + * sets a timeout after which the class is removed again. If there + * already is a timeout waiting it gets updated. + * + * @method TK.Warning#warning + * + * @emits TK.Warning#warning + * + * @param {HTMLElement|SVGElement} element - The DOM node the class should be added to. + * @param {Number} [timeout=250] - The timeout in ms until the class should be removed again. + */ + warning: function (element, timeout) { + if (!timeout) timeout = 250; + if (this.__wto) window.clearTimeout(this.__wto); + this.__wto = null; + TK.add_class(element, "toolkit-warn"); + this.__wto = window.setTimeout(function () { + TK.remove_class(element, "toolkit-warn"); + }.bind(this), timeout); + /** + * Gets fired when {@link TK.Warning#warning} was called. + * + * @event TK.Warning#warning + */ + this.fire_event("warning"); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/README b/share/web_surfaces/builtin/mixer/toolkit/modules/README new file mode 100644 index 0000000000..33a9116298 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/README @@ -0,0 +1,6 @@ +@category: Modules + +Modules are Classes to build other widgets from. They normally can't +exist on their own and require a parent item which makes use of them. +For example: Graph is mainly a SVG path which needs a parent item containing +a SVG image. diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/circular.js b/share/web_surfaces/builtin/mixer/toolkit/modules/circular.js new file mode 100644 index 0000000000..2640f88f11 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/circular.js @@ -0,0 +1,626 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { +function interpret_label(x) { + if (typeof x === "object") return x; + if (typeof x === "number") return { pos: x }; + TK.error("Unsupported label type ", x); +} +var __rad = Math.PI / 180; +function _get_coords(deg, inner, outer, pos) { + deg = +deg; + inner = +inner; + outer = +outer; + pos = +pos; + deg = deg * __rad; + return { + x1: Math.cos(deg) * outer + pos, + y1: Math.sin(deg) * outer + pos, + x2: Math.cos(deg) * inner + pos, + y2: Math.sin(deg) * inner + pos + } +} +function _get_coords_single(deg, inner, pos) { + deg = +deg; + inner = +inner; + pos = +pos; + deg = deg * __rad; + return { + x: Math.cos(deg) * inner + pos, + y: Math.sin(deg) * inner + pos + } +} +var format_path = TK.FORMAT("M %f,%f " + + "A %f,%f 0 %d,%d %f,%f " + + "L %f,%f " + + "A %f,%f 0 %d,%d %f,%f z"); +var format_translate = TK.FORMAT("translate(%f, %f)"); +var format_translate_rotate = TK.FORMAT("translate(%f %f) rotate(%f %f %f)"); +var format_rotate = TK.FORMAT("rotate(%f %f %f)"); + +function draw_dots() { + // depends on dots, dot, min, max, size + var _dots = this._dots; + var O = this.options; + var dots = O.dots; + var dot = O.dot; + var angle = O.angle; + TK.empty(_dots); + for (var i = 0; i < dots.length; i++) { + var m = dots[i]; + var r = TK.make_svg("rect", {"class": "toolkit-dot"}); + + var length = m.length === void(0) + ? dot.length : m.length; + var width = m.width === void(0) + ? dot.width : m.width; + var margin = m.margin === void(0) + ? dot.margin : m.margin; + var pos = Math.min(O.max, Math.max(O.min, m.pos)); + // TODO: consider adding them all at once + _dots.appendChild(r); + if (m["class"]) TK.add_class(r, m["class"]); + if (m.color) r.style.fill = m.color; + + r.setAttribute("x", O.size - length - margin); + r.setAttribute("y", O.size / 2 - width / 2); + + r.setAttribute("width", length); + r.setAttribute("height", width); + + r.setAttribute("transform", "rotate(" + + (this.val2coef(this.snap(pos)) * angle) + " " + + (O.size / 2) + " " + (this.options.size / 2) + ")"); + } + /** + * Is fired when dots are (re)drawn. + * @event TK.Circular#dotsdrawn + */ + this.fire_event("dotsdrawn"); +} +function draw_markers() { + // depends on size, markers, marker, min, max + var I = this.invalid; + var O = this.options; + var markers = O.markers; + var marker = O.marker; + TK.empty(this._markers); + + var stroke = this._get_stroke(); + var outer = O.size / 2; + var angle = O.angle; + + for (var i = 0; i < markers.length; i++) { + var m = markers[i]; + var thick = m.thickness === void(0) + ? marker.thickness : m.thickness; + var margin = m.margin === void(0) + ? marker.margin : m.margin; + var inner = outer - thick; + var outer_p = outer - margin - stroke / 2; + var inner_p = inner - margin - stroke / 2; + var from, to; + + if (m.from === void(0)) + from = O.min; + else + from = Math.min(O.max, Math.max(O.min, m.from)); + + if (m.to === void(0)) + to = O.max; + else + to = Math.min(O.max, Math.max(O.min, m.to)); + + var s = TK.make_svg("path", {"class": "toolkit-marker"}); + this._markers.appendChild(s); + + if (m["class"]) TK.add_class(s, m["class"]); + if (m.color) s.style.fill = m.color; + if (!m.nosnap) { + from = this.snap(from); + to = this.snap(to); + } + from = this.val2coef(from) * angle; + to = this.val2coef(to) * angle; + + draw_slice.call(this, from, to, inner_p, outer_p, outer, s); + } + /** + * Is fired when markers are (re)drawn. + * @event TK.Circular#markersdrawn + */ + this.fire_event("markersdrawn"); +} +function draw_labels() { + // depends on size, labels, label, min, max, start + var _labels = this._labels; + var O = this.options; + var labels = O.labels; + TK.empty(this._labels); + + if (!labels.length) return; + + var outer = O.size / 2; + var a = new Array(labels.length); + var i; + + var l, p, positions = new Array(labels.length); + + for (i = 0; i < labels.length; i++) { + l = labels[i]; + p = TK.make_svg("text", {"class": "toolkit-label", + style: "dominant-baseline: central;" + }); + + if (l["class"]) TK.add_class(p, l["class"]); + if (l.color) p.style.fill = l.color; + + + if (l.label !== void(0)) + p.textContent = l.label; + else + p.textContent = O.label.format(l.pos); + + p.setAttribute("text-anchor", "middle"); + + _labels.appendChild(p); + a[i] = p; + } + /* FORCE_RELAYOUT */ + + TK.S.add(function() { + var i, p; + for (i = 0; i < labels.length; i++) { + l = labels[i]; + p = a[i]; + + var margin = l.margin !== void(0) ? l.margin : O.label.margin; + var align = (l.align !== void(0) ? l.align : O.label.align) === "inner"; + var pos = Math.min(O.max, Math.max(O.min, l.pos)); + var bb = p.getBBox(); + var angle = (this.val2coef(this.snap(pos)) * O.angle + O.start) % 360; + var outer_p = outer - margin; + var coords = _get_coords_single(angle, outer_p, outer); + + var mx = ((coords.x - outer) / outer_p) * (bb.width + bb.height / 2.5) / (align ? -2 : 2); + var my = ((coords.y - outer) / outer_p) * bb.height / (align ? -2 : 2); + + positions[i] = format_translate(coords.x + mx, coords.y + my); + } + + TK.S.add(function() { + for (i = 0; i < labels.length; i++) { + p = a[i]; + p.setAttribute("transform", positions[i]); + } + /** + * Is fired when labels are (re)drawn. + * @event TK.Circular#labelsdrawn + */ + this.fire_event("labelsdrawn"); + }.bind(this), 1); + }.bind(this)); +} +function draw_slice(a_from, a_to, r_inner, r_outer, pos, slice) { + a_from = +a_from; + a_to = +a_to; + r_inner = +r_inner; + r_outer = +r_outer; + pos = +pos; + // ensure from !== to + if(a_from % 360 === a_to % 360) a_from += 0.001; + // ensure from and to in bounds + while (a_from < 0) a_from += 360; + while (a_to < 0) a_to += 360; + if (a_from > 360) a_from %= 360; + if (a_to > 360) a_to %= 360; + // get drawing direction (sweep = clock-wise) + if (this.options.reverse && a_to <= a_from + || !this.options.reverse && a_to > a_from) + var sweep = 1; + else + var sweep = 0; + // get large flag + if (Math.abs(a_from - a_to) >= 180) + var large = 1; + else + var large = 0; + // draw this slice + var from = _get_coords(a_from, r_inner, r_outer, pos); + var to = _get_coords(a_to, r_inner, r_outer, pos); + + var path = format_path(from.x1, from.y1, + r_outer, r_outer, large, sweep, to.x1, to.y1, + to.x2, to.y2, + r_inner, r_inner, large, !sweep, from.x2, from.y2); + slice.setAttribute("d", path); +} +TK.Circular = TK.class({ + /** + * TK.Circular is a SVG group element containing two paths for displaying + * numerical values in a circular manner. TK.Circular is able to draw labels, + * dots and markers and can show a hand. TK.Circular e.g. is implemented by + * {@link TK.Clock} to draw hours, minutes and seconds. + * + * @class TK.Circular + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.value=0] - Sets the value on the hand and on the + * ring at the same time. + * @property {Number} [options.value_hand=0] - Sets the value on the hand. + * @property {Number} [options.value_ring=0] - Sets the value on the ring. + * @property {Number} [options.size=100] - The diameter of the circle. This + * is the base value for all following layout-related parameters. Keeping + * it set to 100 offers percentual lenghts. Set the final size of the widget + * via CSS. + * @property {Number} [options.thickness=3] - The thickness of the circle. + * @property {Number} [options.margin=0] - The margin between base and value circles. + * @property {Boolean} [options.show_hand=true] - Draw the hand. + * @property {Object} [options.hand] - Dimensions of the hand. + * @property {Number} [options.hand.width=2] - Width of the hand. + * @property {Number} [options.hand.length=30] - Length of the hand. + * @property {Number} [options.hand.margin=10] - Margin of the hand. + * @property {Number} [options.start=135] - The starting point in degrees. + * @property {Number} [options.angle=270] - The maximum degree of the rotation when + * options.value === options.max. + * @property {Number|Boolean} [options.base=false] - If a base value is set in degrees, + * circular starts drawing elements from this position. + * @property {Boolean} [options.show_base=true] - Draw the base ring. + * @property {Boolean} [options.show_value=true] - Draw the value ring. + * @property {Number} [options.x=0] - Horizontal displacement of the circle. + * @property {Number} [options.y=0] - Vertical displacement of the circle. + * @property {Boolean} [options.show_dots=true] - Show/hide all dots. + * @property {Object} [options.dot] - This option acts as default values for the individual dots + * specified in options.dots. + * @property {Number} [options.dot.width=2] - Width of the dots. + * @property {Number} [options.dot.length=2] - Length of the dots. + * @property {Number} [options.dot.margin=5] - Margin of the dots. + * @property {Array} [options.dots=[]] - An array of objects describing where dots should be placed + * along the circle. Members are position pos in the value range and optionally + * color and class and any of the properties of options.dot. + * @property {Boolean} [options.show_markers=true] - Show/hide all markers. + * @property {Object} [options.marker] - This option acts as default values of the individual markers + * specified in options.markers. + * @property {Number} [options.marker.thickness=3] - Thickness of the marker. + * @property {Number} [options.marker.margin=3] - Margin of the marker. + * @property {Array} [options.markers=[]] - An array containing objects which describe where markers + * are to be places. Members are the position as from and to and optionally + * color, class and any of the properties of options.marker. + * @property {Boolean} [options.show_labels=true] - Show/hide all labels. + * @property {Object} [options.label] - This option acts as default values for the individual labels + * specified in options.labels. + * @property {Integer} [options.label.margin=8] - Distance of the label from the circle of diameter + * options.size. + * @property {String} [options.label.align="outer"] - This option controls if labels are positioned + * inside or outside of the circle with radius options.size/2 - margin. + * @property {Function} [options.label.format] - Optional formatting function for the label. + * Receives the label value as first argument. + * @property {Array} [options.labels=[]] - An array containing objects which describe labels + * to be displayed. Either a value or an object whose members are the position pos + * insie the value range and optionally color, class and any of the + * properties of options.label. + * + * @extends TK.Widget + * + * @mixes TK.Warning + * + * @mixes TK.Ranged + */ + _class: "Circular", + Extends: TK.Widget, + Implements: [TK.Warning, TK.Ranged], + _options: Object.assign(Object.create(TK.Widget.prototype._options), TK.Ranged.prototype._options, { + value: "number", + value_hand: "number", + value_ring: "number", + size: "number", + thickness: "number", + margin: "number", + hand: "object", + start: "number", + angle: "number", + base: "number|boolean", + show_base: "boolean", + show_value: "boolean", + show_hand: "boolean", + x: "number", + y: "number", + dot: "object", + dots: "array", + marker: "object", + markers: "array", + label: "object", + labels: "array" + }), + static_events: { + set_value: function(value) { + this.set("value_hand", value); + this.set("value_ring", value); + }, + initialized: function() { + // calculate the stroke here once. this happens before + // the initial redraw + TK.S.after_frame(this._get_stroke.bind(this)); + this.set("value", this.options.value); + }, + }, + options: { + value: 0, + value_hand: 0, + value_ring: 0, + size: 100, + thickness: 3, + margin: 0, + hand: {width: 2, length: 30, margin: 10}, + start: 135, + angle: 270, + base: false, + show_base: true, + show_value: true, + show_hand: true, + x: 0, + y: 0, + dot: {width: 2, length: 2, margin: 5}, + dots: [], + marker: {thickness: 3, margin: 0}, + markers: [], + label: {margin: 8, align: "inner", format: function(val){return val;}}, + labels: [] + }, + + initialize: function (options) { + TK.Widget.prototype.initialize.call(this, options); + var E; + + /** + * @member {SVGImage} TK.Circular#element - The main SVG element. + * Has class toolkit-circular + */ + this.element = E = TK.make_svg("g", {"class": "toolkit-circular"}); + this.widgetize(E, true, true, true); + + /** + * @member {SVGPath} TK.Circular#_base - The base of the ring. + * Has class toolkit-base + */ + this._base = TK.make_svg("path", {"class": "toolkit-base"}); + E.appendChild(this._base); + + /** + * @member {SVGPath} TK.Circular#_value - The ring showing the value. + * Has class toolkit-value + */ + this._value = TK.make_svg("path", {"class": "toolkit-value"}); + E.appendChild(this._value); + + /** + * @member {SVGRect} TK.Circular#_hand - The hand of the knob. + * Has class toolkit-hand + */ + this._hand = TK.make_svg("rect", {"class": "toolkit-hand"}); + E.appendChild(this._hand); + + if (this.options.labels) + this.set("labels", this.options.labels); + }, + + resize: function () { + this.invalid.labels = true; + this.trigger_draw(); + TK.Widget.prototype.resize.call(this); + }, + + redraw: function () { + TK.Widget.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + var E = this.element; + var outer = O.size / 2; + var tmp; + + if (I.validate("x", "y") || I.start || I.size) { + E.setAttribute("transform", format_translate_rotate(O.x, O.y, O.start, outer, outer)); + this._labels.setAttribute("transform", format_rotate(-O.start, outer, outer)); + } + + if (O.show_labels && (I.validate("show_labels", "labels", "label") || + I.size || I.min || I.max || I.start)) { + draw_labels.call(this); + } + + if (O.show_dots && (I.validate("show_dots", "dots", "dot") || + I.min || I.max || I.size)) { + draw_dots.call(this); + } + + if (O.show_markers && (I.validate("show_markers", "markers", "marker") || + I.size || I.min || I.max)) { + draw_markers.call(this); + } + + var stroke = this._get_stroke(); + var inner = outer - O.thickness; + var outer_p = outer - stroke / 2 - O.margin; + var inner_p = inner - stroke / 2 - O.margin; + + if (I.show_value || I.value_ring) { + I.show_value = false; + if (O.show_value) { + draw_slice.call(this, this.val2coef(this.snap(O.base)) * O.angle, this.val2coef(this.snap(O.value_ring)) * O.angle, inner_p, outer_p, outer, + this._value); + } else { + this._value.removeAttribute("d"); + } + } + + if (I.show_base) { + I.show_base = false; + if (O.show_base) { + draw_slice.call(this, 0, O.angle, inner_p, outer_p, outer, this._base); + } else { + /* TODO: make this a child element */ + this._base.removeAttribute("d"); + } + } + if (I.show_hand) { + I.show_hand = false; + if (O.show_hand) { + this._hand.style.display = "block"; + } else { + this._hand.style.display = "none"; + } + } + if (I.validate("size", "value_hand", "hand", "min", "max", "start")) { + tmp = this._hand; + tmp.setAttribute("x", O.size - O.hand.length - O.hand.margin); + tmp.setAttribute("y", (O.size - O.hand.width) / 2.0); + tmp.setAttribute("width", O.hand.length); + tmp.setAttribute("height",O.hand.width); + tmp.setAttribute("transform", + format_rotate(this.val2coef(this.snap(O.value_hand)) * O.angle, O.size / 2, O.size / 2)); + } + }, + + destroy: function () { + this._dots.remove(); + this._markers.remove(); + this._base.remove(); + this._value.remove(); + TK.Widget.prototype.destroy.call(this); + }, + _get_stroke: function () { + if (this.hasOwnProperty("_stroke")) return this._stroke; + var strokeb = parseInt(TK.get_style(this._base, "stroke-width")) || 0; + var strokev = parseInt(TK.get_style(this._value, "stroke-width")) || 0; + this._stroke = Math.max(strokeb, strokev); + return this._stroke; + }, + + /** + * Adds a label. + * + * @method TK.Circular#add_label + * @param {Object|Number} label - The label. Please refer to the initial options + * to learn more about possible values. + * @returns {Object} label - The interpreted object to build the label from. + */ + add_label: function(label) { + var O = this.options; + + if (!O.labels) { + O.labels = []; + } + + label = interpret_label(label); + + if (label) { + O.labels.push(label); + this.invalid.labels = true; + this.trigger_draw(); + return label; + } + }, + + /** + * Removes a label. + * + * @method TK.Circular#remove_label + * @param {Object} label - The label object as returned from `add_label`. + * @returns {Object} label - The removed label options. + */ + remove_label: function(label) { + var O = this.options; + + if (!O.labels) return; + + var i = O.labels.indexOf(label); + + if (i === -1) return; + + O.labels.splice(i); + this.invalid.labels = true; + this.trigger_draw(); + }, + + // GETTERS & SETTERS + set: function (key, value) { + switch (key) { + case "dot": + case "marker": + case "label": + value = Object.assign(this.options[key], value); + break; + case "base": + if (value === false) value = this.options.min; + break; + case "value": + if (value > this.options.max || value < this.options.min) + this.warning(this.element); + value = this.snap(value); + break; + case "labels": + if (value) + for (var i = 0; i < value.length; i++) { + value[i] = interpret_label(value[i]); + } + break; + } + + return TK.Widget.prototype.set.call(this, key, value); + } +}); +/** + * @member {SVGGroup} TK.Circular#_markers - A SVG group containing all markers. + * Has class toolkit-markers + */ +TK.ChildElement(TK.Circular, "markers", { + //option: "markers", + //display_check: function(v) { return !!v.length; }, + show: true, + create: function() { + return TK.make_svg("g", {"class": "toolkit-markers"}); + }, +}); +/** + * @member {SVGGroup} TK.Circular#_dots - A SVG group containing all dots. + * Has class toolkit-dots + */ +TK.ChildElement(TK.Circular, "dots", { + //option: "dots", + //display_check: function(v) { return !!v.length; }, + show: true, + create: function() { + return TK.make_svg("g", {"class": "toolkit-dots"}); + }, +}); +/** + * @member {SVGGroup} TK.Circular#_labels - A SVG group containing all labels. + * Has class toolkit-labels + */ +TK.ChildElement(TK.Circular, "labels", { + //option: "labels", + //display_check: function(v) { return !!v.length; }, + show: true, + create: function() { + return TK.make_svg("g", {"class": "toolkit-labels"}); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/drag.js b/share/web_surfaces/builtin/mixer/toolkit/modules/drag.js new file mode 100644 index 0000000000..d1c3fa8a54 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/drag.js @@ -0,0 +1,179 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ + +function extract_matrix (t) { + var a = t.indexOf("matrix("); + if (a < 0) return; + t = t.substr(a+7); + return (t.split(")"))[0].split(",").map(function(v){return parseInt(v.trim())}); +} + +function xy_from_transform (t) { + var mx = extract_matrix(t); + return (!mx || !mx.length) ? [0, 0] : [mx[4], mx[5]]; +} + +function startdrag(e, drag) { + this._dragged = 0; + var O = this.options; + if (!O.active) return; + if (e.button !== void(0) && e.button > 0) return; + this._xstart = this._xlast = e.pageX; + this._ystart = this._ylast = e.pageY; + if (O.transform) { + var xy = xy_from_transform(this._style.transform); + this._xpos = xy[0]; + this._ypos = xy[1]; + } else { + this._xpos = O.node.offsetLeft; + this._ypos = O.node.offsetTop; + } + TK.add_class(O.node, "toolkit-dragging"); +} +function stopdrag(e, drag) { + if (!this.options.active) return; + if (e.button !== void(0) && e.button > 0) return; + TK.remove_class(this.options.node, "toolkit-dragging"); +} +function dragging(e, drag) { + var O = this.options; + if (!O.active) return; + if (e.button !== void(0) && e.button > 0) return; + this._dragged += (Math.abs(e.pageX - this._xlast) + + Math.abs(e.pageY - this._ylast)) / 2; + if (this._dragged < O.initial) return; + this._xlast = e.pageX; + this._ylast = e.pageY; + var x = this._xpos + e.pageX - this._xstart; + var y = this._ypos + e.pageY - this._ystart; + if (O.min.x !== false) x = Math.max(O.min.x, x); + if (O.max.x !== false) x = Math.min(O.max.x, x); + if (O.min.y !== false) y = Math.max(O.min.y, y); + if (O.max.y !== false) y = Math.min(O.max.y, y); + if (O.transform) { + var t = this._style.transform; + var mx = extract_matrix(t); + mx[4] = x; + mx[5] = y; + var nt = t.replace(/matrix\([0-9 \,]*\)/, "matrix(" + mx.join(",") + ")"); + O.node.style.transform = nt; + } else { + O.node.style.top = y + "px"; + O.node.style.left = x + "px"; + } +} +function set_handle() { + var h = this.options.handle; + if (this.drag) + this.drag.destroy(); + var range = new TK.Range({}); + this.drag = new TK.DragValue(this, { + node: h, + range: function () { return range; }, + get: function() { return 0; }, + set: function(v) { return; }, + }); +} +/** + * TK.Drag enables dragging of elements on the screen positioned absolutely or by CSS transformation. + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {HTMLElement|SVGElement} options.node - The element to drag. + * @property {HTMLElement|SVGElement} [options.handle] A DOM node to be used as a handle. If not set, options.node is used. + * @property {Boolean} [options.active=true] - Enable or disable dragging + * @property {Object|Boolean} [options.min={x: false, y: false}] - Object containing the minimum positions for x and y. A value of false is interpreted as no minimum. + * @property {Object|Boolean} [options.max={x: false, y: false}] - Object containing the maximum positions for x and y. A value of false is interpreted as no maximum. + * @property {Number} [options.initial=2] - Number of pixels the user has to move until dragging starts. + * @property {Boolean} [options.transform=false] - Use CSS transformations instead of absolute positioning. + * + * @extends TK.Base + * + * @class TK.Drag + */ +TK.Drag = TK.class({ + _class: "Drag", + Extends: TK.Base, + _options: { + node : "object", + handle : "object", + active : "boolean", + min : "object", + max : "object", + initial : "number", + transform : "boolean", + }, + options: { + node : null, + handle : null, + active : true, + min : {x: false, y: false}, + max : {x: false, y: false}, + initial : 2, + transform : false, + }, + /** + * The user is dragging this item. + * + * @event TK.Drag#dragging + * + * @param {DOMEvent} event - The native DOM event. + */ + /** + * The user started dragging this item. + * + * @event TK.Drag#startdrag + * + * @param {DOMEvent} event - The native DOM event. + */ + /** + * The user stopped dragging this item. + * + * @event TK.Drag#stopdrag + * + * @param {DOMEvent} event - The native DOM event. + */ + static_events: { + startdrag: startdrag, + dragging: dragging, + stopdrag: stopdrag, + }, + initialize: function (options) { + TK.Base.prototype.initialize.call(this, options); + this.set("handle", this.options.handle); + this.set("node", this.options.node); + }, + // GETTERS & SETTERS + set: function (key, value) { + if (key === "node") + this._style = window.getComputedStyle(value); + if (key === "handle" && !value) + value = this.options.node; + + TK.Base.prototype.set.call(this, key, value); + + if (key === "handle") + set_handle.call(this); + if (key === "initial" && this.drag) + this.drag.set("initial", value); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/dragcapture.js b/share/web_surfaces/builtin/mixer/toolkit/modules/dragcapture.js new file mode 100644 index 0000000000..76376802b7 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/dragcapture.js @@ -0,0 +1,345 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +var document = window.document; + +/* this has no global symbol */ +function CaptureState(start) { + this.start = start; + this.prev = start; + this.current = start; +} +CaptureState.prototype = { + /* distance from start */ + distance: function() { + var v = this.vdistance(); + return Math.sqrt(v[0]*v[0] + v[1]*v[1]); + }, + set_current: function(ev) { + this.prev = this.current; + this.current = ev; + return true; + }, + vdistance: function() { + var start = this.start; + var current = this.current; + return [ current.clientX - start.clientX, current.clientY - start.clientY ]; + }, + prev_distance: function() { + var prev = this.prev; + var current = this.current; + return [ current.clientX - prev.clientX, current.clientY - prev.clientY ]; + }, +}; +/* general api */ +function startcapture(state) { + /* do nothing, let other handlers be called */ + if (this.drag_state) return; + + /** + * Capturing started. + * + * @event TK.DragCapture#startcapture + * + * @param {object} state - An internal state object. + * @param {DOMEvent} start - The event object of the initial event. + */ + + var v = this.fire_event("startcapture", state, state.start); + + if (v === true) { + /* we capture this event */ + this.drag_state = state; + this.set("state", true); + } + + return v; +} +function movecapture(ev) { + var d = this.drag_state; + + /** + * A movement was captured. + * + * @event TK.DragCapture#movecapture + * + * @param {DOMEvent} event - The event object of the current move event. + */ + + if (!d.set_current(ev) || this.fire_event("movecapture", d) === false) { + stopcapture.call(this, ev); + return false; + } +} +function stopcapture(ev) { + var s = this.drag_state; + if (s === null) return; + + /** + * Capturing stopped. + * + * @event TK.DragCapture#stopcapture + * + * @param {object} state - An internal state object. + * @param {DOMEvent} event - The event object of the current event. + */ + + this.fire_event("stopcapture", s, ev); + this.set("state", false); + s.destroy(); + this.drag_state = null; +} + +/* mouse handling */ +function MouseCaptureState(start) { + this.__mouseup = null; + this.__mousemove = null; + CaptureState.call(this, start); +} +MouseCaptureState.prototype = Object.assign(Object.create(CaptureState.prototype), { + set_current: function(ev) { + var start = this.start; + /* If the buttons have changed, we assume that the capture has ended */ + if (!this.is_dragged_by(ev)) return false; + return CaptureState.prototype.set_current.call(this, ev); + }, + init: function(widget) { + this.__mouseup = mouseup.bind(widget); + this.__mousemove = mousemove.bind(widget); + document.addEventListener("mousemove", this.__mousemove); + document.addEventListener("mouseup", this.__mouseup); + }, + destroy: function() { + document.removeEventListener("mousemove", this.__mousemove); + document.removeEventListener("mouseup", this.__mouseup); + this.__mouseup = null; + this.__mousemove = null; + }, + is_dragged_by: function(ev) { + var start = this.start; + if (start.buttons !== ev.buttons || start.which !== ev.which) return false; + return true; + }, +}); +function mousedown(ev) { + var s = new MouseCaptureState(ev); + var v = startcapture.call(this, s); + + /* ignore this event */ + if (v === void(0)) return; + + ev.stopPropagation(); + ev.preventDefault(); + + /* we did capture */ + if (v === true) s.init(this); + + return false; +} +function mousemove(ev) { + movecapture.call(this, ev); +} +function mouseup(ev) { + stopcapture.call(this, ev); +} + +/* touch handling */ + +/* + * Old Safari versions will keep the same Touch objects for the full lifetime + * and simply update the coordinates, etc. This is a bug, which we work around by + * cloning the information we need. + */ +function clone_touch(t) { + return { + clientX: t.clientX, + clientY: t.clientY, + identifier: t.identifier, + }; +} + +function TouchCaptureState(start) { + CaptureState.call(this, start); + var touch = start.changedTouches.item(0); + touch = clone_touch(touch); + this.stouch = touch; + this.ptouch = touch; + this.ctouch = touch; +} +TouchCaptureState.prototype = Object.assign(Object.create(CaptureState.prototype), { + find_touch: function(ev) { + var id = this.stouch.identifier; + var touches = ev.changedTouches; + var touch; + + for (var i = 0; i < touches.length; i++) { + touch = touches.item(i); + if (touch.identifier === id) return touch; + } + + return null; + }, + set_current: function(ev) { + var touch = clone_touch(this.find_touch(ev)); + this.ptouch = this.ctouch; + this.ctouch = touch; + return CaptureState.prototype.set_current.call(this, ev); + }, + vdistance: function() { + var start = this.stouch; + var current = this.ctouch; + return [ current.clientX - start.clientX, current.clientY - start.clientY ]; + }, + prev_distance: function() { + var prev = this.ptouch; + var current = this.ctouch; + return [ current.clientX - prev.clientX, current.clientY - prev.clientY ]; + }, + destroy: function() { + }, + is_dragged_by: function(ev) { + return this.find_touch(ev) !== null; + }, +}); +function touchstart(ev) { + /* if cancelable is false, this is an async touchstart, which happens + * during scrolling */ + if (!ev.cancelable) return; + + /* the startcapture event handler has return false. we do not handle this + * pointer */ + var v = startcapture.call(this, new TouchCaptureState(ev)); + + if (v === void(0)) return; + + ev.preventDefault(); + ev.stopPropagation(); + return false; +} +function touchmove(ev) { + if (!this.drag_state) return; + /* we are scrolling, ignore the event */ + if (!ev.cancelable) return; + /* if we cannot find the right touch, some other touchpoint + * triggered this event and we do not care about that */ + if (!this.drag_state.find_touch(ev)) return; + /* if movecapture returns false, the capture has ended */ + if (movecapture.call(this, ev) !== false) { + ev.preventDefault(); + ev.stopPropagation(); + return false; + } +} +function touchend(ev) { + var s; + if (!ev.cancelable) return; + s = this.drag_state; + /* either we are not dragging or it is another touch point */ + if (!s || !s.find_touch(ev)) return; + stopcapture.call(this, ev); + ev.stopPropagation(); + ev.preventDefault(); + return false; +} +function touchcancel(ev) { + return touchend.call(this, ev); +} +var dummy = function() {}; + +function get_parents(e) { + var ret = []; + if (Array.isArray(e)) e.map(function(e) { e = e.parentNode; if (e) ret.push(e); }); + else if (e = e.parentNode) ret.push(e); + return ret; +} + +var static_events = { + set_node: function(value) { + this.delegate_events(value); + }, + contextmenu: function() { return false; }, + delegated: [ + function(element, old_element) { + /* cancel the current capture */ + if (old_element) stopcapture.call(this); + }, + function(elem, old) { + /* NOTE: this works around a bug in chrome (#673102) */ + if (old) TK.remove_event_listener(get_parents(old), "touchstart", dummy); + if (elem) TK.add_event_listener(get_parents(elem), "touchstart", dummy); + } + ], + touchstart: touchstart, + touchmove: touchmove, + touchend: touchend, + touchcancel: touchcancel, + mousedown: mousedown, +}; + +TK.DragCapture = TK.class({ + + /** + * TK.DragCapture is a low-level class for tracking drag events on + * both, touch and mouse events. It can be used for implementing drag'n'drop + * functionality as well as dragging the value of e.g. {@link TK.Fader} or + * {@link TK.Knob}. {@link TK.DragValue} derives from TK.DragCapture. + * + * @extends TK.Module + * + * @param {Object} widget - The parent widget making use of DragValue. + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {HTMLElement} [options.node] - The DOM element receiving the drag events. If not set the widgets element is used. + * + * @class TK.DragCapture + */ + + Extends: TK.Module, + _class: "DragCapture", + _options: { + node: "object", + state: "boolean", /* internal, undocumented */ + }, + options: { + state: false, + }, + static_events: static_events, + initialize: function(widget, O) { + TK.Module.prototype.initialize.call(this, widget, O); + this.drag_state = null; + if (O.node === void(0)) O.node = widget.element; + this.set("node", O.node); + }, + destroy: function() { + TK.Base.prototype.destroy.call(this); + stopcapture.call(this); + }, + cancel_drag: stopcapture, + dragging: function() { + return this.options.state; + }, + state: function() { + return this.drag_state; + }, + is_dragged_by: function(ev) { + return this.drag_state !== null && this.drag_state.is_dragged_by(ev); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/dragvalue.js b/share/web_surfaces/builtin/mixer/toolkit/modules/dragvalue.js new file mode 100644 index 0000000000..6af4ebfbfd --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/dragvalue.js @@ -0,0 +1,283 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ + +function start_drag(value) { + if (!value) return; + var O = this.options; + this.start_pos = O.range.call(this).val2px(O.get.call(this)); + this.fire_event("startdrag", this.drag_state.start); + if (O.events) O.events.call(this).fire_event("startdrag", this.drag_state.start); +} + +/* This version integrates movements, instead + * of using the global change since the beginning */ +function movecapture_int(O, range, state) { + /* O.direction is always 'polar' here */ + + /* movement since last event */ + var v = state.prev_distance(); + var RO = range.options; + + if (!v[0] && !v[1]) return; + + var V = O._direction; + + var dist = Math.sqrt(v[0]*v[0] + v[1]*v[1]); + + var c = (V[0]*v[0] - V[1]*v[1]) / dist; + + if (Math.abs(c) > O._cutoff) return; + + if (v[0] * V[1] + v[1] * V[0] < 0) dist = -dist; + + var multi = RO.step || 1; + var e = state.current; + + if (e.ctrlKey || e.altKey) { + multi *= RO.shift_down; + } else if (e.shiftKey) { + multi *= RO.shift_up; + } + + dist *= multi; + var v = this.start_pos + dist; + + var nval = range.px2val(v); + if (O.limit) + O.set.call(this, Math.min(RO.max, Math.max(RO.min, nval))); + else + O.set.call(this, nval); + + if (!(nval > RO.min) || !(nval < RO.max)) return; + + this.start_pos = v; +} + +function movecapture_abs(O, range, state) { + var dist; + var RO = range.options + switch(O.direction) { + case "vertical": + dist = -state.vdistance()[1]; + break; + default: + TK.warn("Unsupported direction:", O.direction); + case "horizontal": + dist = state.vdistance()[0]; + break; + } + if (O.reverse) + dist *= -1; + + var multi = RO.step || 1; + var e = state.current; + + if (e.ctrlKey && e.shiftKey) { + multi *= RO.shift_down; + } else if (e.shiftKey) { + multi *= RO.shift_up; + } + + dist *= multi; + + var nval = range.px2val(this.start_pos + dist); + + if (O.limit) + O.set.call(this, Math.min(RO.max, Math.max(RO.min, nval))); + else + O.set.call(this, nval); +} + +function movecapture(state) { + var O = this.options; + + if (O.active === false) return false; + + var state = this.drag_state; + var range = O.range.call(this); + + if (O.direction === "polar") { + movecapture_int.call(this, O, range, state); + } else { + movecapture_abs.call(this, O, range, state); + } + + this.fire_event("dragging", state.current); + if (O.events) O.events.call(this).fire_event("dragging", state.current); +} + +function stop_drag(state, ev) { + this.fire_event("stopdrag", ev); + var O = this.options; + if (O.events) O.events.call(this).fire_event("stopdrag", ev); +} + +function angle_diff(a, b) { + // returns an unsigned difference between two angles + var d = (Math.abs(a - b) + 360) % 360; + return d > 180 ? 360 - d : d; +} +TK.DragValue = TK.class({ + /** + * TK.DragValue enables dragging an element and setting a + * value according to the dragged distance. TK.DragValue is for example + * used in {@link TK.Knob} and {@link TK.ValueButton}. + * + * @class TK.DragValue + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Element} options.node - The DOM node used for dragging. + * All DOM events are registered with this Element. + * @property {Function} [options.range] - A function returning a + * {@link TK.Range} object for + * calculating the value. Returns its parent (usually having + * {@link TK.Ranged}-features) by default. + * @property {Function} [options.events] - Returns an element firing the + * events startdrag, dragging and stopdrag. + * By default it returns this.parent. + * @property {Element|boolean} [options.classes=false] - While dragging, the class + * toolkit-dragging will be added to this element. If set to false + * the class will be set on options.node. + * @property {Function} [options.get] - Callback function returning the value to drag. + * By default it returns this.parent.options.value. + * @property {Function} [options.set] - Callback function for setting the value. + * By default it calls this.parent.userset("value", [value]);. + * @property {String} [options.direction="polar"] - Direction for changing the value. + * Can be polar, vertical or horizontal. + * @property {Boolean} [options.active=true] - If false, dragging is deactivated. + * @property {Boolean} [options.cursor=false] - If true, a {@link TK.GlobalCursor} is set while dragging. + * @property {Number} [options.blind_angle=20] - If options.direction is polar, + * this is the angle of separation between positive and negative value changes + * @property {Number} [options.rotation=45] - Defines the angle of the center of the positive value + * changes. 0 means straight upward. For instance, a value of 45 leads to increasing value when + * moving towards top and right. + * @property {Boolean} [options.reverse=false] - If true, the difference of pointer travel is inverted. + * @property {Boolean} [options.limit=false] - Limit the returned value to min and max of the range. + * + * @extends TK.Module + * + * @mixes TK.GlobalCursor + */ + _class: "DragValue", + Extends: TK.DragCapture, + Implements: TK.GlobalCursor, + _options: { + get: "function", + set: "function", + range: "function", + events: "function", + classes: "object|boolean", + direction: "string", + active: "boolean", + cursor: "boolean", + blind_angle: "number", + rotation: "number", + reverse: "boolean", + limit: "boolean", + }, + options: { + range: function () { return this.parent; }, + classes: false, + get: function () { return this.parent.options.value; }, + set: function (v) { this.parent.userset("value", v); }, + events: function () { return this.parent; }, + direction: "polar", + active: true, + cursor: false, + blind_angle: 20, + rotation: 45, + reverse: false, + limit: false, + }, + /** + * Is fired while a user is dragging. + * + * @event TK.DragValue#dragging + * + * @param {DOMEvent} event - The native DOM event. + */ + /** + * Is fired when a user starts dragging. + * + * @event TK.DragValue#startdrag + * + * @param {DOMEvent} event - The native DOM event. + */ + /** + * Is fired when a user stops dragging. + * + * @event TK.DragValue#stopdrag + * + * @param {DOMEvent} event - The native DOM event. + */ + static_events: { + set_state: start_drag, + stopcapture: stop_drag, + startcapture: function() { + if (this.options.active) return true; + }, + set_rotation: function(v) { + v *= Math.PI / 180; + this.set("_direction", [ -Math.sin(v), Math.cos(v) ]); + }, + set_blind_angle: function(v) { + v *= Math.PI / 360; + this.set("_cutoff", Math.cos(v)); + }, + movecapture: movecapture, + startdrag: function(ev) { + TK.S.add(function() { + var O = this.options; + TK.add_class(O.classes || O.node, "toolkit-dragging"); + if (O.cursor) { + if (O.direction === "vertical") { + this.global_cursor("row-resize"); + } else { + this.global_cursor("col-resize"); + } + } + }.bind(this), 1); + }, + stopdrag: function() { + TK.S.add(function() { + var O = this.options; + TK.remove_class(O.classes || O.node, "toolkit-dragging"); + + if (O.cursor) { + if (O.direction === "vertical") { + this.remove_cursor("row-resize"); + } else { + this.remove_cursor("col-resize"); + } + } + }.bind(this), 1); + }, + }, + initialize: function (widget, options) { + TK.DragCapture.prototype.initialize.call(this, widget, options); + this.start_pos = 0; + var O = this.options; + this.set("rotation", O.rotation); + this.set("blind_angle", O.blind_angle); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/eqband.js b/share/web_surfaces/builtin/mixer/toolkit/modules/eqband.js new file mode 100644 index 0000000000..224aeeab11 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/eqband.js @@ -0,0 +1,185 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ + +var type_to_mode = { + parametric: "circular", + notch: "line-vertical", + lowpass1: "block-right", + lowpass2: "block-right", + lowpass3: "block-right", + lowpass4: "block-right", + highpass1: "block-left", + highpass2: "block-left", + highpass3: "block-left", + highpass4: "block-left", + "low-shelf": "line-vertical", + "high-shelf": "line-vertical", +}; + +var type_to_pref = { + parametric: ["left", "top", "right", "bottom"], + notch: ["left", "right", "top", "bottom"], + lowpass1: ["left", "top-left", "bottom-left", "right", "top-right", "bottom-right", "top", "bottom"], + lowpass2: ["left", "top-left", "bottom-left", "right", "top-right", "bottom-right", "top", "bottom"], + lowpass3: ["left", "top-left", "bottom-left", "right", "top-right", "bottom-right", "top", "bottom"], + lowpass4: ["left", "top-left", "bottom-left", "right", "top-right", "bottom-right", "top", "bottom"], + highpass1: ["right", "top-right", "bottom-right", "left", "top-left", "bottom-left", "top", "bottom"], + highpass2: ["right", "top-right", "bottom-right", "left", "top-left", "bottom-left", "top", "bottom"], + highpass3: ["right", "top-right", "bottom-right", "left", "top-left", "bottom-left", "top", "bottom"], + highpass4: ["right", "top-right", "bottom-right", "left", "top-left", "bottom-left", "top", "bottom"], + "low-shelf": ["left", "right", "top", "bottom"], + "high-shelf": ["left", "right", "top", "bottom"], +}; + +TK.EqBand = TK.class({ + /** + * An TK.EqBand extends a {@link TK.ResponseHandle} and holds a + * dependent {@link TK.Filter}. It is used as a fully functional representation + * of a single equalizer band in {@link TK.Equalizer} TK.EqBand needs a {@link TK.Chart} + * or any other derivate to be drawn in. + * + * @class TK.EqBand + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String|Function} [options.type="parametric"] - The type of the filter. + * Possible values are parametric, notch, + * low-shelf, high-shelf, lowpass[n] or + * highpass[n]. + * @property {Number} options.freq - Frequency setting. This is an alias for the option x + * defined by {@link TK.ResponseHandle}. + * @property {Number} options.gain - Gain setting. This is an alias for the option y + * defined by {@link TK.ResponseHandle}. + * @property {Number} options.q - Quality setting. This is an alias for the option z + * defined by {@link TK.ResponseHandle}. + * + * @extends TK.ResponseHandle + */ + _class: "EqBand", + Extends: TK.ResponseHandle, + _options: Object.assign(Object.create(TK.ResponseHandle.prototype._options), { + type: "string|function", + gain: "number", + freq: "number", + x: "number", + y: "number", + z: "number", + q: "number", + }), + options: { + type: "parametric" + }, + static_events: { + set_freq: function(v) { this.set("x", v); }, + set_gain: function(v) { this.set("y", v); }, + set_q: function(v) { this.set("z", v); }, + useraction: function(k, v) { + if (k === 'x') this.set("freq", v); + if (k === 'y') this.set("gain", v); + if (k === 'z') this.set("q", v); + }, + }, + + initialize: function (options) { + /** + * @member {TK.Filter} TK.EqBand#filter - The filter providing the graphical calculations. + */ + this.filter = new TK.Filter({ + type: options.type, + }); + + TK.ResponseHandle.prototype.initialize.call(this, options); + + if (options.mode) var _m = options.mode; + this.set("type", this.options.type); + if (_m) this.set("mode", options.mode); + + if (options.x !== void(0)) + this.set("x", options.x); + else if (options.freq !== void(0)) + this.set("freq", options.freq); + if (options.y !== void(0)) + this.set("y", options.y); + else if (options.gain !== void(0)) + this.set("gain", options.gain); + if (options.z !== void(0)) + this.set("z", options.z); + else if (options.q !== void(0)) + this.set("q", options.q); + + /** + * @member {HTMLDivElement} TK.EqBand#element - The main SVG group. + * Has class toolkit-eqband. + */ + TK.add_class(this.element, "toolkit-eqband"); + + this.filter.reset(); + }, + + /** + * Calculate the gain for a given frequency in Hz. + * + * @method TK.EqBand#freq2gain + * + * @param {number} freq - The frequency. + * + * @returns {number} The gain at the given frequency. + */ + freq2gain: function (freq) { + return this.filter.get_freq2gain()(freq); + }, + + // GETTER & SETTER + set: function (key, value) { + switch (key) { + case "type": + if (typeof value === "string") { + var mode = type_to_mode[value]; + var pref = type_to_pref[value]; + if (!mode) { + TK.warn("Unsupported type:", value); + return; + } + this.set("mode", mode); + this.set("preferences", pref); + this.set("show_axis", mode === "line-vertical"); + } + this.filter.set("type", value); + break; + case "freq": + case "gain": + case "q": + value = this.filter.set(key, value); + break; + case "x": + value = this.range_x.snap(value); + break; + case "y": + value = this.range_y.snap(value); + break; + case "z": + value = this.range_z.snap(value); + break; + } + return TK.ResponseHandle.prototype.set.call(this, key, value); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/filter.js b/share/web_surfaces/builtin/mixer/toolkit/modules/filter.js new file mode 100644 index 0000000000..c17c689dd8 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/filter.js @@ -0,0 +1,401 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +/* These formulae for 'standard' biquad filter coefficients are + * from + * "Cookbook formulae for audio EQ biquad filter coefficients" + * by Robert Bristow-Johnson. + * + */ + +function Null(O) { + /* this biquad does not do anything */ + return { + b0: 1, + b1: 1, + b2: 1, + a0: 1, + a1: 1, + a2: 1, + sample_rate: O.sample_rate, + }; +} + +function LowShelf(O) { + var cos = Math.cos, + sqrt = Math.sqrt, + A = Math.pow(10, O.gain / 40), + w0 = 2*Math.PI*O.freq/O.sample_rate, + alpha = Math.sin(w0)/(2*O.q); + return { + b0: A*( (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha ), + b1: 2*A*( (A-1) - (A+1)*cos(w0) ), + b2: A*( (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha ), + a0: (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha, + a1: -2*( (A-1) + (A+1)*cos(w0) ), + a2: (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha, + sample_rate: O.sample_rate, + }; +} + +function HighShelf(O) { + var cos = Math.cos; + var sqrt = Math.sqrt; + var A = Math.pow(10, O.gain / 40); + var w0 = 2*Math.PI*O.freq/O.sample_rate; + var alpha = Math.sin(w0)/(2*O.q); + return { + b0: A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha ), + b1: -2*A*( (A-1) + (A+1)*cos(w0) ), + b2: A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha ), + a0: (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha, + a1: 2*( (A-1) - (A+1)*cos(w0) ), + a2: (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha, + sample_rate: O.sample_rate, + }; +} + +function Peaking(O) { + var cos = Math.cos; + var A = Math.pow(10, O.gain / 40); + var w0 = 2*Math.PI*O.freq/O.sample_rate; + var alpha = Math.sin(w0)/(2*O.q); + return { + b0: 1 + alpha*A, + b1: -2*cos(w0), + b2: 1 - alpha*A, + a0: 1 + alpha/A, + a1: -2*cos(w0), + a2: 1 - alpha/A, + sample_rate: O.sample_rate, + }; +} + +function Notch(O) { + var cos = Math.cos; + var w0 = 2*Math.PI*O.freq/O.sample_rate; + var alpha = Math.sin(w0)/(2*O.q); + return { + b0: 1, + b1: -2*cos(w0), + b2: 1, + a0: 1 + alpha, + a1: -2*cos(w0), + a2: 1 - alpha, + sample_rate: O.sample_rate, + }; +} + +/* This is a standard lowpass filter with transfer function + * H(s) = 1/(1+s) + */ +function LowPass1(O) { + var w0 = 2*Math.PI*O.freq/O.sample_rate, + s0 = Math.sin(w0), + c0 = Math.cos(w0); + return { + b0: 1-c0, + b1: 2*(1-c0), + b2: 1-c0, + a0: 1 - c0 + s0, + a1: 2*(1-c0), + a2: 1 - c0 - s0, + sample_rate: O.sample_rate, + }; +} + +function LowPass2(O) { + var cos = Math.cos; + var w0 = 2*Math.PI*O.freq/O.sample_rate; + var alpha = Math.sin(w0)/(2*O.q); + return { + b0: (1 - cos(w0))/2, + b1: 1 - cos(w0), + b2: (1 - cos(w0))/2, + a0: 1 + alpha, + a1: -2*cos(w0), + a2: 1 - alpha, + sample_rate: O.sample_rate, + }; +} + +function LowPass4(O) { + O = LowPass2(O); + O.factor = 2; + return O; +} + +/* This is a standard highpass filter with transfer function + * H(s) = s/(1+s) + */ +function HighPass1(O) { + var w0 = 2*Math.PI*O.freq/O.sample_rate, + s0 = Math.sin(w0), + c0 = Math.cos(w0); + return { + b0: s0, + b1: 0, + b2: -s0, + a0: 1 - c0 + s0, + a1: 2*(1-c0), + a2: 1 - c0 - s0, + sample_rate: O.sample_rate, + }; +} + +function HighPass2(O) { + var cos = Math.cos; + var w0 = 2*Math.PI*O.freq/O.sample_rate; + var alpha = Math.sin(w0)/(2*O.q); + return { + b0: (1 + cos(w0))/2, + b1: -(1 + cos(w0)), + b2: (1 + cos(w0))/2, + a0: 1 + alpha, + a1: -2*cos(w0), + a2: 1 - alpha, + sample_rate: O.sample_rate, + }; +} + +function HighPass4(O) { + O = HighPass2(O); + O.factor = 2; + return O; +} + +var Filters = { + Null: Null, + LowShelf: LowShelf, + HighShelf: HighShelf, + Peaking: Peaking, + Notch: Notch, + LowPass1: LowPass1, + LowPass2: LowPass2, + LowPass4: LowPass4, + HighPass1: HighPass1, + HighPass2: HighPass2, + HighPass4: HighPass4, +}; + +var standard_biquads = { + "null": BiquadFilter(Null), + "low-shelf": BiquadFilter(LowShelf), + "high-shelf": BiquadFilter(HighShelf), + parametric: BiquadFilter(Peaking), + notch: BiquadFilter(Notch), + lowpass1: BiquadFilter(LowPass1), + lowpass2: BiquadFilter(LowPass2), + lowpass3: BiquadFilter(LowPass1, LowPass2), + lowpass4: BiquadFilter(LowPass4), + highpass1: BiquadFilter(HighPass1), + highpass2: BiquadFilter(HighPass2), + highpass3: BiquadFilter(HighPass1, HighPass2), + highpass4: BiquadFilter(HighPass4), +}; + +var NullModule = { freq2gain: function(f) { return 0.0; } }; + +function BilinearModule(w, O) { + var log = Math.log; + var sin = Math.sin; + + var LN10_10 = (O.factor||1.0) * 10/Math.LN10; + var PI = +(Math.PI/O.sample_rate); + var Ra = +((O.a0 + O.a1) * (O.a0 + O.a1) / 4); + var Rb = +((O.b0 + O.b1) * (O.b0 + O.b1) / 4); + var Ya = +(O.a1 * O.a0); + var Yb = +(O.b1 * O.b0); + + if (Ra === Rb && Ya === Yb) return NullModule; + + function freq2gain(f) { + f = +f; + var S = +sin(PI * f) + S *= S; + return LN10_10 * log( (Rb - S * Yb) / + (Ra - S * Ya) ); + } + + return { freq2gain: freq2gain }; +} + +function BiquadModule(w, O) { + var log = Math.log; + var sin = Math.sin; + + var LN10_10 = (O.factor||1.0) * 10/Math.LN10; + var PI = +(Math.PI/O.sample_rate); + var Ra = +((O.a0 + O.a1 + O.a2) * (O.a0 + O.a1 + O.a2) / 4); + var Rb = +((O.b0 + O.b1 + O.b2) * (O.b0 + O.b1 + O.b2) / 4); + var Xa = +(4 * O.a0 * O.a2); + var Ya = +(O.a1 * (O.a0 + O.a2)); + var Xb = +(4 * O.b0 * O.b2); + var Yb = +(O.b1 * (O.b0 + O.b2)); + + if (Ra === Rb && Ya === Yb && Xa === Xb) return NullModule; + + function freq2gain(f) { + f = +f; + var S = +sin(PI * f) + S *= S; + return LN10_10 * log( (Rb - S * (Xb * (1 - S) + Yb)) / + (Ra - S * (Xa * (1 - S) + Ya)) ); + } + + return { freq2gain: freq2gain }; +} + +function BiquadFilter1(trafo) { + function factory(stdlib, O) { + return BiquadModule(stdlib, trafo(O)); + } + + return factory; +} + +function BiquadFilterN(trafos) { + function factory(stdlib, O) { + var A = new Array(trafos.length); + var i; + + for (i = 0; i < trafos.length; i ++) { + A[i] = BiquadModule(stdlib, trafos[i](O)).freq2gain; + } + + return { + freq2gain: function(f) { + var ret = 0.0; + var i; + + for (i = 0; i < A.length; i++) { + ret += A[i](f); + } + + return ret; + } + }; + } + + return factory; +} + +function BiquadFilter() { + if (arguments.length === 1) return BiquadFilter1(arguments[0]); + + return BiquadFilterN.call(this, Array.prototype.slice.call(arguments)); +} + +TK.BiquadFilter = BiquadFilter; + +function reset() { + this.freq2gain = null; + /** + * Is fired when a filters drawing function is reset. + * + * @event TK.Filter#reset + */ + this.fire_event("reset"); +} + +TK.Filter = TK.class({ + /** + * TK.Filter provides the math for calculating a gain from + * a given frequency for different types of biquad filters. + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Stgring|Function} [options.type="parametric"] - The type of the filter. Possible values are + * parametric, notch, low-shelf, + * high-shelf, lowpass[n] or highpass[n]. + * @property {Number} [options.freq=1000] - The initial frequency. + * @property {Number} [options.gain=0] - The initial gain. + * @property {Number} [options.q=1] - The initial Q of the filter. + * @property {Number} [options.sample_rate=44100] - The sample rate. + * + * @mixin TK.Filter + * + * @extends TK.Base + * + * @mixes TK.AudioMath + * @mixes TK.Notes + */ + + /** + * Returns the gain for a given frequency + * + * @method TK.Filter#freq2gain + * + * @param {number} frequency - The frequency to calculate the gain for. + * + * @returns {number} gain - The gain at the given frequency. + */ + _class: "Filter", + Extends: TK.Base, + _options: { + type: "string|function", + freq: "number", + gain: "number", + q: "number", + sample_rate: "number", + }, + options: { + type: "parametric", + freq: 1000, + gain: 0, + q: 1, + sample_rate: 44100, + }, + static_events: { + set_freq: reset, + set_type: reset, + set_q: reset, + set_gain: reset, + initialized: reset, + }, + create_freq2gain: function() { + var O = this.options; + var m; + + if (typeof(O.type) === "string") { + m = standard_biquads[O.type]; + + if (!m) { + TK.error("Unknown standard filter: "+O.type); + return; + } + } else if (typeof(O.type) === "function") { + m = O.type; + } else { + TK.error("Unsupported option 'type'."); + return; + } + this.freq2gain = m(window, O).freq2gain; + }, + get_freq2gain: function() { + if (this.freq2gain === null) this.create_freq2gain(); + return this.freq2gain; + }, + reset: reset, +}); + +TK.Filters = Filters; + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/graph.js b/share/web_surfaces/builtin/mixer/toolkit/modules/graph.js new file mode 100644 index 0000000000..d0b7dae094 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/graph.js @@ -0,0 +1,352 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK) { +function range_change_cb() { + this.invalidate_all(); + this.trigger_draw(); +}; +function transform_dots(dots) { + if (dots === void(0)) return ""; + if (typeof dots === "string") return dots; + if (typeof dots === "object") { + if (Array.isArray(dots)) { + if (!dots.length || !dots[0]) return null; + var ret = { }; + var start, stop; + + for (var name in dots[0]) if (dots[0].hasOwnProperty(name)) { + var a = []; + ret[name] = a; + for (var i = 0; i < dots.length; i++) { + a[i] = dots[i][name]; + } + } + return ret; + } else return dots; + } else { + TK.error("Unsupported option 'dots':", dots); + return ""; + } +} +// this is not really a rounding operation but simply adds 0.5. we do this to make sure +// that integer pixel positions result in actual pixels, instead of being spread across +// two pixels with half opacity +function svg_round(x) { + x = +x; + return x + 0.5; +} +function svg_round_array(x) { + var i; + for (i = 0; i < x.length; i++) { + x[i] = +x[i] + 0.5; + } + return x; +} +function _start(d, s) { + var w = this.range_x.options.basis; + var h = this.range_y.options.basis; + var t = this.options.type; + var m = this.options.mode; + var x = this.range_x.val2px(d.x[0]); + var y = this.range_y.val2px(d.y[0]); + switch (m) { + case "bottom": + // fill the lower part of the graph + s.push( + "M " + svg_round(x - 1) + " ", + svg_round(h + 1) + " " + t + " ", + svg_round(x - 1) + " ", + svg_round(y) + ); + break; + case "top": + // fill the upper part of the graph + s.push("M " + svg_round(x - 1) + " " + svg_round(-1), + " " + t + " " + svg_round(x - 1) + " ", + svg_round(y) + ); + break; + case "center": + // fill from the mid + s.push( + "M " + svg_round(x - 1) + " ", + svg_round(0.5 * h) + ); + break; + case "base": + // fill from variable point + s.push( + "M " + svg_round(x - 1) + " ", + svg_round((1 - this.options.base) * h) + ); + break; + default: + TK.error("Unsupported mode:", m); + /* FALL THROUGH */ + case "line": + // fill nothing + s.push("M " + svg_round(x) + " " + svg_round(y)); + break; + } +} +function _end(d, s) { + var a = 0.5; + var h = this.range_y.options.basis; + var t = this.options.type; + var m = this.options.mode; + var x = this.range_x.val2px(d.x[d.x.length-1]); + var y = this.range_y.val2px(d.y[d.y.length-1]); + switch (m) { + case "bottom": + // fill the graph below + s.push(" " + t + " " + svg_round(x) + " " + svg_round(h + 1) + " Z"); + break; + case "top": + // fill the upper part of the graph + s.push(" " + t + " " + svg_round(x + 1) + " " + svg_round(-1) + " Z"); + break; + case "center": + // fill from mid + s.push(" " + t + " " + svg_round(x + 1) + " " + svg_round(0.5 * h) + " Z"); + break; + case "base": + // fill from variable point + s.push(" " + t + " " + svg_round(x + 1) + " " + svg_round((-m + 1) * h) + " Z"); + break; + default: + TK.error("Unsupported mode:", m); + /* FALL THROUGH */ + case "line": + // fill nothing + break; + } +} + +TK.Graph = TK.class({ + /** + * TK.Graph is a single SVG path element. It provides + * some functions to easily draw paths inside Charts and other + * derivates. + * + * @class TK.Graph + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Function|Object} options.range_x - Callback function + * returning a {@link TK.Range} module for x axis or an object with options + * for a new {@link Range}. + * @property {Function|Object} options.range_y - Callback function + * returning a {@link TK.Range} module for y axis or an object with options + * for a new {@link Range}. + * @property {Array|String} options.dots=[] - The dots of the path. + * Can be a ready-to-use SVG-path-string or an array of objects like + * {x: x, y: y [, x1, y1, x2, y2]} (depending on the type). + * @property {String} [options.type="L"] - Type of the graph (needed values in dots object): + *
    + *
  • L: normal (needs x,y)
  • + *
  • T: smooth quadratic Bézier (needs x, y)
  • + *
  • H[n]: smooth horizontal, [n] = smoothing factor between 1 (square) and 5 (nearly no smooth)
  • + *
  • Q: quadratic Bézier (needs: x1, y1, x, y)
  • + *
  • C: CurveTo (needs: x1, y1, x2, y2, x, y)
  • + *
  • S: SmoothCurve (needs: x1, y1, x, y)
  • + *
+ * @property {String} [options.mode="line"] - Drawing mode of the graph, possible values are: + *
    + *
  • line: line only
  • + *
  • bottom: fill below the line
  • + *
  • top: fill above the line
  • + *
  • center: fill from the vertical center of the canvas
  • + *
  • base: fill from a percentual position on the canvas (set with base)
  • + *
+ * @property {Number} [options.base=0] - If mode is base set the position + * of the base line to fill from between 0 (bottom) and 1 (top). + * @property {String} [options.color=""] - Set the color of the path. + * Better use stroke and fill via CSS. + * @property {Number} [options.width=0] - The width of the graph. + * @property {Number} [options.height=0] - The height of the graph. + * @property {String|Boolean} [options.key=false] - Show a description + * for this graph in the charts key, false to turn it off. + * + * @extends TK.Widget + * + * @mixes TK.Ranges + */ + _class: "Graph", + Extends: TK.Widget, + Implements: TK.Ranges, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + dots: "array", + type: "string", + mode: "string", + base: "number", + color: "string", + range_x: "object", + range_y: "object", + width: "number", + height: "number", + key: "string|boolean", + element: void(0), + }), + options: { + dots: null, + type: "L", + mode: "line", + base: 0, + color: "", + width: 0, + height: 0, + key: false + }, + + initialize: function (options) { + TK.Widget.prototype.initialize.call(this, options); + /** @member {SVGPath} TK.Graph#element - The SVG path. Has class toolkit-graph + */ + this.element = this.widgetize(TK.make_svg("path"), true, true, true); + TK.add_class(this.element, "toolkit-graph"); + /** @member {TK.Range} TK.Graph#range_x - The range for the x axis. + */ + /** @member {TK.Range} TK.Graph#range_y - The range for the y axis. + */ + if (this.options.range_x) this.set("range_x", this.options.range_x); + if (this.options.range_y) this.set("range_y", this.options.range_y); + this.set("color", this.options.color); + this.set("mode", this.options.mode); + if (this.options.dots) this.options.dots = transform_dots(this.options.dots); + }, + + redraw: function () { + var I = this.invalid; + var O = this.options; + var E = this.element; + + if (I.color) { + I.color = false; + E.style.stroke = O.color; + } + + if (I.mode) { + I.mode = false; + TK.remove_class(E, "toolkit-filled"); + TK.remove_class(E, "toolkit-outline"); + TK.add_class(E, O.mode === "line" ? "toolkit-outline" : "toolkit-filled"); + } + + if (I.validate("dots", "type", "width", "height")) { + var a = 0.5; + var dots = O.dots; + var range_x = this.range_x; + var range_y = this.range_y; + var w = range_x.options.basis; + var h = range_y.options.basis; + + if (typeof dots === "string") { + E.setAttribute("d", dots); + } else if (!dots) { + E.setAttribute("d", ""); + } else { + var x = svg_round_array(dots.x.map(range_x.val2px)); + var y = svg_round_array(dots.y.map(range_y.val2px)); + var x1, x2, y1, y2; + // if we are drawing a line, _start will do the first point + var i = O.type === "line" ? 1 : 0; + var s = []; + var f; + + _start.call(this, dots, s); + + switch (O.type.substr(0,1)) { + case "L": + case "T": + for (; i < x.length; i++) + s.push(" " + O.type + " " + x[i] + " " + y[i]); + break; + case "Q": + case "S": + x1 = svg_round_array(dots.x1.map(range_x.val2px)); + y1 = svg_round_array(dots.y1.map(range_y.val2px)); + for (; i < x.length; i++) + s.push(" " + O.type + " " + + x1[i] + "," + y1[i] + " " + + x[i] + "," + y[i]); + break; + case "C": + x1 = svg_round_array(dots.x1.map(range_x.val2px)); + x2 = svg_round_array(dots.x2.map(range_x.val2px)); + y1 = svg_round_array(dots.y1.map(range_y.val2px)); + y2 = svg_round_array(dots.y2.map(range_y.val2px)); + for (; i < x.length; i++) + s.push(" " + O.type + " " + + x1[i] + "," + y1[i] + " " + + x2[i] + "," + y2[i] + " " + + x[i] + "," + y[i]); + break; + case "H": + f = O.type.length > 1 ? parseFloat(O.type.substr(1)) : 3; + if (i === 0) { + i++; + s.push(" S" + x[0] + "," + y[0] + " " + x[0] + "," + y[0]); + } + for (; i < x.length-1; i++) + s.push(" S" + (x[i] - Math.round(x[i] - x[i-1])/f) + "," + + y[i] + " " + x[i] + "," + y[i]); + if (i < x.length) + s.push(" S" + x[i] + "," + y[i] + " " + x[i] + "," + y[i]); + break; + default: + TK.error("Unsupported graph type", O.type); + } + + _end.call(this, dots, s); + E.setAttribute("d", s.join("")); + } + } + TK.Widget.prototype.redraw.call(this); + }, + + // GETTER & SETTER + set: function (key, value) { + if (key === "dots") { + value = transform_dots(value); + } + TK.Widget.prototype.set.call(this, key, value); + switch (key) { + case "range_x": + case "range_y": + this.add_range(value, key); + value.add_event("set", range_change_cb.bind(this)); + break; + case "width": + this.range_x.set("basis", value); + break; + case "height": + this.range_y.set("basis", value); + break; + case "dots": + /** + * Is fired when the graph changes + * @event TK.Graph#graphchanged + */ + this.fire_event("graphchanged"); + break; + } + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/grid.js b/share/web_surfaces/builtin/mixer/toolkit/modules/grid.js new file mode 100644 index 0000000000..2b3cf5925f --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/grid.js @@ -0,0 +1,272 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK) { +function draw_lines(a, mode, last) { + var labels = new Array(a.length); + var coords = new Array(a.length); + var i, label, obj; + + for (i = 0; i < a.length; i++) { + obj = a[i]; + if (obj.label) { + label = TK.make_svg("text"); + label.textContent = obj.label; + label.style["dominant-baseline"] = "central"; + TK.add_class(label, "toolkit-grid-label"); + TK.add_class(label, mode ? "toolkit-horizontal" : "toolkit-vertical"); + if (obj["class"]) TK.add_class(label, obj["class"]); + + this.element.appendChild(label); + labels[i] = label; + } + } + + var w = this.range_x.options.basis; + var h = this.range_y.options.basis; + + + TK.S.add(function() { + /* FORCE_RELAYOUT */ + + for (i = 0; i < a.length; i++) { + obj = a[i]; + label = labels[i]; + if (!label) continue; + var bb; + try { + bb = label.getBBox(); + } catch(e) { + // if we are hidden, this may throw + // we should force redraw at some later point, but + // its hard to do. the grid should really be deactivated + // by an option. + continue; + } + var tw = bb.width; + var th = bb.height; + var p = TK.get_style(label, "padding").split(" "); + if (p.length < 2) + p[1] = p[2] = p[3] = p[0]; + if (p.length < 3) { + p[2] = p[0]; + p[3] = p[1]; + } + if (p.length < 4) + p[3] = p[1]; + var pt = parseInt(p[0]) || 0; + var pr = parseInt(p[1]) || 0; + var pb = parseInt(p[2]) || 0; + var pl = parseInt(p[3]) || 0; + var x, y; + if (mode) { + y = Math.max(th / 2, Math.min(h - th / 2 - pt, this.range_y.val2px(obj.pos))); + if (y > last) continue; + x = w - tw - pl; + coords[i] = { + x : x, + y : y, + m : tw + pl + pr, + }; + last = y - th; + } else { + x = Math.max(pl, Math.min(w - tw - pl, this.range_x.val2px(obj.pos) - tw / 2)); + if (x < last) continue; + y = h-th/2-pt; + coords[i] = { + x : x, + y : y, + m : th + pt + pb, + }; + last = x + tw; + } + } + + TK.S.add(function() { + for (i = 0; i < a.length; i++) { + label = labels[i]; + if (label) { + obj = coords[i]; + if (obj) { + label.setAttribute("x", obj.x); + label.setAttribute("y", obj.y); + } else { + if (label.parentElement == this.element) + this.element.removeChild(label); + } + } + } + + for (i = 0; i < a.length; i++) { + obj = a[i]; + label = coords[i]; + var m; + if (label) m = label.m; + else m = 0; + + if ((mode && obj.pos === this.range_y.options.min) + || ( mode && obj.pos === this.range_y.options.max) + || (!mode && obj.pos === this.range_x.options.min) + || (!mode && obj.pos === this.range_x.options.max)) + continue; + var line = TK.make_svg("path"); + TK.add_class(line, "toolkit-grid-line"); + TK.add_class(line, mode ? "toolkit-horizontal" : "toolkit-vertical"); + if (obj["class"]) TK.add_class(line, obj["class"]); + if (obj.color) line.setAttribute("style", "stroke:" + obj.color); + if (mode) { + // line from left to right + line.setAttribute("d", "M0 " + Math.round(this.range_y.val2px(obj.pos)) + + ".5 L" + (this.range_x.options.basis - m) + " " + + Math.round(this.range_y.val2px(obj.pos)) + ".5"); + } else { + // line from top to bottom + line.setAttribute("d", "M" + Math.round(this.range_x.val2px(obj.pos)) + + ".5 0 L" + Math.round(this.range_x.val2px(obj.pos)) + + ".5 " + (this.range_y.options.basis - m)); + } + this.element.appendChild(line); + } + }.bind(this), 1); + }.bind(this)); +} +TK.Grid = TK.class({ + /** + * TK.Grid creates a couple of lines and labels in a SVG + * image on the x and y axis. It is used in e.g. {@link TK.Graph} and + * {@link TK.FrequencyResponse} to draw markers and values. TK.Grid needs a + * parent SVG image do draw into. The base element of a TK.Grid is a + * SVG group containing all the labels and lines. + * + * @class TK.Grid + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Array} [options.grid_x=[]] - Array for vertical grid line definitions with the members: + * @property {Number} [options.grid_x.pos] - The value where to draw grid line and correspon ding label. + * @property {String} [options.grid_x.color] - A valid CSS color string to colorize the elements. + * @property {String} [options.grid_x.class] - A class name for the elements. + * @property {String} [options.grid_x.label] - A label string. + * @property {Array} [options.grid_y=[]] - Array for horizontal grid lines with the members: + * @property {Number} [options.grid_y.pos] - The value where to draw grid line and corresponding label. + * @property {String} [options.grid_y.color] - A valid CSS color string to colorize the elements. + * @property {String} [options.grid_y.class] - A class name for the elements. + * @property {String} [options.grid_y.label] - A label string. + * @property {Function|Object} [options.range_x={}] - A function returning + * a {@link TK.Range} instance for vertical grid lines or an object + * containing options. for a new {@link Range}. + * @property {Function|Object} [options.range_y={}] - A function returning + * a {@link TK.Range} instance for horizontal grid lines or an object + * containing options. for a new {@link Range}. + * @property {Number} [options.width=0] - Width of the grid. + * @property {Number} [options.height=0] - Height of the grid. + * + * @extends TK.Widget + * + * @mixes TK.Ranges + */ + _class: "Grid", + Extends: TK.Widget, + Implements: TK.Ranges, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + grid_x: "array", + grid_y: "array", + range_x: "object", + range_y: "object", + width: "number", + height: "number", + }), + options: { + grid_x: [], + grid_y: [], + range_x: {}, + range_y: {}, + width: 0, + height: 0 + }, + initialize: function (options) { + TK.Widget.prototype.initialize.call(this, options); + /** + * @member {SVGGroup} TK.Grid#element - The main SVG group containing all grid elements. Has class toolkit-grid. + */ + this.element = this.widgetize( + TK.make_svg("g", {"class": "toolkit-grid"}), true, true, true); + /** + * @member {TK.Range} TK.Grid#range_x - The range for the x axis. + */ + /** + * @member {TK.Range} TK.Grid#range_y - The range for the y axis. + */ + this.add_range(this.options.range_x, "range_x"); + this.add_range(this.options.range_y, "range_y"); + if (this.options.width) + this.set("width", this.options.width); + if (this.options.height) + this.set("height", this.options.width); + this.invalidate_ranges = function (key, value) { + this.invalid.range_x = true; + this.invalid.range_y = true; + this.trigger_draw(); + }.bind(this); + this.range_x.add_event("set", this.invalidate_ranges); + this.range_y.add_event("set", this.invalidate_ranges); + }, + + redraw: function () { + var I = this.invalid, O = this.options; + if (I.validate("grid_x", "grid_y", "range_x", "range_y")) { + TK.empty(this.element); + + draw_lines.call(this, O.grid_x, false, 0); + draw_lines.call(this, O.grid_y, true, this.range_y.options.basis); + } + TK.Widget.prototype.redraw.call(this); + }, + destroy: function () { + this.range_x.remove_event("set", this.invalidate_ranges); + this.range_y.remove_event("set", this.invalidate_ranges); + TK.Widget.prototype.destroy.call(this); + }, + // GETTER & SETTER + set: function (key, value) { + this.options[key] = value; + switch (key) { + case "grid_x": + case "grid_y": + /** + * Is fired when the grid has changed. + * + * @event TK.Grid#gridchanged + * + * @param {Array} grid_x - The grid elements for x axis. + * @param {Array} grid_y - The grid elements for y axis. + */ + this.fire_event("gridchanged", this.options.grid_x, this.options.grid_y); + break; + case "width": + this.range_x.set("basis", value); + break; + case "height": + this.range_y.set("basis", value); + break; + } + TK.Widget.prototype.set.call(this, key, value); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/range.js b/share/web_surfaces/builtin/mixer/toolkit/modules/range.js new file mode 100644 index 0000000000..c167c462fb --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/range.js @@ -0,0 +1,103 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK) { +TK.Range = TK.class({ + /** + * TK.Range is used for calculating linear scales from + * different values. They are useful to build coordinate systems, + * calculate pixel positions for different scale types and the like. + * TK.Range is used e.g. in {@link TK.Scale}, {@link TK.MeterBase} and {@link TK.Graph} to draw + * elements on a certain position according to a value on an + * arbitrary scale. + * + * @class TK.Range + * + * @extends TK.Base + * + * @mixes TK.Ranged + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String|Function} [options.scale="linear"] - Type of the value. + * linear, decibel, log2, frequency + * or a function (value, options, coef) {}. + * If a function instead of a constant is handed over, it receives the + * actual options object as the second argument and is supposed to return a + * coefficient between 0 and 1. If the third argument "coef" is true, it is + * supposed to return a value depending on a coefficient handed over as the + * first argument. + * @property {Boolean} [options.reverse=false] - true if + * the range is reversed. + * @property {Number} [options.basis=0] - Dimensions of the range, set to + * width/height in pixels, if you need it for drawing purposes, to 100 if + * you need percentual values or to 1 if you just need a linear + * coefficient for a e.g. logarithmic scale. + * @property {Number} [options.min=0] - The minimum value possible. + * @property {Number} [options.max=0] - The maximum value possible. + * @property {Number} [options.step=1] - Step size, needed for e.g. user + * interaction + * @property {Number} [options.shift_up=4] - Multiplier for e.g. SHIFT pressed + * while stepping + * @property {Number} [options.shift_down=0.25] - Multiplier for e.g. SHIFT + CONTROL + * pressed while stepping + * @property {Number|Array} [options.snap=0] - Snap the value to a virtual grid + * with this distance. Numbers define the step size between snaps, an + * array contains a list of values to snap to. + * Using snap option with float values + * causes the range to reduce its minimum and maximum values depending + * on the amount of decimal digits because of the implementation of + * math in JavaScript. Using a step size of e.g. 1.125 + * reduces the maximum usable value from 9,007,199,254,740,992 to + * 9,007,199,254,740.992 (note the decimal point). + * @property {Boolean} [options.round=false] - if snap is set, + * decide how to jump between snaps. Setting this to true + * slips to the next snap if the value is more than on its half way to it. + * Otherwise the value has to reach the next snap until it is hold there + * again. + */ + Extends : TK.Base, + _class: "Range", + Implements: [TK.Ranged], + _options: { + scale: "string|function", + reverse: "boolean", + basis: "number", + min: "number", + max: "number", + step: "number", + shift_up: "number", + shift_down: "number", + snap: "number|array", + round: "boolean", + }, + options: { + scale: "linear", + reverse: false, + basis: 0, + min: 0, + max: 0, + step: 1, + shift_up: 4, + shift_down: 0.25, + snap: 0, + round: false + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/resize.js b/share/web_surfaces/builtin/mixer/toolkit/modules/resize.js new file mode 100644 index 0000000000..86c5470311 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/resize.js @@ -0,0 +1,136 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function dragstart(e, drag) { + var O = this.options; + if (!O.active) return; + var E = O.node; + this._xstart = e.pageX; + this._ystart = e.pageY; + this._xsize = E.offsetWidth; + this._ysize = E.offsetHeight; + this._xpos = E.offsetLeft; + this._ypos = E.offsetTop; + /** + * Is fired when resizing starts. + * + * @event TK.Resize#resizestart + * + * @param {DOMEvent} event - The native DOM event. + */ + this.fire_event("resizestart", e); +} +function dragend(e, drag) { + if (!this.options.active) return; + /** + * Is fired when resizing stops. + * + * @event TK.Resize#resizestop + * + * @param {DOMEvent} event - The native DOM event. + */ + this.fire_event("resizestop", e); +} +function dragging(e, drag) { + var O = this.options; + if (!O.active) return; + var w = this._xsize + e.pageX - this._xstart; + var h = this._ysize + e.pageY - this._ystart; + if (O.min.x >= -1) w = Math.max(O.min.x, w); + if (O.max.x >= -1) w = Math.min(O.max.x, w); + if (O.min.y >= -1) h = Math.max(O.min.y, h); + if (O.max.y >= -1) h = Math.min(O.max.y, h); + O.node.style.width = w + "px"; + O.node.style.height = h + "px"; + + /** + * Is fired when resizing is in progress. + * + * @event TK.Resize#resizing + * + * @param {DOMEvent} event - The native DOM event. + * @param {int} width - The new width of the element. + * @param {int} height - The new height of the element. + */ + this.fire_event("resizing", e, w, h); +} +function set_handle() { + var h = this.options.handle; + if (this.drag) + this.drag.destroy(); + var range = new TK.Range({}); + this.drag = new TK.DragValue(this, { node: h, + range: function () { return range; }, + onStartdrag : dragstart.bind(this), + onStopdrag : dragend.bind(this), + onDragging : dragging.bind(this) + }); +} +/** + * TK.Resize allows resizing of elements. It does that by continuously resizing an + * element while the user drags a handle. + * + * @class TK.Resize + * + * @extends TK.Base + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {HTMLElement} options.node - The element to resize. + * @property {HTMLElement} [options.handle] - A DOM node used as handle. + * If none set the element is used. + * @property {Boolean} [active=true] - Set to false to disable resizing. + * @property {Object} [options.min={x: -1, y: -1}] - Object containing x + * and y determining minimum size. A value of -1 means no minimum. + * @property {Object} [options.max={x: -1, y: -1}] - Object containing x + * and y determining maximum size. A value of -1 means no maximum. + */ +TK.Resize = TK.class({ + // TK.Resize enables resizing of elements on the screen. + _class: "Resize", + Extends: TK.Base, + _options: { + handle : "object", + active : "boolean", + min : "object", + max : "object", + node : "object" + }, + options: { + node : null, + handle : null, + active : true, + min : {x: -1, y: -1}, + max : {x: -1, y: -1} + }, + initialize: function (options) { + TK.Base.prototype.initialize.call(this, options); + this.set("handle", this.options.handle); + }, + // GETTERS & SETTERS + set: function (key, value) { + if (key === "handle") { + if (!value) value = this.options.node; + set_handle.call(this); + } + TK.Base.prototype.set.call(this, key, value); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/responsehandle.js b/share/web_surfaces/builtin/mixer/toolkit/modules/responsehandle.js new file mode 100644 index 0000000000..ea37eaf791 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/responsehandle.js @@ -0,0 +1,1354 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +"use strict"; +(function(w, TK){ +var MODES = [ + "circular", + "line-horizontal", + "line-vertical", + "block-top", + "block-bottom", + "block-left", + "block-right", + "block", +]; +function normalize(v) { + var n = Math.sqrt(v[0]*v[0] + v[1]*v[1]); + v[0] /= n; + v[1] /= n; +} +function scrollwheel(e) { + var direction; + e.preventDefault(); + var d = e.wheelDelta !== void(0) && e.wheelDelta ? e.wheelDelta : e.detail; + if (d > 0) { + direction = 1; + } else if (d < 0) { + direction = -1; + } else return; + + if (this.__sto) window.clearTimeout(this.__sto); + this.set("dragging", true); + TK.add_class(this.element, "toolkit-active"); + this.__sto = window.setTimeout(function () { + this.set("dragging", false); + TK.remove_class(this.element, "toolkit-active"); + this.fire_event("zchangeended", this.options.z); + }.bind(this), 250); + var s = this.range_z.get("step") * direction; + if (e.ctrlKey && e.shiftKey) + s *= this.range_z.get("shift_down"); + else if (e.shiftKey) + s *= this.range_z.get("shift_up"); + this.userset("z", this.get("z") + s); + if (!this._zwheel) + this.fire_event("zchangestarted", this.options.z); + this._zwheel = true; +} + +/* The following functions turn positioning options + * into somethine we can calculate with */ + +function ROT(a) { + return [ +Math.sin(+a), +Math.cos(+a) ]; +} + +var ZHANDLE_POSITION_circular = { + "top": ROT(Math.PI), + "center": [1e-10, 1e-10], + "top-right": ROT(Math.PI*3/4), + "right": ROT(Math.PI/2), + "bottom-right": ROT(Math.PI/4), + "bottom": ROT(0), + "bottom-left": ROT(Math.PI*7/4), + "left": ROT(Math.PI*3/2), + "top-left": ROT(Math.PI*5/4), +}; + +function get_zhandle_position_movable(O, X) { + var vec = ZHANDLE_POSITION_circular[O.z_handle]; + var x = (X[0]+X[2])/2; + var y = (X[1]+X[3])/2; + var R = (X[2] - X[0] - O.z_handle_size)/2; + + return [ + x + R*vec[0], + y + R*vec[1] + ]; +} + +var Z_HANDLE_SIZE_corner = [ 1, 1, 0, 0 ]; +var Z_HANDLE_SIZE_horiz = [ 1, 0, 0, 1 ]; +var Z_HANDLE_SIZE_vert = [ 0, 1, 1, 0 ]; + +function Z_HANDLE_SIZE(pos) { + switch (pos) { + default: + TK.warn("Unsupported z_handle position:", pos); + case "top-right": + case "bottom-right": + case "bottom-left": + case "top-left": + case "center": + return Z_HANDLE_SIZE_corner; + case "top": + case "bottom": + return Z_HANDLE_SIZE_vert; + case "left": + case "right": + return Z_HANDLE_SIZE_horiz; + } +}; + +function get_zhandle_size(O, X) { + var vec = Z_HANDLE_SIZE(O.z_handle); + var z_handle_size = O.z_handle_size; + var z_handle_centered = O.z_handle_centered; + var width = X[2] - X[0]; + var height = X[3] - X[1]; + + if (z_handle_centered < 1) { + width *= z_handle_centered; + height *= z_handle_centered; + } else { + width = z_handle_centered; + height = z_handle_centered; + } + + width = vec[0] * z_handle_size + vec[2] * width; + height = vec[1] * z_handle_size + vec[3] * height; + + if (width < z_handle_size) width = z_handle_size; + if (height < z_handle_size) height = z_handle_size; + + return [width, height]; +} + +var Z_HANDLE_POS = { + "top": [ 0, -1 ], + "top-right": [ 1, -1 ], + "right": [ 1, 0 ], + "bottom-right": [ 1, 1 ], + "bottom": [ 0, 1 ], + "bottom-left": [ -1, 1 ], + "left": [ -1, 0 ], + "top-left": [ -1, -1 ], + "center": [ 0, 0 ], +}; + +function get_zhandle_position(O, X, zhandle_size) { + var x = +(+X[0]+X[2]-+zhandle_size[0])/2; + var y = +(+X[1]+X[3]-+zhandle_size[1])/2; + var width = +X[2] - +X[0]; + var height = +X[3] - +X[1]; + var vec = Z_HANDLE_POS[O.z_handle] || Z_HANDLE_POS["top-right"]; + + x += +vec[0] * +(width - +zhandle_size[0])/2; + y += +vec[1] * +(height - +zhandle_size[1])/2; + + return [x, y]; +} + +function mode_to_handle(mode) { + if (mode === "block-left" || mode === "block-right" || + mode === "block-top" || mode === "block-bottom") + return "block"; + return mode; +} + +var LABEL_ALIGN = { + "line-vertical": { + "top": "middle", + "bottom": "middle", + "left": "end", + "top-left": "end", + "bottom-left":"end", + "right": "start", + "top-right":"start", + "bottom-right":"start", + "center" : "middle", + }, + "line-horizontal": { + "top": "middle", + "bottom": "middle", + "left": "start", + "top-left": "start", + "bottom-left":"start", + "right": "end", + "top-right":"end", + "bottom-right":"end", + "center" : "middle", + }, + "circular": { + "top": "middle", + "bottom": "middle", + "left": "end", + "top-left": "start", + "bottom-left":"start", + "right": "start", + "top-right":"end", + "bottom-right":"end", + "center" : "middle", + }, + "block": { + "top": "middle", + "bottom": "middle", + "left": "start", + "top-left": "start", + "bottom-left":"start", + "right": "end", + "top-right":"end", + "bottom-right":"end", + "center" : "middle", + } +} + +function get_label_align(O, pos) { + return LABEL_ALIGN[mode_to_handle(O.mode)][pos]; +} + +/* The following arrays contain multipliers, alternating x and y, starting with x. + * The first pair is a multiplier for the handle width and height + * The second pair is a multiplier for the label size + * The third pair is a multiplier for the margin +*/ + +var LABEL_POSITION = { + "line-vertical": { + top: [ 0, -1, 0, 0, 0, 1 ], + right: [ 1, 0, 0, -1/2, 1, 0 ], + left: [ -1, 0, 0, -1/2, -1, 0 ], + bottom: [ 0, 1, 0, -1, 0, -1 ], + "bottom-left": [ -1, 1, 0, -1, -1, -1 ], + "bottom-right": [ 1, 1, 0, -1, 1, -1 ], + "top-right": [ 1, -1, 0, 0, 0, 1 ], + "top-left": [ -1, -1, 0, 0, -1, 1 ], + center: [ 0, 0, 0, -1/2, 0, 0 ], + }, + "line-horizontal": { + top: [ 0, -1, 0, -1, 0, -1 ], + right: [ 1, 0, 0, -1/2, 1, 0 ], + left: [ -1, 0, 0, -1/2, -1, 0 ], + bottom: [ 0, 1, 0, 0, 0, 1 ], + "bottom-left": [ -1, 1, 0, 0, 1, 1 ], + "bottom-right": [ 1, 1, 0, 0, -1, 1 ], + "top-right": [ 1, -1, 0, -1, -1, -1 ], + "top-left": [ -1, -1, 0, -1, 1, -1 ], + center: [ 0, 0, 0, -1/2, 0, 0 ], + }, + "circular": { + top: [ 0, -1, 0, -1, 0, -1 ], + right: [ 1, 0, 0, -1/2, 1, 0 ], + left: [ -1, 0, 0, -1/2, -1, 0 ], + bottom: [ 0, 1, 0, 0, 0, 1 ], + "bottom-left": [ -1, 1, 0, 0, 0, 1 ], + "bottom-right": [ 1, 1, 0, 0, 0, 1 ], + "top-right": [ 1, -1, 0, -1, 0, -1 ], + "top-left": [ -1, -1, 0, -1, 0, -1 ], + center: [ 0, 0, 0, -1/2, 0, 0 ], + }, + "block": { + top: [ 0, -1, 0, 0, 0, 1 ], + bottom: [ 0, 1, 0, -1, 0, -1 ], + right: [ 1, 0, 0, -1/2, -1, 0 ], + left: [ -1, 0, 0, -1/2, 1, 0 ], + "bottom-left": [ -1, 1, 0, -1, 1, -1 ], + "bottom-right": [ 1, 1, 0, -1, -1, -1 ], + "top-right": [ 1, -1, 0, 0, -1, 1 ], + "top-left": [ -1, -1, 0, 0, 1, 1 ], + center: [ 0, 0, 0, -1/2, 0, 0 ], + } +} + +function get_label_position(O, X, pos, label_size) { + /* X: array containing [X0, Y0, X1, Y1] of the handle + * pos: string describing the position of the label ("top", "bottom-right", ...) + * label_size: array containing width and height of the label + */ + var m = O.margin; + + // Pivot (x, y) is the center of the handle. + var x = (X[0]+X[2])/2; + var y = (X[1]+X[3])/2; + + // Size of handle + var width = +X[2]-+X[0]; + var height = +X[3]-+X[1]; + + // multipliers + var vec = LABEL_POSITION[mode_to_handle(O.mode)][pos]; + + x += vec[0] * width/2 + vec[2] * label_size[0] + vec[4] * m; + y += vec[1] * height/2 + vec[3] * label_size[1] + vec[5] * m; + + // result is [x, y] of the "real" label position. Please note that + // the final x position depends on the LABEL_ALIGN value for pos. + // Y value is the top border of the overall label. + return [x,y]; +} + +function remove_zhandle() { + var E = this._zhandle; + if (!E) return; + this._zhandle = null; + if (this.z_drag.get("node") === E) + this.z_drag.set("node", null); + + E.remove(); +} + +function create_zhandle() { + var E; + var O = this.options; + + if (this._zhandle) remove_zhandle.call(this); + + E = TK.make_svg( + O.mode === "circular" ? "circle" : "rect", { + "class": "toolkit-z-handle", + } + ); + + this._zhandle = E; + if (this.z_drag.get("node") !== document) + this.z_drag.set("node", E); +} + +function create_line1() { + if (this._line1) remove_line1.call(this); + this._line1 = TK.make_svg("path", { + "class": "toolkit-line toolkit-line-1" + }); +} +function create_line2() { + if (this._line2) remove_line2.call(this); + this._line2 = TK.make_svg("path", { + "class": "toolkit-line toolkit-line-2" + }); +} +function remove_line1() { + if (!this._line1) return; + this._line1.remove(); + this._line1 = null; +} +function remove_line2() { + if (!this._line2) return; + this._line2.remove(); + this._line2 = null; +} + +/* Prints a line, making sure that an offset of 0.5 px aligns them on + * pixel boundaries */ +var format_line = TK.FORMAT("M %.0f.5 %.0f.5 L %.0f.5 %.0f.5"); + +/* calculates the actual label positions based on given alignment + * and dimensions */ +function get_label_dimensions(align, X, label_size) { + switch (align) { + case "start": + return [ X[0], X[1], X[0]+label_size[0], X[1]+label_size[1] ]; + case "middle": + return [ X[0]-label_size[0]/2, X[1], X[0]+label_size[0]/2, X[1]+label_size[1] ]; + case "end": + return [ X[0]-label_size[0], X[1], X[0], X[1]+label_size[1] ]; + } +} + +function redraw_handle(O, X) { + var _handle = this._handle; + + if (!O.show_handle) { + if (_handle) remove_handle.call(this); + return; + } + + var range_x = this.range_x; + var range_y = this.range_y; + var range_z = this.range_z; + + if (!range_x.options.basis || !range_y.options.basis) return; + + var x = range_x.val2px(O.x); + var y = range_y.val2px(O.y); + var z = range_z.val2coef(O.z); + + var tmp; + + if (O.mode === "circular") { + tmp = Math.max(O.min_size, z * O.max_size)/2; + X[0] = x-tmp; + X[1] = y-tmp; + X[2] = x+tmp; + X[3] = y+tmp; + + _handle.setAttribute("r", tmp.toFixed(2)); + _handle.setAttribute("cx", x.toFixed(2)); + _handle.setAttribute("cy", y.toFixed(2)); + } else if (O.mode === "block") { + tmp = Math.max(O.min_size, z)/2; + X[0] = x-tmp; + X[1] = y-tmp; + X[2] = x+tmp; + X[3] = y+tmp; + + _handle.setAttribute("x", Math.round(+X[0]).toFixed(0)); + _handle.setAttribute("y", Math.round(+X[1]).toFixed(0)); + _handle.setAttribute("width", Math.round(+X[2]-X[0]).toFixed(0)); + _handle.setAttribute("height", Math.round(+X[3]-X[1]).toFixed(0)); + } else { + var x_min = O.x_min !== false ? range_x.val2px(range_x.snap(O.x_min)) : 0; + var x_max = O.x_max !== false ? range_x.val2px(range_x.snap(O.x_max)) : range_x.options.basis; + + if (x_min > x_max) { + tmp = x_min; + x_min = x_max; + x_max = tmp; + } + + var y_min = O.y_min !== false ? range_y.val2px(range_y.snap(O.y_min)) : 0; + var y_max = O.y_max !== false ? range_y.val2px(range_y.snap(O.y_max)) : range_y.options.basis; + + if (y_min > y_max) { + tmp = y_min; + y_min = y_max; + y_max = tmp; + } + + tmp = O.min_size / 2; + + /* All other modes are drawn as rectangles */ + switch (O.mode) { + case "line-vertical": + tmp = Math.max(tmp, z * O.max_size/2); + X[0] = x-tmp; + X[1] = y_min; + X[2] = x+tmp; + X[3] = y_max; + break; + case "line-horizontal": + // line horizontal + tmp = Math.max(tmp, z * O.max_size/2); + X[0] = x_min; + X[1] = y - tmp; + X[2] = x_max; + X[3] = y + tmp; + break; + case "block-left": + // rect lefthand + X[0] = 0; + X[1] = y_min; + X[2] = Math.max(x, tmp); + X[3] = y_max; + break; + case "block-right": + // rect righthand + X[0] = x; + X[1] = y_min; + X[2] = range_x.options.basis; + X[3] = y_max; + if (X[2] - X[0] < tmp) X[0] = X[2] - tmp; + break; + case "block-top": + // rect top + X[0] = x_min; + X[1] = 0; + X[2] = x_max; + X[3] = Math.max(y, tmp); + break; + case "block-bottom": + // rect bottom + X[0] = x_min; + X[1] = y; + X[2] = x_max; + X[3] = range_y.options.basis; + if (X[3] - X[1] < tmp) X[1] = X[3] - tmp; + break; + default: + TK.warn("Unsupported mode:", O.mode); + } + + /* Draw the rectangle */ + _handle.setAttribute("x", Math.round(+X[0]).toFixed(0)); + _handle.setAttribute("y", Math.round(+X[1]).toFixed(0)); + _handle.setAttribute("width", Math.round(+X[2]-X[0]).toFixed(0)); + _handle.setAttribute("height", Math.round(+X[3]-X[1]).toFixed(0)); + } +} + +function redraw_zhandle(O, X) { + var vec; + var size; + var zhandle = this._zhandle; + + if (!O.show_handle || O.z_handle === false) { + if (zhandle) remove_zhandle.call(this); + return; + } + + if (!zhandle.parentNode) + this.element.appendChild(zhandle); + + if (this._handle && O.z_handle_below) + this.element.appendChild(this._handle); + + if (O.mode === "circular") { + /* + * position the z_handle on the circle. + */ + vec = get_zhandle_position_movable(O, X); + /* width and height are equal here */ + zhandle.setAttribute("cx", vec[0].toFixed(1)); + zhandle.setAttribute("cy", vec[1].toFixed(1)); + zhandle.setAttribute("r", (O.z_handle_size / 2).toFixed(1)); + + this.zhandle_position = vec; + } else if (O.mode === "block") { + /* + * position the z_handle on the box. + */ + vec = get_zhandle_position_movable(O, X); + size = O.z_handle_size / 2; + /* width and height are equal here */ + zhandle.setAttribute("x", vec[0].toFixed(0) - size); + zhandle.setAttribute("y", vec[1].toFixed(0) - size); + zhandle.setAttribute("width", O.z_handle_size); + zhandle.setAttribute("height", O.z_handle_size); + + this.zhandle_position = vec; + } else { + // all other handle types (lines/blocks) + this.zhandle_position = vec = get_zhandle_size(O, X); + + zhandle.setAttribute("width", vec[0].toFixed(0)); + zhandle.setAttribute("height", vec[1].toFixed(0)); + + vec = get_zhandle_position(O, X, vec); + + zhandle.setAttribute("x", vec[0].toFixed(0)); + zhandle.setAttribute("y", vec[1].toFixed(0)); + + /* adjust to the center of the zhandle */ + this.zhandle_position[0] /= 2; + this.zhandle_position[1] /= 2; + this.zhandle_position[0] += vec[0]; + this.zhandle_position[1] += vec[1]; + } + + this.zhandle_position[0] -= (X[0]+X[2])/2; + this.zhandle_position[1] -= (X[1]+X[3])/2; + normalize(this.zhandle_position); +} + +function prevent_default(e) { + e.preventDefault(); + return false; +} + +function create_label() { + var E; + this._label = E = TK.make_svg("text", { + "class": "toolkit-label" + }); + TK.add_event_listener(E, "mousewheel", this._scrollwheel); + TK.add_event_listener(E, "DOMMouseScroll", this._scrollwheel); + TK.add_event_listener(E, 'contextmenu', prevent_default); +} + +function remove_label() { + var E = this._label; + this._label = null; + E.remove(); + TK.remove_event_listener(E, "mousewheel", this._scrollwheel); + TK.remove_event_listener(E, "DOMMouseScroll", this._scrollwheel); + TK.remove_event_listener(E, 'contextmenu', prevent_default); + + this.label = [0,0,0,0]; +} + +function STOP() { return false; }; + +function create_handle() { + var O = this.options; + var E; + + if (this._handle) remove_handle.call(this); + + E = TK.make_svg(O.mode === "circular" ? "circle" : "rect", + { class: "toolkit-handle" }); + TK.add_event_listener(E, "mousewheel", this._scrollwheel); + TK.add_event_listener(E, "DOMMouseScroll", this._scrollwheel); + TK.add_event_listener(E, 'selectstart', prevent_default); + TK.add_event_listener(E, 'contextmenu', prevent_default); + this._handle = E; + this.element.appendChild(E); +} + +function remove_handle() { + var E = this._handle; + if (!E) return; + this._handle = null; + E.remove(); + TK.remove_event_listener(E, "mousewheel", this._scrollwheel); + TK.remove_event_listener(E, "DOMMouseScroll", this._scrollwheel); + TK.remove_event_listener(E, "selectstart", prevent_default); + TK.remove_event_listener(E, 'contextmenu', prevent_default); +} + +function redraw_label(O, X) { + if (!O.show_handle || O.label === false) { + if (this._label) remove_label.call(this); + return false; + } + + var a = O.label.call(this, O.title, O.x, O.y, O.z).split("\n"); + var c = this._label.childNodes; + + while (c.length < a.length) { + this._label.appendChild(TK.make_svg("tspan", {dy:"1.0em"})); + } + while (c.length > a.length) { + this._label.removeChild(this._label.lastChild); + } + for (var i = 0; i < a.length; i++) { + TK.set_text(c[i], a[i]); + } + + if (!this._label.parentNode) this.element.appendChild(this._label); + + TK.S.add(function() { + var w = 0; + for (var i = 0; i < a.length; i++) { + w = Math.max(w, c[i].getComputedTextLength()); + } + + var bbox; + + try { + bbox = this._label.getBBox(); + } catch(e) { + /* _label is not in the DOM yet */ + return; + } + + TK.S.add(function() { + var label_size = [ w, bbox.height ]; + + var i; + var pref = O.preferences; + var area = 0; + var label_position; + var text_position; + var text_anchor; + var tmp; + + /* + * Calculate possible positions of the labels and calculate their intersections. Choose + * that position which has the smallest intersection area with all other handles and labels + */ + for (i = 0; i < pref.length; i++) { + + /* get alignment */ + var align = get_label_align(O, pref[i]); + + /* get label position */ + var LX = get_label_position(O, X, pref[i], label_size); + + /* calculate the label bounding box using anchor and dimensions */ + var pos = get_label_dimensions(align, LX, label_size); + + tmp = O.intersect(pos, this); + + /* We require at least one square px smaller intersection + * to avoid flickering label positions */ + if (area === 0 || tmp.intersect + 1 < area) { + area = tmp.intersect; + label_position = pos; + text_position = LX; + text_anchor = align; + + /* there is no intersections, we are done */ + if (area === 0) break; + } + } + + this.label = label_position; + tmp = Math.round(text_position[0]) + "px"; + this._label.setAttribute("x", tmp); + this._label.setAttribute("y", Math.round(text_position[1]) + "px"); + this._label.setAttribute("text-anchor", text_anchor); + var c = this._label.childNodes; + for (var i = 0; i < c.length; i++) + c[i].setAttribute("x", tmp); + + redraw_lines.call(this, O, X); + }.bind(this), 1); + }.bind(this)); + + return true; +} + +function redraw_lines(O, X) { + + if (!O.show_handle) { + if (this._line1) remove_line1.call(this); + if (this._line2) remove_line2.call(this); + return; + } + + var pos = this.label; + var range_x = this.range_x; + var range_y = this.range_y; + var range_z = this.range_z; + + var x = range_x.val2px(O.x); + var y = range_y.val2px(O.y); + var z = range_z.val2px(O.z); + switch (O.mode) { + case "circular": + case "block": + if (O.show_axis) { + this._line1.setAttribute("d", + format_line(((y >= pos[1] && y <= pos[3]) ? Math.max(X[2], pos[2]) : X[2]) + O.margin, y, + range_x.options.basis, y)); + this._line2.setAttribute("d", + format_line(x, ((x >= pos[0] && x <= pos[2]) ? Math.max(X[3], pos[3]) : X[3]) + O.margin, + x, range_y.options.basis)); + } else { + if (this._line1) remove_line1.call(this); + if (this._line2) remove_line2.call(this); + } + break; + case "line-vertical": + case "block-left": + case "block-right": + this._line1.setAttribute("d", format_line(x, X[1], x, X[3])); + if (O.show_axis) { + this._line2.setAttribute("d", format_line(0, y, range_x.options.basis, y)); + } else if (this._line2) { + remove_line2.call(this); + } + break; + case "line-horizontal": + case "block-top": + case "block-bottom": + this._line1.setAttribute("d", format_line(X[0], y, X[2], y)); + if (O.show_axis) { + this._line2.setAttribute("d", format_line(x, 0, x, range_y.options.basis)); + } else if (this._line2) { + remove_line2.call(this); + } + break; + default: + TK.warn("Unsupported mode", pref[i]); + } + + if (this._line1 && !this._line1.parentNode) this.element.appendChild(this._line1); + if (this._line2 && !this._line2.parentNode) this.element.appendChild(this._line2); +} + +function set_main_class(O) { + var E = this.element; + var i; + + for (i = 0; i < MODES.length; i++) TK.remove_class(E, "toolkit-"+MODES[i]); + + TK.remove_class(E, "toolkit-line"); + TK.remove_class(E, "toolkit-block"); + + switch (O.mode) { + case "line-vertical": + case "line-horizontal": + TK.add_class(E, "toolkit-line"); + case "circular": + break; + case "block-left": + case "block-right": + case "block-top": + case "block-bottom": + case "block": + TK.add_class(E, "toolkit-block"); + break; + default: + TK.warn("Unsupported mode:", O.mode); + return; + } + + TK.add_class(E, "toolkit-"+O.mode); +} + +function startdrag() { + this.draw_once(function() { + var e = this.element; + var p = e.parentNode; + TK.add_class(e, "toolkit-active"); + this.set("dragging", true); + + /* TODO: move this into the parent */ + TK.add_class(this.parent.element, "toolkit-dragging"); + + this.global_cursor("move"); + + if (p.lastChild !== e) + p.appendChild(e); + }); +} + +function enddrag() { + this.draw_once(function() { + var e = this.element; + TK.remove_class(e, "toolkit-active"); + this.set("dragging", false); + + /* TODO: move this into the parent */ + TK.remove_class(this.parent.element, "toolkit-dragging"); + + this.remove_cursor("move"); + }); +} + +/** + * Class which represents a draggable SVG element, which can be used to represent and change + * a value inside of a {@link TK.ResponseHandler} and is drawn inside of a chart. + * + * @class TK.ResponseHandle + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Function|Object} options.range_x - Callback returning a {@link TK.Range} + * for the x-axis or an object with options for a {@link TK.Range}. This is usually + * the x_range of the parent chart. + * @property {Function|Object} options.range_y - Callback returning a {@link TK.Range} + * for the y-axis or an object with options for a {@link TK.Range}. This is usually + * the y_range of the parent chart. + * @property {Function|Object} options.range_z - Callback returning a {@link TK.Range} + * for the z-axis or an object with options for a {@link TK.Range}. + * @property {String} [options.mode="circular"] - Type of the handle. Can be one out of + * circular, line-vertical, line-horizontal, + * block-left, block-right, block-top or + * block-right. + * @property {Number} [options.x] - Value of the x-coordinate. + * @property {Number} [options.y] - Value of the y-coordinate. + * @property {Number} [options.z] - Value of the z-coordinate. + * @property {Number} [options.min_size=24] - Minimum size of the handle in px. + * @property {Number} [options.max_size=100] - Maximum size of the handle in px. + * @property {Function|Boolean} options.label - Label formatting function. Arguments are + * title, x, y, z. If set to false, no label is displayed. + * @property {Array} [options.preferences=["left", "top", "right", "bottom"]] - Possible label + * positions by order of preference. Depending on the selected mode this can + * be a subset of top, top-right, right, + * bottom-right, bottom, bottom-left, + * left, top-left and center. + * @property {Number} [options.margin=3] - Margin in px between the handle and the label. + * @property {Boolean|String} [options.z_handle=false] - If not false, a small handle is drawn at the given position (`top`, `top-left`, `top-right`, `left`, `center`, `right`, `bottom-left`, `bottom`, `bottom-right`), which can + * be dragged to change the value of the z-coordinate. + * @property {Number} [options.z_handle_size=6] - Size in px of the z-handle. + * @property {Number} [options.z_handle_centered=0.1] - Size of the z-handle in center positions. + * If this options is between 0 and 1, it is interpreted as a ratio, otherwise as a px size. + * @property {Number} [options.z_handle_below=false] - Render the z-handle below the normal handle in the DOM. SVG doesn't know CSS attribute z-index, so this workaround is needed from time to time. + * @property {Number} [options.x_min] - Minimum value of the x-coordinate. + * @property {Number} [options.x_max] - Maximum value of the x-coordinate. + * @property {Number} [options.y_min] - Minimum value of the y-coordinate. + * @property {Number} [options.y_max] - Maximum value of the y-coordinate. + * @property {Number} [options.z_min] - Minimum value of the z-coordinate. + * @property {Number} [options.z_max] - Maximum value of the z-coordinate. + * @property {Boolean} [options.show_axis=false] - If set to true, additional lines are drawn at the coordinate values. + * + * @mixes TK.Ranges + * @mixes TK.Warning + * @mixes TK.GlobalCursor + */ + +/** + * @member {SVGText} TK.ResponseHandle#_label - The label. Has class toolkit-label. + */ +/** + * @member {SVGPath} TK.ResponseHandle#_line1 - The first line. Has class toolkit-line toolkit-line-1. + */ +/** + * @member {SVGPath} TK.ResponseHandle#_line2 - The second line. Has class toolkit-line toolkit-line-2. + */ + +function set_min(value, key) { + var name = key.substr(0, 1); + var O = this.options; + if (value !== false && O[name] < value) this.set(name, value); +} + +function set_max(value, key) { + var name = key.substr(0, 1); + var O = this.options; + if (value !== false && O[name] > value) this.set(name, value); +} + +/** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the options x, y and z. + * + * @event TK.ResponseHandle#useraction + * + * @param {string} name - The name of the option which was changed due to the users action. + * @param {mixed} value - The new value of the option. + */ +TK.ResponseHandle = TK.class({ + _class: "ResponseHandle", + Extends: TK.Widget, + Implements: [TK.GlobalCursor, TK.Ranges, TK.Warning], + _options: Object.assign(Object.create(TK.Widget.prototype._options), TK.Ranges.prototype._options, { + range_x: "mixed", + range_y: "mixed", + range_z: "mixed", + intersect: "function", + mode: "string", + preferences: "array", + label: "function|boolean", + x: "number", + y: "number", + z: "number", + min_size: "number", + max_size: "number", + margin: "number", + z_handle: "boolean|string", + z_handle_size: "number", + z_handle_centered: "number", + z_handle_below: "boolean", + min_drag: "number", + x_min: "number", + x_max: "number", + y_min: "number", + y_max: "number", + z_min: "number", + z_max: "number", + active: "boolean", + show_axis: "boolean", + title: "string", + hover: "boolean", + dragging: "boolean", + show_handle: "boolean" + }), + options: { + range_x: {}, + range_y: {}, + range_z: {}, + intersect: function () { return { intersect: 0, count: 0 } }, + // NOTE: this is currently not a public API + // callback function for checking intersections: function (x1, y1, x2, y2, id) {} + // returns a value describing the amount of intersection with other handle elements. + // intersections are weighted depending on the intersecting object. E.g. SVG borders have + // a very high impact while intersecting in comparison with overlapping handle objects + // that have a low impact on intersection + mode: "circular", + preferences: ["left", "top", "right", "bottom"], + label: TK.FORMAT("%s\n%d Hz\n%.2f dB\nQ: %.2f"), + x: 0, + y: 0, + z: 0, + min_size: 24, + max_size: 100, + margin: 3, + z_handle: false, + z_handle_size: 6, + z_handle_centered:0.1, + z_handle_below: false, + min_drag: 0, + // NOTE: not yet a public API + // amount of pixels the handle has to be dragged before it starts to move + x_min: false, + x_max: false, + y_min: false, + y_max: false, + z_min: false, + z_max: false, + active: true, + show_axis: false, + hover: false, + dragging: false, + show_handle: true + }, + static_events: { + set_show_axis: function(value) { + var O = this.options; + if (O.mode === "circular") create_line1.call(this); + create_line2.call(this); + }, + set_label: function(value) { + if (value !== false && !this._label) create_label.call(this); + }, + set_show_handle: function(value) { + this.set("mode", this.options.mode); + this.set("show_axis", this.options.show_axis); + this.set("label", this.options.label); + }, + set_mode: function(value) { + var O = this.options; + if (!O.show_handle) return; + create_handle.call(this); + if (O.z_handle !== false) create_zhandle.call(this); + if (value !== "circular") create_line1.call(this); + }, + set_x_min: set_min, + set_y_min: set_min, + set_z_min: set_min, + set_x_max: set_max, + set_y_max: set_max, + set_z_max: set_max, + mouseenter: function() { + this.set("hover", true); + }, + mouseleave: function() { + this.set("hover", false); + }, + set_active: function(v) { + if (!v) { + this.pos_drag.cancel_drag(); + this.z_drag.cancel_drag(); + } + }, + }, + + initialize: function (options) { + this.label = [0,0,0,0]; + this.handle = [0,0,0,0]; + this._zwheel = false; + this.__sto = 0; + TK.Widget.prototype.initialize.call(this, options); + var O = this.options; + + /** + * @member {TK.Range} TK.ResponseHandle#range_x - The {@link TK.Range} for the x axis. + */ + /** + * @member {TK.Range} TK.ResponseHandle#range_y - The {@link TK.Range} for the y axis. + */ + /** + * @member {TK.Range} TK.ResponseHandle#range_z - The {@link TK.Range} for the z axis. + */ + this.add_range(O.range_x, "range_x"); + this.add_range(O.range_y, "range_y"); + this.add_range(O.range_z, "range_z"); + + var set_cb = function() { + this.invalid.x = true; + this.trigger_draw(); + }.bind(this); + + this.range_x.add_event("set", set_cb); + this.range_y.add_event("set", set_cb); + this.range_z.add_event("set", set_cb); + + var E = TK.make_svg("g"); + + /** + * @member {SVGGroup} TK.ResponseHandle#element - The main SVG group containing all handle elements. Has class toolkit-response-handle. + */ + this.element = E; + + this.widgetize(E, true, true); + + TK.add_class(E, "toolkit-response-handle"); + /** + * @member {SVGCircular} TK.ResponseHandle#_handle - The main handle. + * Has class toolkit-handle. + */ + + /** + * @member {SVGCircular} TK.ResponseHandle#_zhandle - The handle for manipulating z axis. + * Has class toolkit-z-handle. + */ + + this._scrollwheel = scrollwheel.bind(this); + + this._handle = this._zhandle = this._line1 = this._line2 = this._label = null; + + this.z_drag = new TK.DragCapture(this, { + node: null, + onstartcapture: function(state) { + var self = this.parent; + var O = self.options; + if (!O.active) return; + state.z = self.range_z.val2px(O.z); + + /* the main handle is active, + * this is a z gesture */ + var pstate = self.pos_drag.state(); + if (pstate) { + var d; + var v = [ state.current.clientX - pstate.prev.clientX, + state.current.clientY - pstate.prev.clientY ]; + normalize(v); + state.vector = v; + } else { + state.vector = self.zhandle_position; + } + /** + * Is fired when the user grabs the z-handle. The argument is the + * actual z value. + * + * @event TK.ResponseHandle#zchangestarted + * + * @param {number} z - The z value. + */ + self.fire_event("zchangestarted", O.z); + startdrag.call(self); + return true; + }, + onmovecapture: function(state) { + var self = this.parent; + var O = self.options; + + var zv = state.vector; + var v = state.vdistance(); + + var d = zv[0] * v[0] + zv[1] * v[1]; + + /* ignore small movements */ + if (O.min_drag > 0 && O.min_drag > d) return; + + var range_z = self.range_z; + var z = range_z.px2val(state.z + d); + + self.userset("z", z); + }, + onstopcapture: function() { + var self = this.parent; + /** + * Is fired when the user releases the z-handle. The argument is the + * actual z value. + * + * @event TK.ResponseHandle#zchangeended + * + * @param {number} z - The z value. + */ + self.fire_event("zchangeended", self.options.z); + enddrag.call(self); + }, + }); + this.pos_drag = new TK.DragCapture(this, { + node: this.element, + onstartcapture: function(state) { + var self = this.parent; + var O = self.options; + if (!O.active) return; + + var button = state.current.button; + var E = self.element; + var p = E.parentNode; + var ev = state.current; + + self.z_drag.set("node", document); + + /* right click triggers move to the back */ + if (ev.button === 2) { + if (E !== p.firstChild) + self.draw_once(function() { + var e = this.element; + var p = e.parentNode; + if (p && e !== p.firstChild) p.insertBefore(e, p.firstChild); + }); + /* cancel everything else, but do not drag */ + ev.preventDefault(); + ev.stopPropagation(); + return false; + } + + state.x = self.range_x.val2px(O.x); + state.y = self.range_y.val2px(O.y); + /** + * Is fired when the main handle is grabbed by the user. + * The argument is an object with the following members: + *
    + *
  • x: the actual value on the x axis
  • + *
  • y: the actual value on the y axis
  • + *
  • pos_x: the position in pixels on the x axis
  • + *
  • pos_y: the position in pixels on the y axis
  • + *
+ * + * @event TK.ResponseHandle#handlegrabbed + * + * @param {Object} positions - An object containing all relevant positions of the pointer. + */ + self.fire_event("handlegrabbed", { + x: O.x, + y: O.y, + pos_x: state.x, + pos_y: state.y + }); + startdrag.call(self); + return true; + }, + onmovecapture: function(state) { + var self = this.parent; + var O = self.options; + + /* ignore small movements */ + if (O.min_drag > 0 && O.min_drag > state.distance()) return; + + /* we are changing z right now using a gesture, irgnore this movement */ + if (self.z_drag.dragging()) return; + + var v = state.vdistance(); + var range_x = self.range_x; + var range_y = self.range_y; + var x = range_x.px2val(state.x + v[0]); + var y = range_y.px2val(state.y + v[1]); + + self.userset("x", x); + self.userset("y", y); + }, + onstopcapture: function() { + /** + * Is fired when the user releases the main handle. + * The argument is an object with the following members: + *
    + *
  • x: the actual value on the x axis
  • + *
  • y: the actual value on the y axis
  • + *
  • pos_x: the position in pixels on the x axis
  • + *
  • pos_y: the position in pixels on the y axis
  • + *
+ * + * @event TK.ResponseHandle#handlereleased + * + * @param {Object} positions - An object containing all relevant positions of the pointer. + */ + var self = this.parent; + var O = self.options; + self.fire_event("handlereleased", { + x: O.x, + y: O.y, + pos_x: self.range_x.val2px(O.x), + pos_y: self.range_y.val2px(O.y), + }); + enddrag.call(self); + self.z_drag.set("node", self._zhandle); + }, + }); + + this.set("mode", O.mode); + this.set("show_handle", O.show_handle); + this.set("show_axis", O.show_axis); + this.set("active", O.active); + this.set("x", O.x); + this.set("y", O.y); + this.set("z", O.z); + this.set("z_handle", O.z_handle); + this.set("label", O.label); + + }, + + redraw: function () { + TK.Widget.prototype.redraw.call(this); + var O = this.options; + var I = this.invalid; + + var range_x = this.range_x; + var range_y = this.range_y; + var range_z = this.range_z; + + /* These are the coordinates of the corners (x1, y1, x2, y2) + * NOTE: x,y are not necessarily in the midde. */ + var X = this.handle; + + if (I.mode) set_main_class.call(this, O); + + if (I.hover) { + I.hover = false; + TK.toggle_class(this.element, "toolkit-hover", O.hover); + } + if (I.dragging) { + I.dragging = false; + TK.toggle_class(this.element, "toolkit-dragging", O.dragging); + } + + if (I.active || I.disabled) { + I.disabled = false; + // TODO: this is not very nice, we should really use the options + // for that. 1) set "active" from the mouse handlers 2) set disabled instead + // of active + TK.toggle_class(this.element, "toolkit-disabled", !O.active || O.disabled); + } + + var moved = I.validate("x", "y", "z", "mode", "active", "show_handle"); + + if (moved) redraw_handle.call(this, O, X); + + // Z-HANDLE + + if (I.validate("z_handle") || moved) { + redraw_zhandle.call(this, O, X); + } + + var delay_lines; + + // LABEL + if (I.validate("label", "title", "preference") || moved) { + delay_lines = redraw_label.call(this, O, X); + } + + // LINES + if (I.validate("show_axis") || moved) { + if (!delay_lines) redraw_lines.call(this, O, X); + } + }, + set: function(key, value) { + var O = this.options; + + switch (key) { + case "z_handle": + if (value !== false && !ZHANDLE_POSITION_circular[value]) { + TK.warn("Unsupported z_handle option:", value); + value = false; + } + if (value !== false) create_zhandle.call(this); + break; + case "x": + value = this.range_x.snap(value); + if (O.x_min !== false && value < O.x_min) value = O.x_min; + if (O.x_max !== false && value > O.x_max) value = O.x_max; + break; + case "y": + value = this.range_y.snap(value); + if (O.y_min !== false && value < O.y_min) value = O.y_min; + if (O.y_max !== false && value > O.y_max) value = O.y_max; + break; + case "z": + value = this.range_z.snap(value); + if (O.z_min !== false && value < O.z_min) { + value = O.z_min; + this.warning(this.element); + } else if (O.z_max !== false && value > O.z_max) { + value = O.z_max; + this.warning(this.element); + } + break; + } + + return TK.Widget.prototype.set.call(this, key, value); + }, + destroy: function () { + remove_zhandle.call(this); + remove_line1.call(this); + remove_line2.call(this); + remove_label.call(this); + remove_handle.call(this); + TK.Widget.prototype.destroy.call(this); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/scale.js b/share/web_surfaces/builtin/mixer/toolkit/modules/scale.js new file mode 100644 index 0000000000..7b8926079d --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/scale.js @@ -0,0 +1,571 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK) { +function get_base(O) { + return Math.max(Math.min(O.max, O.base), O.min); +} +function vert(O) { + return O.layout === "left" || O.layout === "right"; +} +function fill_interval(range, levels, i, from, to, min_gap, result) { + var level = levels[i]; + var x, j, pos, last_pos, last; + var diff; + + var to_pos = range.val2px(to); + last_pos = range.val2px(from); + + if (Math.abs(to_pos - last_pos) < min_gap) return; + + if (!result) result = { + values: [], + positions: [], + }; + + var values = result.values; + var positions = result.positions; + + if (from > to) level = -level; + last = from; + + for (j = ((to-from)/level)|0, x = from + level; j > 0; x += level, j--) { + pos = range.val2px(x); + diff = Math.abs(last_pos - pos); + if (Math.abs(to_pos - pos) < min_gap) break; + if (diff >= min_gap) { + if (i > 0 && diff >= min_gap * 2) { + // we have a chance to fit some more labels in + fill_interval(range, levels, i-1, + last, x, min_gap, result); + } + values.push(x); + positions.push(pos); + last_pos = pos; + last = x; + } + } + + if (i > 0 && Math.abs(last_pos - to_pos) >= min_gap * 2) { + fill_interval(range, levels, i-1, last, to, min_gap, result); + } + + return result; +} +// remove collisions from a with b given a minimum gap +function remove_collisions(a, b, min_gap, vert) { + var pa = a.positions, pb = b.positions; + var va = a.values; + var dim; + + min_gap = +min_gap; + + if (typeof vert === "boolean") + dim = vert ? b.height : b.width; + + if (!(min_gap > 0)) min_gap = 1; + + if (!pb.length) return a; + + var i, j; + var values = []; + var positions = []; + var pos_a, pos_b; + var size; + + var last_pos = +pb[0], + last_size = min_gap; + + if (dim) last_size += +dim[0] / 2; + + // If pb is just length 1, it does not matter + var direction = pb.length > 1 && pb[1] < last_pos ? -1 : 1; + + for (i = 0, j = 0; i < pa.length && j < pb.length;) { + pos_a = +pa[i]; + pos_b = +pb[j]; + size = min_gap; + + if (dim) size += dim[j] / 2; + + if (Math.abs(pos_a - last_pos) < last_size || + Math.abs(pos_a - pos_b) < size) { + // try next position + i++; + continue; + } + + if (j < pb.length - 1 && (pos_a - pos_b)*direction > 0) { + // we left the current interval, lets try the next one + last_pos = pos_b; + last_size = size; + j++; + continue; + } + + values.push(+va[i]); + positions.push(pos_a); + + i++; + } + + return { + values: values, + positions: positions, + }; +} +function create_dom_nodes(data, create) { + var nodes = []; + var values, positions; + var i; + var E = this.element; + var node; + + data.nodes = nodes; + values = data.values; + positions = data.positions; + + for (i = 0; i < values.length; i++) { + nodes.push(node = create(values[i], positions[i])); + E.appendChild(node); + } +} +function create_label(value, position) { + var O = this.options; + var elem = document.createElement("SPAN"); + elem.className = "toolkit-label"; + + if (vert(O)) { + elem.style.bottom = position.toFixed(1) + "px"; + } else { + elem.style.left = position.toFixed(1) + "px"; + } + + TK.set_content(elem, O.labels(value)); + + if (get_base(O) === value) + TK.add_class(elem, "toolkit-base"); + if (O.max === value) + TK.add_class(elem, "toolkit-max"); + if (O.min === value) + TK.add_class(elem, "toolkit-min"); + + return elem; +} +function create_dot(value, position) { + var O = this.options; + var elem = document.createElement("DIV"); + elem.className = "toolkit-dot"; + + if (O.layout === "left" || O.layout === "right") { + elem.style.bottom = Math.round(position + 0.5) + "px"; + } else { + elem.style.left = Math.round(position - 0.5) + "px"; + } + + if (get_base(O) === value) + TK.add_class(elem, "toolkit-base"); + else if (O.max === value) + TK.add_class(elem, "toolkit-max"); + else if (O.min === value) + TK.add_class(elem, "toolkit-min"); + + return elem; +} +function measure_dimensions(data) { + var nodes = data.nodes; + var width = []; + var height = []; + + for (var i = 0; i < nodes.length; i++) { + width.push(TK.outer_width(nodes[i])); + height.push(TK.outer_height(nodes[i])); + } + + data.width = width; + data.height = height; +} +function handle_end(O, labels, i) { + var node = labels.nodes[i]; + var v = labels.values[i]; + + if (v === O.min) { + TK.add_class(node, "toolkit-min"); + } else if (v === O.max) { + TK.add_class(node, "toolkit-max"); + } else return; +} +function generate_scale(from, to, include_from, show_to) { + var O = this.options; + var labels; + + if (O.show_labels || O.show_markers) + labels = { + values: [], + positions: [], + }; + + var dots = { + values: [], + positions: [], + }; + var is_vert = vert(O); + var tmp; + + if (include_from) { + tmp = this.val2px(from); + + if (labels) { + labels.values.push(from); + labels.positions.push(tmp); + } + + dots.values.push(from); + dots.positions.push(tmp); + } + + var levels = O.levels; + + fill_interval(this, levels, levels.length - 1, from, to, O.gap_dots, dots); + + if (labels) { + if (O.levels_labels) levels = O.levels_labels; + + fill_interval(this, levels, levels.length - 1, from, to, O.gap_labels, labels); + + tmp = this.val2px(to); + + if (show_to || Math.abs(tmp - this.val2px(from)) >= O.gap_labels) { + labels.values.push(to); + labels.positions.push(tmp); + + dots.values.push(to); + dots.positions.push(tmp); + } + } else { + dots.values.push(to); + dots.positions.push(this.val2px(to)); + } + + if (O.show_labels) { + create_dom_nodes.call(this, labels, create_label.bind(this)); + + if (labels.values.length && labels.values[0] === get_base(O)) { + TK.add_class(labels.nodes[0], "toolkit-base"); + } + } + + var render_cb = function() { + var markers; + + if (O.show_markers) { + markers = { + values: labels.values, + positions: labels.positions, + }; + create_dom_nodes.call(this, markers, create_dot.bind(this)); + for (var i = 0; i < markers.nodes.length; i++) + TK.add_class(markers.nodes[i], "toolkit-marker"); + } + + if (O.show_labels && labels.values.length > 1) { + handle_end(O, labels, 0); + handle_end(O, labels, labels.nodes.length-1); + } + + if (O.avoid_collisions && O.show_labels) { + dots = remove_collisions(dots, labels, O.gap_dots, is_vert); + } else if (markers) { + dots = remove_collisions(dots, markers, O.gap_dots); + } + + create_dom_nodes.call(this, dots, create_dot.bind(this)); + }; + + if (O.show_labels && O.avoid_collisions) + TK.S.add(function() { + measure_dimensions(labels); + TK.S.add(render_cb.bind(this), 3); + }.bind(this), 2); + else render_cb.call(this); +} +function mark_markers(labels, dots) { + var i, j; + + var a = labels.values; + var b = dots.values; + var nodes = dots.nodes; + + for (i = j = 0; i < a.length && j < b.length;) { + if (a[i] < b[j]) i++; + else if (a[i] > b[j]) j++; + else { + TK.add_class(nodes[j], "toolkit-marker"); + i++; + j++; + } + } +} +/** + * TK.Scale can be used to draw scales. It is used in {@link TK.MeterBase} and + * {@link TK.Fader}. TK.Scale draws labels and markers based on its parameters + * and the available space. Scales can be drawn both vertically and horizontally. + * Scale mixes in {@link TK.Ranged} and inherits all its options. + * + * @extends TK.Widget + * + * @mixes TK.Ranged + * + * @class TK.Scale + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String} [options.layout="right"] - The layout of the TK.Scale. right and + * left are vertical layouts with the labels being drawn right and left of the scale, + * respectively. top and bottom are horizontal layouts for which the + * labels are drawn on top and below the scale, respectively. + * @property {Integer} [options.division=1] - Minimal step size of the markers. + * @property {Array} [options.levels=[1]] - Array of steps for labels and markers. + * @property {Number} [options.base=false]] - Base of the scale. If set to false it will + * default to the minimum value. + * @property {Function} [options.labels=TK.FORMAT("%.2f")] - Formatting function for the labels. + * @property {Integer} [options.gap_dots=4] - Minimum gap in pixels between two adjacent markers. + * @property {Integer} [options.gap_labels=40] - Minimum gap in pixels between two adjacent labels. + * @property {Boolean} [options.show_labels=true] - If true, labels are drawn. + * @property {Boolean} [options.show_max=true] - If true, display a label and a + * dot for the 'max' value. + * @property {Boolean} [options.show_min=true] - If true, display a label and a + * dot for the 'min' value. + * @property {Boolean} [options.show_base=true] - If true, display a label and a + * dot for the 'base' value. + * @property {Array|Boolean} [options.fixed_dots] - This option can be used to specify fixed positions + * for the markers to be drawn at. The values must be sorted in ascending order. false disables + * fixed markers. + * @property {Array|Boolean} [options.fixed_labels] - This option can be used to specify fixed positions + * for the labels to be drawn at. The values must be sorted in ascending order. false disables + * fixed labels. + * @property {Boolean} [options.show_markers=true] - If true, every dot which is located at the same + * position as a label has the toolkit-marker class set. + * @property {Number|Boolean} [options.pointer=false] - The value to set the pointers position to. Set to `false` to hide the pointer. + * @property {Number|Boolean} [options.bar=false] - The value to set the bars height to. Set to `false` to hide the bar. + */ +TK.Scale = TK.class({ + _class: "Scale", + + Extends: TK.Widget, + Implements: [TK.Ranged], + _options: Object.assign(Object.create(TK.Widget.prototype._options), TK.Ranged.prototype._options, { + layout: "string", + division: "number", + levels: "array", + levels_labels: "array", + base: "number", + labels: "function", + gap_dots: "number", + gap_labels: "number", + show_labels: "boolean", + show_min: "boolean", + show_max: "boolean", + show_base: "boolean", + fixed_dots: "boolean|array", + fixed_labels: "boolean|array", + avoid_collisions: "boolean", + show_markers: "boolean", + bar: "boolean|number", + pointer: "boolean|number", + }), + options: { + layout: "right", + division: 1, + levels: [1], + base: false, + labels: TK.FORMAT("%.2f"), + avoid_collisions: false, + gap_dots: 4, + gap_labels: 40, + show_labels: true, + show_min: true, + show_max: true, + show_base: true, + show_markers: true, + fixed_dots: false, + fixed_labels: false, + bar: false, + pointer: false, + }, + + initialize: function (options) { + var E; + TK.Widget.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Scale#element - The main DIV element. Has class toolkit-scale + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-scale"); + this.element = this.widgetize(E, true, true, true); + }, + + redraw: function () { + TK.Widget.prototype.redraw.call(this); + + var I = this.invalid; + var O = this.options; + var E = this.element; + + if (I.layout) { + I.layout = false; + TK.remove_class(E, "toolkit-vertical", "toolkit-horizontal", "toolkit-top", + "toolkit-bottom", "toolkit-right", "toolkit-left"); + switch (O.layout) { + case "left": + TK.add_class(E, "toolkit-vertical", "toolkit-left"); + break; + case "right": + TK.add_class(E, "toolkit-vertical", "toolkit-right"); + break; + case "top": + TK.add_class(E, "toolkit-horizontal", "toolkit-top"); + break; + case "bottom": + TK.add_class(E, "toolkit-horizontal", "toolkit-bottom"); + break; + default: + TK.warn("Unsupported layout setting:", O.layout); + } + } + + if (I.reverse) { + /* NOTE: reverse will be validated below */ + TK.toggle_class(E, "toolkit-reverse", O.reverse); + } + + if (I.validate("base", "show_base", "gap_labels", "min", "show_min", "division", "max", "show_markers", + "fixed_dots", "fixed_labels", "levels", "basis", "scale", "reverse", "show_labels")) { + TK.empty(E); + + if (O.fixed_dots && O.fixed_labels) { + var labels; + + if (O.show_labels) { + labels = { + values: O.fixed_labels, + positions: O.fixed_labels.map(this.val2px, this), + }; + create_dom_nodes.call(this, labels, create_label.bind(this)); + } + + var dots = { + values: O.fixed_dots, + positions: O.fixed_dots.map(this.val2px, this), + }; + create_dom_nodes.call(this, dots, create_dot.bind(this)); + + if (O.show_markers && labels) { + mark_markers(labels, dots); + } + } else { + var base = get_base(O); + + if (base !== O.max) generate_scale.call(this, base, O.max, true, O.show_max); + if (base !== O.min) generate_scale.call(this, base, O.min, base === O.max, O.show_min); + } + if (this._bar) + this.element.appendChild(this._bar); + if (this._pointer) + this.element.appendChild(this._pointer); + } + }, + + resize: function () { + TK.Widget.prototype.resize.call(this); + var O = this.options; + this.set("basis", vert(O) ? TK.inner_height(this.element) + : TK.inner_width(this.element) ); + }, + + // GETTER & SETTER + set: function (key, value) { + TK.Widget.prototype.set.call(this, key, value); + switch (key) { + case "division": + case "levels": + case "labels": + case "gap_dots": + case "gap_labels": + case "show_labels": + /** + * Gets fired when an option the rendering depends on was changed + * + * @event TK.Scale#scalechanged + * + * @param {string} key - The name of the option which changed the {@link TK.Scale}. + * @param {mixed} value - The value of the option. + */ + this.fire_event("scalechanged", key, value) + break; + } + } +}); + +/** + * @member {HTMLDivElement} TK.Fader#_pointer - The DIV element of the pointer. It can be used to e.g. visualize the value set in the backend. + */ +TK.ChildElement(TK.Scale, "pointer", { + show: false, + toggle_class: true, + option: "pointer", + draw_options: Object.keys(TK.Ranged.prototype._options).concat([ "pointer", "basis" ]), + draw: function(O) { + if (this._pointer) { + var tmp = this.val2px(this.snap(O.pointer)) + "px"; + if (vert(O)) { + if (TK.supports_transform) + this._pointer.style.transform = "translateY(-"+tmp+")"; + else + this._pointer.style.bottom = tmp; + } else { + if (TK.supports_transform) + this._pointer.style.transform = "translateX("+tmp+")"; + else + this._pointer.style.left = tmp; + } + } + }, +}); + +/** + * @member {HTMLDivElement} TK.Fader#_bar - The DIV element of the bar. It can be used to e.g. visualize the value set in the backend or to draw a simple levelmeter. + */ +TK.ChildElement(TK.Scale, "bar", { + show: false, + toggle_class: true, + option: "bar", + draw_options: Object.keys(TK.Ranged.prototype._options).concat([ "bar", "basis" ]), + draw: function(O) { + if (this._bar) { + var tmp = this.val2px(this.snap(O.bar)) + "px"; + if (vert(O)) + this._bar.style.height = tmp; + else + this._bar.style.width = tmp; + } + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/modules/scrollvalue.js b/share/web_surfaces/builtin/mixer/toolkit/modules/scrollvalue.js new file mode 100644 index 0000000000..6bdd58dac7 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/modules/scrollvalue.js @@ -0,0 +1,174 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK) { +function scroll_timeout() { + /** + * Is fired when scrolling ended. + * + * @event TK.ScrollValue#scrollended + */ + fire_event.call(this, "scrollended"); + this._wheel = false; + this.__sto = false; + this.set("scrolling", false); + TK.remove_class(this.options.classes, "toolkit-scrolling"); +} +function scrollwheel(e) { + var O = this.options; + if (!O.active) return; + e.preventDefault(); + var DIR = O.scroll_direction; + var d = e.deltaX * DIR[0] + e.deltaY * DIR[1] + e.deltaZ * DIR[2]; + var direction = d > 0 ? 1 : -1; + var range = O.range.call(this); + var RO = range.options; + + var v; + + // timeout for resetting the class + if (this._wheel) { + v = this._raw_value; + window.clearTimeout(this.__sto); + } else { + this._raw_value = v = O.get.call(this); + TK.add_class(O.classes, "toolkit-scrolling"); + /** + * Is fired when scrolling starts. + * + * @event TK.ScrollValue#scrollstarted + * + * @param {DOMEvent} event - The native DOM event. + */ + fire_event.call(this, "scrollstarted", e); + this._wheel = true; + } + this.__sto = window.setTimeout(scroll_timeout.bind(this), 200); + + // calc step depending on options.step, .shift up and .shift down + var step = (RO.step || 1) * direction; + if (e.ctrlKey || e.altKey) { + step *= RO.shift_down; + } else if (e.shiftKey) { + step *= RO.shift_up; + } + + var pos = range.val2px(v); + + pos += step; + + v = range.px2val(pos); + + if (O.limit) + O.set.call(this, Math.min(RO.max, Math.max(RO.min, v))); + else + O.set.call(this, v); + + /** + * Is fired while scrolling happens. + * + * @event TK.ScrollValue#scrolling + * + * @param {DOMEvent} event - The native DOM event. + */ + fire_event.call(this, "scrolling", e); + + /* do not remember out of range values */ + if (v > RO.min && v < RO.max) + this._raw_value = v; + + return false; +} +function fire_event(title, event) { + var O = this.options; + // fire an event on this drag object and one with more + // information on the draggified element + this.fire_event(title, this, event); + var e = O.events.call(this); + if (e) e.fire_event(title, event, O.get.call(this), O.node, this, O.range.call(this)); +} +/** + * TK.ScrollValue enables the scroll wheel for setting a value of an + * object. For instance, it is used by {@link TK.Knob} to allow turning + * the knob using the scroll wheel. + * + * @class TK.ScrollValue + * + * @extends TK.Module + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {HTMLElement} options.node - The element receiving the scroll event. + * @property {Function} [options.get=function () { return this.parent.options.value; }] - Callback returning the value. + * @property {Function} [options.set=function (v) { return this.parent.userset("value", v); }] - Callback setting the value. + * @property {Function} [options.range=function () { return this.parent; }] - A function returning a {@link TK.Range} instance or options for a new one. + * @property {Function|Boolean} [options.events=false] - A function returning + * an element receiving events or false to fire events on the main element. + * @property {HTMLElement|Boolean} [options.classes=false] - Element receiving + * classes or false to set classes on the main element. + * @property {Boolean} [options.active=true] - Disable the scroll event. + * @property {Array} [options.scroll_direction=[0, -1, 0]] - An array + * containing values for x, y and z defining the direction of scrolling. + * @property {Boolean} [options.limit=false] - Limit the returned value to min and max of the range. + */ +TK.ScrollValue = TK.class({ + _class: "ScrollValue", + Extends: TK.Module, + _options: { + get: "function", + set: "function", + range: "function", + events: "function", + classes: "object|boolean", + node: "object|boolean", + active: "boolean", + scroll_direction: "array", + limit: "boolean", + }, + options: { + range: function () { return this.parent; }, + events: function () { return this.parent; }, + classes: false, + get: function () { return this.parent.options.value; }, + set: function (v) { return this.parent.userset("value", v); }, + active: true, + scroll_direction: [0, -1, 0], + limit: false, + }, + initialize: function (widget, options) { + TK.Module.prototype.initialize.call(this, widget, options); + this._wheel = false; + this._raw_value = 0.0; + this.set("node", this.options.node); + this.set("events", this.options.events); + this.set("classes", this.options.classes); + }, + static_events: { + set_node: function(value) { + this.delegate_events(value); + if (value && !this.options.classes) this.set("classes", value); + }, + wheel: scrollwheel, + }, + set: function (key, value) { + if ((key === "classes") && !value) value = this.options.node; + return TK.Module.prototype.set.call(this, key, value); + } +}) +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/polyfill/raf.js b/share/web_surfaces/builtin/mixer/toolkit/polyfill/raf.js new file mode 100644 index 0000000000..de073ab2c7 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/polyfill/raf.js @@ -0,0 +1,39 @@ +/* This file contains polyfills for some functionality not present in older browsers. */ + +// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ +// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating + +// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel + +// MIT license + +(function() { + var lastTime = 0; + var vendors = ['ms', 'moz', 'webkit', 'o']; + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] + || window[vendors[x]+'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16 - (currTime - lastTime)); + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + + if (!window.cancelAnimationFrame) + window.cancelAnimationFrame = function(id) { + clearTimeout(id); + }; +}()); +if (!String.prototype.startsWith) { + String.prototype.startsWith = function(searchString, position){ + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; +} diff --git a/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.css b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.css new file mode 100644 index 0000000000..2b44cabbfb --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.css @@ -0,0 +1,141 @@ +@font-face { + font-family: "Toolkit"; + font-style: normal; + font-weight: normal; + src: url("Toolkit.ttf") format("truetype"); +} + +.toolkit-icon { + font-family: "Toolkit", sans-serif; + font-size: 24px; + text-align: center; + display: inline-block; + vertical-align: middle; +} + +.toolkit-button.toolkit-icon::before{ + display: table-cell; + vertical-align: middle; + text-align: center; +} + +.toolkit-icon.toolkit-12 { font-size: 12px; } +.toolkit-icon.toolkit-16 { font-size: 16px; } +.toolkit-icon.toolkit-20 { font-size: 20px; } +.toolkit-icon.toolkit-24 { font-size: 24px; } +.toolkit-icon.toolkit-32 { font-size: 32px; } +.toolkit-icon.toolkit-40 { font-size: 40px; } +.toolkit-icon.toolkit-48 { font-size: 48px; } +.toolkit-icon.toolkit-56 { font-size: 56px; } +.toolkit-icon.toolkit-64 { font-size: 64px; } +.toolkit-icon.toolkit-128 { font-size: 128px; } +.toolkit-icon.toolkit-256 { font-size: 256px; } + +.toolkit-icon.warning::before { font-family: 'Toolkit'; content: '1'; } +.toolkit-icon.information::before { font-family: 'Toolkit'; content: '2'; } +.toolkit-icon.info::before { font-family: 'Toolkit'; content: '2'; } +.toolkit-icon.error::before { font-family: 'Toolkit'; content: '3'; } +.toolkit-icon.message::before { font-family: 'Toolkit'; content: '4'; } +.toolkit-icon.close::before { font-family: 'Toolkit'; content: '5'; } +.toolkit-icon.success::before { font-family: 'Toolkit'; content: '6'; } +.toolkit-icon.hourglass::before { font-family: 'Toolkit'; content: '7'; } +.toolkit-icon.loading::before { font-family: 'Toolkit'; content: '7'; } +.toolkit-icon.questionmark::before { font-family: 'Toolkit'; content: '8'; } +.toolkit-icon.screensize::before { font-family: 'Toolkit'; content: '9'; } +.toolkit-icon.windowminimize::before { font-family: 'Toolkit'; content: 'a'; } +.toolkit-icon.winmin::before { font-family: 'Toolkit'; content: 'a'; } +.toolkit-icon.windowclose::before { font-family: 'Toolkit'; content: '0'; } +.toolkit-icon.winclose::before { font-family: 'Toolkit'; content: '0'; } +.toolkit-icon.windowmaximize::before { font-family: 'Toolkit'; content: 'b'; } +.toolkit-icon.winmax::before { font-family: 'Toolkit'; content: 'b'; } +.toolkit-icon.windowmaximizevertical::before { font-family: 'Toolkit'; content: 'c'; } +.toolkit-icon.winmaxvert::before { font-family: 'Toolkit'; content: 'c'; } +.toolkit-icon.windowmaximizehorizontal::before { font-family: 'Toolkit'; content: 'd'; } +.toolkit-icon.winmaxhoriz::before { font-family: 'Toolkit'; content: 'd'; } +.toolkit-icon.windowshrink::before { font-family: 'Toolkit'; content: 'e'; } +.toolkit-icon.winshrink::before { font-family: 'Toolkit'; content: 'e'; } +.toolkit-icon.windowresize::before { font-family: 'Toolkit'; content: 'f'; } +.toolkit-icon.unchecked::before { font-family: 'Toolkit'; content: 'g'; } +.toolkit-icon.checked::before { font-family: 'Toolkit'; content: 'h'; } +.toolkit-icon.speaker::before { font-family: 'Toolkit'; content: 'i'; } +.toolkit-icon.speakeractive::before { font-family: 'Toolkit'; content: 'i'; } +.toolkit-icon.sound::before { font-family: 'Toolkit'; content: 'i'; } +.toolkit-icon.speakermute::before { font-family: 'Toolkit'; content: 'j'; } +.toolkit-icon.mute::before { font-family: 'Toolkit'; content: 'j'; } +.toolkit-icon.gear::before { font-family: 'Toolkit'; content: 'k'; } +.toolkit-icon.cogwheel::before { font-family: 'Toolkit'; content: 'k'; } +.toolkit-icon.cog::before { font-family: 'Toolkit'; content: 'k'; } +.toolkit-icon.power::before { font-family: 'Toolkit'; content: 'l'; } +.toolkit-icon.phase::before { font-family: 'Toolkit'; content: 'm'; } +.toolkit-icon.phaseinvert::before { font-family: 'Toolkit'; content: 'm'; } +.toolkit-icon.arrowdown::before { font-family: 'Toolkit'; content: 'n'; } +.toolkit-icon.arrowup::before { font-family: 'Toolkit'; content: 'o'; } +.toolkit-icon.arrowleft::before { font-family: 'Toolkit'; content: 'p'; } +.toolkit-icon.arrowright::before { font-family: 'Toolkit'; content: 'q'; } +.toolkit-icon.hide::before { font-family: 'Toolkit'; content: 'r'; } +.toolkit-icon.show::before { font-family: 'Toolkit'; content: 's'; } +.toolkit-icon.highpass::before { font-family: 'Toolkit'; content: 't'; } +.toolkit-icon.hipass::before { font-family: 'Toolkit'; content: 't'; } +.toolkit-icon.lowpass::before { font-family: 'Toolkit'; content: 'u'; } +.toolkit-icon.lopass::before { font-family: 'Toolkit'; content: 'u'; } +.toolkit-icon.highshelf::before { font-family: 'Toolkit'; content: 'v'; } +.toolkit-icon.hishelf::before { font-family: 'Toolkit'; content: 'v'; } +.toolkit-icon.lowshelf::before { font-family: 'Toolkit'; content: 'w'; } +.toolkit-icon.loshelf::before { font-family: 'Toolkit'; content: 'w'; } +.toolkit-icon.bandpass::before { font-family: 'Toolkit'; content: 'x'; } +.toolkit-icon.bandreject::before { font-family: 'Toolkit'; content: 'y'; } +.toolkit-icon.notch::before { font-family: 'Toolkit'; content: 'z'; } +.toolkit-icon.parametric::before { font-family: 'Toolkit'; content: 'A'; } +.toolkit-icon.sine::before { font-family: 'Toolkit'; content: 'B'; } +.toolkit-icon.pulse::before { font-family: 'Toolkit'; content: 'C'; } +.toolkit-icon.sawtooth::before { font-family: 'Toolkit'; content: 'D'; } +.toolkit-icon.triangle::before { font-family: 'Toolkit'; content: 'E'; } +.toolkit-icon.bypass::before { font-family: 'Toolkit'; content: 'F'; } +.toolkit-icon.stairs::before { font-family: 'Toolkit'; content: 'G'; } +.toolkit-icon.aliased::before { font-family: 'Toolkit'; content: 'G'; } +.toolkit-icon.stereo::before { font-family: 'Toolkit'; content: 'H'; } +.toolkit-icon.mono::before { font-family: 'Toolkit'; content: 'I'; } +.toolkit-icon.zoom::before { font-family: 'Toolkit'; content: 'J'; } +.toolkit-icon.magnifier::before { font-family: 'Toolkit'; content: 'J'; } +.toolkit-icon.zoomin::before { font-family: 'Toolkit'; content: 'K'; } +.toolkit-icon.zoomout::before { font-family: 'Toolkit'; content: 'L'; } +.toolkit-icon.home::before { font-family: 'Toolkit'; content: 'M'; } +.toolkit-icon.start::before { font-family: 'Toolkit'; content: 'M'; } +.toolkit-icon.rewind::before { font-family: 'Toolkit'; content: 'N'; } +.toolkit-icon.rew::before { font-family: 'Toolkit'; content: 'N'; } +.toolkit-icon.pause::before { font-family: 'Toolkit'; content: 'O'; } +.toolkit-icon.play::before { font-family: 'Toolkit'; content: 'P'; } +.toolkit-icon.stop::before { font-family: 'Toolkit'; content: 'Q'; } +.toolkit-icon.record::before { font-family: 'Toolkit'; content: 'R'; } +.toolkit-icon.rec::before { font-family: 'Toolkit'; content: 'R'; } +.toolkit-icon.eject::before { font-family: 'Toolkit'; content: 'S'; } +.toolkit-icon.fastforward::before { font-family: 'Toolkit'; content: 'T'; } +.toolkit-icon.ffwd::before { font-family: 'Toolkit'; content: 'T'; } +.toolkit-icon.end::before { font-family: 'Toolkit'; content: 'U'; } +.toolkit-icon.repeat::before { font-family: 'Toolkit'; content: 'V'; } +.toolkit-icon.random::before { font-family: 'Toolkit'; content: 'W'; } +.toolkit-icon.day::before { font-family: 'Toolkit'; content: 'X'; } +.toolkit-icon.sun::before { font-family: 'Toolkit'; content: 'X'; } +.toolkit-icon.daylight::before { font-family: 'Toolkit'; content: 'X'; } +.toolkit-icon.light::before { font-family: 'Toolkit'; content: 'X'; } +.toolkit-icon.night::before { font-family: 'Toolkit'; content: 'Y'; } +.toolkit-icon.moon::before { font-family: 'Toolkit'; content: 'Y'; } +.toolkit-icon.dark::before { font-family: 'Toolkit'; content: 'Y'; } +.toolkit-icon.connected::before { font-family: 'Toolkit'; content: 'Z'; } +.toolkit-icon.disconnected::before { font-family: 'Toolkit'; content: 'ö'; } +.toolkit-icon.headphones::before { font-family: 'Toolkit'; content: 'ä'; } +.toolkit-icon.headphonesactive::before { font-family: 'Toolkit'; content: 'ä'; } +.toolkit-icon.headphonesmute::before { font-family: 'Toolkit'; content: 'ü'; } +.toolkit-icon.microphone::before { font-family: 'Toolkit'; content: 'Ä'; } +.toolkit-icon.mic::before { font-family: 'Toolkit'; content: 'Ä'; } +.toolkit-icon.micro::before { font-family: 'Toolkit'; content: 'Ä'; } +.toolkit-icon.microphonemute::before { font-family: 'Toolkit'; content: 'Ö'; } +.toolkit-icon.micmute::before { font-family: 'Toolkit'; content: 'Ö'; } +.toolkit-icon.micromute::before { font-family: 'Toolkit'; content: 'Ö'; } +.toolkit-icon.trash::before { font-family: 'Toolkit'; content: 'Ü'; } +.toolkit-icon.delete::before { font-family: 'Toolkit'; content: 'Ü'; } +.toolkit-icon.save::before { font-family: 'Toolkit'; content: '!'; } +.toolkit-icon.diskette::before { font-family: 'Toolkit'; content: '!'; } +.toolkit-icon.load::before { font-family: 'Toolkit'; content: '"'; } +.toolkit-icon.folder::before { font-family: 'Toolkit'; content: '"'; } +.toolkit-icon.open::before { font-family: 'Toolkit'; content: '"'; } diff --git a/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.css.in b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.css.in new file mode 100644 index 0000000000..3e6868f299 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.css.in @@ -0,0 +1,32 @@ +@font-face { + font-family: "[name]"; + font-style: normal; + font-weight: normal; + src: url("[name].ttf") format("truetype"); +} + +.[prefix]icon { + font-family: "[name]", sans-serif; + font-size: 24px; + text-align: center; + display: inline-block; + vertical-align: middle; +} + +.[prefix]button.[prefix]icon::before{ + display: table-cell; + vertical-align: middle; + text-align: center; +} + +.[prefix]icon.[prefix]12 { font-size: 12px; } +.[prefix]icon.[prefix]16 { font-size: 16px; } +.[prefix]icon.[prefix]20 { font-size: 20px; } +.[prefix]icon.[prefix]24 { font-size: 24px; } +.[prefix]icon.[prefix]32 { font-size: 32px; } +.[prefix]icon.[prefix]40 { font-size: 40px; } +.[prefix]icon.[prefix]48 { font-size: 48px; } +.[prefix]icon.[prefix]56 { font-size: 56px; } +.[prefix]icon.[prefix]64 { font-size: 64px; } +.[prefix]icon.[prefix]128 { font-size: 128px; } +.[prefix]icon.[prefix]256 { font-size: 256px; } diff --git a/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.html b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.html new file mode 100644 index 0000000000..b4c759b8ff --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.html @@ -0,0 +1,117 @@ + + + ToolkitIcons + + + + +

ToolkitIcons

+ + + + + + + +
TitleToolkitIcons
NameToolkit
FamilyToolkit
CSS-FileToolkit.css
TTF-FileToolkit.ttf
HTML<link href="Toolkit.css" rel="stylesheet" type="text/css">
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IconCharNameCSSHTML
1Warningwarning<span class='toolkit-icon warning'></span>
2Informationinformation
info
<span class='toolkit-icon information'></span>
<span class='toolkit-icon info'></span>
3Errorerror<span class='toolkit-icon error'></span>
4Messagemessage<span class='toolkit-icon message'></span>
5Closeclose<span class='toolkit-icon close'></span>
6Successsuccess<span class='toolkit-icon success'></span>
7Hourglasshourglass
loading
<span class='toolkit-icon hourglass'></span>
<span class='toolkit-icon loading'></span>
8Questionmarkquestionmark<span class='toolkit-icon questionmark'></span>
9Screen Sizescreensize<span class='toolkit-icon screensize'></span>
aWindow Minimizewindowminimize
winmin
<span class='toolkit-icon windowminimize'></span>
<span class='toolkit-icon winmin'></span>
0Window Closewindowclose
winclose
<span class='toolkit-icon windowclose'></span>
<span class='toolkit-icon winclose'></span>
bWindow Maximizewindowmaximize
winmax
<span class='toolkit-icon windowmaximize'></span>
<span class='toolkit-icon winmax'></span>
cWindow Maximize Verticalwindowmaximizevertical
winmaxvert
<span class='toolkit-icon windowmaximizevertical'></span>
<span class='toolkit-icon winmaxvert'></span>
dWindow Maximize Horizontalwindowmaximizehorizontal
winmaxhoriz
<span class='toolkit-icon windowmaximizehorizontal'></span>
<span class='toolkit-icon winmaxhoriz'></span>
eWindow Shrinkwindowshrink
winshrink
<span class='toolkit-icon windowshrink'></span>
<span class='toolkit-icon winshrink'></span>
fWindow Resizewindowresize<span class='toolkit-icon windowresize'></span>
gUncheckedunchecked<span class='toolkit-icon unchecked'></span>
hCheckedchecked<span class='toolkit-icon checked'></span>
iSpeakerspeaker
speakeractive
sound
<span class='toolkit-icon speaker'></span>
<span class='toolkit-icon speakeractive'></span>
<span class='toolkit-icon sound'></span>
jSpeaker Mutespeakermute
mute
<span class='toolkit-icon speakermute'></span>
<span class='toolkit-icon mute'></span>
kGeargear
cogwheel
cog
<span class='toolkit-icon gear'></span>
<span class='toolkit-icon cogwheel'></span>
<span class='toolkit-icon cog'></span>
lPowerpower<span class='toolkit-icon power'></span>
mPhasephase
phaseinvert
<span class='toolkit-icon phase'></span>
<span class='toolkit-icon phaseinvert'></span>
nArrow Downarrowdown<span class='toolkit-icon arrowdown'></span>
oArrow Uparrowup<span class='toolkit-icon arrowup'></span>
pArrow Leftarrowleft<span class='toolkit-icon arrowleft'></span>
qArrow Rightarrowright<span class='toolkit-icon arrowright'></span>
rHidehide<span class='toolkit-icon hide'></span>
sShowshow<span class='toolkit-icon show'></span>
tHigh Passhighpass
hipass
<span class='toolkit-icon highpass'></span>
<span class='toolkit-icon hipass'></span>
uLow Passlowpass
lopass
<span class='toolkit-icon lowpass'></span>
<span class='toolkit-icon lopass'></span>
vHigh Shelfhighshelf
hishelf
<span class='toolkit-icon highshelf'></span>
<span class='toolkit-icon hishelf'></span>
wLow Shelflowshelf
loshelf
<span class='toolkit-icon lowshelf'></span>
<span class='toolkit-icon loshelf'></span>
xBand Passbandpass<span class='toolkit-icon bandpass'></span>
yBand Rejectbandreject<span class='toolkit-icon bandreject'></span>
zNotchnotch<span class='toolkit-icon notch'></span>
AParametricparametric<span class='toolkit-icon parametric'></span>
BSinesine<span class='toolkit-icon sine'></span>
CPulsepulse<span class='toolkit-icon pulse'></span>
DSawtoothsawtooth<span class='toolkit-icon sawtooth'></span>
ETriangletriangle<span class='toolkit-icon triangle'></span>
FBypassbypass<span class='toolkit-icon bypass'></span>
GStairsstairs
aliased
<span class='toolkit-icon stairs'></span>
<span class='toolkit-icon aliased'></span>
HStereostereo<span class='toolkit-icon stereo'></span>
IMonomono<span class='toolkit-icon mono'></span>
JZoomzoom
magnifier
<span class='toolkit-icon zoom'></span>
<span class='toolkit-icon magnifier'></span>
KZoom Inzoomin<span class='toolkit-icon zoomin'></span>
LZoom Outzoomout<span class='toolkit-icon zoomout'></span>
MHomehome
start
<span class='toolkit-icon home'></span>
<span class='toolkit-icon start'></span>
NRewindrewind
rew
<span class='toolkit-icon rewind'></span>
<span class='toolkit-icon rew'></span>
OPausepause<span class='toolkit-icon pause'></span>
PPlayplay<span class='toolkit-icon play'></span>
QStopstop<span class='toolkit-icon stop'></span>
RRecordrecord
rec
<span class='toolkit-icon record'></span>
<span class='toolkit-icon rec'></span>
SEjecteject<span class='toolkit-icon eject'></span>
TFast Forwardfastforward
ffwd
<span class='toolkit-icon fastforward'></span>
<span class='toolkit-icon ffwd'></span>
UEndend<span class='toolkit-icon end'></span>
VRepeatrepeat<span class='toolkit-icon repeat'></span>
WRandomrandom<span class='toolkit-icon random'></span>
XDayday
sun
daylight
light
<span class='toolkit-icon day'></span>
<span class='toolkit-icon sun'></span>
<span class='toolkit-icon daylight'></span>
<span class='toolkit-icon light'></span>
YNightnight
moon
dark
<span class='toolkit-icon night'></span>
<span class='toolkit-icon moon'></span>
<span class='toolkit-icon dark'></span>
ZConnectedconnected<span class='toolkit-icon connected'></span>
öDisconnecteddisconnected<span class='toolkit-icon disconnected'></span>
äHeadphonesheadphones
headphonesactive
<span class='toolkit-icon headphones'></span>
<span class='toolkit-icon headphonesactive'></span>
üHeadphones Muteheadphonesmute<span class='toolkit-icon headphonesmute'></span>
ÄMicrophonemicrophone
mic
micro
<span class='toolkit-icon microphone'></span>
<span class='toolkit-icon mic'></span>
<span class='toolkit-icon micro'></span>
ÖMicrophone Mutemicrophonemute
micmute
micromute
<span class='toolkit-icon microphonemute'></span>
<span class='toolkit-icon micmute'></span>
<span class='toolkit-icon micromute'></span>
ÜTrashtrash
delete
<span class='toolkit-icon trash'></span>
<span class='toolkit-icon delete'></span>
!Savesave
diskette
<span class='toolkit-icon save'></span>
<span class='toolkit-icon diskette'></span>
"Loadload
folder
open
<span class='toolkit-icon load'></span>
<span class='toolkit-icon folder'></span>
<span class='toolkit-icon open'></span>
+ + + + + diff --git a/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.html.in b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.html.in new file mode 100644 index 0000000000..e8f57bbe38 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.html.in @@ -0,0 +1,41 @@ + + + [title] + + + + +

[title]

+ + + + + + + +
Title[title]
Name[name]
Family[family]
CSS-File[name].css
TTF-File[name].ttf
HTML<link href="[name].css" rel="stylesheet" type="text/css">
+ [glyphs] + + diff --git a/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.svg b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.svg new file mode 100644 index 0000000000..f21e71ca81 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.svg @@ -0,0 +1,849 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.ttf b/share/web_surfaces/builtin/mixer/toolkit/styles/fonts/Toolkit.ttf new file mode 100644 index 0000000000000000000000000000000000000000..45d5423fbf725f13967079c37981a897dfd25002 GIT binary patch literal 14144 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 T<#d%KND1|amZ2ZD%gFx$7`Eh( literal 0 HcmV?d00001 diff --git a/share/web_surfaces/builtin/mixer/toolkit/styles/toolkit.css b/share/web_surfaces/builtin/mixer/toolkit/styles/toolkit.css new file mode 100644 index 0000000000..cf4df93568 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/styles/toolkit.css @@ -0,0 +1,1304 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +@import "fonts/Toolkit.css"; + +/* ############################ RESET CSS ########################### */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 1em; + font: inherit; + outline: none; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +pre, code { + font-family: monospace; +} + + +/* ########################### GENERIC CSS ########################## */ + +.toolkit-disabled { + opacity: 0.3; + pointer-events: none; +} + +.toolkit-inactive { + opacity: 0.3; +} + +.toolkit-widget { + contain: style layout; + display: inline-block; + vertical-align: middle; + position: relative; + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer */ + -khtml-user-select: none; /* KHTML browsers (e.g. Konqueror) */ + -webkit-user-select: none; /* Chrome, Safari, and Opera */ + -webkit-touch-callout: none; /* Disable Android and iOS callouts*/ +} +.toolkit-input { + -moz-user-select: all; /* Firefox */ + -ms-user-select: all; /* Internet Explorer */ + -khtml-user-select: all; /* KHTML browsers (e.g. Konqueror) */ + -webkit-user-select: all; /* Chrome, Safari, and Opera */ + -webkit-touch-callout: all; /* Disable Android and iOS callouts*/ +} + + +/* #################### WIDGETS DEFAULT STYLES #################### */ + +/* ROOT */ + +.toolkit-root { + display: block; +} + +/* BUTTON */ + +.toolkit-button { + cursor: pointer; + display: inline-flex; + justify-content: center; + align-items: center; +} +.toolkit-button > .toolkit-label, +.toolkit-button > .toolkit-icon { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + margin: auto; + pointer-events: none; +} +.toolkit-button > .toolkit-icon { + background-position: center center; + -webkit-background-size: contain; + -moz-background-size: contain; + -ms-background-size: contain; + -o-background-size: contain; + background-size: contain; + background-repeat: no-repeat; + order: 1; + flex: 0 0 auto; +} +.toolkit-button > .toolkit-label { + order: 2; + flex: 0 0 auto; +} +.toolkit-button.toolkit-vertical { + flex-direction: column; +} + +/* BUTTONARRAY */ + +.toolkit-buttonarray { + box-sizing: border-box; + display: inline-flex; +} +.toolkit-buttonarray.toolkit-vertical { + height: 100%; + flex-direction: column; +} +.toolkit-buttonarray.toolkit-horizontal { + width: 100%; +} +.toolkit-buttonarray > .toolkit-previous, +.toolkit-buttonarray > .toolkit-next { + flex: 0 0 auto; + margin: 0; +} +.toolkit-buttonarray > .toolkit-clip { + flex: 1 1 100%; + overflow: hidden; + box-sizing: border-box; +} +.toolkit-buttonarray.toolkit-horizontal.toolkit-scroll > .toolkit-clip { + overflow-x: scroll !important; +} +.toolkit-buttonarray.toolkit-vertical.toolkit-scroll > .toolkit-clip { + overflow-y: scroll !important; +} +.toolkit-buttonarray > .toolkit-clip > .toolkit-container { + position: relative; +} +.toolkit-buttonarray.toolkit-horizontal > .toolkit-clip > .toolkit-container { + height: 100%; + white-space: nowrap; +} +.toolkit-buttonarray.toolkit-vertical > .toolkit-clip > .toolkit-container { + width: 100%; +} + +/* CONTAINER */ + +.toolkit-container.toolkit-hide { + display: none !important; +} + +/* CHART */ + +.toolkit-chart { + overflow: hidden; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.toolkit-chart > svg { + box-sizing: content-box; +} +.toolkit-chart > svg > .toolkit-key { + pointer-events: none; +} + +/* CLOCK */ + +.toolkit-widget.toolkit-clock { + -webkit-border-radius: 100%; + -moz-border-radius: 100%; + border-radius: 100%; +} + +/* COLORPICKER */ + +.toolkit-color-picker { + display: grid; + grid-template-areas: "red canvas hue" + "green canvas saturation" + "blue canvas lightness" + "cancel hex apply"; +} + +.toolkit-color-picker > .toolkit-canvas { + grid-area: canvas; + padding-top: 100%; + min-width: 360px; + position: relative; + z-index: 10; + + background-repeat: no-repeat; + -webkit-background-size: 100% 100%; + -moz-background-size: 100% 100%; + -ms-background-size: 100% 100%; + -o-background-size: 100% 100%; + background-size: 100% 100%; + background-color: black; + background-image: url(data:image/png;base64,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); +} +.toolkit-color-picker > .toolkit-canvas > .toolkit-grayscale { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + + background-repeat: no-repeat; + -webkit-background-size: 100% 100%; + -moz-background-size: 100% 100%; + -ms-background-size: 100% 100%; + -o-background-size: 100% 100%; + background-size: 100% 100%; + background: rgb(0,0,0); + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background: -moz-linear-gradient(top, rgba(0,0,0,1) 0%, rgba(255,255,255,1) 100%); + background: -webkit-linear-gradient(top, rgba(0,0,0,1) 0%,rgba(255,255,255,1) 100%); + background: linear-gradient(to bottom, rgba(0,0,0,1) 0%,rgba(255,255,255,1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#000000', endColorstr='#ffffff',GradientType=0 ); +} +.toolkit-color-picker > .toolkit-canvas > .toolkit-indicator { + position: absolute; + + width: 40px; + height: 40px; + margin-top: -20px; + margin-left: -20px; + + -webkit-border-radius: 20px; + -moz-border-radius: 20px; + border-radius: 20px; +} + + +.toolkit-color-picker > .toolkit-hex { + grid-area: hex; + text-align: center; +} +.toolkit-color-picker > .toolkit-hex > .toolkit-input { + height: 100%; +} + +.toolkit-color-picker > .toolkit-hue { + grid-area: hue; +} +.toolkit-color-picker >.toolkit-saturation { + grid-area: saturation; +} +.toolkit-color-picker > .toolkit-lightness { + grid-area: lightness; +} + +.toolkit-color-picker > .toolkit-red { + grid-area: red; +} +.toolkit-color-picker > .toolkit-green { + grid-area: green; +} +.toolkit-color-picker > .toolkit-blue { + grid-area: blue; +} + +.toolkit-color-picker > .toolkit-apply { + grid-area: apply; +} +.toolkit-color-picker > .toolkit-cancel { + grid-area: cancel; +} + +/* DIALOG */ + +.toolkit-dialog { + position: fixed; + z-index: 100000000000; + + -webkit-animation: -webkit-dialog-fade-in 0.5s; + -moz-animation: -moz-dialog-fade-in 0.5s; + -ms-animation: -ms-dialog-fade-in 0.5s; + -o-animation: -o-dialog-fade-in 0.5s; + animation: dialog-fade-in 0.5s; + + -webkit-transform: scale(1); + -moz-transform: scale(1); + -ms-transform: scale(1); + -o-transform: scale(1); + transform: scale(1); +} +.toolkit-dialog.toolkit-show { + opacity: 1; +} +.toolkit-dialog.toolkit-hiding { + opacity: 0; + + -webkit-transition: opacity 0.5s, transform 0.5s; + -moz-transition: opacity 0.5s, transform 0.5s; + -ms-transition: opacity 0.5s, transform 0.5s; + -o-transition: opacity 0.5s, transform 0.5s; + transition: opacity 0.5s, transform 0.5s; + + -webkit-transform: scale(0); + -moz-transform: scale(0); + -ms-transform: scale(0); + -o-transform: scale(0); + transform: scale(0); +} +@-webkit-keyframes -webit-dialog-fade-in { + 0% { opacity: 0; -webit-transform: scale(1); } + 100% { opacity: 1; -webit-transform: scale(1);} +} +@-moz-keyframes -moz-dialog-fade-in { + 0% { opacity: 0; -moz-transform: scale(1); } + 100% { opacity: 1; -moz-transform: scale(1);} +} +@-ms-keyframes -ms-dialog-fade-in { + 0% { opacity: 0; -ms-transform: scale(1); } + 100% { opacity: 1; -ms-transform: scale(1);} +} +@-o-keyframes -o-dialog-fade-in { + 0% { opacity: 0; -o-transform: scale(1); } + 100% { opacity: 1; -o-transform: scale(1);} +} +@keyframes dialog-fade-in { + 0% { opacity: 0; transform: scale(1); } + 100% { opacity: 1; transform: scale(1);} +} + +/* EXPANDER */ + +.toolkit-expander { + +} + +.toolkit-expander.toolkit-always-expanded > .toolkit-toggle-expand { + display: none; +} + +/* popup */ +.toolkit-expander.toolkit-popup.toolkit-expanded { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000000; + margin: 0; +} +.toolkit-expander.toolkit-popup > .toolkit-toggle-expand { + box-sizing: border-box; + position: absolute; + z-index: 1; + top: 0; + left: 0; + right: 0; + bottom: 0; + width: 100%; + height: 100%; + margin: 0; + opacity: 0; +} +.toolkit-expander.toolkit-popup.toolkit-expanded > .toolkit-toggle-expand { + opacity: 1; + top: 10px; + right: 10px; + left: auto; + bottom: auto; +} + +/* drawer right */ +.toolkit-expander.toolkit-drawer-right { + position: absolute; + top: 0; + right: 0; + transform: translate3d(100%, 0, 0); + transition: transform 500ms; +} + +.toolkit-expander.toolkit-drawer-right.toolkit-expanded { + transform: translate3d(0%, 0, 0); +} + +.toolkit-expander.toolkit-drawer-right > .toolkit-toggle-expand { + position: absolute; + top: 0; + left: 0; + transform: translate3d(-100%, 0, 0); +} + +/* drawer left */ +.toolkit-expander.toolkit-drawer-left { + position: absolute; + top: 0; + left: 0; + transform: translate3d(-100%, 0, 0); + transition: transform 500ms; +} + +.toolkit-expander.toolkit-drawer-left.toolkit-expanded { + transform: translate3d(0%, 0, 0); +} + +.toolkit-expander.toolkit-drawer-left > .toolkit-toggle-expand { + position: absolute; + top: 0; + right: 0; + transform: translate3d(100%, 0, 0); +} + +/* drawer top */ +.toolkit-expander.toolkit-drawer-top { + position: absolute; + top: 0; + left: 0; + transform: translate3d(0, -100%, 0); + transition: transform 500ms; +} + +.toolkit-expander.toolkit-drawer-top.toolkit-expanded { + transform: translate3d(0, 0, 0); +} + +.toolkit-expander.toolkit-drawer-top > .toolkit-toggle-expand { + position: absolute; + bottom: 0; + right: 0; + transform: translate3d(0, 100%, 0); +} + +/* drawer bottom */ +.toolkit-expander.toolkit-drawer-bottom { + position: absolute; + top: 0; + left: 0; + transform: translate3d(0, 100%, 0); + transition: transform 500ms; +} + +.toolkit-expander.toolkit-drawer-bottom.toolkit-expanded { + transform: translate3d(0, 0, 0); +} + +.toolkit-expander.toolkit-drawer-bottom > .toolkit-toggle-expand { + position: absolute; + top: 0; + right: 0; + transform: translate3d(0, -100%, 0); +} + +/* hide */ +.toolkit-expander.toolkit-remove { + display: none; +} + +.toolkit-expander.toolkit-remove.toolkit-expanded { + display: block; +} + +.toolkit-expander.toolkit-remove > .toolkit-toggle-expand { + display: none; +} + +/* FADER */ + +.toolkit-fader { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + box-sizing: border-box; + + display: inline-grid; +} + +.toolkit-fader.toolkit-horizontal { + grid-template-columns: 1fr auto; + grid-template-rows: auto auto auto; +} + +.toolkit-fader.toolkit-vertical { + grid-template-columns: auto auto; + grid-template-rows: auto 1fr auto; +} + +.toolkit-fader.toolkit-top { + grid-template-areas: "track value" + "scale label"; +} +.toolkit-fader.toolkit-left { + grid-template-areas: "label ." + "track scale" + "value ."; +} +.toolkit-fader.toolkit-right { + grid-template-areas: ". label" + "scale track" + ". value"; +} +.toolkit-fader.toolkit-bottom { + grid-template-areas: "scale label" + "track value"; +} + +.toolkit-fader > .toolkit-track { + grid-area: track; + position: relative; +} +.toolkit-fader > .toolkit-scale { + grid-area: scale; +} +.toolkit-fader > .toolkit-label { + grid-area: label; +} +.toolkit-fader.toolkit-vertical > .toolkit-label { + text-align: center; +} +.toolkit-fader.toolkit-horizontal > .toolkit-label { + text-align: left; +} +.toolkit-fader > .toolkit-value { + grid-area: value; +} +.toolkit-fader.toolkit-horizontal > .toolkit-value > .toolkit-input { + text-align: left; +} +.toolkit-fader.toolkit-vertical > .toolkit-value > .toolkit-input { + text-align: center; +} + +.toolkit-fader > .toolkit-track > .toolkit-handle { + position: absolute; + z-index: 10; + will-change: transform; + transform: translate3d(0, 0, 0); +} + +.toolkit-fader.toolkit-horizontal > .toolkit-track > .toolkit-handle { + left: 0; +} + +.toolkit-fader.toolkit-vertical > .toolkit-track > .toolkit-handle { + bottom: 0; +} + +/* GAUGE */ + +.toolkit-gauge { + +} + +.toolkit-gauge > svg { + width: 100%; + height: 100%; +} + +/* ICON */ + +.toolkit-icon { + background-position: center center; + -webkit-background-size: contain; + -moz-background-size: contain; + -ms-background-size: contain; + -o-background-size: contain; + background-size: contain; + background-repeat: no-repeat; +} + +/* KNOB */ + +.toolkit-knob > svg { + width: 100%; + height: 100%; + transform: translate3d(0,0,0); + will-change: transform; + cursor: pointer; +} + +/* LABEL */ + +.toolkit-button.toolkit-select > .toolkit-label { + text-overflow: ellipsis; +} + +/* METERBASE */ + +.toolkit-meter-base { + display: inline-grid; + overflow: hidden; +} +.toolkit-meter-base.toolkit-vertical { + min-height: 128px; + grid-template-columns: auto auto; + grid-template-rows: auto 1fr auto; +} +.toolkit-meter-base.toolkit-horizontal { + min-width: 128px; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto auto auto; +} +.toolkit-meter-base.toolkit-top { + grid-template-areas: + "bar bar" + "scale scale" + "title label"; +} +.toolkit-meter-base.toolkit-bottom { + grid-template-areas: + "title label" + "scale scale" + "bar bar"; +} +.toolkit-meter-base.toolkit-left { + grid-template-areas: + "label ." + "bar scale" + "title ."; +} +.toolkit-meter-base.toolkit-right { + grid-template-areas: + ". label" + "scale bar" + ". title"; +} +.toolkit-meter-base > .toolkit-label { + grid-area: label; + white-space: nowrap; +} +.toolkit-meter-base.toolkit-horizontal > .toolkit-label { + justify-self: end; + align-self: center; +} +.toolkit-meter-base.toolkit-vertical > .toolkit-label { + justify-self: center; + align-self: center; +} +.toolkit-meter-base > .toolkit-title { + grid-area: title; + white-space: nowrap; +} +.toolkit-meter-base.toolkit-horizontal > .toolkit-title { + justify-self: start; + align-self: center; +} +.toolkit-meter-base.toolkit-vertical > .toolkit-title { + justify-self: center; + align-self: center; +} +.toolkit-meter-base > .toolkit-scale { + grid-area: scale; +} +.toolkit-meter-base.toolkit-vertical > .toolkit-scale { + height: 100%; +} +.toolkit-meter-base.toolkit-horizontal > .toolkit-scale { + width: 100%; +} +.toolkit-meter-base.toolkit-top > .toolkit-scale { + justify-self: center; + align-self: start; +} +.toolkit-meter-base.toolkit-bottom > .toolkit-scale { + justify-self: center; + align-self: end; +} +.toolkit-meter-base.toolkit-left > .toolkit-scale { + justify-self: start; + align-self: center; +} +.toolkit-meter-base.toolkit-right > .toolkit-scale { + justify-self: end; + align-self: center; +} +.toolkit-meter-base > .toolkit-bar { + grid-area: bar; + position: relative; + overflow: hidden; + contain: style layout; +} +.toolkit-meter-base.toolkit-vertical > .toolkit-bar { + height: 100%; +} +.toolkit-meter-base.toolkit-horizontal > .toolkit-bar { + width: 100%; +} +.toolkit-meter-base.toolkit-top > .toolkit-bar { + justify-self: center; + align-self: end; +} +.toolkit-meter-base.toolkit-bottom > .toolkit-bar { + justify-self: center; + align-self: start; +} +.toolkit-meter-base.toolkit-left > .toolkit-bar { + justify-self: end; + align-self: center; +} +.toolkit-meter-base.toolkit-right > .toolkit-bar { + justify-self: start; + align-self: center; +} +.toolkit-meter-base > .toolkit-bar > .toolkit-mask { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + will-change: transform; + transform: translate3d(0, 0, 0); +} + +/* LEVELMETER */ +/* out of order because multimeter would override properties */ +.toolkit-level-meter.toolkit-vertical { + grid-template-columns: auto auto; + grid-template-rows: auto auto 1fr auto; +} +.toolkit-level-meter.toolkit-horizontal { + grid-template-columns: 1fr 1fr auto; + grid-template-rows: auto auto auto; +} +.toolkit-level-meter.toolkit-top { + grid-template-areas: + "bar bar clip" + "scale scale ." + "title label label"; +} +.toolkit-level-meter.toolkit-bottom { + grid-template-areas: + "title label label" + "scale scale ." + "bar bar clip"; +} +.toolkit-level-meter.toolkit-left { + grid-template-areas: + "label ." + "clip ." + "bar scale" + "title ."; +} +.toolkit-level-meter.toolkit-right { + grid-template-areas: + ". label" + ". clip" + "scale bar" + ". title"; +} +.toolkit-level-meter > .toolkit-clip { + grid-area: clip; +} +.toolkit-level-meter > .toolkit-bar > .toolkit-peak { + position: absolute; + z-index: 1000; +} + +.toolkit-level-meter.toolkit-vertical.toolkit-left > .toolkit-bar > .toolkit-peak .toolkit-peak-label { + float: left; +} +.toolkit-level-meter.toolkit-vertical.toolkit-right > .toolkit-bar > .toolkit-peak .toolkit-peak-label { + float: right; +} + +.toolkit-level-meter.toolkit-horizontal > .toolkit-bar > .toolkit-peak > .toolkit-peak-label { + transform: rotate(-90deg); +} +.toolkit-level-meter.toolkit-horizontal.toolkit-top > .toolkit-bar > .toolkit-peak > .toolkit-peak-label { + float: right; +} +.toolkit-level-meter.toolkit-horizontal.toolkit-bottom > .toolkit-bar > .toolkit-peak > .toolkit-peak-label { + float: left; +} + +/* MULTIMETER */ + +.toolkit-multi-meter { + display: inline-flex; + flex-wrap: wrap; + justify-content: center; + align-items: stretch; + align-content: center; +} +.toolkit-multi-meter.toolkit-horizontal { + flex-direction: column; +} +.toolkit-multi-meter > .toolkit-title { + height: 1.2em; + line-height: 1.2em; + flex: 1 0 100%; + align-self: start; +} +.toolkit-multi-meter > .toolkit-level-meter { + flex: 0 0 auto; + align-self: stretch; +} +.toolkit-multi-meter.toolkit-horizontal .toolkit-level-meter { + grid-template-columns: 48px 1fr auto 48px; + grid-template-rows: auto auto; +} +.toolkit-multi-meter.toolkit-top .toolkit-level-meter { + grid-template-areas: + "title bar clip label" + ". scale . ."; +} +.toolkit-multi-meter.toolkit-bottom .toolkit-level-meter { + grid-template-areas: + ". scale . ." + "title bar clip label"; +} +.toolkit-multi-meter.toolkit-horizontal .toolkit-level-meter > .toolkit-title { + justify-self: end !important; +} + +/* PAGER */ + +.toolkit-pager { + box-sizing: border-box; + overflow: hidden; + width: 100%; + height: 100%; + display: flex; +} +.toolkit-pager.toolkit-vertical { + flex-direction: column; +} +.toolkit-pager > .toolkit-clip { + flex: 1 1 100%; + position: relative; +} +.toolkit-pager.toolkit-left > .toolkit-clip, +.toolkit-pager.toolkit-top > .toolkit-clip { + order: 2; +} +.toolkit-pager.toolkit-right > .toolkit-clip, +.toolkit-pager.toolkit-bottom > .toolkit-clip { + order: 1; +} +.toolkit-pager > .toolkit-clip > .toolkit-page { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 1; + overflow: auto; + will-change: transform; + transform: translate3d(0,0,0); +} +.toolkit-pager > .toolkit-buttonarray { + display: flex; + flex: 0 0 auto; +} +.toolkit-pager.toolkit-left > .toolkit-buttonarray, +.toolkit-pager.toolkit-top > .toolkit-buttonarray { + order: 1; +} +.toolkit-pager.toolkit-right > .toolkit-buttonarray, +.toolkit-pager.toolkit-bottom > .toolkit-buttonarray { + order: 2; +} + + + +.toolkit-pager.toolkit-forward.toolkit-vertical > .toolkit-clip > .toolkit-page.toolkit-hiding { + animation: toolkit-page-vertical-out-forward 0.5s ease-in-out; + transform: translateX(-100%); +} +.toolkit-pager.toolkit-forward.toolkit-vertical > .toolkit-clip > .toolkit-page.toolkit-showing { + animation: toolkit-page-vertical-in-forward 0.5s ease-in-out; +} + +.toolkit-pager.toolkit-backward.toolkit-vertical > .toolkit-clip > .toolkit-page.toolkit-hiding { + animation: toolkit-page-vertical-out-backward 0.5s ease-in-out; + transform: translateX(100%); +} +.toolkit-pager.toolkit-backward.toolkit-vertical > .toolkit-clip > .toolkit-page.toolkit-showing { + animation: toolkit-page-vertical-in-backward 0.5s ease-in-out; +} + +.toolkit-pager.toolkit-forward.toolkit-horizontal > .toolkit-clip > .toolkit-page.toolkit-hiding { + animation: toolkit-page-horizontal-out-forward 0.5s ease-in-out; + transform: translateY(-100%); +} +.toolkit-pager.toolkit-forward.toolkit-horizontal > .toolkit-clip > .toolkit-page.toolkit-showing { + animation: toolkit-page-horizontal-in-forward 0.5s ease-in-out; +} + +.toolkit-pager.toolkit-backward.toolkit-horizontal > .toolkit-clip > .toolkit-page.toolkit-hiding { + animation: toolkit-page-horizontal-out-backward 0.5s ease-in-out; + transform: translateY(100%); +} +.toolkit-pager.toolkit-backward.toolkit-horizontal > .toolkit-clip > .toolkit-page.toolkit-showing { + animation: toolkit-page-horizontal-in-backward 0.5s ease-in-out; +} + +@keyframes toolkit-page-horizontal-in-forward { + 0% { transform: translateY(100%); } + 100% { transform: translateY(0); } +} +@keyframes toolkit-page-horizontal-out-forward { + 0% { transform: translateY(0); } + 100% { transform: translateY(-100%); } +} +@keyframes toolkit-page-horizontal-in-backward { + 0% { transform: translateY(-100%); } + 100% { transform: translateY(0); } +} +@keyframes toolkit-page-horizontal-out-backward { + 0% { transform: translateY(0); } + 100% { transform: translateY(100%); } +} + +@keyframes toolkit-page-vertical-in-forward { + 0% { transform: translateX(100%); } + 100% { transform: translateX(0); } +} +@keyframes toolkit-page-vertical-out-forward { + 0% { transform: translateX(0); } + 100% { transform: translateX(-100%); } +} +@keyframes toolkit-page-vertical-in-backward { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(0); } +} +@keyframes toolkit-page-vertical-out-backward { + 0% { transform: translateX(0); } + 100% { transform: translateX(100%); } +} + + +/* RESPONSEHANDLE */ + +.toolkit-response-handle { + display: block; +} +.toolkit-response-handle.toolkit-inactive { + display: none; +} +.toolkit-response-handle.toolkit-circular > .toolkit-line { + display: none; +} +.toolkit-response-handle.toolkit-circular.toolkit-active > .toolkit-line { + display: block; +} +.toolkit-response-handle.toolkit-line > .toolkit-line-2 { + display: none; +} +.toolkit-response-handle.toolkit-line.toolkit-active > .toolkit-line-2 { + display: block; +} +.toolkit-response-handle.toolkit-block > .toolkit-line-2 { + display: none; +} +.toolkit-response-handle.toolkit-block.toolkit-active > .toolkit-line-2 { + display: block; +} + +/* SCALE */ + +.toolkit-scale { + +} + +.toolkit-scale > .toolkit-label { + position: absolute; + z-index: 4; + display: block; + float: left; +} + +.toolkit-scale > .toolkit-dot { + position: absolute; + z-index: 3; +} + +.toolkit-scale.toolkit-vertical > .toolkit-label { + transform: translateY(50%); +} + +.toolkit-scale.toolkit-vertical > .toolkit-max, +.toolkit-scale.toolkit-vertical.toolkit-reverse > .toolkit-min { + transform: translateY(100%); +} + +.toolkit-scale.toolkit-vertical > .toolkit-min, +.toolkit-scale.toolkit-vertical.toolkit-reverse > .toolkit-max { + transform: translateY(0); +} + +.toolkit-scale.toolkit-horizontal > .toolkit-label { + transform: translateX(-50%); +} + +.toolkit-scale.toolkit-horizontal > .toolkit-max, +.toolkit-scale.toolkit-horizontal.toolkit-reverse > .toolkit-min { + transform: translateX(-100%); +} + +.toolkit-scale.toolkit-horizontal > .toolkit-min, +.toolkit-scale.toolkit-horizontal.toolkit-reverse > .toolkit-max { + transform: translateX(0); +} +.toolkit-scale > .toolkit-pointer { + box-sizing: border-box; + position: absolute; + z-index: 5; + overflow: hidden; + width: 12px; + height: 12px; + border-width: 6px; + border-color: transparent; + border-style: solid; +} +.toolkit-scale.toolkit-vertical > .toolkit-pointer { + margin-bottom: -6px; + bottom: 0px; +} +.toolkit-scale.toolkit-left > .toolkit-pointer { + left: -6px; +} +.toolkit-scale.toolkit-right > .toolkit-pointer { + right: -6px; +} +.toolkit-scale.toolkit-horizontal > .toolkit-pointer { + margin-left: -6px; + left: 0; +} +.toolkit-scale.toolkit-top > .toolkit-pointer { + top: -6px; +} +.toolkit-scale.toolkit-bottom > .toolkit-pointer { + bottom: -6px; +} +.toolkit-scale > .toolkit-bar { + position: absolute; + z-index: 0; +} +.toolkit-scale.toolkit-vertical > .toolkit-bar { + bottom: 0px; +} +.toolkit-scale.toolkit-left > .toolkit-bar { + left: 0px; +} +.toolkit-scale.toolkit-right > .toolkit-bar { + right: 0px; +} +.toolkit-scale.toolkit-horizontal > .toolkit-bar { + left: 0px; +} +.toolkit-scale.toolkit-top > .toolkit-bar { + top: 0px; +} +.toolkit-scale.toolkit-bottom > .toolkit-bar { + bottom: 0px; +} + + +/* SELECT */ + +.toolkit-button.toolkit-select { + flex-direction: row; + justify-content: flex-end; +} +.toolkit-button.toolkit-select > .toolkit-label { + order: 1; + flex: 1 1 auto; +} +.toolkit-button.toolkit-select > .toolkit-icon { + order: 2; + flex: 0 0 auto; + font-size: 16px; + line-height: 16px; + height: 16px; + width: 16px; + margin: 0 0 0 4px !important; +} +.toolkit-select-list { + position: absolute; + z-index: 100000; + box-sizing: border-box; + overflow-y: auto; +} +.toolkit-select-list > .toolkit-option { + white-space: nowrap; + display: block; +} + +/* SLIDER */ + +.toolkit-slider { + background-position: 0 0; + background-repeat: no-repeat; + cursor: pointer; +} + +/* STATE */ + +.toolkit-state > .toolkit-mask { + width: 100%; + height: 100%; +} + +/* TOOLTIP */ + +.toolkit-tooltip { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + pointer-events: none; + z-index: 9999; +} +.toolkit-tooltip > .toolkit-table { + display: table; + width: 100vw; + height: 100vh; + table-layout: auto; +} +.toolkit-tooltip > .toolkit-table > .toolkit-row { + display: table-row; +} +.toolkit-tooltip > .toolkit-table > .toolkit-row > .toolkit-cell { + display: table-cell; + height: auto; +} +.toolkit-tooltip > .toolkit-table > .toolkit-row > .toolkit-cell > .toolkit-entry { + white-space: nowrap; + display: table-cell; +} + +/* VALUE */ + +.toolkit-value > .toolkit-input { + -webkit-user-select: auto !important; + -khtml-user-select: auto !important; + -moz-user-select: auto !important; + -ms-user-select: auto !important; + user-select: auto !important; + text-align: center; + border: none; + outline: none; +} + +/* VALUEBUTTON */ + +.toolkit-valuebutton { + display: inline-grid; + + justify-content: stretch; + align-items: center; + + grid-template-columns: auto 1fr auto; + grid-template-rows: auto auto; + grid-template-areas: "icon label value" + "scale scale value"; +} +.toolkit-valuebutton > .toolkit-icon { + grid-area: icon; +} +.toolkit-valuebutton > .toolkit-label { + grid-area: label; +} +.toolkit-valuebutton > .toolkit-value { + grid-area: value; +} +.toolkit-valuebutton > .toolkit-scale { + grid-area: scale; +} + +/* VALUEKNOB */ + +.toolkit-valueknob { + display: inline-flex; + flex-direction: column; + justify-content: center; + align-items: center; + +} +.toolkit-valueknob > .toolkit-label { + flex: 0 0 auto; +} +.toolkit-valueknob > .toolkit-knob { + width: 100px; + height: 100px; + flex: 0 0 auto; +} +.toolkit-valueknob > .toolkit-value { + flex: 0 0 auto; +} + +/* WINDOW */ + +.toolkit-window { + position: absolute; + z-index: 10000; + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: space-between; +} +.toolkit-window > .toolkit-content { + overflow: auto; + order: 2; + flex: 1 1 auto; +} +.toolkit-window > .toolkit-header { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: default; + order: 1; + flex: 0 0 auto; + display: flex; + justify-content: space-between; +} +.toolkit-window.toolkit-draggable > .toolkit-header { + cursor: pointer; +} + +.toolkit-window > .toolkit-footer { + cursor: default; + order: 3; + flex: 0 0 auto; + display: flex; + justify-content: space-between; +} +.toolkit-window > .toolkit-header > .toolkit-title, +.toolkit-window > .toolkit-footer > .toolkit-title { + white-space: nowrap; + flex: 1 1 auto; +} +.toolkit-window > .toolkit-header .toolkit-status, +.toolkit-window > .toolkit-footer .toolkit-status { + white-space: nowrap; + flex: 1 1 auto; +} +.toolkit-window > .toolkit-header > .toolkit-icon, +.toolkit-window > .toolkit-footer > .toolkit-icon { + flex: 0 0 auto; +} +.toolkit-window > .toolkit-header > .toolkit-button, +.toolkit-window > .toolkit-footer > .toolkit-button { + flex: 0 0 auto; +} +.toolkit-window.toolkit-shrinked > .toolkit-footer, +.toolkit-window.toolkit-shrinked .toolkit-resize { + display: none !important; +} +.toolkit-window > .toolkit-header > .toolkit-resize, +.toolkit-window > .toolkit-footer > .toolkit-resize { + cursor: pointer; +} +.toolkit-window > .toolkit-header > .toolkit-spacer, +.toolkit-window > .toolkit-footer > .toolkit-spacer { + flex: 1 1 auto; +} diff --git a/share/web_surfaces/builtin/mixer/toolkit/toolkit.js b/share/web_surfaces/builtin/mixer/toolkit/toolkit.js new file mode 100644 index 0000000000..df84bb66f6 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/toolkit.js @@ -0,0 +1,1370 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; + +/** @namespace TK + * + * @description This is the namespace of the Toolkit library. + * It contains all Toolkit classes and constant. + * There are also a couple of utility functions which provide + * compatibility for older browsers. + */ +var TK; + +(function(w) { + +var has_class, add_class, remove_class, toggle_class; +// IE9 +function get_class_name(e) { + if (HTMLElement.prototype.isPrototypeOf(e)) { + return e.className; + } else { + return e.getAttribute("class") || ""; + } +} +function set_class_name(e, s) { + if (HTMLElement.prototype.isPrototypeOf(e)) { + e.className = s; + } else { + e.setAttribute("class", s); + } +} + +if ('classList' in document.createElement("_") && 'classList' in make_svg('text')) { + /** + * Returns true if the node has the given class. + * @param {HTMLElement|SVGElement} node - The DOM node. + * @param {string} name - The class name. + * @returns {boolean} + * @function TK.has_class + */ + has_class = function (e, cls) { return e.classList.contains(cls); } + /** + * Adds a CSS class to a DOM node. + * + * @param {HTMLElement|SVGElement} node - The DOM node. + * @param {...*} names - The class names. + * @function TK.add_class + */ + add_class = function (e) { + var i; + e = e.classList; + for (i = 1; i < arguments.length; i++) { + var a = arguments[i].split(" "); + for (var j = 0; j < a.length; j++) + e.add(a[j]); + } + } + /** + * Removes a CSS class from a DOM node. + * @param {HTMLElement|SVGElement} node - The DOM node. + * @param {...*} names - The class names. + * @function TK.remove_class + */ + remove_class = function (e) { + var i; + e = e.classList; + for (i = 1; i < arguments.length; i++) + e.remove(arguments[i]); + } + /** + * Toggles a CSS class from a DOM node. + * @param {HTMLElement|SVGElement} node - The DOM node. + * @param {string} name - The class name. + * @function TK.toggle_class + */ + toggle_class = function (e, cls, cond) { + /* The second argument to toggle is not implemented in IE, + * so we never use it */ + if (arguments.length >= 3) { + if (cond) { + add_class(e, cls); + } else { + remove_class(e, cls); + } + } else e.classList.toggle(cls); + }; +} else { + has_class = function (e, cls) { + return get_class_name(e).split(" ").indexOf(cls) !== -1; + }; + add_class = function (e) { + var i, cls; + var a = get_class_name(e).split(" "); + + for (i = 1; i < arguments.length; i++) { + cls = arguments[i]; + if (a.indexOf(cls) === -1) { + a.push(cls); + } + } + set_class_name(e, a.join(" ")); + }; + remove_class = function(e) { + var j, cls, i; + var a = get_class_name(e).split(" "); + + for (j = 1; j < arguments.length; j++) { + cls = arguments[j]; + i = a.indexOf(cls); + + if (i !== -1) { + do { + a.splice(i, 1); + i = a.indexOf(cls); + } while (i !== -1); + + } + } + + set_class_name(e, a.join(" ")); + }; + toggle_class = function(e, cls, cond) { + if (arguments.length < 3) { + cond = !has_class(e, cls); + } + if (cond) { + add_class(e, cls); + } else { + remove_class(e, cls); + } + }; +} + +var data_store; +var data; + +if ('WeakMap' in window) { + data = function(e) { + var r; + if (!data_store) data_store = new window.WeakMap(); + + r = data_store[e]; + + if (!r) { + data_store[e] = r = {}; + } + + return r; + }; +} else { + data_store = []; + var data_keys = []; + data = function(e) { + if (typeof(e) !== "object") throw("Cannot store data for non-objects."); + var k = data_keys.indexOf(e); + var r; + if (k === -1) { + data_keys.push(e); + k = data_store.push({}) - 1; + } + return data_store[k]; + }; +} + +var get_style; + +if ('getComputedStyle' in window) { + /** + * Returns the computed style of a node. + * + * @param {HTMLElement|SVGElement} node - The DOM node. + * @param {string} property - The CSS property name. + * @returns {string} + * + * @function TK.get_style + */ + get_style = function(e, style) { + return window.getComputedStyle(e).getPropertyValue(style); + }; +} else { + get_style = function(e, style) { + return e.currentStyle[style]; + }; +} + +var class_regex = /[^A-Za-z0-9_\-]/; +function is_class_name (str) { + /** + * Returns true ii a string could be a class name. + * @param {string} string - The string to test + * @function TK.is_class_name + * @returns {boolean} + */ + return !class_regex.test(str); +} + +function get_max_time(string) { + /** + * Returns the maximum value (float) of a comma separated string. It is used + * to find the longest CSS animation in a set of multiple animations. + * @param {string} string - The comma separated string. + * @function TK.get_max_time + * @returns {number} + * @example + * get_max_time(get_style(DOMNode, "animation-duration")); + */ + var ret = 0, i, tmp, s = string; + + if (typeof(s) === "string") { + s = s.split(","); + for (i = 0; i < s.length; i++) { + tmp = parseFloat(s[i]); + + if (tmp > 0) { + if (-1 === s[i].search("ms")) tmp *= 1000; + if (tmp > ret) ret = tmp; + } + } + } + + return ret|0; +} + +function get_duration(element) { + /** + * Returns the longest animation duration of CSS animations and transitions. + * @param {HTMLElement} element - The element to evalute the animation duration for. + * @function TK.get_duration + * @returns {number} + */ + return Math.max(get_max_time(get_style(element, "animation-duration")) + + get_max_time(get_style(element, "animation-delay")), + get_max_time(get_style(element, "transition-duration")) + + get_max_time(get_style(element, "transition-delay"))); +} + +function get_id(id) { + /** + * Returns the DOM node with the given ID. Shorthand for document.getElementById. + * @param {string} id - The ID to search for + * @function TK.get_id + * @returns {HTMLElement} + */ + return document.getElementById(id); +} +function get_class(cls, element) { + /** + * Returns all elements as NodeList of a given class name. Optionally limit the list + * to all children of a specific DOM node. Shorthand for element.getElementsByClassName. + * @param {string} class - The name of the class + * @param {DOMNode} element - Limit search to child nodes of this element. Optional. + * @returns {NodeList} + * @function TK.get_class + */ + return (element ? element : document).getElementsByClassName(cls); +} +function get_tag(tag, element) { + /** + * Returns all elements as NodeList of a given tag name. Optionally limit the list + * to all children of a specific DOM node. Shorthand for element.getElementsByTagName. + * @param {string} tag - The name of the tag + * @param {DOMNode} element - Limit search to child nodes of this element. Optional. + * @returns {NodeList} + * @function TK.get_tag + */ + return (element ? element : document).getElementsByTagName(tag); +} +function element(tag) { + /** + * Returns a newly created HTMLElement. + * @param {string} tag - The type of the element + * @param {...object} attributes - Optional mapping of attributes for the new node + * @param {...string} class - Optional class name for the new node + * @returns HTMLElement + * @function TK.element + */ + var n = document.createElement(tag); + var i, v, j; + for (i = 1; i < arguments.length; i++) { + v = arguments[i]; + if (typeof v === "object") { + for (var key in v) { + if (v.hasOwnProperty(key)) + n.setAttribute(key, v[key]); + } + } else if (typeof v === "string") { + add_class(n, v); + } else throw("unsupported argument to TK.element"); + } + return n; +} +function empty(element) { + /** + * Removes all child nodes from an HTMLElement. + * @param {HTMLElement} element - The element to clean up + * @function TK.empty + */ + while (element.lastChild) element.removeChild(element.lastChild); +} +function set_text(element, text) { + /** + * Sets a string as new exclusive text node of an HTMLElement. + * @param {HTMLElement} element - The element to clean up + * @param {string} text - The string to set as text content + * @function TK.set_text + */ + if (element.childNodes.length === 1 && typeof element.childNodes[0].data === "string") + element.childNodes[0].data = text; + else + element.textContent = text; +} +function html(string) { + /** + * Returns a documentFragment containing the result of a string parsed as HTML. + * @param {string} html - A string to parse as HTML + * @returns {HTMLFragment} + * @function TK.html + */ + /* NOTE: setting innerHTML on a document fragment is not supported */ + var e = document.createElement("div"); + var f = document.createDocumentFragment(); + e.innerHTML = string; + while (e.firstChild) f.appendChild(e.firstChild); + return f; +} +function set_content(element, content) { + /** + * Sets the (exclusive) content of an HTMLElement. + * @param {HTMLElement} element - The element receiving the content + * @param {String|HTMLElement} content - A string or HTMLElement to set as content + * @function TK.set_content + */ + if (is_dom_node(content)) { + empty(element); + if (content.parentNode) { + TK.warn("set_content: possible reuse of a DOM node. cloning\n"); + content = content.cloneNode(true); + } + element.appendChild(content); + } else { + set_text(element, content + ""); + } +} +function insert_after(newnode, refnode) { + /** + * Inserts one HTMLELement after another in the DOM tree. + * @param {HTMLElement} newnode - The new node to insert into the DOM tree + * @param {HTMLElement} refnode - The reference element to add the new element after + * @function TK.insert_after + */ + if (refnode.parentNode) + refnode.parentNode.insertBefore(newnode, refnode.nextSibling); +} +function insert_before(newnode, refnode) { + /** + * Inserts one HTMLELement before another in the DOM tree. + * @param {HTMLElement} newnode - The new node to insert into the DOM tree + * @param {HTMLElement} refnode - The reference element to add the new element before + * @function TK.insert_before + */ + if (refnode.parentNode) + refnode.parentNode.insertBefore(newnode, refnode); +} +function width() { + /** + * Returns the width of the viewport. + * @returns {number} + * @function TK.width + */ + return Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0, document.body.clientWidth || 0); +} +function height() { + /** + * Returns the height of the viewport. + * @returns {number} + * @function TK.height + */ + return Math.max(document.documentElement.clientHeight, window.innerHeight || 0, document.body.clientHeight || 0); +} +function scroll_top(element) { + /** + * Returns the amount of CSS pixels the document or an optional element is scrolled from top. + * @param {HTMLElement} element - The element to evaluate. Optional. + * @returns {number} + * @functionTK.scroll_top + */ + if (element) + return element.scrollTop; + return Math.max(document.documentElement.scrollTop || 0, window.pageYOffset || 0, document.body.scrollTop || 0); +} +function scroll_left(element) { + /** + * Returns the amount of CSS pixels the document or an optional element is scrolled from left. + * @param {HTMLElement} element - The element to evaluate. Optional. + * @returns {number} + * @functionTK.scroll_left + */ + if (element) + return element.scrollLeft; + return Math.max(document.documentElement.scrollLeft, window.pageXOffset || 0, document.body.scrollLeft || 0); +} +function scroll_all_top(element) { + /** + * Returns the sum of CSS pixels an element and all of its parents are scrolled from top. + * @param {HTMLElement} element - The element to evaluate + * @returns {number} + * @functionTK.scroll_all_top + */ + var v = 0; + while (element = element.parentNode) v += element.scrollTop || 0; + return v; +} +function scroll_all_left(element) { + /** + * Returns the sum of CSS pixels an element and all of its parents are scrolled from left. + * @param {HTMLElement} element - The element to evaluate + * @returns {number} + * @functionTK.scroll_all_left + */ + var v = 0; + while (element = element.parentNode) v += element.scrollLeft || 0; + return v; +} +function position_top(e, rel) { + /** + * Returns the position from top of an element in relation to the document + * or an optional HTMLElement. Scrolling of the parent is taken into account. + * @param {HTMLElement} element - The element to evaluate + * @param {HTMLElement} relation - The element to use as reference. Optional. + * @returns {number} + * @function TK.position_top + */ + var top = parseInt(e.getBoundingClientRect().top); + var f = fixed(e) ? 0 : scroll_top(); + return top + f - (rel ? position_top(rel) : 0); +} +function position_left(e, rel) { + /** + * Returns the position from the left of an element in relation to the document + * or an optional HTMLElement. Scrolling of the parent is taken into account. + * @param {HTMLElement} element - The element to evaluate + * @param {HTMLElement} relation - The element to use as reference. Optional. + * @returns {number} + * @function TK.position_left + */ + var left = parseInt(e.getBoundingClientRect().left); + var f = fixed(e) ? 0 : scroll_left(); + return left + f - (rel ? position_left(rel) : 0); +} +function fixed(e) { + /** + * Returns if an element is positioned fixed to the viewport + * @param {HTMLElement} element - the element to evaluate + * @returns {boolean} + * @function TK.fixed + */ + return getComputedStyle(e).getPropertyValue("position") === "fixed"; +} +function outer_width(element, margin, width) { + /** + * Gets or sets the outer width of an element as CSS pixels. The box sizing + * method is taken into account. + * @param {HTMLElement} element - the element to evaluate / manipulate + * @param {boolean} margin - Determine if margin is included + * @param {number} width - If defined the elements outer width is set to this value + * @returns {number} + * @functionTK.outer_width + */ + var m = 0; + if (margin) { + var cs = getComputedStyle(element); + m += parseFloat(cs.getPropertyValue("margin-left")); + m += parseFloat(cs.getPropertyValue("margin-right")); + } + if (width !== void(0)) { + if (box_sizing(element) === "content-box") { + var css = css_space(element, "padding", "border"); + width -= css.left + css.right; + } + width -= m; + // TODO: fixme + if (width < 0) return 0; + element.style.width = width + "px"; + return width; + } else { + var w = element.getBoundingClientRect().width; + return w + m; + } +} +function outer_height(element, margin, height) { + /** + * Gets or sets the outer height of an element as CSS pixels. The box sizing + * method is taken into account. + * @param {HTMLElement} element - the element to evaluate / manipulate + * @param {boolean} margin - Determine if margin is included + * @param {number} height - If defined the elements outer height is set to this value + * @returns {number} + * @functionTK.outer_height + */ + var m = 0; + if (margin) { + var cs = getComputedStyle(element, null); + m += parseFloat(cs.getPropertyValue("margin-top")); + m += parseFloat(cs.getPropertyValue("margin-bottom")); + } + if (height !== void(0)) { + if (box_sizing(element) === "content-box") { + var css = css_space(element, "padding", "border"); + height -= css.top + css.bottom; + } + height -= m; + // TODO: fixme + if (height < 0) return 0; + element.style.height = height + "px"; + return height; + } else { + var h = element.getBoundingClientRect().height; + return h + m; + } +} +function inner_width(element, width) { + /** + * Gets or sets the inner width of an element as CSS pixels. The box sizing + * method is taken into account. + * @param {HTMLElement} element - the element to evaluate / manipulate + * @param {number} width - If defined the elements inner width is set to this value + * @returns {number} + * @functionTK.inner_width + */ + var css = css_space(element, "padding", "border"); + var x = css.left + css.right; + if (width !== void(0)) { + if (box_sizing(element) === "border-box") + width += x; + // TODO: fixme + if (width < 0) return 0; + element.style.width = width + "px"; + return width; + } else { + var w = element.getBoundingClientRect().width; + return w - x; + } +} +function inner_height(element, height) { + /** + * Gets or sets the inner height of an element as CSS pixels. The box sizing + * method is taken into account. + * @param {HTMLElement} element - the element to evaluate / manipulate + * @param {number} height - If defined the elements outer height is set to this value + * @returns {number} + * @functionTK.inner_height + */ + var css = css_space(element, "padding", "border"); + var y = css.top + css.bottom; + if (height !== void(0)) { + if (box_sizing(element) === "border-box") + height += y; + // TODO: fixme + if (height < 0) return 0; + element.style.height = height + "px"; + return height; + } else { + var h = element.getBoundingClientRect().height; + return h - y; + } +} +function box_sizing(element) { + /** + * Returns the box-sizing method of an HTMLElement. + * @param {HTMLElement} element - The element to evaluate + * @returns {string} + * @functionTK.box_sizing + */ + var cs = getComputedStyle(element, null); + if (cs.getPropertyValue("box-sizing")) return cs.getPropertyValue("box-sizing"); + if (cs.getPropertyValue("-moz-box-sizing")) return cs.getPropertyValue("-moz-box-sizing"); + if (cs.getPropertyValue("-webkit-box-sizing")) return cs.getPropertyValue("-webkit-box-sizing"); + if (cs.getPropertyValue("-ms-box-sizing")) return cs.getPropertyValue("-ms-box-sizing"); + if (cs.getPropertyValue("-khtml-box-sizing")) return cs.getPropertyValue("-khtml-box-sizing"); +} +function css_space(element) { + /** + * Returns the overall spacing around an HTMLElement of all given attributes. + * @param {HTMLElement} element - The element to evaluate + * @param{...string} The CSS attributes to take into account + * @returns {object} An object with the members "top", "bottom", "lfet", "right" + * @function TK.css_space + * @example + * TK.css_space(element, "padding", "border"); + */ + var cs = getComputedStyle(element, null); + var o = {top: 0, right: 0, bottom: 0, left: 0}; + var a; + var s; + for (var i = 1; i < arguments.length; i++) { + a = arguments[i]; + for (var p in o) { + if (o.hasOwnProperty(p)) { + s = a + "-" + p; + if (a === "border") s += "-width"; + } + o[p] += parseFloat(cs.getPropertyValue(s)); + } + } + return o; +} + +var number_attributes = [ + "animation-iteration-count", + "column-count", + "flex-grow", + "flex-shrink", + "opacity", + "order", + "z-index" +] + +function set_styles(elem, styles) { + /** + * Set multiple CSS styles onto an HTMLElement. + * @param {HTMLElement} element - the element to add the styles to + * @param {object} styles - A mapping containing all styles to add + * @function TK.set_styles + * @example + * TK.set_styles(element, {"width":"100px", "height":"100px"}); + */ + var key, v; + var s = elem.style; + for (key in styles) if (styles.hasOwnProperty(key)) { + v = styles[key]; + if (typeof v !== "number" && !v) { + delete s[key]; + } else { + if (typeof v === "number" && number_attributes.indexOf(key) == -1) { + TK.warn("TK.set_styles: use of implicit px conversion is _deprecated_ and will be removed in the future."); + v = v.toFixed(3) + "px"; + } + s[key] = v; + } + } +} +function set_style(e, style, value) { + /** + * Sets a single CSS style onto an HTMLElement. It is used to autimatically + * add "px" to numbers and trim them to 3 digits at max. DEPRECATED! + * @param {HTMLElement} element - The element to set the style to + * @param {string} style - The CSS attribute to set + * @param {string|number} value - The value to set the CSS attribute to + * @function TK.set_style + */ + if (typeof value === "number") { + /* By default, numbers are transformed to px. I believe this is a very _dangerous_ default + * behavior, because it breaks other number like properties _without_ warning. + * this is now deprecated. */ + TK.warn("TK.set_style: use of implicit px conversion is _deprecated_ and will be removed in the future."); + value = value.toFixed(3) + "px"; + } + e.style[style] = value; +} +var _id_cnt = 0; +function unique_id() { + /** + * Generate a unique ID string. + * @returns {string} + * @function TK.unique_id + */ + var id; + do { id = "tk-" + _id_cnt++; } while (document.getElementById(id)); + return id; +}; + +/** + * Generates formatting functions from sprintf-style format strings. + * This is generally faster when the same format string is used many times. + * + * @returns {function} A formatting function. + * @param {string} fmt - The format string. + * @function TK.FORMAT + * @example + * var f = TK.FORMAT("%.2f Hz"); + * @see TK.sprintf + */ +function FORMAT(fmt) { + var args = []; + var s = "return "; + var res; + var last = 0; + var argnum = 0; + var precision; + var regexp = /%(\.\d+)?([bcdefgosO%])/g; + var argname; + + while (res = regexp.exec(fmt)) { + if (argnum) s += "+"; + s += JSON.stringify(fmt.substr(last, regexp.lastIndex - res[0].length - last)); + s += "+"; + argname = "a"+argnum; + if (args.indexOf(argname) === -1) + args.push(argname); + if (argnum+1 < arguments.length) { + argname = "(" + sprintf(arguments[argnum+1].replace("%", "%s"), argname) + ")"; + } + switch (res[2].charCodeAt(0)) { + case 100: // d + s += "("+argname+" | 0)"; + break; + case 102: // f + if (res[1]) { // length qualifier + precision = parseInt(res[1].substr(1)); + s += "(+"+argname+").toFixed("+precision+")"; + } else { + s += "(+"+argname+")"; + } + break; + case 115: // s + s += argname; + break; + case 37: + s += "\"%\""; + argnum--; + break; + case 79: + case 111: + s += "JSON.stringify("+argname+")"; + break; + default: + throw("unknown format:"+res[0]); + break; + } + argnum++; + last = regexp.lastIndex; + } + + if (argnum) s += "+"; + s += JSON.stringify(fmt.substr(last)); + + return new Function(args, s); +} + +/** + * Formats the arguments according to a given format string. + * @returns {function} A formatting function. + * @param {string} fmt - The format string. + * @param {...*} args - The format arguments. + * @function TK.sprintf + * @example + * TK.sprintf("%d Hz", 440); + * @see TK.FORMAT + */ +function sprintf(fmt) { + var arg_len = arguments.length; + var i, last_fmt; + var c, arg_num = 1; + var ret = []; + var precision, s; + var has_precision = false; + + for (last_fmt = 0; -1 !== (i = fmt.indexOf("%", last_fmt)); last_fmt = i+1) { + if (last_fmt < i) { + ret.push(fmt.substring(last_fmt, i)); + } + + i ++; + + if (has_precision = (fmt.charCodeAt(i) === 46 /* '.' */)) { + i++; + precision = parseInt(fmt.substr(i)); + while ((c = fmt.charCodeAt(i)) >= 48 && c <= 57) i++; + } + + c = fmt.charCodeAt(i); + + if (c === 37) { + ret.push("%"); + continue; + } + + s = arguments[arg_num++]; + + switch (fmt.charCodeAt(i)) { + case 102: /* f */ + s = +s; + if (has_precision) { + s = s.toFixed(precision); + } + break; + case 100: /* d */ + s = s|0; + break; + case 115: /* s */ + break; + case 79: /* O */ + case 111: /* o */ + s = JSON.stringify(s); + break; + default: + throw("Unsupported format."); + } + + ret.push(s); + + last_fmt = i+1; + } + + if (last_fmt < fmt.length) { + ret.push(fmt.substring(last_fmt, fmt.length)); + } + + return ret.join(""); +} + +function escapeHTML(text) { + /** + * Escape an HTML string to be displayed as text. + * @param {string} html - The HTML code to escape + * @returns {string} + * @function TK.escapeHTML + */ + var map = { + '&' : '&', + '<' : '<', + '>' : '>', + '"' : '"', + "'" : ''' + }; + return text.replace(/[&<>"']/g, function(m) { return map[m]; }); +} + +function is_touch() { + /** + * Check if a device is touch-enabled. + * @returns {boolean} + * @function TK.is_touch + */ + return 'ontouchstart' in window // works on most browsers + || 'onmsgesturechange' in window; // works on ie10 +} +function os() { + /** + * Return the operating system + * @returns {string} + * @function TK.os + */ + var ua = navigator.userAgent.toLowerCase(); + if (ua.indexOf("android") > -1) + return "Android"; + if (/iPad/i.test(ua) || /iPhone OS 3_1_2/i.test(ua) || /iPhone OS 3_2_2/i.test(ua)) + return "iOS"; + if ((ua.match(/iPhone/i)) || (ua.match(/iPod/i))) + return "iOS"; + if (navigator.appVersion.indexOf("Win")!=-1) + return "Windows"; + if (navigator.appVersion.indexOf("Mac")!=-1) + return "MacOS"; + if (navigator.appVersion.indexOf("X11")!=-1) + return "UNIX"; + if (navigator.appVersion.indexOf("Linux")!=-1) + return "Linux"; +} +function make_svg(tag, args) { + /** + * Creates and returns an SVG child element. + * @param {string} tag - The element to create as string, e.g. "line" or "g" + * @param {object} arguments - The attributes to set onto the element + * @returns {SVGElement} + */ + var el = document.createElementNS('http://www.w3.org/2000/svg', "svg:" + tag); + for (var k in args) + el.setAttribute(k, args[k]); + return el; +} +function seat_all_svg(parent) { + /** + * Searches for all SVG that don't have the class "svg-fixed" and re-positions them + * in order to avoid blurry lines. + * @param {HTMLElement} parent - If set only children of parent are searched + * @function TK.seat_all_svg + */ + var a = get_tag("svg", parent); + for (var i = 0; i < a.length; i++) { + if (!has_class(a[i], "svg-fixed")) + seat_svg(a[i]); + } +} +function seat_svg(e) { + /** + * Move SVG for some sub-pixel if their position in viewport is not int. + * @param {SVGElement} svg - The SVG to manipulate + * @function TK.seat_svg + */ + if (retrieve(e, "margin-left") === null) { + store(e, "margin-left", parseFloat(get_style(e, "margin-left"))); + } else { + e.style.marginLeft = retrieve(e, "margin-left") || 0; + } + var l = parseFloat(retrieve(e, "margin-left")) || 0; + var b = e.getBoundingClientRect(); + var x = b.left % 1; + if (x) { + if (x < 0.5) l -= x; + else l += (1 - x); + } + if (e.parentElement && get_style(e.parentElement, "text-align") === "center") + l += 0.5; + e.style.marginLeft = l + "px"; + //console.log(l); + if (retrieve(e, "margin-top") === null) { + store(e, "margin-top", parseFloat(get_style(e, "margin-top"))); + } else { + e.style.marginTop = retrieve(e, "margin-top") || 0; + } + var t = parseFloat(retrieve(e, "margin-top") || 0); + var b = e.getBoundingClientRect(); + var y = b.top % 1; + if (y) { + if (x < 0.5) t -= y; + else t += (1 - y); + } + //console.log(t); + e.style.marginTop = t + "px"; +} +function delayed_callback(timeout, cb, once) { + var tid; + var args; + + var my_cb = function() { + tid = null; + cb.apply(this, args); + }; + return function() { + args = Array.prototype.slice.call(arguments); + + if (tid) + window.clearTimeout(tid); + else if (once) once(); + tid = window.setTimeout(my_cb, timeout); + }; +} + +function store(e, key, val) { + /** + * Store a piece of data in an object. + * @param {object} object - The object to store the data + * @param {string} key - The key to identify the memory + * @param {*} data - The data to store + * @function TK.store + */ + data(e)[key] = val; +} +function retrieve(e, key) { + /** + * Retrieve a piece of data from an object. + * @param {object} object - The object to retrieve the data from + * @param {string} key - The key to identify the memory + * @function TK.retrieve + * @returns {*} + */ + return data(e)[key]; +} +function merge(dst) { + /** + * Merge two or more objects. The second and all following objects + * will be merged into the first one. + * @param {...object} object - The objects to merge + * @returns {object} + * @function TK.merge + */ + //console.log("merging", src, "into", dst); + var key, i, src; + for (i = 1; i < arguments.length; i++) { + src = arguments[i]; + for (key in src) { + dst[key] = src[key]; + } + } + return dst; +} +function object_and(orig, filter) { + /** + * Filter an object via white list. + * @param {object} origin - The object to filter + * @param {object} filter - The object containing the white list + * @returns {object} The filtered result + * @function TK.object_and + */ + var ret = {}; + for (var key in orig) { + if (filter[key]) ret[key] = orig[key]; + } + return ret; +} +function object_sub(orig, filter) { + /** + * Filter an object via black list. + * @param {object} origin - The object to filter + * @param {object} filter - The object containing the black list + * @returns {object} The filtered result + * @function TK.object_sub + */ + var ret = {}; + for (var key in orig) { + if (!filter[key]) ret[key] = orig[key]; + } + return ret; +} +function to_array(collection) { + /** + * Convert any collection (like NodeList) into an array. + * @param {collection} collection - The collection to convert into an array + * @returns {array} + * @functionTK.to_array + */ + var ret = new Array(collection.length); + var i; + + for (i = 0; i < ret.length; i++) { + ret[i] = collection[i]; + } + + return ret; +} +function is_dom_node(o) { + /* this is broken for SVG */ + return typeof o === "object" && o instanceof Node; +} + +// NOTE: IE9 will throw errors when console is used without debugging tools. In general, it +// is better for log/warn to silently fail in case of error. This unfortunately means that +// warnings might be lost, but probably better than having diagnostics and debugging code +// break an application + +/** + * Generates an error to the JavaScript console. This is virtually identical to console.error, however + * it can safely be used in browsers which do not support it. + * + * @param {...*} args + * @function TK.error + */ +function error() { + if (!window.console) return; + try { + window.console.error.apply(window.console, arguments); + } catch(e) {} +} + +/** + * Generates a warning to the JavaScript console. This is virtually identical to console.warn, however + * it can safely be used in browsers which do not support it. + * + * @param {...*} args + * @function TK.warn + */ +function warn() { + if (!window.console) return; + try { + window.console.warn.apply(window.console, arguments); + } catch(e) {} +} +/** + * Generates a log message to the JavaScript console. This is virtually identical to console.log, however + * it can safely be used in browsers which do not support it. + * + * @param {...*} args + * @function TK.log + */ +function log() { + if (!window.console) return; + try { + window.console.log.apply(window.console, arguments); + } catch(e) {} +} + +function print_widget_tree(w, depth) { + if (!depth) depth = 0; + + var print = function(fmt) { + var extra = Array.prototype.slice.call(arguments, 1); + if (depth) fmt = nchars(depth, " ") + fmt; + var args = [ fmt ]; + log.apply(TK, args.concat(extra)); + }; + + var nchars = function(n, c) { + var ret = new Array(n); + + for (var i = 0; i < n; i++) ret[i] = c; + + return ret.join(""); + }; + + var C = w.children; + var nchildren = C ? C.length : 0; + + var state = [ ]; + + state.push(w._drawn ? "show" : "hide"); + + if (w.needs_redraw) state.push("redraw"); + if (w.needs_resize) state.push("resize"); + + + print("%s (%s, children: %o)", w._class, state.join(" "), nchildren); + + if (C) { + for (var i = 0; i < C.length; i++) print_widget_tree(C[i], depth+1); + } +} + +/* Detection and handling for passive event handler support. + * The chrome team has threatened to make passive event handlers + * the default in a future version. To make sure that this does + * not break our code, we explicitly register 'active' event handlers + * for most cases. + */ + +/* generic code, supports node arrays */ +function add_event_listener(e, type, cb, options) { + if (Array.isArray(e)) { + for (var i = 0; i < e.length; i++) + e[i].addEventListener(type, cb, options); + } else e.addEventListener(type, cb, options); +} +function remove_event_listener(e, type, cb, options) { + if (Array.isArray(e)) { + for (var i = 0; i < e.length; i++) + e[i].removeEventListener(type, cb, options); + } else e.removeEventListener(type, cb, options); +} + +/* Detect if the 'passive' option is supported. + * This code has been borrowed from mdn */ +var passiveSupported = false; + +try { + var options = Object.defineProperty({}, "passive", { + get: function() { + passiveSupported = true; + } + }); + + window.addEventListener("test", null, options); + window.removeEventListener("test", null); +} catch(err) {} + +var active_options, passive_options; + +if (passiveSupported) { + active_options = { passive: false }; + passive_options = { passive: true }; +} else { + active_options = false; + passive_options = false; +} + +function add_active_event_listener(e, type, cb) { + add_event_listener(e, type, cb, active_options); +} +function remove_active_event_listener(e, type, cb) { + remove_event_listener(e, type, cb, active_options); +} +function add_passive_event_listener(e, type, cb) { + add_event_listener(e, type, cb, passive_options); +} +function remove_passive_event_listener(e, type, cb) { + remove_event_listener(e, type, cb, passive_options); +} + +TK = w.toolkit = w.TK = { + // ELEMENTS + S: new w.DOMScheduler(), + is_dom_node: is_dom_node, + get_id: get_id, + get_class: get_class, + get_tag: get_tag, + element : element, + empty: empty, + set_text : set_text, + set_content : set_content, + has_class : has_class, + remove_class : remove_class, + add_class : add_class, + toggle_class : toggle_class, + is_class_name : is_class_name, + + insert_after: insert_after, + insert_before: insert_before, + + // WINDOW + + width: width, + height: height, + + // DIMENSIONS + + scroll_top: scroll_top, + scroll_left: scroll_left, + scroll_all_top: scroll_all_top, + scroll_all_left: scroll_all_left, + + position_top: position_top, + position_left: position_left, + + fixed: fixed, + + outer_width : outer_width, + + outer_height : outer_height, + + inner_width: inner_width, + + inner_height: inner_height, + + box_sizing: box_sizing, + + css_space: css_space, + + // CSS AND CLASSES + + set_styles : set_styles, + set_style: set_style, + get_style: get_style, + get_duration: get_duration, + + // STRINGS + + unique_id: unique_id, + + FORMAT : FORMAT, + + sprintf : sprintf, + html : html, + + escapeHTML : escapeHTML, + + // OS AND BROWSER CAPABILITIES + + is_touch: is_touch, + os: os, + + browser: function () { + /** + * Returns the name of the browser + * @returns {string} + * @function TK.browser + */ + var ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; + if (/trident/i.test(M[1])) { + tem = /\brv[ :]+(\d+)/g.exec(ua) || []; + return { name : 'IE', version : (tem[1]||'') }; + } + if (M[1] === 'Chrome') { + tem = ua.match(/\bOPR\/(\d+)/) + if (tem!=null) + return { name : 'Opera', version : tem[1] }; + } + M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?']; + if ((tem = ua.match(/version\/(\d+)/i)) !== null) { M.splice(1, 1, tem[1]); } + return { name : M[0], version : M[1] }; + }(), + + supports_transform: function () { return 'transform' in document.createElement("div").style; }(), + + // SVG + + make_svg: make_svg, + seat_all_svg: seat_all_svg, + seat_svg: seat_svg, + + + // EVENTS + + delayed_callback : delayed_callback, + add_event_listener: add_active_event_listener, + remove_event_listener: remove_active_event_listener, + add_passive_event_listener: add_passive_event_listener, + remove_passive_event_listener: remove_passive_event_listener, + + // OTHER + + data: data, + store: store, + retrieve: retrieve, + merge: merge, + object_and: object_and, + object_sub: object_sub, + to_array: to_array, + warn: warn, + error: error, + log: log, + assign_warn: function(a) { + for (var i = 1; i < arguments.length; i++) { + var b = arguments[i]; + for (var key in b) if (b.hasOwnProperty(key)) { + if (a[key] === b[key]) { + TK.warn("overwriting identical", key, "(", a[key], ")"); + } else if (a[key]) { + TK.warn("overwriting", key, "(", a[key], "vs", b[key], ")"); + } + a[key] = b[key]; + } + } + + return a; + }, + print_widget_tree: print_widget_tree, +}; + +// POLYFILLS + +if (Array.isArray === void(0)) { + Array.isArray = function(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + } +}; + +if (Object.assign === void(0)) { + Object.defineProperty(Object, 'assign', { + enumerable: false, + configurable: true, + writable: true, + value: function(target) { + 'use strict'; + if (target === void(0) || target === null) { + throw new TypeError('Cannot convert first argument to object'); + } + + var to = Object(target); + for (var i = 1; i < arguments.length; i++) { + var nextSource = arguments[i]; + if (nextSource === void(0) || nextSource === null) { + continue; + } + nextSource = Object(nextSource); + + var keysArray = Object.keys(Object(nextSource)); + for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { + var nextKey = keysArray[nextIndex]; + var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); + if (desc !== void(0) && desc.enumerable) { + to[nextKey] = nextSource[nextKey]; + } + } + } + return to; + } + }); +} + +if (!('remove' in Element.prototype)) { + Element.prototype.remove = function() { + this.parentNode.removeChild(this); + }; +} +})(this); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/README b/share/web_surfaces/builtin/mixer/toolkit/widgets/README new file mode 100644 index 0000000000..9235a14e0b --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/README @@ -0,0 +1,5 @@ +@category: Widgets + +Widgets are fully functional elements to build user interfaces. +They typically rely on other elements like #Modules and #Implements and +are somehow based on #Widget itself or other widgets. diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/button.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/button.js new file mode 100644 index 0000000000..2053a5dacd --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/button.js @@ -0,0 +1,114 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +"use strict"; +(function(w, TK){ + +TK.Button = TK.class({ + /** + * TK.Button is a simple, clickable widget containing an + * {@link TK.Icon} and a {@link TK.Label} to trigger functions. + * Button serves as base for other widgets, too, e.g. + * {@link TK.Toggle}, {@link TK.ConfirmButton} and {@link TK.Select}. + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String|Boolean} [options.label=false] - Text for the + * button label. Set to false to remove the label + * from DOM. + * @property {String|Boolean} [options.icon=false] - URL to an image + * file or an icon class (see styles/fonts/Toolkit.html). If set + * to false, the icon is removed from DOM. + * @property {Boolean} [options.state=false] - State of the button, + * reflected as class toolkit-active. + * @property {String} [options.layout="horizontal"] - Define the + * arrangement of label and icon. vertical means icon + * above the label, horizontal places the icon left + * to the label. + * + * @extends TK.Widget + * + * @class TK.Button + */ + _class: "Button", + Extends: TK.Widget, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + label: "string|boolean", + icon: "string|boolean", + state: "boolean", + layout: "string", + }), + options: { + label: false, + icon: false, + state: false, + layout: "horizontal" + }, + initialize: function (options) { + var E; + TK.Widget.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Button#element - The main DIV element. + * Has class toolkit-button. + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-button"); + this.widgetize(E, true, true, true); + }, + destroy: function () { + TK.Widget.prototype.destroy.call(this); + }, + + redraw: function() { + TK.Widget.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + var E = this.element; + + if (I.layout) { + I.layout = false; + TK.toggle_class(E, "toolkit-vertical", O.layout === "vertical"); + TK.toggle_class(E, "toolkit-horizontal", O.layout !== "vertical"); + } + + if (I.state) { + I.state = false; + TK.toggle_class(E, "toolkit-active", O.state); + } + }, +}); +/** + * @member {TK.Icon} TK.Button#icon - The {@link TK.Icon} widget. + */ +TK.ChildWidget(TK.Button, "icon", { + create: TK.Icon, + option: "icon", + inherit_options: true, + toggle_class: true, +}); +/** + * @member {TK.Label} TK.Button#label - The {@link TK.Label} of the button. + */ +TK.ChildWidget(TK.Button, "label", { + create: TK.Label, + option: "label", + inherit_options: true, + toggle_class: true, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/buttonarray.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/buttonarray.js new file mode 100644 index 0000000000..09bfe39532 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/buttonarray.js @@ -0,0 +1,459 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + /** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the option show. + * + * @event TK.Knob#useraction + * @param {string} name - The name of the option which was changed due to the users action. + * @param {mixed} value - The new value of the option. + */ + +"use strict"; +(function(w, TK){ +function hide_arrows() { + if (!this._prev.parentNode) return; + if (this._prev.parentNode) this._prev.remove(); + if (this._next.parentNode) this._next.remove(); + var E = this.element; + TK.remove_class(E, "toolkit-over"); + this.trigger_resize(); +} +function show_arrows() { + if (this._prev.parentNode) return; + var E = this.element; + E.insertBefore(this._prev, this._clip); + E.appendChild(this._next); + TK.add_class(E, "toolkit-over"); + this.trigger_resize(); +} +function prev_clicked(e) { + this.userset("show", Math.max(0, this.options.show - 1)); +} +function prev_dblclicked(e) { + this.userset("show", 0); +} + +function next_clicked(e) { + this.userset("show", Math.min(this.buttons.length-1, this.options.show + 1)); +} +function next_dblclicked(e) { + this.userset("show", this.buttons.length-1); +} + +function button_clicked(button) { + this.userset("show", this.buttons.indexOf(button)); +} +function easeInOut (t, b, c, d) { + t /= d/2; + if (t < 1) return c/2*t*t + b; + t--; + return -c/2 * (t*(t-2) - 1) + b; +} + +var zero = { width: 0, height: 0}; + +TK.ButtonArray = TK.class({ + /** + * TK.ButtonArray is a list of ({@link TK.Button})s, arranged + * either vertically or horizontally. TK.ButtonArray is able to + * add arrow buttons automatically if the overal size is less + * than the width/height of the buttons list. + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Array} [options.buttons=[]] - A list of + * button options objects or label strings which is converted to + * button instances on init. If `get` is called, a converted list + * of button instances is returned. + * @property {Boolean} [options.auto_arrows=true] - Set to `false` + * to disable auto-generated arrow buttons on overflow. + * @property {String} [options.direction="horizontal"] - The layout + * of the button list, either "horizontal" or "vertical". + * @property {Integer|TK.Button} [options.show=-1] - The {@link TK.Button} + * to scroll to and highlight, expects either the button index starting + * from zero or the {@link TK.Button} instance itself. Set to `-1` to + * de-select any selected button. + * @property {Integer} [options.scroll=0] - Offer scrollbars for generic + * scrolling. This reduces performance because movement is done in JS + * instead of (pesumably accelerated) CSS transitions. 0 for standard + * behavior, n > 0 is handled as milliseconds for transitions. + * @property {Object} [options.button_class=TK.Button] - A class to + * be used for instantiating the buttons. + * + * @class TK.ButtonArray + * + * @extends TK.Container + */ + _class: "ButtonArray", + Extends: TK.Container, + _options: Object.assign(Object.create(TK.Container.prototype._options), { + buttons: "array", + auto_arrows: "boolean", + direction: "string", + show: "int", + resized: "boolean", + scroll: "int", + button_class: "TK.Button", + }), + options: { + buttons: [], + auto_arrows: true, + direction: "horizontal", + show: -1, + resized: false, + scroll: 0, + button_class: TK.Button, + }, + static_events: { + set_buttons: function(value) { + for (var i = 0; i < this.buttons.length; i++) + this.buttons[i].destroy(); + this.buttons = []; + this.add_buttons(value); + }, + set_direction: function(value) { + this.prev.set("label", value === "vertical" ? "\u25B2" : "\u25C0"); + this.next.set("label", value === "vertical" ? "\u25BC" : "\u25B6"); + }, + set_show: function(value) { + var button = this.current(); + if (button) { + button.set("state", true); + /** + * Is fired when a button is activated. + * + * @event TK.ButtonArray#changed + * + * @param {TK.Button} button - The {@link TK.Button} which was clicked. + * @param {int} id - the ID of the clicked {@link TK.Button}. + */ + this.fire_event("changed", button, value); + } + }, + }, + initialize: function (options) { + /** + * @member {Array} TK.ButtonArray#buttons - An array holding all {@link TK.Button}s. + */ + this.buttons = []; + TK.Container.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.ButtonArray#element - The main DIV container. + * Has class toolkit-buttonarray. + */ + TK.add_class(this.element, "toolkit-buttonarray"); + /** + * @member {HTMLDivElement} TK.ButtonArray#_clip - A clipping area containing the list of {@link TK.Button}s. + * Has class toolkit-clip. + */ + this._clip = TK.element("div", "toolkit-clip"); + /** + * @member {HTMLDivElement} TK.ButtonArray#_container - A container for all the {@link TK.Button}s. + * Has class toolkit-container. + */ + this._container = TK.element("div", "toolkit-container"); + this.element.appendChild(this._clip); + this._clip.appendChild(this._container); + + var vert = this.get("direction") === "vertical"; + + /** + * @member {TK.Button} TK.ButtonArray#prev - The previous arrow {@link TK.Button} instance. + */ + this.prev = new TK.Button({class: "toolkit-previous", dblclick:400}); + /** + * @member {TK.Button} TK.ButtonArray#next - The next arrow {@link TK.Button} instance. + */ + this.next = new TK.Button({class: "toolkit-next", dblclick:400}); + + this.prev.add_event("click", prev_clicked.bind(this)); + this.prev.add_event("doubleclick", prev_dblclicked.bind(this)); + this.next.add_event("click", next_clicked.bind(this)); + this.next.add_event("doubleclick", next_dblclicked.bind(this)); + + /** + * @member {HTMLDivElement} TK.ButtonArray#_prev - The HTMLDivElement of the previous {@link TK.Button}. + */ + this._prev = this.prev.element; + /** + * @member {HTMLDivElement} TK.ButtonArray#_next - The HTMLDivElement of the next {@link TK.Button}. + */ + this._next = this.next.element; + + this.set("direction", this.options.direction); + this.set("scroll", this.options.scroll); + this.add_children([this.prev, this.next]); + this.add_buttons(this.options.buttons); + this._sizes = null; + }, + + resize: function () { + var tmp, e; + + var os = this._sizes; + var s = { + container: this._container.getBoundingClientRect(), + clip: { + height: TK.inner_height(this._clip), + width: TK.inner_width(this._clip), + }, + buttons: [], + buttons_pos: [], + prev: this._prev.parentNode ? this._prev.getBoundingClientRect() : os ? os.prev : zero, + next: this._next.parentNode ? this._next.getBoundingClientRect() : os ? os.next : zero, + element: this.element.getBoundingClientRect(), + }; + + this._sizes = s; + + for (var i = 0; i < this.buttons.length; i++) { + e = this.buttons[i].element; + s.buttons[i] = e.getBoundingClientRect(); + s.buttons_pos[i] = { left: e.offsetLeft, top: e.offsetTop }; + } + + TK.Container.prototype.resize.call(this); + }, + + /** + * Adds an array of buttons to the end of the list. + * + * @method TK.ButtonArray#add_buttons + * + * @param {Array.} options - An Array containing objects + * with options for the buttons (see {@link TK.Button} for more + * information) or strings for the buttons labels. + */ + add_buttons: function (options) { + for (var i = 0; i < options.length; i++) + this.add_button(options[i]); + }, + + /** + * Adds a {@link TK.Button} to the TK.ButtonArray. + * + * @method TK.ButtonArray#add_button + * + * @param {Object|string} options - An object containing options for the + * {@link TK.Button} to add or a string for the label. + * @param {integer} [position] - The position to add the {@link TK.Button} + * to. If `undefined`, the {@link TK.Button} is added to the end of the list. + * + * @returns {TK.Button} The {@link TK.Button} instance. + */ + add_button: function (options, position) { + if (typeof options === "string") + options = {label: options} + var b = new this.options.button_class(options); + var len = this.buttons.length; + var vert = this.options.direction === "vertical"; + if (position === void(0)) + position = this.buttons.length; + if (position === len) { + this.buttons.push(b); + this._container.appendChild(b.element); + } else { + this.buttons.splice(position, 0, b); + this._container.insertBefore(b.element, + this._container.childNodes[position]); + } + + this.add_child(b); + + this.trigger_resize(); + b.add_event("click", button_clicked.bind(this, b)); + /** + * A {@link TK.Button} was added to the TK.ButtonArray. + * + * @event TK.ButtonArray#added + * + * @param {TK.Button} button - The {@link TK.Button} which was added to TK.ButtonArray. + */ + if (b === this.current()) + b.set("state", true); + this.fire_event("added", b); + + return b; + }, + /** + * Removes a {@link TK.Button} from the TK.ButtonArray. + * + * @method TK.ButtonArray#remove_button + * + * @param {integer|TK.Button} button - button index or the {@link TK.Button} + * instance to be removed. + */ + remove_button: function (button) { + if (typeof button === "object") + button = this.buttons.indexOf(button); + if (button < 0 || button >= this.buttons.length) + return; + /** + * A {@link TK.Button} was removed from the TK.ButtonArray. + * + * @event TK.ButtonArray#removed + * + * @param {TK.Button} button - The {@link TK.Button} instance which was removed. + */ + this.fire_event("removed", this.buttons[button]); + if (this.current() && button <= this.options.show) { + this.options.show --; + this.invalid.show = true; + this.trigger_draw(); + } + this.buttons[button].destroy(); + this.buttons.splice(button, 1); + this.trigger_resize(); + }, + + destroy: function () { + for (var i = 0; i < this.buttons.length; i++) + this.buttons[i].destroy(); + this.prev.destroy(); + this.next.destroy(); + this._container.remove(); + this._clip.remove(); + TK.Container.prototype.destroy.call(this); + }, + + redraw: function() { + TK.Container.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + var S = this._sizes; + + if (I.direction) { + var E = this.element; + TK.remove_class(E, "toolkit-vertical", "toolkit-horizontal"); + TK.add_class(E, "toolkit-"+O.direction); + } + + if (I.validate("direction", "auto_arrows") || I.resized) { + if (O.auto_arrows && O.resized && !O.needs_resize) { + var dir = O.direction === "vertical"; + var subd = dir ? 'top' : 'left'; + var subs = dir ? 'height' : 'width'; + + var clipsize = S.clip[subs]; + var listsize = 0; + + if (this.buttons.length) + listsize = S.buttons_pos[this.buttons.length-1][subd] + + S.buttons[this.buttons.length-1][subs]; + if (Math.round(listsize) > Math.round(clipsize)) { + show_arrows.call(this); + } else if (Math.round(listsize) <= Math.round(clipsize)) { + hide_arrows.call(this); + } + } else if (!O.auto_arrows) { + hide_arrows.call(this); + } + } + if (I.validate("show", "direction", "resized")) { + if (O.resized && !O.needs_resize) { + var show = O.show + if (show >= 0 && show < this.buttons.length) { + /* move the container so that the requested button is shown */ + var dir = O.direction === "vertical"; + var subd = dir ? 'top' : 'left'; + var subt = dir ? 'scrollTop' : 'scrollLeft'; + var subs = dir ? 'height' : 'width'; + + var btnrect = S.buttons[show]; + var clipsize = S.clip[subs]; + var listsize = 0; + var btnsize = 0; + var btnpos = 0; + if (S.buttons.length) { + listsize = S.buttons_pos[this.buttons.length-1][subd] + + S.buttons[this.buttons.length-1][subs]; + btnsize = S.buttons[show][subs]; + btnpos = S.buttons_pos[show][subd]; + } + + var p = (Math.max(0, Math.min(listsize - clipsize, btnpos - (clipsize / 2 - btnsize / 2)))); + if (this.options.scroll) { + var s = this._clip[subt]; + this._scroll = {to: ~~p, from: s, dir: p > s ? 1 : -1, diff: ~~p - s, time: Date.now()}; + this.invalid.scroll = true; + if (this._container.style[subd]) + this._container.style[subd] = null; + } else { + this._container.style[subd] = -p + "px"; + if (s) + this._clip[subt] = 0; + } + } + } + } + if (this.invalid.scroll && this._scroll) { + var subt = O.direction === "vertical" ? 'scrollTop' : 'scrollLeft'; + var s = ~~this._clip[subt]; + var _s = this._scroll; + var now = Date.now(); + if ((s >= _s.to && _s.dir > 0) + || (s <= _s.to && _s.dir < 0) + || now > (_s.time + O.scroll)) { + this.invalid.scroll = false; + this._clip[subt] = _s.to; + } else { + this._clip[subt] = easeInOut(Date.now() - _s.time, _s.from, _s.diff, O.scroll); + this.trigger_draw_next(); + } + } + }, + + /** + * The currently active button. + * + * @method TK.ButtonArray#current + * + * @returns {TK.Button} The active {@link TK.Button} or null, if none is selected. + */ + current: function() { + var n = this.options.show; + if (n >= 0 && n < this.buttons.length) { + return this.buttons[n]; + } + return null; + }, + + set: function (key, value) { + var button; + if (key === "show") { + //if (value < 0) value = 0; + //if (value >= this.buttons.length) value = this.buttons.length - 1; + if (value === this.options.show) return value; + + button = this.current(); + if (button) button.set("state", false); + } + if (key == "scroll") { + TK[value>0?"add_class":"remove_class"](this.element, "toolkit-scroll"); + this.trigger_resize(); + } + return TK.Container.prototype.set.call(this, key, value); + }, + get: function (key) { + if (key === "buttons") return this.buttons; + return TK.Container.prototype.get.call(this, key); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/chart.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/chart.js new file mode 100644 index 0000000000..9c8846c552 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/chart.js @@ -0,0 +1,839 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ + +function calculate_overlap(X, Y) { + /* no overlap, return 0 */ + if (X[2] < Y[0] || Y[2] < X[0] || X[3] < Y[1] || Y[3] < X[1]) return 0; + + return (Math.min(X[2], Y[2]) - Math.max(X[0], Y[0])) * + (Math.min(X[3], Y[3]) - Math.max(X[1], Y[1])); +} + +function show_handles() { + var handles = this.handles; + + for (var i = 0; i < handles.length; i++) { + this.add_child(handles[i]); + } +} + +function hide_handles() { + var handles = this.handles; + + for (var i = 0; i < handles.length; i++) { + this.remove_child(handles[i]); + } +} + +var STOP = function(e) { + e.preventDefault(); + e.stopPropagation(); + return false; +} +function draw_key() { + var __key, bb; + + var _key = this._key; + var _key_bg = this._key_background; + + if (!_key || !_key_bg) return; + + while (_key.firstChild !== _key.lastChild) + _key.removeChild(_key.lastChild); + + TK.empty(_key.firstChild); + + var O = this.options; + + var disp = "none"; + var gpad = TK.css_space(_key, "padding"); + var gmarg = TK.css_space(_key, "margin"); + var c = 0; + var w = 0; + var top = 0; + var lines = []; + for (var i = 0; i < this.graphs.length; i++) { + if (this.graphs[i].get("key") !== false) { + var t = TK.make_svg("tspan", {"class": "toolkit-label", + style: "dominant-baseline: central;" + }); + t.textContent = this.graphs[i].get("key"); + t.setAttribute("x", gpad.left); + _key.firstChild.appendChild(t); + + if (!bb) bb = _key.getBoundingClientRect(); + top += c ? parseInt(TK.get_style(t, "line-height")) : gpad.top; + t.setAttribute("y", top + bb.height / 2); + + lines.push({ + x: (parseInt(TK.get_style(t, "margin-right")) || 0), + y: Math.round(top), + width: Math.round(bb.width), + height: Math.round(bb.height), + "class": this.graphs[i].element.getAttribute("class"), + color: (this.graphs[i].element.getAttribute("color") || ""), + style: this.graphs[i].element.getAttribute("style") + }) + w = Math.max(w, t.getComputedTextLength()); + disp = "block"; + c++; + } + } + for (var i = 0; i < lines.length; i++) { + var b = TK.make_svg("rect", { + "class": lines[i]["class"] + " toolkit-rect", + color: lines[i].color, + style: lines[i].style, + x: lines[i].x + 0.5 + w + gpad.left, + y: lines[i].y + 0.5 + parseInt(lines[i].height / 2 - O.key_size.y / 2), + height: O.key_size.y, + width: O.key_size.x + }); + _key.appendChild(b); + } + _key_bg.style.display = disp; + _key.style.display = disp; + + bb = _key.getBoundingClientRect(); + var width = this.range_x.options.basis; + var height = this.range_y.options.basis; + + switch (O.key) { + case "top-left": + __key = { + x1: gmarg.left, + y1: gmarg.top, + x2: gmarg.left + parseInt(bb.width) + gpad.left + gpad.right, + y2: gmarg.top + parseInt(bb.height) + gpad.top + gpad.bottom + } + break; + case "top-right": + __key = { + x1: width - gmarg.right - parseInt(bb.width) - gpad.left - gpad.right, + y1: gmarg.top, + x2: width - gmarg.right, + y2: gmarg.top + parseInt(bb.height) + gpad.top + gpad.bottom + } + break; + case "bottom-left": + __key = { + x1: gmarg.left, + y1: height - gmarg.bottom - parseInt(bb.height) - gpad.top - gpad.bottom, + x2: gmarg.left + parseInt(bb.width) + gpad.left + gpad.right, + y2: height - gmarg.bottom + } + break; + case "bottom-right": + __key = { + x1: width - gmarg.right - parseInt(bb.width) - gpad.left - gpad.right, + y1: height -gmarg.bottom - parseInt(bb.height) - gpad.top - gpad.bottom, + x2: width - gmarg.right, + y2: height - gmarg.bottom + } + break; + default: + TK.warn("Unsupported key", O.key); + } + _key.setAttribute("transform", "translate(" + __key.x1 + "," + __key.y1 + ")"); + _key_bg.setAttribute("x", __key.x1); + _key_bg.setAttribute("y", __key.y1); + _key_bg.setAttribute("width", __key.x2 - __key.x1); + _key_bg.setAttribute("height", __key.y2 - __key.y1); +} +function draw_title() { + var _title = this._title; + if (!_title) return; + + _title.textContent = this.options.title; + + /* FORCE_RELAYOUT */ + TK.S.add(function() { + var mtop = parseInt(TK.get_style(_title, "margin-top") || 0); + var mleft = parseInt(TK.get_style(_title, "margin-left") || 0); + var mbottom = parseInt(TK.get_style(_title, "margin-bottom") || 0); + var mright = parseInt(TK.get_style(_title, "margin-right") || 0); + var bb = _title.getBoundingClientRect(); + var x,y,anchor, range_x = this.range_x, range_y = this.range_y; + switch (this.options.title_position) { + case "top-left": + anchor = "start"; + x = mleft; + y = mtop + bb.height / 2; + break; + case "top": + anchor = "middle"; + x = range_x.options.basis / 2; + y = mtop + bb.height / 2; + break; + case "top-right": + anchor = "end"; + x = range_x.options.basis - mright; + y = mtop + bb.height / 2; + break; + case "left": + anchor = "start"; + x = mleft; + y = range_y.options.basis / 2; + break; + case "center": + anchor = "middle"; + x = range_x.options.basis / 2; + y = range_y.options.basis / 2; + break; + case "right": + anchor = "end"; + x = range_x.options.basis - mright; + y = range_y.options.basis / 2; + break; + case "bottom-left": + anchor = "start"; + x = mleft; + y = range_y.options.basis - mtop - bb.height / 2; + break; + case "bottom": + anchor = "middle"; + x = range_x.options.basis / 2; + y = range_y.options.basis - mtop - bb.height / 2; + break; + case "bottom-right": + anchor = "end"; + x = range_x.options.basis - mright; + y = range_y.options.basis - mtop - bb.height / 2; + break; + default: + TK.warn("Unsupported title_position", this.options.title_position); + } + TK.S.add(function() { + _title.setAttribute("text-anchor", anchor); + _title.setAttribute("x", x); + _title.setAttribute("y", y); + }, 1); + }.bind(this)); +} + +/** + * TK.Chart is an SVG image containing one or more Graphs. TK.Chart + * extends {@link TK.Widget} and contains a {@link TK.Grid} and two + * {@link TK.Range}s. + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String|Boolean} [options.title=""] - A title for the Chart. + * Set to `false` to remove the title from the DOM. + * @property {String} [options.title_position="top-right"] - Position of the + * title inside of the chart. Possible values are + * "top-left", "top", "top-right", + * "left", "center", "right", + * "bottom-left", "bottom" and + * "bottom-right". + * @property {Boolean|String} [options.key=false] - If set to a string + * a key is rendered into the chart at the given position. The key + * will detail names and colors of the graphs inside of this chart. + * Possible values are "top-left", "top-right", + * "bottom-left" and "bottom-right". Set to `false` + * to remove the key from the DOM. + * @property {Object} [options.key_size={x:20,y:10}] - Size of the colored + * rectangles inside of the key describing individual graphs. + * @property {Array} [options.grid_x=[]] - An array containing + * objects with the following optional members to draw the grid: + * @property {Number} [options.grid_x.pos] - The value where to draw grid line and corresponding label. + * @property {String} [options.grid_x.color] - A valid CSS color string to colorize the elements. + * @property {String} [options.grid_x.class] - A class name for the elements. + * @property {String} [options.grid_x.label] - A label string. + * @property {Array} [options.grid_y=[]] - An array containing + * objects with the following optional members to draw the grid: + * @property {Number} [options.grid_y.pos] - The value where to draw grid line and corresponding label. + * @property {String} [options.grid_y.color] - A valid CSS color string to colorize the elements. + * @property {String} [options.grid_y.class] - A class name for the elements. + * @property {String} [options.grid_y.label] - A label string. + * @property {Boolean} [options.show_grid=true] - Set to false to + * hide the grid. + * @property {Function|Object} [options.range_x={}] - Either a function + * returning a {@link TK.Range} or an object containing options for a + * new {@link TK.Range}. + * @property {Function|Object} [options.range_y={}] - Either a function + * returning a {@link TK.Range} or an object containing options for a + * new {@link TK.Range}. + * @property {Object|Function} [options.range_z={ scale: "linear", min: 0, max: 1 }] - + * Either a function returning a {@link TK.Range} or an object + * containing options for a new {@link TK.Range}. + * @property {Number} [options.importance_label=4] - Multiplicator of + * square pixels on hit testing labels to gain importance. + * @property {Number} [options.importance_handle=1] - Multiplicator of + * square pixels on hit testing handles to gain importance. + * @property {Number} [options.importance_border=50] - Multiplicator of + * square pixels on hit testing borders to gain importance. + * @property {Array} [options.handles=[]] - An array of options for + * creating {@link TK.ResponseHandle} on init. + * @property {Boolean} [options.show_handles=true] - Show or hide all + * handles. + * + * @class TK.Chart + * + * @extends TK.Widget + */ +function geom_set(value, key) { + this.set_style(key, value+"px"); + TK.error("using deprecated '"+key+"' options"); +} +TK.Chart = TK.class({ + _class: "Chart", + Extends: TK.Widget, + Implements: TK.Ranges, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + width: "int", + height: "int", + _width: "int", + _height: "int", + range_x: "object", + range_y: "object", + range_z: "object", + key: "string", + key_size: "object", + title: "string", + title_position: "string", + resized: "boolean", + + importance_label: "number", + importance_handle: "number", + importance_border: "number", + handles: "array", + show_handles: "boolean", + depth: "number", + }), + options: { + grid_x: [], + grid_y: [], + range_x: {}, // an object with options for a range for the x axis + // or a function returning a TK.Range instance (only on init) + range_y: {}, // an object with options for a range for the y axis + // or a function returning a TK.Range instance (only on init) + range_z: { scale: "linear", min: 0, max: 1 }, // TK.Range z options + key: false, // key draws a description for the graphs at the given + // position, use false for no key + key_size: {x:20, y:10}, // size of the key rects + title: "", // a title for the chart + title_position: "top-right", // the position of the title + resized: false, + + importance_label: 4, // multiplicator of square pixels on hit testing + // labels to gain importance + importance_handle: 1, // multiplicator of square pixels on hit testing + // handles to gain importance + importance_border: 50, // multiplicator of square pixels on hit testing + // borders to gain importance + handles: [], // list of bands to create on init + show_handles: true, + }, + static_events: { + set_width: geom_set, + set_height: geom_set, + + mousewheel: STOP, + DOMMouseScroll: STOP, + set_depth: function(value) { + this.range_z.set("basis", value); + }, + set_show_handles: function(value) { + (value ? show_handles : hide_handles).call(this); + }, + }, + initialize: function (options) { + var E, S; + /** + * @member {Array} TK.Chart#graphs - An array containing all SVG paths acting as graphs. + */ + this.graphs = []; + /** + * @member {Array} TK.Chart#handles - An array containing all {@link TK.ResponseHandle} instances. + */ + this.handles = []; + TK.Widget.prototype.initialize.call(this, options); + + /** + * @member {TK.Range} TK.Chart#range_x - The {@link TK.Range} for the x axis. + */ + /** + * @member {TK.Range} TK.Chart#range_y - The {@link TK.Range} for the y axis. + */ + this.add_range(this.options.range_x, "range_x"); + this.add_range(this.options.range_y, "range_y"); + this.add_range(this.options.range_z, "range_z"); + this.range_y.set("reverse", true, true, true); + + /** + * @member {HTMLDivElement} TK.Chart#element - The main DIV container. + * Has class toolkit-chart. + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-chart"); + this.widgetize(E, true, true, true); + + this.svg = S = TK.make_svg("svg"); + + if (!this.options.width) + this.options.width = this.range_x.options.basis; + if (!this.options.height) + this.options.height = this.range_y.options.basis; + + /** + * @member {SVGGroup} TK.Chart#_graphs - The SVG group containing all graphs. + * Has class toolkit-graphs. + */ + this._graphs = TK.make_svg("g", {"class": "toolkit-graphs"}); + S.appendChild(this._graphs); + E.appendChild(S); + + if (this.options.width) this.set("width", this.options.width); + if (this.options.height) this.set("height", this.options.height); + + /** + * @member {SVGGroup} TK.Chart#_handles - The SVG group containing all handles. + * Has class toolkit-handles. + */ + this._handles = TK.make_svg("g", {"class": "toolkit-handles"}); + this.svg.appendChild(this._handles); + this.svg.onselectstart = function () { return false; }; + this.add_handles(this.options.handles); + }, + resize: function () { + var E = this.element; + var O = this.options; + var S = this.svg; + + TK.Widget.prototype.resize.call(this); + + var tmp = TK.css_space(S, "border", "padding"); + var w = TK.inner_width(E) - tmp.left - tmp.right; + var h = TK.inner_height(E) - tmp.top - tmp.bottom; + + if (w > 0 && O._width !== w) { + this.set("_width", w); + this.range_x.set("basis", w); + this.invalid._width = true; + this.trigger_draw(); + } + if (h > 0 && O._height !== h) { + this.set("_height", h); + this.range_y.set("basis", h); + this.invalid._height = true; + this.trigger_draw(); + } + }, + redraw: function () { + var I = this.invalid; + var E = this.svg; + var O = this.options; + + TK.Widget.prototype.redraw.call(this); + + if (I.validate("ranges", "_width", "_height", "range_x", "range_y")) { + /* we need to redraw both key and title, because + * they do depend on the size */ + I.title = true; + I.key = true; + var w = O._width; + var h = O._height; + if (w && h) { + E.setAttribute("width", w + "px"); + E.setAttribute("height", h + "px"); + } + } + + if (I.graphs) { + for (var i = 0; i < this.graphs.length; i++) { + this.graphs[i].redraw(); + } + } + if (I.validate("title", "title_position")) { + draw_title.call(this); + } + if (I.validate("key", "key_size", "graphs")) { + draw_key.call(this); + } + if (I.show_handles) { + I.show_handles = false; + if (O.show_handles) { + this._handles.style.removeProperty("display"); + } else { + this._handles.style.display = "none"; + } + } + }, + destroy: function () { + for (var i = 0; i < this._graphs.length; i++) { + this._graphs[i].destroy(); + } + this._graphs.remove(); + this._handles.remove(); + TK.Widget.prototype.destroy.call(this); + }, + add_child: function(child) { + if (child instanceof TK.Graph) { + this.add_graph(child); + return; + } + + TK.Widget.prototype.add_child.call(this, child); + }, + remove_child: function(child) { + if (child instanceof TK.Graph) { + this.remove_graph(child); + return; + } + + TK.Widget.prototype.remove_child.call(this, child); + }, + /** + * Add a graph to the chart. + * + * @method TK.Chart#add_graph + * + * @param {Object} graph - The graph to add. This can be either an + * instance of {@link TK.Graph} or an object of options to + * {@link TK.Graph}. + * + * @returns {Object} The instance of {@link TK.Graph}. + * + * @emits TK.Chart#graphadded + */ + add_graph: function (options) { + var g; + + if (TK.Graph.prototype.isPrototypeOf(options)) { + g = options; + } else { + g = new TK.Graph(options); + } + + g.set("container", this._graphs); + if (!g.options.range_x) g.set("range_x", this.range_x); + if (!g.options.range_y) g.set("range_y", this.range_y); + + this.graphs.push(g); + g.add_event("set", function (key, value, obj) { + if (key === "color" || key === "class" || key === "key") { + this.invalid.graphs = true; + this.trigger_draw(); + } + }.bind(this)); + /** + * Is fired when a graph was added. Arguments are the graph + * and its position in the array. + * + * @event TK.Chart#graphadded + * + * @param {TK.Graph} graph - The {@link TK.Graph} which was added. + * @param {int} id - The ID of the added {@link TK.Graph}. + */ + this.fire_event("graphadded", g, this.graphs.length - 1); + + this.invalid.graphs = true; + this.trigger_draw(); + TK.Widget.prototype.add_child.call(this, g); + return g; + }, + /** + * Remove a graph from the chart. + * + * @method TK.Chart#remove_graph + * + * @param {TK.Graph} graph - The {@link TK.Graph} to remove. + * + * @emits TK.Chart#graphremoved + */ + remove_graph: function (g) { + var i; + if ((i = this.graphs.indexOf(g)) !== -1) { + /** + * Is fired when a graph was removed. Arguments are the graph + * and its position in the array. + * + * @event TK.Chart#graphremoved + * + * @param {TK.Graph} graph - The {@link TK.Graph} which was removed. + * @param {int} id - The ID of the removed {@link TK.Graph}. + */ + this.fire_event("graphremoved", g, i); + g.destroy(); + this.graphs.splice(i, 1); + TK.Widget.prototype.remove_child.call(this, g); + this.invalid.graphs = true; + this.trigger_draw(); + } + }, + /** + * Remove all graphs from the chart. + * + * @method TK.Chart#empty + * + * @emits TK.Chart#emptied + */ + empty: function () { + this.graphs.map(this.remove_graph, this); + /** + * Is fired when all graphs are removed from the chart. + * + * @event TK.Chart#emptied + */ + this.fire_event("emptied"); + }, + + /** + * Add a new handle to the widget. Options is an object containing + * options for the {@link TK.ResponseHandle}. + * + * @method TK.Chart#add_handle + * + * @param {Object} [options={ }] - An object containing initial options. - The options for the {@link TK.ResponseHandle}. + * @param {Object} [type=TK.ResponseHandle] - A widget class to be used as the new handle. + * + * @emits TK.Chart#handleadded + */ + add_handle: function (options, type) { + type = type || TK.ResponseHandle; + options.container = this._handles; + if (options.range_x === void(0)) + options.range_x = function () { return this.range_x; }.bind(this); + if (options.range_y === void(0)) + options.range_y = function () { return this.range_y; }.bind(this); + if (options.range_z === void(0)) + options.range_z = function () { return this.range_z; }.bind(this); + + options.intersect = this.intersect.bind(this); + + var h = new type(options); + this.handles.push(h); + if (this.options.show_handles) + this.add_child(h); + /** + * Is fired when a new handle was added. + * + * @param {TK.ResponseHandle} handle - The {@link TK.ResponseHandle} which was added. + * + * @event TK.Chart#handleadded + */ + this.fire_event("handleadded", h); + return h; + }, + /** + * Add multiple new {@link TK.ResponseHandle} to the widget. Options is an array + * of objects containing options for the new instances of {@link TK.ResponseHandle}. + * + * @method TK.Chart#add_handles + * + * @param {Array} options - An array of options objects for the {@link TK.ResponseHandle}. + * @param {Object} [type=TK.ResponseHandle] - A widget class to be used for the new handles. + */ + add_handles: function (handles, type) { + for (var i = 0; i < handles.length; i++) + this.add_handle(handles[i], type); + }, + /** + * Remove a handle from the widget. + * + * @method TK.Chart#remove_handle + * + * @param {TK.ResponseHandle} handle - The {@link TK.ResponseHandle} to remove. + * + * @emits TK.Chart#handleremoved + */ + remove_handle: function (handle) { + // remove a handle from the widget. + for (var i = 0; i < this.handles.length; i++) { + if (this.handles[i] === handle) { + if (this.options.show_handles) + this.remove_child(handle); + this.handles[i].destroy(); + this.handles.splice(i, 1); + /** + * Is fired when a handle was removed. + * + * @event TK.Chart#handleremoved + */ + this.fire_event("handleremoved"); + break; + } + } + }, + /** + * Remove multiple or all {@link TK.ResponseHandle} from the widget. + * + * @method TK.Chart#remove_handles + * + * @param {Array} handles - An array of + * {@link TK.ResponseHandle} instances. If the argument reveals to + * `false`, all handles are removed from the widget. + */ + remove_handles: function (handles) { + var H = handles || this.handles.slice(); + for (var i = 0; i < H.length; i++) { + this.remove_handle(H[i]); + } + if (!handles) { + this.handles = []; + /** + * Is fired when all handles are removed. + * + * @event TK.Chart#emptied + */ + this.fire_event("emptied"); + } + }, + + intersect: function (X, handle) { + // this function walks over all known handles and asks for the coords + // of the label and the handle. Calculates intersecting square pixels + // according to the importance set in options. Returns an object + // containing intersect (the amount of intersecting square pixels) and + // count (the amount of overlapping elements) + var c = 0; + var a = 0, _a; + var O = this.options; + var importance_handle = O.importance_handle + var importance_label = O.importance_label + + for (var i = 0; i < this.handles.length; i++) { + var h = this.handles[i]; + if (h === handle || !h.get("active") || !h.get("show_handle")) continue; + _a = calculate_overlap(X, h.handle); + + if (_a) { + c ++; + a += _a * importance_handle; + } + + _a = calculate_overlap(X, h.label); + + if (_a) { + c ++; + a += _a * importance_label; + } + } + if (this.bands && this.bands.length) { + for (var i = 0; i < this.bands.length; i++) { + var b = this.bands[i]; + if (b === handle || !b.get("active") || !b.get("show_handle")) continue; + _a = calculate_overlap(X, b.handle); + + if (_a > 0) { + c ++; + a += _a * importance_handle; + } + + _a = calculate_overlap(X, b.label); + if (_a > 0) { + c ++; + a += _a * importance_label; + } + } + } + /* calculate intersection with border */ + _a = calculate_overlap(X, [ 0, 0, this.range_x.options.basis, this.range_y.options.basis ]); + a += ((X[2] - X[0]) * (X[3] - X[1]) - _a) * O.importance_border; + return {intersect: a, count: c}; + }, +}); +/** + * @member {TK.Grid} TK.Chart#grid - The grid element of the chart. + * Has class toolkit-grid. + */ +TK.ChildWidget(TK.Chart, "grid", { + create: TK.Grid, + show: true, + append: function() { + this.svg.insertBefore(this.grid.element, this.svg.firstChild); + }, + map_options: { + grid_x: "grid_x", + grid_y: "grid_y", + }, + default_options: function () { + return { + range_x: this.range_x, + range_y: this.range_y, + }; + }, +}); +function key_hover_cb(ev) { + var b = ev.type === "mouseenter"; + TK.toggle_class(this, "toolkit-hover", b); + /* this.nextSibling is the key */ + TK.toggle_class(this.nextSibling, "toolkit-hover", b); +} +/** + * @member {SVGRect} TK.Chart#_key_background - The SVG rectangle of the key. + * Has class toolkit-background. + */ +TK.ChildElement(TK.Chart, "key_background", { + option: "key", + display_check: function(v) { + return !!v; + }, + create: function() { + var k = TK.make_svg("rect", {"class": "toolkit-background"}); + k.addEventListener("mouseenter", key_hover_cb); + k.addEventListener("mouseleave", key_hover_cb); + return k; + }, + append: function() { + this.svg.appendChild(this._key_background); + }, +}); +/** + * @member {SVGGroup} TK.Chart#_key - The SVG group containing all descriptions. + * Has class toolkit-key. + */ +TK.ChildElement(TK.Chart, "key", { + option: "key", + display_check: function(v) { + return !!v; + }, + create: function() { + var key = TK.make_svg("g", {"class": "toolkit-key"}); + key.appendChild(TK.make_svg("text", {"class": "toolkit-key-text"})); + return key; + }, + append: function() { + this.svg.appendChild(this._key); + }, +}); +/** + * @member {SVGText} TK.Chart#_title - The title of the chart. + * Has class toolkit-title. + */ +TK.ChildElement(TK.Chart, "title", { + option: "title", + display_check: function(v) { + return typeof(v) === "string" && v.length; + }, + create: function() { + return TK.make_svg("text", { + "class": "toolkit-title", + style: "dominant-baseline: central;" + }); + }, + append: function() { + var svg = this.svg; + svg.insertBefore(this._title, svg.firstChild); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/clock.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/clock.js new file mode 100644 index 0000000000..89a12d97fc --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/clock.js @@ -0,0 +1,382 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +var format_viewbox = TK.FORMAT("0 0 %d %d"); +function draw_time(force) { + var tmp, drawn; + var O = this.options; + var t = O.time; + + if ((tmp = t.getSeconds()) !== this.__sec || force) { + this.circulars.seconds.set("value", tmp); + this.__sec = tmp; + } + if ((tmp = t.getMinutes()) !== this.__min || force) { + this.circulars.minutes.set("value", tmp); + this.__min = tmp; + } + if ((tmp = t.getHours() % 12) !== this.__hour || force) { + this.circulars.hours.set("value", tmp); + this.__hour = tmp; + } + + var args = [t, + t.getFullYear(), + t.getMonth(), + t.getDate(), + t.getDay(), + t.getHours(), + t.getMinutes(), + t.getSeconds(), + t.getMilliseconds(), + Math.round(t.getMilliseconds() / (1000 / O.fps)), + O.months, + O.days]; + if ((tmp = O.label.apply(this, args)) !== this.__label || force) { + TK.set_text(this._label, tmp); + this.__label = tmp; + drawn = true; + } + if ((tmp = O.label_upper.apply(this, args)) !== this.__upper || force) { + TK.set_text(this._label_upper, tmp); + this.__upper = tmp; + drawn = true; + } + if ((tmp = O.label_lower.apply(this, args)) !== this.__lower || force) { + TK.set_text(this._label_lower, tmp); + this.__lower = tmp; + drawn = true; + } + + if (drawn) + /** + * Is fired when the time was drawn. + * + * @param {Date} time - The time which was drawn. + * + * @event TK.Clock#timedrawn + */ + this.fire_event("timedrawn", O.time); +} +function set_labels() { + var O = this.options; + var E = this._label; + var s = O.label(new Date(2000, 8, 30, 24, 59, 59, 999), 2000, 8, + 30, 6, 24, 59, 59, 999, 999, + O.months, O.days); + TK.set_text(E, s); + + E.setAttribute("transform", ""); + + /* FORCE_RELAYOUT */ + + TK.S.add(function() { + var bb = E.getBoundingClientRect(); + if (bb.width === 0) return; // we are hidden + var mleft = parseInt(TK.get_style(E, "margin-left")) || 0; + var mright = parseInt(TK.get_style(E, "margin-right")) || 0; + var mtop = parseInt(TK.get_style(E, "margin-top")) || 0; + var mbottom = parseInt(TK.get_style(E, "margin-bottom")) || 0; + var space = O.size - mleft - mright - this._margin * 2; + var scale = space / bb.width; + var pos = O.size / 2; + + TK.S.add(function() { + E.setAttribute("transform", "translate(" + pos + "," + pos + ") " + + "scale(" + scale + ")"); + + /* FORCE_RELAYOUT */ + + TK.S.add(function() { + bb = E.getBoundingClientRect(); + + TK.S.add(function() { + this._label_upper.setAttribute("transform", "translate(" + pos + "," + (pos - bb.height / 2 - mtop) + ") " + + "scale(" + (scale * O.label_scale) + ")"); + this._label_lower.setAttribute("transform", "translate(" + pos + "," + (pos + bb.height / 2 + mtop) + ") " + + "scale(" + (scale * O.label_scale) + ")"); + draw_time.call(this, true); + }.bind(this), 1); + }.bind(this)); + }.bind(this), 1); + }.bind(this)); +} +function timeout() { + if (this.__to) + window.clearTimeout(this.__to); + var O = this.options; + if (!O) return; + if (O.timeout) { + var d = O.time; + var ts = +Date.now(); + + if (O.offset) { + ts += (O.offset|0); + } + + d.setTime(ts); + this.set("time", d); + + var targ = (O.timeout|0); + if (O.timeadd) { + targ += (O.timeadd|0) - ((ts % 1000)|0) + } + this.__to = window.setTimeout(this.__timeout, targ); + } else this.__to = false; +} +function onhide() { + if (this.__to) { + window.clearTimeout(this.__to); + this.__to = false; + } +} + +TK.Clock = TK.class({ + /** + * TK.Clock shows a customized clock with circulars displaying hours, minutes + * and seconds. It additionally offers three freely formatable labels. + * + * @class TK.Clock + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Integer} [options.thickness=10] - Thickness of the rings in percent of the maximum dimension. + * @property {Integer} [options.margin=0] - Margin between the {@link TK.Circular} in percent of the maximum dimension. + * @property {Integer} [options.size=200] - Width and height of the widget. + * @property {Boolean} [options.show_seconds=true] - Show seconds ring. + * @property {Boolean} [options.show_minutes=true] - Show minutes ring. + * @property {Boolean} [options.show_hours=true] - Show hours ring. + * @property {Integer} [options.timeout=1000] - The timeout of the redraw trigger. + * @property {Integer} [options.timeadd=10] - Set additional milliseconds to add to the timeout target system clock regulary. + * @property {Integer} [options.offset=0] - If a timeout is set offset the system time in milliseconds. + * @property {Integer} [options.fps=25] - Framerate for calculating SMTP frames + * @property {Array} [options.months=["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]] - Array containing all months names. + * @property {Array} [options.days=["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]] - Array containing all days names. + * @property {Function} [options.label=function (_date, year, month, date, day, hour, minute, second, millisecond, frame, months, days) { return ((hour < 10) ? ("0" + hour) : hour) + ":" + ((minute < 10) ? ("0" + minute) : minute) + ":" + ((second < 10) ? ("0" + second) : second);] - Callback to format the main label. + * @property {Function} [options.label_upper=function (_date, year, month, date, day, hour, minute, second, millisecond, frame, months, days) { return days[day]; }] - Callback to format the upper label. + * @property {Function} [options.label_lower=function (_date, year, month, date, day, hour, minute, second, millisecond, frame, months, days) { return ((date < 10) ? ("0" + date) : date) + ". " + months[month] + " " + year; }] - Callback to format the lower label. + * @property {Number} [options.label_scale=0.33] - The scale of `label_upper` and `label_lower` compared to the main label. + * @property {Number|String|Date} [options.time] - Set a specific time and date. To avoid auto-udates, set `timeout` to 0. + * For more information about the value, please refer to W3Schools. + */ + _class: "Clock", + Extends: TK.Widget, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + thickness: "number", + margin: "number", + size: "number", + show_seconds: "boolean", + show_minutes: "boolean", + show_hours: "boolean", + timeout: "int", + timeadd: "int", + offset: "int", + fps: "number", + months: "array", + days: "array", + label: "function", + label_upper: "function", + label_lower: "function", + label_scale: "number", + time: "object|string|number", + }), + options: { + thickness: 10, // thickness of the rings + margin: 0, // margin between the circulars + size: 200, // diameter of the whole clock + show_seconds: true, // show the seconds ring + show_minutes: true, // show the minutes ring + show_hours: true, // show the hours ring + timeout: 1000, // set a timeout to update the clock with the + // system clock regulary + timeadd: 10, // set additional milliseconds for the + // timeout target + // system clock regulary + offset: 0, // if a timeout is set offset the system time + // in milliseconds + fps: 25, // framerate for calculatind SMTP frames + months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], + label: function (_date, year, month, date, day, hour, minute, second, millisecond, frame, months, days) { + return ((hour < 10) ? ("0" + hour) : hour) + ":" + + ((minute < 10) ? ("0" + minute) : minute) + ":" + + ((second < 10) ? ("0" + second) : second); + }, + label_upper: function (_date, year, month, date, day, hour, minute, second, millisecond, frame, months, days) { + return days[day]; + }, + label_lower: function (_date, year, month, date, day, hour, minute, second, millisecond, frame, months, days) { + return ((date < 10) ? ("0" + date) : date) + ". " + months[month] + " " + year; + }, + label_scale: 0.33 // the scale of the upper and lower labels + // compared to the main label + }, + static_events: { + hide: onhide, + show: timeout, + timeout: timeout, + }, + initialize: function (options) { + var E, S; + /** + * @member {Object} TK.Clock#circulars - An object holding all three TK.Circular as members seconds, minutes and hours. + */ + this.circulars = {}; + this._margin = -1; + TK.Widget.prototype.initialize.call(this, options); + this.set("time", this.options.time); + + /** + * @member {HTMLDivElement} TK.Clock#element - The main DIV element. Has class toolkit-clock + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + /** + * @member {SVGImage} TK.Clock#svg - The main SVG image. + */ + this.svg = S = TK.make_svg("svg"); + this.widgetize(E, true, true, true); + TK.add_class(E, "toolkit-clock"); + + /** + * @member {SVGText} TK.Clock#_label - The center label showing the time. Has classtoolkit-label + */ + this._label = TK.make_svg("text", { + "class": "toolkit-label", + "text-anchor": "middle", + "style": "dominant-baseline: central;" + }); + /** + * @member {SVGText} TK.Clock#_label_upper - The upper label showing the day. Has classtoolkit-label-upper + */ + this._label_upper = TK.make_svg("text", { + "class": "toolkit-label-upper", + "text-anchor": "middle", + "style": "dominant-baseline: central;" + }); + /** @member {SVGText} TK.Clock#_label_lower - The lower label showing the date. Has classtoolkit-label-lower + */ + this._label_lower = TK.make_svg("text", { + "class": "toolkit-label-lower", + "text-anchor": "middle", + "style": "dominant-baseline: central;" + }); + S.appendChild(this._label); + S.appendChild(this._label_upper); + S.appendChild(this._label_lower); + E.appendChild(S); + + var circ_options = { + container: S, + show_hand: false, + start: 270, + angle: 360, + min: 0 + }; + + /** + * @member {Object} TK.Clock#circulars - An object containing the {@link TK.Circular} + * widgets. Members are `seconds`, `minutes` and `hours`. + */ + this.circulars.seconds = new TK.Circular(Object.assign({}, circ_options, + {max: 60, "class": "toolkit-seconds"})); + this.circulars.minutes = new TK.Circular(Object.assign({}, circ_options, + {max: 60, "class": "toolkit-minutes"})); + this.circulars.hours = new TK.Circular(Object.assign({}, circ_options, + {max: 12, "class": "toolkit-hours"})); + + this.add_child(this.circulars.seconds); + this.add_child(this.circulars.minutes); + this.add_child(this.circulars.hours); + + // start the clock + this.__timeout = timeout.bind(this); + }, + + redraw: function () { + var I = this.invalid, O = this.options; + + TK.Widget.prototype.redraw.call(this); + + if (I.size) { + this.svg.setAttribute("viewBox", format_viewbox(O.size, O.size)); + } + + if (I.validate("show_hours", "show_minutes", "show_seconds", "thickness", "margin") || I.size) { + var margin = 0; + for (var i in this.circulars) { + var circ = this.circulars[i]; + if (O["show_" + i]) { + circ.set("thickness", O.thickness); + circ.set("show_base", true); + circ.set("show_value", true); + circ.set("size", O.size); + circ.set("margin", margin); + margin += O.thickness; + margin += circ._get_stroke(); + margin += O.margin; + } else { + circ.set("show_base", false); + circ.set("show_value", false); + } + } + if(this._margin < 0) { + this._margin = margin; + } else { + this._margin = margin; + } + // force set_labels + I.label = true; + } + + if (I.validate("label", "months", "days", "size", "label_scale")) { + set_labels.call(this); + } + + if (I.validate("time", "label", "label_upper", "label_lower", "label_scale")) { + draw_time.call(this, false); + } + }, + + destroy: function () { + this._label.remove(); + this._label_upper.remove(); + this._label_lower.remove(); + this.circulars.seconds.destroy(); + this.circulars.minutes.destroy(); + this.circulars.hours.destroy(); + if (this.__to) + window.clearTimeout(this.__to); + TK.Widget.prototype.destroy.call(this); + }, + + set: function (key, value) { + switch (key) { + case "time": + if (Object.prototype.toString.call(value) === '[object Date]') + break; + value = new Date(value); + break; + } + return TK.Widget.prototype.set.call(this, key, value); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/colorpicker.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/colorpicker.js new file mode 100644 index 0000000000..e29afa0c69 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/colorpicker.js @@ -0,0 +1,565 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +var color_options = [ "rgb", "hsl", "hex", "hue", "saturation", "lightness", "red", "green", "blue" ]; + +var checkinput = function (e) { + var I = this.hex._input; + if (e.keyCode && e.keyCode == 13) { + apply.call(this); + return; + } + if (e.keyCode && e.keyCode == 27) { + cancel.call(this); + return; + } + if (I.value.substr(0, 1) == "#") + I.value = I.value.substr(1); + if (e.type == "paste" && I.value.length == 3) { + I.value = I.value[0] + I.value[0] + + I.value[1] + I.value[1] + + I.value[2] + I.value[2]; + } + if (I.value.length == 6) { + this.set("hex", I.value); + } +} +var cancel = function () { + /** + * Is fired whenever the cancel button gets clicked or ESC is hit on input. + * + * @event TK.ColorPicker#cancel + */ + fevent.call(this, "cancel"); +} +var apply = function () { + /** + * Is fired whenever the apply button gets clicked or return is hit on input. + * + * @event TK.ColorPicker#apply + * @param {object} colors - Object containing all color objects: `rgb`, `hsl`, `hex`, `hue`, `saturation`, `lightness`, `red`, `green`, `blue` + */ + fevent.call(this, "apply", true); +} + +var fevent = function (e, useraction) { + var O = this.options; + if (useraction) { + this.fire_event("userset", "rgb", O.rgb); + this.fire_event("userset", "hsl", O.hsl); + this.fire_event("userset", "hex", O.hex); + this.fire_event("userset", "hue", O.hue); + this.fire_event("userset", "saturation", O.saturation); + this.fire_event("userset", "lightness", O.lightness); + this.fire_event("userset", "red", O.red); + this.fire_event("userset", "green", O.green); + this.fire_event("userset", "blue", O.blue); + } + this.fire_event(e, { + rgb: O.rgb, + hsl: O.hsl, + hex: O.hex, + hue: O.hue, + saturation: O.saturation, + lightness: O.lightness, + red: O.red, + green: O.green, + blue: O.blue, + }); +} + +var color_atoms = { "hue":"hsl", "saturation":"hsl", "lightness":"hsl", "red":"rgb", "green":"rgb", "blue":"rgb" }; + +function set_atoms (key, value) { + var O = this.options; + var atoms = Object.keys(color_atoms); + for ( var i = 0; i < atoms.length; i++) { + var atom = atoms[i]; + if (key !== atom) { + O[atom] = O[color_atoms[atom]][atom.substr(0,1)] + if (this[atom]) + this[atom].set("value", O[atom]); + } + } + if (key !== "hex") + O.hex = this.rgb2hex(O.rgb); +} + + +/** + * TK.ColorPicker provides a collection of widgets to select a color in + * RGB or HSL color space. + * + * @class TK.ColorPicker + * + * @extends TK.Container + * + * @implements TK.Colors + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {object} [hsl={h:0, s:0.5, l:0}] - An object containing members `h`ue, `s`aturation and `l`ightness as numerical values. + * @property {object} [rgb={r:0, r:0, b:0}] - An object containing members `r`ed, `g`reen and `b`lue as numerical values. + * @property {string} [hex=000000] - A HEX color value, either with or without leading `#`. + * @property {number} [hue=0] - A numerical value 0..1 for the hue. + * @property {number} [saturation=0] - A numerical value 0..1 for the saturation. + * @property {number} [lightness=0] - A numerical value 0..1 for the lightness. + * @property {number} [red=0] - A numerical value 0..255 for the amount of red. + * @property {number} [green=0] - A numerical value 0..255 for the amount of green. + * @property {number} [blue=0] - A numerical value 0..255 for the amount of blue. + * @property {boolean} [show_hue=true] - Set to `false` to hide the {@link TK.ValueKnob} for hue. + * @property {boolean} [show_saturation=true] - Set to `false` to hide the {@link TK.ValueKnob} for saturation. + * @property {boolean} [show_lightness=true] - Set to `false` to hide the {@link TK.ValueKnob} for lightness. + * @property {boolean} [show_red=true] - Set to `false` to hide the {@link TK.ValueKnob} for red. + * @property {boolean} [show_green=true] - Set to `false` to hide the {@link TK.ValueKnob} for green. + * @property {boolean} [show_blue=true] - Set to `false` to hide the {@link TK.ValueKnob} for blue. + * @property {boolean} [show_hex=true] - Set to `false` to hide the {@link TK.Value} for the HEX color. + * @property {boolean} [show_apply=true] - Set to `false` to hide the {@link TK.Button} to apply. + * @property {boolean} [show_cancel=true] - Set to `false` to hide the {@link TK.Button} to cancel. + * @property {boolean} [show_canvas=true] - Set to `false` to hide the color canvas. + * @property {boolean} [show_grayscale=true] - Set to `false` to hide the grayscale. + * @property {boolean} [show_indicator=true] - Set to `false` to hide the color indicator. + */ + + +TK.ColorPicker = TK.class({ + + _class: "ColorPicker", + Extends: TK.Container, + Implements: [TK.Colors], + + _options: Object.assign(Object.create(TK.Container.prototype._options), { + hsl: "object", + rgb: "object", + hex: "string", + hue: "number", + saturation: "number", + lightness: "number", + red: "number", + green: "number", + blue: "number", + }), + options: { + hsl: {h:0, s:0.5, l:0}, + rgb: {r:0, g:0, b:0}, + hex: "000000", + hue: 0, + saturation: 0.5, + lightness: 0, + red: 0, + green: 0, + blue: 0, + }, + initialize: function (options) { + TK.Container.prototype.initialize.call(this, options); + var E = this.element; + /** @member {HTMLDivElement} TK.ColorPicker#element - The main DIV container. + * Has class toolkit-color-picker. + */ + TK.add_class(E, "toolkit-color-picker"); + + /** + * @member {TK.Range} TK.ColorPicker#range_x - The {@link TK.Range} for the x axis. + */ + this.range_x = new TK.Range({ + min: 0, + max: 1, + }); + + /** + * @member {TK.Range} TK.ColorPicker#range_y - The {@link TK.Range} for the y axis. + */ + this.range_y = new TK.Range({ + min: 0, + max: 1, + reverse: true, + }); + + /** + * @member {TK.Range} TK.ColorPicker#drag_x - The {@link TK.DragValue} for the x axis. + */ + this.drag_x = new TK.DragValue(this, { + range: (function () { return this.range_x; }).bind(this), + get: function () { return this.parent.options.hue; }, + set: function (v) { this.parent.userset("hue", this.parent.range_x.snap(v)); }, + direction: "horizontal", + onstartcapture: function (e) { + if (e.start.target.classList.contains("toolkit-indicator")) return; + var ev = e.stouch ? e.stouch : e.start; + var x = ev.clientX - this.parent._canvas.getBoundingClientRect().left; + this.parent.set("hue", this.options.range().px2val(x)); + } + }); + /** + * @member {TK.Range} TK.ColorPicker#drag_y - The {@link TK.DragValue} for the y axis. + */ + this.drag_y = new TK.DragValue(this, { + range: (function () { return this.range_y; }).bind(this), + get: function () { return this.parent.options.lightness; }, + set: function (v) { this.parent.userset("lightness", this.parent.range_y.snap(v)); }, + direction: "vertical", + onstartcapture: function (e) { + if (e.start.target.classList.contains("toolkit-indicator")) return; + var ev = e.stouch ? e.stouch : e.start; + var y = ev.clientY - this.parent._canvas.getBoundingClientRect().top; + this.parent.set("lightness", 1 - this.options.range().px2val(y)); + } + }); + + if (options.rgb) + this.set("rgb", options.rgb); + if (options.hex) + this.set("hex", options.hex); + if (options.hsl) + this.set("hsl", options.hsl); + }, + resize: function () { + var rect = this._canvas.getBoundingClientRect(); + this.range_x.set("basis", rect.width); + this.range_y.set("basis", rect.height); + }, + redraw: function () { + TK.Container.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + var E = this.element; + if (I.validate("rgb", "hsl", "hex", "hue", "saturation", "lightness", "red", "green", "blue")) { + var bw = this.rgb2bw(O.rgb); + var bg = "rgb("+parseInt(O.red)+","+parseInt(O.green)+","+parseInt(O.blue)+")"; + this.hex._input.style.backgroundColor = bg; + this.hex._input.style.color = bw; + this.hex.set("value", O.hex); + + this._indicator.style.left = (O.hue * 100) + "%"; + this._indicator.style.top = (O.lightness * 100) + "%"; + this._indicator.style.backgroundColor = bg; + this._indicator.style.color = bw; + + this._grayscale.style.opacity = 1 - O.saturation; + } + }, + set: function (key, value) { + var O = this.options; + if (color_options.indexOf(key) > -1) { + switch (key) { + case "rgb": + O.hsl = this.rgb2hsl(value); + break; + case "hsl": + O.rgb = this.hsl2rgb(value); + break; + case "hex": + O.rgb = this.hex2rgb(value); + O.hsl = this.rgb2hsl(O.rgb); + break; + case "hue": + O.hsl = {h:Math.min(1,Math.max(0,value)), s:O.saturation, l:O.lightness}; + O.rgb = this.hsl2rgb(O.hsl); + break; + case "saturation": + O.hsl = {h:O.hue, s:Math.min(1,Math.max(0,value)), l:O.lightness}; + O.rgb = this.hsl2rgb(O.hsl); + break; + case "lightness": + O.hsl = {h:O.hue, s:O.saturation, l:Math.min(1,Math.max(0,value))}; + O.rgb = this.hsl2rgb(O.hsl); + break; + case "red": + O.rgb = {r:Math.min(255,Math.max(0,value)), g:O.green, b:O.blue}; + O.hsl = this.rgb2hsl(O.rgb); + break; + case "green": + O.rgb = {r:O.red, g:Math.min(255,Math.max(0,value)), b:O.blue}; + O.hsl = this.rgb2hsl(O.rgb); + break; + case "blue": + O.rgb = {r:O.red, g:O.green, b:Math.min(255,Math.max(0,value))}; + O.hsl = this.rgb2hsl(O.rgb); + break; + } + set_atoms.call(this, key, value); + } + return TK.Container.prototype.set.call(this, key, value); + } +}); + +/** + * @member {HTMLDivElement} TK.ColorPicker#canvas - The color background. + * Has class `toolkit-canvas`, + */ +TK.ChildElement(TK.ColorPicker, "canvas", { + show: true, + append: function () { + this.element.appendChild(this._canvas); + this.drag_x.set("node", this._canvas); + this.drag_y.set("node", this._canvas); + }, +}); +/** + * @member {HTMLDivElement} TK.ColorPicker#grayscale - The grayscale background. + * Has class `toolkit-grayscale`, + */ +TK.ChildElement(TK.ColorPicker, "grayscale", { + show: true, + append: function () { + this._canvas.appendChild(this._grayscale); + }, +}); +/** + * @member {HTMLDivElement} TK.ColorPicker#indicator - The indicator element. + * Has class `toolkit-indicator`, + */ +TK.ChildElement(TK.ColorPicker, "indicator", { + show: true, + append: function () { + this._canvas.appendChild(this._indicator); + }, +}); + +/** + * @member {TK.Value} TK.ColorPicker#hex - The {@link TK.Value} for the HEX color. + * Has class `toolkit-hex`, + */ +TK.ChildWidget(TK.ColorPicker, "hex", { + create: TK.Value, + show: true, + static_events: { + "userset": function (key, val) { + if (key == "value") this.parent.userset("hex", val); + }, + "keyup": function (e) { checkinput.call(this.parent, e); }, + "paste": function (e) { checkinput.call(this.parent, e); }, + }, + default_options: { + format: TK.FORMAT("%s"), + "class": "toolkit-hex", + set: function (v) { + var p=0, tmp; + if (v[0] == "#") + v = v.substring(1); + while (v.length < 6) { + tmp = v.slice(0, p+1); + tmp += v[p]; + tmp += v.slice(p+1); + v = tmp; + p+=2; + } + return v; + }, + size: 7, + maxlength: 7, + }, + map_options: { + "hex" : "value" + }, + inherit_options: true, +}); + +/** + * @member {TK.ValueKnob} TK.ColorPicker#hue - The {@link TK.ValueKnob} for the hue. + * Has class `toolkit-hue`, + */ +TK.ChildWidget(TK.ColorPicker, "hue", { + create: TK.ValueKnob, + option: "show_hsl", + show: true, + static_events: { + "userset": function (key, val) { + if (key == "value") this.parent.userset("hue", val); + }, + }, + default_options: { + title: "Hue", + min: 0, + max: 1, + "class": "toolkit-hue", + }, + map_options: { + "hue" : "value" + }, + inherit_options: true, + blacklist_options: ["x", "y", "value"], +}); +/** + * @member {TK.ValueKnob} TK.ColorPicker#saturation - The {@link TK.ValueKnob} for the saturation. + * Has class `toolkit-saturation`, + */ +TK.ChildWidget(TK.ColorPicker, "saturation", { + create: TK.ValueKnob, + show: true, + static_events: { + "userset": function (key, val) { + if (key == "value") this.parent.userset("saturation", val); + }, + }, + default_options: { + title: "Saturation", + min: 0, + max: 1, + "class": "toolkit-saturation", + }, + map_options: { + "saturation" : "value" + }, + inherit_options: true, + blacklist_options: ["x", "y", "value"], +}); +/** + * @member {TK.ValueKnob} TK.ColorPicker#lightness - The {@link TK.ValueKnob} for the lightness. + * Has class `toolkit-lightness`, + */ +TK.ChildWidget(TK.ColorPicker, "lightness", { + create: TK.ValueKnob, + option: "show_hsl", + show: true, + static_events: { + "userset": function (key, val) { + if (key == "value") this.parent.userset("lightness", val); + }, + }, + default_options: { + title: "Lightness", + min: 0, + max: 1, + "class": "toolkit-lightness", + }, + map_options: { + "lightness" : "value" + }, + inherit_options: true, + blacklist_options: ["x", "y", "value"], +}); +/** + * @member {TK.ValueKnob} TK.ColorPicker#red - The {@link TK.ValueKnob} for the red color. + * Has class `toolkit-red`, + */ +TK.ChildWidget(TK.ColorPicker, "red", { + create: TK.ValueKnob, + option: "show_rgb", + show: true, + static_events: { + "userset": function (key, val) { + if (key == "value") this.parent.userset("red", val); + }, + }, + default_options: { + title: "Red", + min: 0, + max: 255, + snap: 1, + value_format: function (v) { return parseInt(v); }, + set: function (v) { return Math.round(v); }, + "class": "toolkit-red", + }, + map_options: { + "red" : "value" + }, + inherit_options: true, + blacklist_options: ["x", "y", "value"], +}); +/** + * @member {TK.ValueKnob} TK.ColorPicker#green - The {@link TK.ValueKnob} for the green color. + * Has class `toolkit-green`, + */ +TK.ChildWidget(TK.ColorPicker, "green", { + create: TK.ValueKnob, + option: "show_rgb", + show: true, + static_events: { + "userset": function (key, val) { + if (key == "value") this.parent.userset("green", val); + }, + }, + default_options: { + title: "Green", + min: 0, + max: 255, + snap: 1, + value_format: function (v) { return parseInt(v); }, + set: function (v) { return Math.round(v); }, + "class": "toolkit-green", + }, + map_options: { + "green" : "value" + }, + inherit_options: true, + blacklist_options: ["x", "y", "value"], +}); +/** + * @member {TK.ValueKnob} TK.ColorPicker#blue - The {@link TK.ValueKnob} for the blue color. + * Has class `toolkit-blue`, + */ +TK.ChildWidget(TK.ColorPicker, "blue", { + create: TK.ValueKnob, + option: "show_rgb", + show: true, + static_events: { + "userset": function (key, val) { + if (key == "value") this.parent.userset("blue", val); + }, + }, + default_options: { + title: "Blue", + min: 0, + max: 255, + snap: 1, + value_format: function (v) { return parseInt(v); }, + set: function (v) { return Math.round(v); }, + "class": "toolkit-blue", + }, + map_options: { + "blue" : "value" + }, + inherit_options: true, + blacklist_options: ["x", "y", "value"], +}); +/** + * @member {TK.Button} TK.ColorPicker#apply - The {@link TK.Button} to apply. + * Has class `toolkit-apply`, + */ +TK.ChildWidget(TK.ColorPicker, "apply", { + create: TK.Button, + show: true, + static_events: { + "click": function () { apply.call(this.parent); }, + }, + default_options: { + "label" : "Apply", + "class": "toolkit-apply", + }, +}); +/** + * @member {TK.Button} TK.ColorPicker#cancel - The {@link TK.Button} to cancel. + * Has class `toolkit-cancel`, + */ +TK.ChildWidget(TK.ColorPicker, "cancel", { + create: TK.Button, + show: true, + static_events: { + "click": function () { cancel.call(this.parent); }, + }, + default_options: { + "label" : "Cancel", + "class" : "toolkit-cancel", + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/colorpickerdialog.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/colorpickerdialog.js new file mode 100644 index 0000000000..d4edddb7c5 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/colorpickerdialog.js @@ -0,0 +1,75 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +function cancel () { + var self = this.parent; + self.fire_event.call(self, "cancel"); + self.close(); +} + +function apply (color) { + var self = this.parent; + self.fire_event.call(self, "apply", color); + self.close(); +} + +/** + * A {@link TK.Dialog} window containing a {@link TK.ColorPicker}. It can be opened + * programatically and closes automatically on the appropriate user + * interactions like hitting ESC or clicking `apply`. TK.ColorPickerDialog + * inherits all options of TK.ColorPicker. + * + * @class TK.ColorPickerDialog + * + * @extends TK.Dialog + * + * @param {Object} [options={ }] options - An object containing initial options. + * + */ +TK.ColorPickerDialog = TK.class({ + + _class: "ColorPickerDialog", + Extends: TK.Dialog, + + initialize: function (options) { + TK.Dialog.prototype.initialize.call(this, options); + /** @member {HTMLDivElement} TK.ColorPickerDialog#element - The main DIV container. + * Has class toolkit-color-picker-dialog. + */ + TK.add_class(this.element, "toolkit-color-picker-dialog"); + }, +}); + +/** + * @member {TK.ColorPicker} TK.ColorPickerDialog#colorpicker - The {@ink TK.ColorPicker} widget. + */ +TK.ChildWidget(TK.ColorPickerDialog, "colorpicker", { + create: TK.ColorPicker, + show: true, + inherit_options: true, + userset_delegate: true, + static_events: { + cancel: cancel, + apply: apply, + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/colors.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/colors.js new file mode 100644 index 0000000000..9f83be8f1a --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/colors.js @@ -0,0 +1,819 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ + +/* helpers */ + +var pad = function (n, width, z) { + z = z || '0'; + n = n + ''; + return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; +} + +var decode_args = function () { + /* decode random arguments. Expects an arguments array + * from another function call as first argument and a + * series of member names the arguments should be decoded + * to. E.g. decode_args(arguments, "r", "g", "b") + * Arguments array can consist of: + * A single array: member names are mapped to the array + * ([[50,100,150]],"r","g","b") => {"r":50,"g":100,"b":150} + * + * An object already containing the members: object is returned + * ([{"r":50,"g":100,"b":150}, ...) => {"r":50,"g":100,"b":150} + * + * Multiple values: values are mapped to member names + * ([50,100,150],"r","g","b") => {"r":50,"g":100,"b":150} + */ + var out = {}; + if (arguments[0][0] instanceof Array) { + for (var i = 0; i < arguments.length - 1; i++) + out[arguments[i+1]] = arguments[0][0][i]; + } else if (typeof(arguments[0][0]) === "object") { + out = arguments[0][0]; + } else { + for (var i = 0; i < arguments.length - 1; i++) + out[arguments[i+1]] = arguments[0][i]; + } + return out; +} + +var decode_color = function (args) { + /* detects type of input and disassembles it to a useful object. + * Only argument is an arguments array from another function. + * (["lightcoral"]) => {"type":"string","hex":"#F08080","string":"lightcoral","r":240,"g":128,"b":128} + * (["#F08080"]) => {"type":"hex","hex":"#F08080","r":240,"g":128,"b":128} + * ([[0,0.31,0.28]] => {"type":"hsl","h":0,"s":0.31,"l":0.28} + * ([240,128,128] => {"type":"rgb","r":240,"g":128,"b":128} + * ([{"r":240,"g":128,"b":128}] => {"type":"rgb","r":240,"g":128,"b":128} + */ + if (typeof args[0] === "string" && args[0][0] === "#") { + // HEX string + var res = hex2rgb(args[0]); + res["type"] = "hex"; + res["hex"] = args[0]; + return res; + } + if (typeof args[0] === "string" && color_names[args[0]]) { + // color string + var res = hex2rgb("#" + color_names[args[0]]); + res["type"] = "string"; + res["string"] = args[0]; + res["hex"] = color_names[args[0]]; + return res; + } + var S = decode_args(arguments, "a", "b", "c"); + if (S.a > 0 && S.a < 1 || S.b > 0 && S.b < 1 || S.c > 0 && S.c < 1) { + // HSL + return { "h": S.a, "s": S.b, "l": S.c, "type": "hsl" }; + } + // RGB + return { "r": S.a, "g": S.b, "b": S.c, "type": "rgb" }; +} + + +/* RGB */ + +var rgb2hex = function () { + var col = decode_args(arguments, "r", "g", "b"); + return pad(parseInt(col.r).toString(16),2) + + pad(parseInt(col.g).toString(16),2) + + pad(parseInt(col.b).toString(16),2); +} +var rgb2hsl = function () { + var col = decode_args(arguments, "r", "g", "b"); + var r = col.r, g = col.g, b = col.b; + + r /= 255, g /= 255, b /= 255; + + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, l = (max + min) / 2; + + if (max == min) { + h = s = 0; // achromatic + } else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + + h /= 6; + } + return { h:Math.min(1, Math.max(0, h)), + s:Math.min(1, Math.max(0, s)), + l:Math.min(1, Math.max(0, l)) }; +} +var rgb2bw = function () { + return rgb2gray.apply(null, arguments) >= 0.5 ? "#000000" : "#ffffff"; +} +var rgb2wb = function () { + return rgb2gray.apply(null, arguments) < 0.5 ? "#000000" : "#ffffff"; +} +var rgb2gray = function () { + var col = decode_args(arguments, "r", "g", "b"); + return (col.r * 0.2126 + col.g * 0.7152 + col.b * 0.0722) / 255; +} + + +/* HSL */ + +function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1/6) return p + (q - p) * 6 * t; + if (t < 1/2) return q; + if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return Math.min(1, Math.max(0, p)); +} +var hsl2rgb = function () { + var col = decode_args(arguments, "h", "s", "l"); + var h = col.h, s = col.s, l = col.l; + + var r, g, b; + + if (s == 0) { + r = g = b = l; // achromatic + } else { + + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } + return { r:r*255, g:g*255, b:b*255 }; +} +var hsl2hex = function () { + return rgb2hex(hsl2rgb.apply(null, arguments)); +} +var hsl2bw = function () { + return rgb2bw(hsl2rgb.apply(null, arguments)); +} +var hsl2wb = function () { + return rgb2wb(hsl2rgb.apply(null, arguments)); +} +var hsl2gray = function () { + return rgb2gray(hsl2rgb.apply(null, arguments)); +} + + +/* HEX */ + +var hex2rgb = function (hex) { + hex = hex || "000000"; + if (hex[0] == "#") + hex = hex.substr(1); + if (hex.length == 3) + return { r: parseInt("0x"+hex[0] + hex[0]), + g: parseInt("0x"+hex[1] + hex[1]), + b: parseInt("0x"+hex[2] + hex[2]) }; + return { r: parseInt("0x"+hex.substr(0,2)), + g: parseInt("0x"+hex.substr(2,2)), + b: parseInt("0x"+hex.substr(4,2)) }; +} +var hex2hsl = function (hex) { + return rgb2hsl(hex2rgb(hex)) +} +var hex2bw = function (hex) { + return rgb2bw(hex2rgb(hex)) +} +var hex2wb = function (hex) { + return rgb2wb(hex2rgb(hex)) +} +var hex2gray = function (hex) { + return rgb2gray(hex2rgb(hex)) +} + + +/* STRING */ + +var name2hex = function (name) { + return color_names[name.toLowerCase]; +} +var name2rgb = function (name) { + return hex2rgb(color_names[name.toLowerCase]); +} +var name2hsl = function (name) { + return hex2hsl(color_names[name.toLowerCase]); +} +var name2bw = function (name) { + return hex2bw(color_names[name.toLowerCase]); +} +var name2wb = function (name) { + return hex2wb(color_names[name.toLowerCase]); +} +var name2gray = function (name) { + return hex2gray(color_names[name.toLowerCase]); +} + + +/* UNIVERSAL */ + +var color2rgb = function () { + var C = decode_color(arguments); + switch (C.type) { + case "rgb": return C; + case "hex": return C; + case "hsl": return rgb2hsl(C); + case "string": return C; + } +} +var color2hsl = function () { + var C = decode_color(arguments); + switch (C.type) { + case "rgb": return rgb2hsl(C); + case "hex": return rgb2hsl(C); + case "hsl": return C; + case "string": return rgb2hsl(C); + } +} +var color2hex = function () { + var C = decode_color(arguments); + switch (C.type) { + case "rgb": return rgb2hex(C); + case "hex": return C.hex; + case "hsl": return hsl2hex(C); + case "string": return rgb2hex(C); + } +} +var color2bw = function () { + var C = decode_color(arguments); + switch (C.type) { + case "rgb": return rgb2bw(C); + case "hex": return rgb2bw(C); + case "hsl": return hsl2bw(C); + case "string": return rgb2bw(C); + } +} +var color2wb = function () { + var C = decode_color(arguments); + switch (C.type) { + case "rgb": return rgb2wb(C); + case "hex": return rgb2wb(C); + case "hsl": return hsl2wb(C); + case "string": return rgb2wb(C); + } +} +var color2gray = function () { + var C = decode_color(arguments); + switch (C.type) { + case "rgb": return rgb2bw(C); + case "hex": return rgb2bw(C); + case "hsl": return hsl2bw(C); + case "string": return rgb2bw(C); + } +} + + +/** + * TK.Colors provides a couple of functions for easy-to-use color calculations + * and conversions. Functions requiring RGB or HSL color definitions as + * argument (all `rgb2x` and `hsl2x`) can be called with different types of arguments + * to make using them more convenient. Examples: + *
  • `rgb2hsl(240, 128, 128)`
  • + *
  • rgb2hsl({'r':240,'g':128,'b':128})
  • + *
  • rgb2hsl([240, 128, 128])
+ * The universal functions `color2x` take even more diverse arguments. + * The following examples all define the same color: + *
  • ("lightcoral")
  • + *
  • ("#F08080")
  • + *
  • ([0,0.31,0.28])
  • + *
  • (240,128,128)
  • + *
  • ({"r":240,"g":128,"b":128})
+ * + * @mixin TK.Colors + */ + +TK.Colors = TK.class({ + /** + * Returns an object containing red ('r'), green ('g') and blue ('b') + * from any type of valid color. + * + * @method TK.Colors#color2rgb + * + * @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {object} Object with members r, g and b as numbers (0..255) + */ + color2rgb: color2rgb, + + /** + * Returns an object containing hue ('h'), saturation ('s') and lightness ('l') + * from any type of valid color. + * + * @method TK.Colors#color2hsl + * + * @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {object} Object with members h, s and l as numbers (0..1) + */ + color2hsl: color2hsl, + + /** + * Returns a hex color string + * from any type of valid color. + * + * @method TK.Colors#color2hex + * + * @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {string} Hex color string. + */ + color2hex: color2hex, + + /** + * Returns a hex color string either black or white at highest contrast compared to the argument + * from any type of valid color. + * + * @method TK.Colors#color2bw + * + * @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {string} Hex color string. + */ + color2bw: color2bw, + + /** + * Returns a hex color string either black or white at lowest contrast compared to the argument + * from any type of valid color. + * + * @method TK.Colors#color2wb + * + * @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {string} Hex color string. + */ + color2wb: color2wb, + + /** + * Returns a hex color string of the grayscaled argument + * from any type of valid color. + * + * @method TK.Colors#color2gray + * + * @param {number|array|object|string} 1st_value - red (0..255) or hue (0..1) or object with members `r, g, b` or `h, s, l` or array of RGB or HSL or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {string} Hex color string. + */ + color2gray: color2gray, + + + + /** + * Returns an object containing hue ('h'), saturation ('s') and lightness ('l') + * from a RGB color. + * + * @method TK.Colors#rgb2hsl + * + * @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {object} Object with members h, s and l as numbers (0..1) + */ + rgb2hsl: rgb2hsl, + + /** + * Returns a hex color string + * from a RGB color. + * + * @method TK.Colors#rgb2hex + * + * @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {string} Hex color string. + */ + rgb2hex: rgb2hex, + + /** + * Returns a hex color string either black or white at highest contrast compared to the argument + * from a RGB color. + * + * @method TK.Colors#rgb2bw + * + * @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {string} Hex color string. + */ + rgb2bw: rgb2bw, + + /** + * Returns a hex color string either black or white at lowest contrast compared to the argument + * from a RGB color. + * + * @method TK.Colors#rgb2wb + * + * @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {string} Hex color string. + */ + rgb2wb: rgb2wb, + + /** + * Returns a hex color string of the grayscaled argument + * from a RGB color. + * + * @method TK.Colors#rgb2gray + * + * @param {number|array|object|string} 1st_value - red (0..255) or object with members `r, g, b` or array of RGB or color name or hex string. + * @param {number} [2nd_value] - green (0..255) or saturation (0..1) + * @param {number} [3rd_value] - blue (0..255) or lightnes (0..1) + * + * @returns {string} Hex color string. + */ + rgb2gray: rgb2gray, + + + + /** + * Returns an object containing red ('r'), green ('g') and blue ('b') + * from a HSL color. + * + * @method TK.Colors#hsl2rgb + * + * @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL. + * @param {number} [2nd_value] - saturation (0..1) + * @param {number} [3rd_value] - lightness (0..1) + * + * @returns {object} Object with members r, g and b as numbers (0..255) + */ + hsl2rgb: hsl2rgb, + + /** + * Returns a hex color string + * from a HSL color. + * + * @method TK.Colors#hsl2hex + * + * @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL. + * @param {number} [2nd_value] - saturation (0..1) + * @param {number} [3rd_value] - lightness (0..1) + * + * @returns {string} Hex color string. + */ + hsl2hex: hsl2hex, + + /** + * Returns a hex color string either black or white at highest contrast compared to the argument + * from a HSL color. + * + * @method TK.Colors#hsl2bw + * + * @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL. + * @param {number} [2nd_value] - saturation (0..1) + * @param {number} [3rd_value] - lightness (0..1) + * + * @returns {string} Hex color string. + */ + hsl2bw: hsl2bw, + + /** + * Returns a hex color string either black or white at lowest contrast compared to the argument + * from a HSL color. + * + * @method TK.Colors#hsl2wb + * + * @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL. + * @param {number} [2nd_value] - saturation (0..1) + * @param {number} [3rd_value] - lightness (0..1) + * + * @returns {string} Hex color string. + */ + hsl2wb: hsl2wb, + + /** + * Returns a hex color string of the grayscaled argument + * from a HSL color. + * + * @method TK.Colors#hsl2gray + * + * @param {number|array|object} 1st_value - hue (0..1) or object with members `h, s, l` or array of HSL. + * @param {number} [2nd_value] - saturation (0..1) + * @param {number} [3rd_value] - lightness (0..1) + * + * @returns {string} Hex color string. + */ + hsl2gray: hsl2gray, + + + + /** + * Returns an object containing red ('r'), green ('g') and blue ('b') + * from a hex color string. + * + * @method TK.Colors#hex2rgb + * + * @param {string} hex - Hex color string. + * + * @returns {object} Object with members r, g and b as numbers (0..255) + */ + hex2rgb: hex2rgb, + + /** + * Returns an object containing hue ('h'), saturation ('s') and lightness ('l') + * from a hex color string. + * + * @method TK.Colors#hex2hsl + * + * @param {string} hex - Hex color string. + * + * @returns {object} Object with members h, s and l as numbers (0..1) + */ + hex2hsl: hex2hsl, + + /** + * Returns a hex color string either black or white at highest contrast compared to the argument + * from a hex color string. + * + * @method TK.Colors#hex2bw + * + * @param {string} hex - Hex color string. + * + * @returns {string} Hex color string. + */ + hex2bw: hex2bw, + + /** + * Returns a hex color string either black or white at lowest contrast compared to the argument + * from a hex color string. + * + * @method TK.Colors#hex2wb + * + * @param {string} hex - Hex color string. + * + * @returns {string} Hex color string. + */ + hex2wb: hex2wb, + + /** + * Returns a hex color string of the grayscaled argument + * from a hex color string. + * + * @method TK.Colors#hex2gray + * + * @param {string} hex - Hex color string. + * + * @returns {string} Hex color string. + */ + hex2gray: hex2gray, + + + + /** + * Returns an object containing red ('r'), green ('g') and blue ('b') + * from a color name. + * + * @method TK.Colors#name2rgb + * + * @param {string} color - Color name. + * + * @returns {object} Object with members r, g and b as numbers (0..255) + */ + name2rgb: name2rgb, + + /** + * Returns an object containing hue ('h'), saturation ('s') and lightness ('l') + * from a color name. + * + * @method TK.Colors#name2hsl + * + * @param {string} color - Color name. + * + * @returns {object} Object with members h, s and l as numbers (0..1) + */ + name2hsl: name2hsl, + + /** + * Returns a hex color string + * from a color name. + * + * @method TK.Colors#name2hex + * + * @param {string} color - Color name. + * + * @returns {string} Hex color string. + */ + name2hex: name2hex, + + /** + * Returns a hex color string either black or white at highest contrast compared to the argument + * from a color name. + * + * @method TK.Colors#name2bw + * + * @param {string} color - Color name. + * + * @returns {string} Hex color string. + */ + name2bw: name2bw, + + /** + * Returns a hex color string either black or white at lowest contrast compared to the argument + * from a color name. + * + * @method TK.Colors#name2wb + * + * @param {string} color - Color name. + * + * @returns {string} Hex color string. + */ + name2wb: name2wb, + + /** + * Returns a hex color string of the grayscaled argument + * from a color name. + * + * @method TK.Colors#name2gray + * + * @param {string} color - Color name. + * + * @returns {string} Hex color string. + */ + name2gray: name2gray, + +}); + +var color_names = { + "lightcoral" : "f08080", + "salmon" : "fa8072", + "darksalmon" : "e9967a", + "lightsalmon" : "ffa07a", + "crimson" : "dc143c", + "red" : "ff0000", + "firebrick" : "b22222", + "darkred" : "8b0000", + "pink" : "ffc0cb", + "lightpink" : "ffb6c1", + "hotpink" : "ff69b4", + "deeppink" : "ff1493", + "mediumvioletred" : "c71585", + "palevioletred" : "db7093", + "coral" : "ff7f50", + "tomato" : "ff6347", + "orangered" : "ff4500", + "darkorange" : "ff8c00", + "orange" : "ffa500", + "gold" : "ffd700", + "yellow" : "ffff00", + "lightyellow" : "ffffe0", + "lemonchiffon" : "fffacd", + "lightgoldenrodyellow" : "fafad2", + "papayawhip" : "ffefd5", + "moccasin" : "ffe4b5", + "peachpuff" : "ffdab9", + "palegoldenrod" : "eee8aa", + "khaki" : "f0e68c", + "darkkhaki" : "bdb76b", + "lavender" : "e6e6fa", + "thistle" : "d8bfd8", + "plum" : "dda0dd", + "violet" : "ee82ee", + "orchid" : "da70d6", + "fuchsia" : "ff00ff", + "magenta" : "ff00ff", + "mediumorchid" : "ba55d3", + "mediumpurple" : "9370db", + "amethyst" : "9966cc", + "blueviolet" : "8a2be2", + "darkviolet" : "9400d3", + "darkorchid" : "9932cc", + "darkmagenta" : "8b008b", + "purple" : "800080", + "indigo" : "4b0082", + "slateblue" : "6a5acd", + "darkslateblue" : "483d8b", + "mediumslateblue" : "7b68ee", + "greenyellow" : "adff2f", + "chartreuse" : "7fff00", + "lawngreen" : "7cfc00", + "lime" : "00ff00", + "limegreen" : "32cd32", + "palegreen" : "98fb98", + "lightgreen" : "90ee90", + "mediumspringgreen" : "00fa9a", + "springgreen" : "00ff7f", + "mediumseagreen" : "3cb371", + "seagreen" : "2e8b57", + "forestgreen" : "228b22", + "green" : "008000", + "darkgreen" : "006400", + "yellowgreen" : "9acd32", + "olivedrab" : "6b8e23", + "olive" : "808000", + "darkolivegreen" : "556b2f", + "mediumaquamarine" : "66cdaa", + "darkseagreen" : "8fbc8f", + "lightseagreen" : "20b2aa", + "darkcyan" : "008b8b", + "teal" : "008080", + "aqua" : "00ffff", + "cyan" : "00ffff", + "lightcyan" : "e0ffff", + "paleturquoise" : "afeeee", + "aquamarine" : "7fffd4", + "turquoise" : "40e0d0", + "mediumturquoise" : "48d1cc", + "darkturquoise" : "00ced1", + "cadetblue" : "5f9ea0", + "steelblue" : "4682b4", + "lightsteelblue" : "b0c4de", + "powderblue" : "b0e0e6", + "lightblue" : "add8e6", + "skyblue" : "87ceeb", + "lightskyblue" : "87cefa", + "deepskyblue" : "00bfff", + "dodgerblue" : "1e90ff", + "cornflowerblue" : "6495ed", + "royalblue" : "4169e1", + "blue" : "0000ff", + "mediumblue" : "0000cd", + "darkblue" : "00008b", + "navy" : "000080", + "midnightblue" : "191970", + "cornsilk" : "fff8dc", + "blanchedalmond" : "ffebcd", + "bisque" : "ffe4c4", + "navajowhite" : "ffdead", + "wheat" : "f5deb3", + "burlywood" : "deb887", + "tan" : "d2b48c", + "rosybrown" : "bc8f8f", + "sandybrown" : "f4a460", + "goldenrod" : "daa520", + "darkgoldenrod" : "b8860b", + "peru" : "cd853f", + "chocolate" : "d2691e", + "saddlebrown" : "8b4513", + "sienna" : "a0522d", + "brown" : "a52a2a", + "maroon" : "800000", + "white" : "ffffff", + "snow" : "fffafa", + "honeydew" : "f0fff0", + "mintcream" : "f5fffa", + "azure" : "f0ffff", + "aliceblue" : "f0f8ff", + "ghostwhite" : "f8f8ff", + "whitesmoke" : "f5f5f5", + "seashell" : "fff5ee", + "beige" : "f5f5dc", + "oldlace" : "fdf5e6", + "floralwhite" : "fffaf0", + "ivory" : "fffff0", + "antiquewhite" : "faebd7", + "linen" : "faf0e6", + "lavenderblush" : "fff0f5", + "mistyrose" : "ffe4e1", + "gainsboro" : "dcdcdc", + "lightgrey" : "d3d3d3", + "silver" : "c0c0c0", + "darkgray" : "a9a9a9", + "gray" : "808080", + "dimgray" : "696969", + "lightslategray" : "778899", + "slategray" : "708090", + "darkslategray" : "2f4f4f", + "black" : "000000" +} + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/confirmbutton.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/confirmbutton.js new file mode 100644 index 0000000000..d7d622dea8 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/confirmbutton.js @@ -0,0 +1,162 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +"use strict"; +(function(w, TK){ + +var reset = function (e) { + if (!this.options.state) return; + if (e) { + var t = e.target; + while (t) { + if (t == this.element) return; + t = t.parentElement; + } + } + state_reset.call(this); +} + +var state_set = function () { + var T = this.__temp; + var O = this.options; + if (O.label_confirm) { + T.label = O.label; + this.set("label", O.label_confirm); + } + + if (O.icon_confirm) { + T.icon = O.icon; + this.set("icon", O.icon_confirm); + } + + T.reset = reset.bind(this); + document.addEventListener("click", T.reset, true); + + if (O.timeout) + T.timeout = setTimeout(T.reset, O.timeout); + + T.click = Date.now(); + + this.set("state", true); +} + +var state_reset = function () { + var T = this.__temp; + if (T.label) + this.set("label", T.label); + + if (T.icon) + this.set("icon", T.icon); + + if (T.timeout >= 0) + window.clearTimeout(T.timeout); + + if (T.reset) + document.removeEventListener("click", T.reset, true); + + T.reset = null; + T.timeout = -1; + T.label = ""; + T.icon = ""; + T.click = 0; + + this.set("state", false); +} + +/** + * Is fired when the button was hit two times with at least + * interrupt milliseconds between both clicks. + * + * @event TK.ConfirmButton#confirmed + */ + +var clicked = function (e) { + var T = this.__temp; + var O = this.options; + if (!O.confirm) { + this.fire_event("confirmed"); + } else if (O.state && Date.now() > T.click + O.interrupt) { + this.fire_event("confirmed"); + state_reset.call(this); + } else if (!O.state) { + state_set.call(this); + } +} + +TK.ConfirmButton = TK.class({ + /** + * ConfirmButton is a {@link TK.Button} firing the `confirmed` event + * after it was hit a second time. While waiting for the confirmation, a + * dedicated label and icon can be displayed. The button is reset to + * default if no second click appears. A click outside of the button + * resets it, too. It gets class `toolkit-active` while waiting for + * the confirmation. + * + * @class TK.ConfirmButton + * + * @extends TK.Button + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Boolean} [options.confirm=true] - Defines if the button acts as ConfirmButton or normal Button. + * @property {Number} [options.timeout=2000] - Defines a time in milliseconds after the button resets to defaults if no second click happens. + * @property {Number} [options.interrupt=0] - Defines a duration in milliseconds within further clicks are ignored. Set to avoid double-clicks being recognized as confirmation. + * @property {String} [options.label_confirm] - The label to be used while in active state. + * @property {String} [options.icon_confirm] - The icon to be used while in active state. + */ + _class: "ConfirmButton", + Extends: TK.Button, + + _options: Object.assign(Object.create(TK.Button.prototype._options), { + confirm: "boolean", + timeout: "number", + interrupt: "number", + label_confirm : "string", + icon_confirm: "string", + }), + options: { + confirm: true, + timeout: 2000, + interrupt: 0, + label_confirm: "", + icon_confirm: "", + }, + + initialize: function (options) { + TK.Button.prototype.initialize.call(this, options); + TK.add_class(this.element, "toolkit-confirm-button"); + this.add_event("click", clicked.bind(this)); + this.__temp = { + label: "", + icon: "", + timeout: -1, + reset: null, + click: 0, + } + }, + + set: function (key, value) { + if (key == "confirm" && value == false) { + this.set("state", false); + } + TK.Button.prototype.set.call(this, key, value); + } +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/container.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/container.js new file mode 100644 index 0000000000..476fa69694 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/container.js @@ -0,0 +1,411 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function after_hiding() { + this.__hide_id = false; + if (this.options.display_state === "hiding") + this.set("display_state", "hide"); +} +function after_showing() { + this.__hide_id = false; + if (this.options.display_state === "showing") + this.set("display_state", "show"); +} +function enable_draw_self() { + if (this._drawn) return; + this._drawn = true; + if (this.needs_redraw) { + TK.S.add(this._redraw, 1); + } + /** + * Is fired when the container is shown. + * + * @event TK.Container#show + */ + this.fire_event("show"); +} +function enable_draw_children() { + var C = this.children; + var H = this.hidden_children; + if (C) for (var i = 0; i < C.length; i++) if (!H[i]) C[i].enable_draw(); +} +function disable_draw_self() { + if (!this._drawn) return; + this._drawn = false; + if (this.needs_redraw) { + TK.S.remove(this._redraw, 1); + TK.S.remove_next(this._redraw, 1); + } + /** + * Is fired when the container is hidden. + * + * @event TK.Container#hide + */ + this.fire_event("hide"); +} +function disable_draw_children() { + var C = this.children; + var H = this.hidden_children; + if (C) for (var i = 0; i < C.length; i++) if (!H[i]) C[i].disable_draw(); +} +TK.Container = TK.class({ + /** + * TK.Container represents a <DIV> element contining various + * other widgets or DOMNodes. + * + * Containers have four different display states: show, hide, + * showing and hiding. Each of these states has a corresponding + * CSS class called toolkit-show, toolkit-hide, toolkit-showing + * and toolkit-hiding, respectively. The display state can be controlled using + * the methods {@link TK.Container#show}, {@link TK.Container#hide} and {@link TK.Widget#toggle_hidden}. + * + * A container can keep track of the display states of its child widgets. + * The display state of a child can be changed using {@link TK.Container#hide_child}, + * {@link TK.Container#show_child} and {@link TK.Container#toggle_child}. + * + * @class TK.Container + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String|HTMLElement} [options.content] - The content of the container. It can either be + * a string which is interpreted as Text or a DOM node. Note that this option will remove all + * child nodes from the container element including those added via append_child. + * @property {Number} [options.hiding_duration] - The duration in ms of the hiding CSS + * transition/animation of this container. If this option is not set, the transition duration + * will be determined by the computed style, which can be rather + * expensive. Setting this option explicitly can therefore be an optimization. + * @property {Number} [options.showing_duration] - The duration in ms of the showing CSS + * transition/animation of this container. + * @property {String} [options.display_state="show"] - The current display state of this container. + * Do not modify, manually. + * @property {Array} [options.children=[]] - Add child widgets on init. Will not be maintained on runtime! Just for convenience purposes on init. + */ + _class: "Container", + Extends: TK.Widget, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + content: "string|DOMNode", + display_state: "string", + hiding_duration: "number", + showing_duration: "number", + children: "array", + }), + options: { + display_state : "show", + children: [], + }, + initialize: function (options) { + var E; + TK.Widget.prototype.initialize.call(this, options); + this.hidden_children = []; + /** + * @member {HTMLDivElement} TK.Container#element - The main DIV element. Has class toolkit-container + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-container"); + this.widgetize(E, true, true, true); + + this.__after_hiding = after_hiding.bind(this); + this.__after_showing = after_showing.bind(this); + this.__hide_id = false; + TK.add_class(E, "toolkit-show"); + + if (this.options.children.length) + this.append_children(this.options.children); + }, + + /** + * Calls {@link TK.Container#append_child} for an array of widgets. + * + * @method TK.Container#append_children + * + * @param {Array.} children - The child widgets to append. + */ + append_children : function (a) { + a.map(this.append_child, this); + }, + /** + * Appends child.element to the container element and + * registers child as a child widget. + * + * @method TK.Container#append_child + * + * @param {TK.Widget} child - The child widget to append. + */ + append_child : function(child) { + child.set("container", this.element); + this.add_child(child); + }, + set_parent : function(parent) { + if (parent && !(parent instanceof TK.Container)) { + TK.warn("Container %o should not be child of non-container %o", this, parent); + } + TK.Widget.prototype.set_parent.call(this, parent); + }, + add_child : function(child) { + TK.Widget.prototype.add_child.call(this, child); + var H = this.hidden_children; + if (!H) this.hidden_children = H = []; + H.push(false); + }, + remove_child : function(child) { + if (!child) return; + child.disable_draw(); + child.parent = null; + var C = this.children; + if (C === null) return; + var H = this.hidden_children; + var i = C.indexOf(child); + if (i !== -1) { + C.splice(i, 1); + H.splice(i, 1); + } + }, + enable_draw: function () { + if (this._drawn) return; + enable_draw_self.call(this); + enable_draw_children.call(this); + }, + disable_draw: function () { + if (!this._drawn) return; + disable_draw_self.call(this); + disable_draw_children.call(this); + }, + /** + * Starts the transition of the display_state to hide. + * + * @method TK.Container#hide + * + */ + hide: function () { + var O = this.options; + if (O.display_state === "hide") return; + disable_draw_children.call(this); + enable_draw_self.call(this); + if (O.display_state === "hiding") return; + this.set("display_state", "hiding"); + }, + /** + * Immediately switches the display state of this container to hide. + * Unlike {@link TK.Container#hide} this method does not perform the hiding transition + * and immediately modifies the DOM by setting the toolkit-hide class. + * + * @method TK.Container#force_hide + * + */ + force_hide: function () { + var O = this.options; + if (O.display_state === "hide") return; + this.disable_draw(); + var E = this.element; + O.display_state = "hide"; + TK.add_class(E, "toolkit-hide"); + TK.remove_class(E, "toolkit-hiding", "toolkit-showing", "toolkit-show"); + }, + /** + * Starts the transition of the display_state to show. + * + * @method TK.Container#show + * + */ + show: function() { + var O = this.options; + enable_draw_self.call(this); + if (O.display_state === "show" || O.display_state === "showing") return; + this.set("display_state", "showing"); + }, + /** + * Immediately switches the display state of this container to show. + * Unlike {@link TK.Container#hide} this method does not perform the hiding transition + * and immediately modifies the DOM by setting the toolkit-show class. + * + * @method TK.Container#force_show + * + */ + force_show: function() { + var O = this.options; + if (O.display_state === "show") return; + this.enable_draw(); + var E = this.element; + O.display_state = "show"; + TK.add_class(E, "toolkit-show"); + TK.remove_class(E, "toolkit-hiding", "toolkit-showing", "toolkit-hide"); + }, + show_nodraw: function() { + var O = this.options; + if (O.display_state === "show") return; + this.set("display_state", "show"); + + var C = this.children; + var H = this.hidden_children; + if (C) for (var i = 0; i < C.length; i++) if (!H[i]) C[i].show_nodraw(); + }, + hide_nodraw: function() { + var O = this.options; + if (O.display_state === "hide") return; + this.set("display_state", "hide"); + + var C = this.children; + var H = this.hidden_children; + if (C) for (i = 0; i < C.length; i++) if (!H[i]) C[i].hide_nodraw(); + }, + + /** + * Switches the hidden state of a child to hidden. + * The argument is either the child index or the child itself. + * + * @method TK.Container#hide_child + * @param {Object|integer} child - Child or its index. + * + */ + hide_child: function(i) { + var C = this.children; + var H = this.hidden_children; + + if (typeof i !== "number") { + i = C.indexOf(i); + if (i === -1) throw("Cannot find child."); + } + + H[i] = true; + C[i].hide(); + }, + + /** + * Switches the hidden state of a child to shown. + * The argument is either the child index or the child itself. + * + * @method TK.Container#show_child + * @param {Object|integer} child - Child or its index. + * + */ + show_child: function(i) { + var C = this.children; + var H = this.hidden_children; + + if (typeof i !== "number") { + i = C.indexOf(i); + if (i === -1) throw("Cannot find child."); + } + + if (H[i]) { + H[i] = false; + if (this.is_drawn()) C[i].show(); + else C[i].show_nodraw(); + } + }, + + /** + * Toggles the hidden state of a child. + * The argument is either the child index or the child itself. + * + * @method TK.Container#toggle_child + * @param {Object|integer} child - Child or its index. + * + */ + toggle_child: function(i) { + var C = this.children; + var H = this.hidden_children; + + if (typeof i !== "number") { + i = C.indexOf(i); + if (i === -1) throw("Cannot find child."); + } + if (H[i]) this.show_child(i); + else this.hide_child(i); + }, + + visible_children: function(a) { + if (!a) a = []; + var C = this.children; + var H = this.hidden_children; + if (C) for (var i = 0; i < C.length; i++) { + if (H[i]) continue; + a.push(C[i]); + C[i].visible_children(a); + } + return a; + }, + + hidden: function() { + var state = this.options.display_state; + return TK.Widget.prototype.hidden.call(this) || state === "hiding" || state === "hide"; + }, + + redraw: function() { + var O = this.options; + var I = this.invalid; + var E = this.element; + + TK.Widget.prototype.redraw.call(this); + + if (I.display_state) { + I.display_state = false; + var time; + TK.remove_class(E, "toolkit-hiding", "toolkit-hide", "toolkit-showing", "toolkit-show"); + + if (this.__hide_id) { + window.clearTimeout(this.__hide_id); + this.__hide_id = false; + } + + switch (O.display_state) { + case "hiding": + TK.add_class(E, "toolkit-hiding"); + time = O.hiding_duration || TK.get_duration(E); + if (time > 0) { + this.__hide_id = window.setTimeout(this.__after_hiding, time); + break; + } + this.set("display_state", "hide"); + TK.remove_class(E, "toolkit-hiding"); + /* FALL THROUGH */ + case "hide": + TK.add_class(E, "toolkit-hide"); + disable_draw_self.call(this); + break; + case "showing": + TK.add_class(E, "toolkit-showing"); + time = O.showing_duration || TK.get_duration(E); + if (time > 0) { + this.__hide_id = window.setTimeout(this.__after_showing, time); + enable_draw_children.call(this); + break; + } + this.set("display_state", "show"); + TK.remove_class(E, "toolkit-showing"); + /* FALL THROUGH */ + case "show": + TK.add_class(E, "toolkit-show"); + enable_draw_children.call(this); + break; + } + } + + if (I.content) { + I.content = false; + TK.empty(E); + if (typeof O.content === "string") TK.set_content(E, O.content); + else E.appendChild(O.content); + } + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/crossover.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/crossover.js new file mode 100644 index 0000000000..ab2371a2ab --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/crossover.js @@ -0,0 +1,209 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +"use strict"; +(function(w, TK){ + +function invalidate_bands() { + this.invalid.bands = true; + this.trigger_draw(); +} + +function sort_bands() { + this.bands.sort(function (a, b) { + return a.options.freq - b.options.freq; + }); +} + +function limit_bands () { + if (this.options.leap) return; + sort_bands.call(this); + for (var i = 0; i < this.bands.length; i++) + _set_freq.call(this, i, this.bands[i]); +} + +function set_freq (band) { + if (this.options.leap) return; + var i = this.bands.indexOf(band); + if (i < 0) { + TK.error("Band no member of crossover"); + return; + } + _set_freq.call(this, i, band); +} + +function _set_freq (i, band) { + var freq = band.options.freq; + var dist = freq * this.get("distance") + if (i) + this.bands[i-1].set("x_max", freq - dist); + if (i < this.bands.length-1) + this.bands[i+1].set("x_min", freq + dist); +} + +TK.Crossover = TK.class({ + /** + * TK.Crossover is a {@link TK.Equalizer} displaying the response + * of a multi-band crossover filter. TK.Crossover uses {@link TK.CrossoverBand} + * as response handles. + * + * @class TK.Crossover + * + * @extends TK.Equalizer + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Boolean} [options.leap=true] - Define if bands are allowed to leap over each other. + * @property {Number} [options.distance=0] - Set a minimal distance between bands if leaping is not allowed. + * Value is a factor of x. Example: if distance=0.2 a band cannot be moved beyond 800Hz if the upper next + * band is at 1kHz. + */ + _class: "Crossover", + Extends: TK.Equalizer, + _options: Object.assign(Object.create(TK.Equalizer.prototype._options), { + leap: "boolean", + distance: "number", + }), + options: { + range_y: {min:-60, max: 12, scale: "linear"}, + leap: true, + distance: 0, + }, + initialize: function (options) { + TK.Equalizer.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Equalizer#element - The main DIV container. + * Has class toolkit-response-handler. + */ + TK.add_class(this.element, "toolkit-crossover"); + }, + resize: function () { + invalidate_bands.call(this); + TK.Equalizer.prototype.resize.call(this); + }, + redraw: function () { + var I = this.invalid; + var O = this.options; + var lastb = this.bands.length - 1; + var lastg = this.graphs.length - 1; + if (I.validate("bands", "accuracy")) { + I.bands = false; + I.accuracy = false; + sort_bands.call(this); + for (var i = 0; i < this.bands.length; i++) { + var f = [this.bands[i].lower.get_freq2gain()]; + if (i) + f.push(this.bands[i-1].upper.get_freq2gain()); + this._draw_graph(this.graphs[i], f); + } + this._draw_graph(this.graphs[lastg], [this.bands[lastb].upper.get_freq2gain()]); + } + TK.Equalizer.prototype.redraw.call(this); + }, + add_band: function (options, type) { + type = type || TK.CrossoverBand; + this.add_graph(); + var r = TK.Equalizer.prototype.add_band.call(this, options, type); + var that = this; + r.add_event("set_freq", function (f) { + set_freq.call(that, this); + }); + limit_bands.call(this); + return r; + }, + remove_band: function (band) { + this.remove_graph(this.graphs[this.graphs.length-1]); + var r = TK.Equalizer.prototype.remove_band.call(this, options); + limit_bands.call(this); + return r; + }, +}); + +TK.CrossoverBand = TK.class({ + /** + * TK.CrossoverBand is a {@link TK.EqBand} with an additional filter. + * + * @param {Object} [options={ }] - An object containing additional options. + * + * @property {String|Function} [lower="lowpass3"] - The type of filter for the range below cutoff frequency. See {@link TK.EqBand} for more information. + * @property {String|Function} [upper="highpass3"] - The type of filter for the range above cutoff frequency. See {@link TK.EqBand} for more information. + * @property {Function} [label=function (t, x, y, z) { return TK.sprintf("%.2f Hz", x); }] - The function formatting the handles label. + * + * @class TK.CrossoverBand + * + * @extends TK.EqBand + */ + _class: "CrossoverBand", + Extends: TK.EqBand, + _options: Object.assign(Object.create(TK.EqBand.prototype._options), { + lower: "string", + upper: "string", + }), + options: { + lower: "lowpass3", + upper: "highpass3", + label: function (t, x, y, z) { return TK.sprintf("%.2f Hz", x); }, + mode: "line-vertical", // undocumented, just a default differing from TK.ResponseHanlde + preferences: [ "top-right", "right", "bottom-right", "top-left", "left", "bottom-left"], // undocumented, just a default differing from TK.ResponseHanlde + }, + initialize: function (options) { + /** + * @member {TK.Filter} TK.CrossoverBand#upper - The filter providing the graphical calculations for the upper graph. + */ + this.upper = new TK.Filter(); + /** + * @member {TK.Filter} TK.CrossoverBand#lower - The filter providing the graphical calculations for the lower graph. + */ + this.lower = new TK.Filter(); + TK.EqBand.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.CrossoverBand#element - The main SVG group. + * Has class toolkit-crossoverband. + */ + TK.add_class(this.element, "toolkit-crossoverband"); + + this.set("lower", this.options.lower); + this.set("upper", this.options.upper); + }, + set: function (key, val) { + switch (key) { + case"lower": + this.filter = this.lower; + var r = TK.EqBand.prototype.set.call(this, "type", val); + this.set("mode", "line-vertical"); + return r; + case "upper": + this.filter = this.upper; + var r = TK.EqBand.prototype.set.call(this, "type", val); + this.set("mode", "line-vertical"); + return r; + case "freq": + case "gain": + case "q": + if (this.lower) + this.filter = this.lower; + val = TK.EqBand.prototype.set.call(this, key, val); + this.filter = this.upper; + return TK.EqBand.prototype.set.call(this, key, val); + } + return TK.EqBand.prototype.set.call(this, key, val); + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/dialog.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/dialog.js new file mode 100644 index 0000000000..a146770e77 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/dialog.js @@ -0,0 +1,219 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +function autoclose_cb(e) { + var curr = e.target; + while (curr) { + // TODO: if a dialog is opened out of a dialog both should avoid + // closing any of those on click. former version: + //if (curr === this.element) return; + // this closes tagger in Cabasa Dante Tagger when interacting + // with the colorpicker. + // workaround for the moment: + // don't close on click on any dialog + if (curr.classList.contains("toolkit-dialog")) return; + curr = curr.parentElement; + } + this.close(); +} + +function activate_autoclose() { + if (this._autoclose_active) return; + document.body.addEventListener("click", this._autoclose_cb); + this._autoclose_active = true; +} + +function deactivate_autoclose() { + if (!this._autoclose_active) return; + document.body.removeEventListener("click", this._autoclose_cb); + this._autoclose_active = false; +} + +TK.Dialog = TK.class({ +/** + * TK.Dialog provides a hovering area which can be closed by clicking/tapping + * anywhere on the screen. It can be automatically pushed to the topmost + * DOM position as a child of an AWML-ROOT or the BODY element. On close + * it can be removed from the DOM. The {@link TK.Anchor}-functionality + * makes positioning the dialog window straight forward. + * + * @class TK.Dialog + * + * @extends TK.Container + * @implments TK.Anchor + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Boolean} [options.visible=true] - Hide or show the dialog. + * @property {String} [options.anchor="top-left"] - Origin of `x` and `y` coordinates. + * @property {Number} [options.x=0] - X-position of the dialog. + * @property {Number} [options.y=0] - Y-position of the dialog. + * @property {boolean} [options.auto_close=false] - Set dialog to `visible=false` if clicked outside in the document. + * @property {boolean} [options.auto_remove=false] - Remove the dialogs DOM node after setting `visible=false`. + * @property {boolean} [options.toplevel=false] - Add the dialog DOM node to the topmost position in DOM on `visible=true`. Topmost means either a parenting `AWML-ROOT` or the `BODY` node. + * + */ + _class: "Dialog", + Extends: TK.Container, + Implements: TK.Anchor, + _options: Object.assign(Object.create(TK.Container.prototype._options), { + visible: "boolean", + anchor: "string", + x: "number", + y: "number", + auto_close: "boolean", + auto_remove: "boolean", + toplevel: "boolean", + }), + options: { + visible: true, + anchor: "top-left", + x: 0, + y: 0, + auto_close: false, + auto_remove: false, + toplevel: false, + }, + static_events: { + hide: function() { + deactivate_autoclose.call(this); + if (this.options.auto_remove) + this.element.remove(); + this.fire_event("close"); + }, + set_display_state: function(val) { + var O = this.options; + + if (val === "show") { + if (O.auto_close) + activate_autoclose.call(this); + this.trigger_resize(); + } else { + deactivate_autoclose.call(this); + } + + if (val === "showing") { + var C = O.container; + if (C) C.appendChild(this.element); + this.reposition(); + } + + }, + set_auto_close: function(val) { + if (val) { + if (!this.hidden()) activate_autoclose.call(this); + } else { + deactivate_autoclose.call(this); + } + }, + set_visible: function (val) { + var O = this.options; + if (val) { + deactivate_autoclose.call(this); + if (O.toplevel && O.container.tagName !== "AWML-ROOT" && O.container.tagName !== "BODY") { + var p = this.element; + while ((p = p.parentElement) && p.tagName !== "AWML-ROOT" && p.tagName !== "BODY") {}; + this.set("container", p); + } + this.show(); + } else { + O.container = this.element.parentElement; + this.hide(); + } + }, + }, + initialize: function (options) { + TK.Container.prototype.initialize.call(this, options); + TK.add_class(this.element, "toolkit-dialog"); + var O = this.options; + /* This cannot be a default option because document.body + * is not defined there */ + if (!O.container) O.container = window.document.body; + this._autoclose_active = false; + this._autoclose_cb = autoclose_cb.bind(this); + this.set('visible', O.visible); + if (O.visible) + this.force_show() + else + this.force_hide() + }, + resize: function() { + if (this.options.visible) + this.reposition(); + }, + redraw: function () { + TK.Container.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + var E = this.element; + if (I.x || I.y || I.anchor) { + var bodybox = document.body.getBoundingClientRect(); + var sw = bodybox.width; + var sh = bodybox.height; + var box = this.element.getBoundingClientRect(); + I.x = I.y = I.anchor = false; + var box = E.getBoundingClientRect(); + var pos = this.translate_anchor(O.anchor, O.x, O.y, -box.width, -box.height); + pos.x = Math.min(sw - box.width, Math.max(0, pos.x)); + pos.y = Math.min(sh - box.height, Math.max(0, pos.y)); + E.style.left = pos.x + "px" + E.style.top = pos.y + "px" + } + }, + /** + * Open the dialog. Optionally set x and y position regarding `anchor`. + * + * @method TK.Dialog#open + * + * @param {Number} [x] - New X-position of the dialog. + * @param {Number} [y] - New Y-position of the dialog. + */ + open: function (x, y) { + + this.fire_event("open"); + this.userset("visible", true); + if (typeof x !== "undefined") + this.set("x", x); + if (typeof y !== "undefined") + this.set("y", y); + }, + /** + * Close the dialog. The node is removed from DOM if `auto_remove` is set to `true`. + * + * @method TK.Dialog#close + */ + close: function () { + this.userset("visible", false); + }, + /** + * Reposition the dialog to the current `x` and `y` position. + * + * @method TK.Dialog#reposition + */ + reposition: function () { + var O = this.options; + this.set("x", O.x); + this.set("y", O.y); + } +}); + + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/dynamics.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/dynamics.js new file mode 100644 index 0000000000..24af338ec2 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/dynamics.js @@ -0,0 +1,284 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function range_set(value, key) { + this.range_x.set(key, value); + this.range_y.set(key, value); +} +TK.Dynamics = TK.class({ + /** + * TK.Dynamics are based on {@link TK.Chart} and display the characteristics of dynamic + * processors. They are square widgets drawing a {@link TK.Grid} automatically based on + * the range. + * + * @class TK.Dynamics + * + * @extends TK.Chart + * + * @property {Object} options + * + * @param {Number} [options.min=-96] - Minimum decibels to display. + * @param {Number} [options.max=24] - Maximum decibels to display. + * @param {String} [options.scale="linear"] - Scale of the display, see {@link TK.Range} for details. + * @param {String} [options.type=false] - Type of the dynamics: compressor, expander, gate, limiter or false to draw your own graph. + * @param {Number} [options.threshold=0] - Threshold of the dynamics. + * @param {Number} [options.ratio=1] - Ratio of the dynamics. + * @param {Number} [options.makeup=0] - Makeup of the dynamics. This raises the whole graph after all other parameters are applied. + * @param {Number} [options.range=0] - Range of the dynamics. Only used in type expander. The maximum gain reduction. + * @param {Number} [options.gain=0] - Input gain of the dynamics. + * @param {Number} [options.reference=0] - Input reference of the dynamics. + * @param {Function} [options.grid_labels=function (val) { return val + (!val ? "dB":""); }] - Callback to format the labels of the {@link TK.Grid}. + * @param {Number} [options.db_grid=12] - Draw a grid line every [n] decibels. + */ + _class: "Dynamics", + Extends: TK.Chart, + _options: Object.assign(Object.create(TK.Chart.prototype._options), { + size: "number", // deprecated, undocumented. Is set via CSS. + min: "number", + max: "number", + scale: "string", + type: "string", + threshold: "number", + ratio: "number", + makeup: "number", + range: "number", + gain: "number", + reference: "number", + grid_labels: "function", + db_grid: "number", + }), + options: { + db_grid: 12, + min: -96, + max: 24, + scale: "linear", + type: false, + threshold: 0, + ratio: 1, + makeup: 0, + range: 0, + gain: 0, + reference: 0, + grid_labels: function (val) { return val + (!val ? "dB":""); } + }, + static_events: { + set_size: function(value) { + TK.warn("using deprecated 'size' option"); + this.set("width", value); + this.set("height", value); + }, + set_min: range_set, + set_max: range_set, + set_scale: range_set, + }, + initialize: function (options) { + TK.Chart.prototype.initialize.call(this, options, true); + var O = this.options; + /** + * @member {HTMLDivElement} TK.Dynamics#element - The main DIV container. + * Has class toolkit-dynamics. + */ + TK.add_class(this.element, "toolkit-dynamics"); + this.set("scale", O.scale); + if (O.size) this.set("size", O.size); + this.set("min", O.min); + this.set("max", O.max); + /** + * @member {TK.Graph} TK.Dynamics#steady - The graph drawing the zero line. Has class toolkit-steady + */ + this.steady = this.add_graph({ + dots: [{x:O.min, y:O.min}, + {x:O.max, y:O.max}], + "class": "toolkit-steady", + mode: "line" + }); + }, + + redraw: function () { + var O = this.options; + var I = this.invalid; + + TK.Chart.prototype.redraw.call(this); + + if (I.validate("size", "min", "max", "scale")) { + var grid_x = []; + var grid_y = []; + var min = this.range_x.get("min"); + var max = this.range_x.get("max"); + var step = O.db_grid; + var cls; + for (var i = min; i <= max; i += step) { + cls = i ? "" : "toolkit-highlight"; + grid_x.push({ + pos: i, + label: i === min ? "" : O.grid_labels(i), + "class": cls + }); + grid_y.push({ + pos: i, + label: i === min ? "" : O.grid_labels(i), + "class": cls + }); + } + if (this.grid) { + this.grid.set("grid_x", grid_x); + this.grid.set("grid_y", grid_y); + } + + if (this.steady) + this.steady.set("dots", [{x:O.min, y:O.min}, {x:O.max, y:O.max}]); + } + + if (I.type) { + if (O._last_type) + TK.remove_class(this.element, "toolkit-" + O._last_type); + TK.add_class(this.element, "toolkit-" + O.type); + } + + if (I.validate("ratio", "threshold", "range", "makeup", "gain", "reference")) { + this.draw_graph(); + } + }, + + resize: function() { + var O = this.options; + var E = this.element; + var S = this.svg; + + /* bypass the Chart resize logic here */ + TK.Widget.prototype.resize.call(this); + + var tmp = TK.css_space(S, "border", "padding"); + var w = TK.inner_width(E) - tmp.left - tmp.right; + var h = TK.inner_height(E) - tmp.top - tmp.bottom; + + var s = Math.min(h, w); + + if (s > 0 && s !== O._width) { + this.set("_width", s); + this.set("_height", s); + this.range_x.set("basis", s); + this.range_y.set("basis", s); + } + }, + + draw_graph: function () { + var O = this.options; + if (O.type === false) return; + if (!this.graph) { + this.graph = this.add_graph({ + dots: [{x: O.min, y: O.min}, + {x: O.max, y: O.max}] + }); + } + var curve = []; + var range = O.range; + var ratio = O.ratio; + var thres = O.threshold; + var gain = O.gain; + var ref = O.reference; + var makeup = O.makeup; + var min = O.min; + var max = O.max; + var s; + if (ref == 0) + { + s = 0; + } + else if (!isFinite(ratio)) + { + s = ref; + } + else + { + s = (1 / (Math.max(ratio, 1.001) - 1)) * ratio * ref; + } + var l = 5; // estimated width of line. dirty workaround for + // keeping the line end out of sight in case + // salient point is outside the visible are + switch (O.type) { + case "compressor": + // entry point + curve.push({x: min - l, + y: min + makeup - gain + ref - l}); + // salient point + curve.push({x: thres + gain - s, + y: thres + makeup - s + ref}); + // exit point + if (isFinite(ratio) && ratio > 0) { + curve.push({x: max, + y: thres + makeup + (max - thres - gain) / ratio + }); + } else if (ratio === 0) { + curve.push({x: thres, + y: max + }); + } else { + curve.push({x: max, + y: thres + makeup + }); + } + + break; + case "limiter": + curve.push({x: min, + y: min + makeup - gain}); + curve.push({x: thres + gain, + y: thres + makeup}); + curve.push({x: max, + y: thres + makeup}); + break; + case "gate": + curve.push({x: thres, + y: min}); + curve.push({x: thres, + y: thres + makeup}); + curve.push({x: max, + y: max + makeup}); + break; + case "expander": + if (O.ratio !== 1) { + curve.push({x: min, + y: min + makeup + range}); + + var y = (ratio * range + (ratio - 1) * thres) / (ratio - 1); + curve.push({x: y - range, + y: y + makeup}); + curve.push({x: thres, + y: thres + makeup}); + } + else + curve.push({x: min, + y: min + makeup}); + curve.push({x: max, + y: max + makeup}); + break; + default: + TK.warn("Unsupported type", O.type); + } + this.graph.set("dots", curve); + }, + set: function (key, val) { + if (key == "type") + this.options._last_type = this.options.type; + return TK.Chart.prototype.set.call(this, key, val); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/equalizer.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/equalizer.js new file mode 100644 index 0000000000..cca9645ed9 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/equalizer.js @@ -0,0 +1,346 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +"use strict"; +(function(w, TK){ +function fast_draw_plinear(X, Y) { + var ret = []; + var i, len = X.length; + var dy = 0, x, y, tmp; + + var accuracy = 20; + var c = 0; + + if (len < 2) return ""; + + x = +X[0]; + y = +Y[0]; + + ret.push("M", x.toFixed(2), ",", y.toFixed(2)); + + x = +X[1]; + y = +Y[1]; + + dy = ((y - Y[0])*accuracy)|0; + + for (i = 2; i < len; i++) { + tmp = ((Y[i] - y)*accuracy)|0; + if (tmp !== dy) { + ret.push("L", x.toFixed(2), ",", y.toFixed(2)); + dy = tmp; + c++; + } + x = +X[i]; + y = +Y[i]; + } + + ret.push("L", x.toFixed(2), ",", y.toFixed(2)); + + return ret.join(""); +} +function draw_graph (graph, bands) { + var O = this.options; + var c = 0; + var end = this.range_x.get("basis") | 0; + var step = O.accuracy; + var over = O.oversampling; + var thres = O.threshold; + var x_px_to_val = this.range_x.px2val; + var y_val_to_px = this.range_y.val2px; + var i, j, k; + var x, y; + var pursue; + var diff; + + var X = new Array(end / step); + for (i = 0; i < X.length; i++) { + X[i] = c; + c += step; + } + var Y = new Array(end / step); + var y; + + for (i = 0; i < X.length; i++) { + x = x_px_to_val(X[i]); + y = 0.0; + for (j = 0; j < bands.length; j++) y += bands[j](x); + Y[i] = y_val_to_px(y); + var diff = Math.abs(Y[i] - Y[i-1]) >= thres; + if (i && over > 1 && (diff || pursue)) { + if (diff) pursue = true; + else if (!diff && pursue) pursue = false; + for (k = 1; k < over; k++) { + x = X[i-k] + ((step / over) * k); + X.splice(i, 0, x); + x = x_px_to_val(x); + y = 0.0; + for (j = 0; j < bands.length; j++) y += bands[j](x); + Y.splice(i, 0, y_val_to_px(y)); + i++; + } + } + + if (!isFinite(Y[i])) { + TK.warn("Singular filter in Equalizer."); + graph.set("dots", void(0)); + return; + } + } + graph.set("dots", fast_draw_plinear(X, Y)); +} +function invalidate_bands() { + this.invalid.bands = true; + this.trigger_draw(); +} +function show_bands() { + var b = this.bands; + for (var i = 0; i < b.length; i ++) { + this.add_child(b[i]); + } +} +function hide_bands() { + var b = this.bands; + for (var i = 0; i < b.length; i ++) { + this.remove_child(b[i]); + } +} +TK.Equalizer = TK.class({ + /** + * TK.Equalizer is a {@link TK.ResponseHandler}, utilizing {@link TK.EqBand}s instead of + * simple {@link TK.ResponseHandle}s. + * + * @property {Object} options + * + * @param {Number} [options.accuracy=1] - The distance between points on + * the x axis. Reduces CPU load in favour of accuracy and smoothness. + * @param {Array} [options.bands=[]] - A list of bands to add on init. + * @param {Boolean} [options.show_bands=true] - Show or hide all bands. + * @param {Number} [options.oversampling=5] - If slope of the curve is too + * steep, oversample n times in order to not miss e.g. notch filters. + * @param {Number} [options.threshold=5] - Steepness of slope to oversample, + * i.e. y pixels difference per x pixel + * @class TK.Equalizer + * + * @extends TK.ResponseHandler + */ + _class: "Equalizer", + Extends: TK.ResponseHandler, + _options: Object.assign(Object.create(TK.ResponseHandler.prototype._options), { + accuracy: "number", + oversampling: "number", + threshold: "number", + bands: "array", + show_bands: "boolean", + }), + options: { + accuracy: 1, // the distance between points of curves on the x axis + oversampling: 4, // if slope of the curve is too steep, oversample + // n times in order to not miss a notch filter + threshold: 10, // steepness of slope, i.e. amount of y pixels difference + bands: [], // list of bands to create on init + show_bands: true, + }, + static_events: { + set_bands: function(value) { + if (this.bands.length) this.remove_bands(); + this.add_bands(value); + }, + set_show_bands: function(value) { + (value ? show_bands : hide_bands).call(this); + }, + }, + + initialize: function (options) { + TK.ResponseHandler.prototype.initialize.call(this, options); + /** + * @member {Array} TK.Equalizer#bands - Array of {@link TK.EqBand} instances. + */ + this.bands = this.handles; + + /** + * @member {HTMLDivElement} TK.Equalizer#element - The main DIV container. + * Has class toolkit-equalizer. + */ + TK.add_class(this.element, "toolkit-equalizer"); + + /** + * @member {SVGGroup} TK.Equalizer#_bands - The SVG group containing all the bands SVG elements. + * Has class toolkit-eqbands. + */ + this._bands = this._handles; + TK.add_class(this._bands, "toolkit-eqbands"); + + /** + * @member {TK.Graph} TK.Equalizer#baseline - The graph drawing the zero line. + * Has class toolkit-baseline + */ + this.baseline = this.add_graph({ + range_x: this.range_x, + range_y: this.range_y, + container: this._bands, + dots: [{x: 20, y: 0}, {x: 20000, y: 0}], + "class": "toolkit-baseline" + }); + this.add_bands(this.options.bands); + }, + + destroy: function () { + this.empty(); // Arne: ??? <- Markus: removes all graphs, defined in Chart + this._bands.remove(); + TK.ResponseHandler.prototype.destroy.call(this); + }, + redraw: function () { + var I = this.invalid; + var O = this.options; + TK.ResponseHandler.prototype.redraw.call(this); + if (I.validate("bands", "accuracy")) { + if (this.baseline) { + var f = []; + for (var i = 0; i < this.bands.length; i++) { + if (this.bands[i].get("active")) { + f.push(this.bands[i].filter.get_freq2gain()); + } + } + draw_graph.call(this, this.baseline, f); + } + } + + if (I.show_bands) { + I.show_bands = false; + if (O.show_bands) { + this._bands.style.removeProperty("display"); + } else { + this._bands.style.display = "none"; + } + } + }, + resize: function () { + invalidate_bands.call(this); + TK.ResponseHandler.prototype.resize.call(this); + }, + /** + * Add a new band to the equalizer. Options is an object containing + * options for the {@link TK.EqBand} + * + * @method TK.Equalizer#add_band + * + * @param {Object} [options={ }] - An object containing initial options for the {@link TK.EqBand}. + * @param {Object} [type=TK.EqBand] - A widget class to be used for the new band. + * + * @emits TK.Equalizer#bandadded + */ + add_band: function (options, type) { + var b; + type = type || TK.EqBand; + if (type.prototype.isPrototypeOf(options)) { + b = options; + } else { + options.container = this._bands; + if (options.range_x === void(0)) + options.range_x = function () { return this.range_x; }.bind(this); + if (options.range_y === void(0)) + options.range_y = function () { return this.range_y; }.bind(this); + if (options.range_z === void(0)) + options.range_z = function () { return this.range_z; }.bind(this); + + options.intersect = this.intersect.bind(this); + b = new type(options); + } + + this.bands.push(b); + b.add_event("set", invalidate_bands.bind(this)); + /** + * Is fired when a new band was added. + * + * @event TK.Equalizer#bandadded + * + * @param {TK.Band} band - The {@link TK.EqBand} which was added. + */ + this.fire_event("bandadded", b); + if (this.options.show_bands) + this.add_child(b); + invalidate_bands.call(this); + return b; + }, + /** + * Add multiple new {@link TK.EqBand}s to the equalizer. Options is an array + * of objects containing options for the new instances of {@link TK.EqBand} + * + * @method TK.Equalizer#add_bands + * + * @param {Array} options - An array of options objects for the {@link TK.EqBand}. + * @param {Object} [type=TK.EqBand] - A widget class to be used for the new band. + */ + add_bands: function (bands, type) { + for (var i = 0; i < bands.length; i++) + this.add_band(bands[i], type); + }, + /** + * Remove a band from the widget. + * + * @method TK.Equalizer#remove_handle + * + * @param {TK.EqBand} band - The {@link TK.EqBand} to remove. + * + * @emits TK.Equalizer#bandremoved + */ + remove_band: function (h) { + for (var i = 0; i < this.bands.length; i++) { + if (this.bands[i] === h) { + if (this.options.show_bands) + this.remove_child(h); + + this.bands.splice(i, 1); + /** + * Is fired when a band was removed. + * + * @event TK.Equalizer#bandremoved + * + * @param {TK.EqBand} band - The {@link TK.EqBand} which was removed. + */ + this.fire_event("bandremoved", h); + h.destroy(); + break; + } + } + }, + /** + * Remove multiple {@link TK.EqBand} from the equalizer. Options is an array + * of {@link TK.EqBand} instances. + * + * @method TK.Equalizer#remove_bands + * + * @param {Array} bands - An array of {@link TK.EqBand} instances. + */ + remove_bands: function () { + while (this.bands.length) { + this.remove_band(this.bands[0]); + } + this.bands = []; + /** + * Is fired when all bands are removed. + * + * @event TK.Equalizer#emptied + */ + this.fire_event("emptied"); + invalidate_bands.call(this); + }, + _draw_graph: draw_graph, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/expander.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/expander.js new file mode 100644 index 0000000000..61a6cdfbb1 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/expander.js @@ -0,0 +1,272 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function toggle(e) { + var self = this.parent; + e.preventDefault(); + e.stopPropagation(); + return collapse.call(self, !self.options.expanded); +} +function collapse(state) { + this.userset("expanded", state); + return false; +} +function visible_when_expanded(widget) { + var v = widget.options._expanded; + return v !== false; +} +function visible_when_collapsed(widget) { + var v = widget.options._collapsed; + return v === true; +} +function is_visible(widget) { + var value = this.options.always_expanded || this.options.expanded; + + if (value) { + return visible_when_expanded(widget); + } else { + return visible_when_collapsed(widget); + } +} +function changed_expanded(value) { + var group = this.options.group; + var other_expander; + var grp; + + if (group) { + grp = expander_groups[group]; + if (value) { + other_expander = grp.active; + grp.active = this; + if (other_expander && other_expander !== this) + other_expander.set("expanded", false); + } else if (grp.active === this) { + grp.active = false; + if (grp.default) grp.default.set("expanded", true); + } + } + update_visibility.call(this); +} +function add_to_group(group) { + var grp; + var O = this.options; + if (!(grp = expander_groups[group])) + expander_groups[group] = grp = { active: false, default: false }; + + if (O.group_default) { + grp.default = this; + if (!grp.active) { + this.set("expanded", true); + return; + } + } + + if (O.expanded) + changed_expanded.call(this, O.expanded); +} + +function remove_from_group(group) { + var grp = expander_groups[group]; + + if (grp.default === this) grp.default = false; + if (grp.active === this) { + grp.active = false; + if (grp.default) grp.default.set("expanded", true); + } +} +function remove_group_default(group) { + if (!group) return; + var grp = expander_groups[group]; + grp.default = false; +} +function update_visibility() { + var C = this.children; + var value = this.options.always_expanded || this.options.expanded; + + if (C) { + var test = value ? visible_when_expanded : visible_when_collapsed; + for (var i = 0; i < C.length; i++) { + if (test(C[i])) + this.show_child(i); + else + this.hide_child(i); + } + } + + if (value) { + this.fire_event("expand"); + /** + * Is fired when the expander expands. + * + * @event TK.Expander#expand + */ + } else { + /** + * Is fired when the expander collapses. + * + * @event TK.Expander#collapse + */ + this.fire_event("collapse"); + } +} +var expander_groups = { }; +w.eg = expander_groups; +TK.Expander = TK.class({ + /** + * TK.Expander is a container which can be toggled between two different states, + * expanded and collapsed. It can be used to implement overlay popups, but it is + * not limited to that application. + * In expanded mode the container has the class toolkit-expanded. + * Child widgets are shown or hidden depending on the state of the two pseudo + * options _expanded and _collapsed. If a child widget + * of the expander has _expanded set to true it will be shown in + * expanded state. If a child widget has _collapsed set to false, it + * will be shown in collapsed state. This feature can be used to make interfaces + * more reactive. + * + * @class TK.Expander + * + * @extends TK.Container + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Boolean} [options.expanded=false] - The state of the widget. + * @property {Boolean} [options.always_expanded=false] - This essentially overwrites + * the expanded option. This can be used to switch this widget to be + * always expanded, e.g. when the screen size is big enough. + * @property {String} [options.group=""] - If set, this expander is grouped together with + * all other expanders of the same group name. At most one expander of the same group + * can be open at one time. + * @property {Boolean} [options.group_default=false] - If set, this expander is expanded + * if all other group members are collapsed. + * @property {String} [options.icon=""] - Icon of the {@link TK.Button} which toggles expanded state. + * @property {String} [options.label=""] - Label of the {@link TK.Button} which toggles expanded state. + * @property {Boolean} [options.show_button=true] - Set to `false` to hide the {@link TK.Button} toggling expanded state. + */ + _class: "Expander", + _options: Object.assign(Object.create(TK.Container.prototype._options), { + expanded: "boolean", + always_expanded: "boolean", + group: "string", + group_default: "boolean", + label: "string", + icon: "string", + }), + options: { + expanded: false, + always_expanded: false, + group_default: false, + label: "", + icon: "", + }, + static_events: { + set_expanded: changed_expanded, + set_always_expanded: update_visibility, + set_group: function(value) { + if (value) add_to_group.call(this, value); + } + }, + Extends: TK.Container, + /** + * Toggles the collapsed state of the widget. + * + * @method TK.Expander#toggle + */ + toggle: function() { + toggle.call(this); + }, + redraw: function() { + var I = this.invalid; + var O = this.options; + + TK.Container.prototype.redraw.call(this); + + if (I.always_expanded) { + this[O.always_expanded ? "add_class" : "remove_class"]("toolkit-always-expanded"); + } + + if (I.expanded || I.always_expanded) { + I.always_expanded = I.expanded = false; + var v = O.always_expanded || O.expanded; + this[v ? "add_class" : "remove_class"]("toolkit-expanded"); + this.trigger_resize(); + } + }, + initialize: function (options) { + TK.Container.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Expander#element - The main DIV container. + * Has class toolkit-expander. + */ + TK.add_class(this.element, "toolkit-expander"); + + this._update_visibility = update_visibility.bind(this); + + if (this.options.group) add_to_group.call(this, this.options.group); + + this.set("expanded", this.options.expanded); + this.set("always_expanded", this.options.always_expanded); + + }, + add_child: function(child) { + TK.Container.prototype.add_child.call(this, child); + if (!is_visible.call(this, child)) this.hide_child(child); + child.add_event("set__expanded", this._update_visibility); + child.add_event("set__collapsed", this._update_visibility); + }, + remove_child: function(child) { + TK.Container.prototype.remove_child.call(this, child); + child.remove_event("set__expanded", this._update_visibility); + child.remove_event("set__collapsed", this._update_visibility); + }, + set: function(key, value) { + var group; + if (key === "group") { + group = this.options.group; + // this is reached from init, where this element was never added + // to the group. + if (group && value !== group) remove_from_group.call(this, group); + } else if (key === "group_default") { + if (!value && this.options.group_default) + remove_group_default.call(this, this.options.group); + } + return TK.Container.prototype.set.call(this, key, value); + }, +}); +/** + * @member {TK.Button} TK.Expander#button - The button for toggling the state of the expander. + */ +TK.ChildWidget(TK.Expander, "button", { + create: TK.Button, + show: true, + map_options: { + label: "label", + icon: "icon", + }, + default_options: { + _expanded: true, + _collapsed: true, + class: "toolkit-toggle-expand" + }, + static_events: { + click: toggle, + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/fader.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/fader.js new file mode 100644 index 0000000000..df3692b27f --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/fader.js @@ -0,0 +1,411 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + + /** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the option value. + * + * @event TK.Fader#useraction + * + * @param {string} name - The name of the option which was changed due to the users action + * @param {mixed} value - The new value of the option + */ +"use strict"; +(function(w, TK){ + +function vert(O) { + return O.layout === "left" || O.layout === "right"; +} +function get_value(ev) { + var is_vertical = vert(this.options); + var pos, real, hsize, pad; + hsize = this._handle_size / 2; + pad = this._padding; + + if (is_vertical) { + real = this.options.basis - (ev.offsetY - hsize) + pad.bottom; + } else { + real = ev.offsetX - hsize + pad.left; + } + return this.px2val(real); +} +function tooltip_by_position(ev, tt) { + if (this._handle.contains(ev.target)) { + tooltip_by_value.call(this, ev, tt); + return; + } + var val = this.snap(get_value.call(this, ev)); + TK.set_text(tt, this.options.tooltip(val)); +} +function tooltip_by_value(ev, tt) { + TK.set_text(tt, this.options.tooltip(this.options.value)); +} +function mouseenter (ev) { + if (!this.options.tooltip) return; + TK.tooltip.add(1, this.tooltip_by_position); +} +function clicked(ev) { + var value; + if (this._handle.contains(ev.target)) return; + if (this.value && this.value.element.contains(ev.target)) return; + if (this.label && this.label.element.contains(ev.target)) return; + if (this.scale && this.scale.element.contains(ev.target)) return; + value = this.userset("value", get_value.call(this, ev)); + if (this.options.tooltip && TK.tooltip._entry) + TK.set_text(TK.tooltip._entry, this.options.tooltip(this.options.value)); +} +function mouseleave (ev) { + TK.tooltip.remove(1, this.tooltip_by_position); +} +function startdrag(ev) { + if (!this.options.tooltip) return; + TK.tooltip.add(0, this.tooltip_by_value); +} +function stopdrag(ev) { + TK.tooltip.remove(0, this.tooltip_by_value); +} +function scrolling(ev) { + if (!this.options.tooltip) return; + TK.set_text(TK.tooltip._entry, this.options.tooltip(this.options.value)); +} +function dblclick(ev) { + this.userset("value", this.options.reset); + /** + * Is fired when the handle receives a double click. + * + * @event TK.Fader#doubleclick + * + * @param {number} value - The value of the {@link TK.Fader}. + */ + this.fire_event("doubleclick", this.options.value); +} +function activate_tooltip() { + if (!this.tooltip_by_position) { + this.tooltip_by_position = tooltip_by_position.bind(this); + this.tooltip_by_value = tooltip_by_value.bind(this); + this.__startdrag = startdrag.bind(this); + this.__stopdrag = stopdrag.bind(this); + this.__scrolling = scrolling.bind(this); + } + this.add_event("mouseenter", mouseenter); + this.add_event("mouseleave", mouseleave); + this.drag.add_event("startdrag", this.__startdrag); + this.drag.add_event("stopdrag", this.__stopdrag); + this.scroll.add_event("scrolling", this.__scrolling); +} + +function deactivate_tooltip() { + if (!this.tooltip_by_position) return; + TK.tooltip.remove(0, this.tooltip_by_value); + TK.tooltip.remove(1, this.tooltip_by_position); + this.remove_event("mouseenter", mouseenter); + this.remove_event("mouseleave", mouseleave); + this.drag.remove_event("startdrag", this.__startdrag); + this.drag.remove_event("stopdrag", this.__stopdrag); + this.scroll.remove_event("scrolling", this.__scrolling); +} +/** + * TK.Fader is a slidable control with a {@link TK.Scale} next to it which + * can be both dragged and scrolled. TK.Fader implements {@link TK.Ranged}, + * {@link TK.Warning} and {@link TK.GlobalCursor} and inherits their options. + * A {@link TK.Label} and a {@link TK.Value} are available optionally. + * + * @class TK.Fader + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.value] - The faders position. This options is + * modified by user interaction. + * @property {Function} [options.tooltip=false] - An optional formatting function for + * the tooltip value. The tooltip will show the value the mouse cursor is + * currently hovering over. If this option is not set, no tooltip will be shown. + * @property {Boolean} [options.bind_click=false] - If true, a click + * on the fader will move the handle to the pointed position. + * @property {Boolean} [options.bind_dblclick=true] - If true, a dblclick + * on the fader will reset the fader value to options.reset. + * @property {Number} [options.reset=options.value] - The reset value, which is used by + * the dblclick event and the {@link TK.Fader#reset} method. + * @property {Boolean} [options.show_scale=true] - If true, a {@link TK.Scale} is added to the fader. + * @property {Boolean} [options.show_value=false] - If true, a {@link TK.Value} widget is added to the fader. + * @property {String|Boolean} [options.label=false] - Add a label to the fader. Set to `false` to remove the label from the DOM. + */ +TK.Fader = TK.class({ + _class: "Fader", + Extends: TK.Widget, + Implements: [TK.Ranged, TK.Warning, TK.GlobalCursor], + _options: Object.assign(Object.create(TK.Widget.prototype._options), + TK.Ranged.prototype._options, TK.Scale.prototype._options, { + value: "number", + division: "number", + levels: "array", + gap_dots: "number", + gap_labels: "number", + show_labels: "boolean", + labels: "function", + tooltip: "function", + layout: "string", + direction: "int", + reset: "number", + bind_click: "boolean", + bind_dblclick: "boolean", + }), + options: { + value: 0, + division: 1, + levels: [1, 6, 12, 24], + gap_dots: 3, + gap_labels: 40, + show_labels: true, + labels: function (val) { return val.toFixed(2); }, + tooltip: false, + layout: "left", + bind_click: false, + bind_dblclick: true, + label: false, + }, + static_events: { + set_bind_click: function(value) { + if (value) this.add_event("click", clicked); + else this.remove_event("click", clicked); + }, + set_bind_dblclick: function(value) { + if (value) this.add_event("dblclick", dblclick); + else this.remove_event("dblclick", dblclick); + }, + set_tooltip: function(value) { + (value ? activate_tooltip : deactivate_tooltip).call(this); + }, + set_layout: function(value) { + this.options.direction = vert(this.options) ? "vertical" : "horizontal"; + this.drag.set("direction", this.options.direction); + this.scroll.set("direction", this.options.direction); + }, + }, + initialize: function (options) { + this.__tt = false; + TK.Widget.prototype.initialize.call(this, options); + + var E, O = this.options; + + /** + * @member {HTMLDivElement} TK.Fader#element - The main DIV container. + * Has class toolkit-fader. + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-fader"); + this.widgetize(E, true, true, true); + + /** + * @member {HTMLDivElement} TK.Fader#_track - The track for the handle. Has class toolkit-track. + */ + this._track = TK.element("div", "toolkit-track"); + this.element.appendChild(this._track); + + /** + * @member {HTMLDivElement} TK.Fader#_handle - The handle of the fader. Has class toolkit-handle. + */ + this._handle = TK.element("div", "toolkit-handle"); + this._handle_size = 0; + this._track.appendChild(this._handle); + + if (O.reset === void(0)) + O.reset = O.value; + + if (O.direction === void(0)) + O.direction = vert(O) ? "vertical" : "horizontal"; + /** + * @member {TK.DragValue} TK.Fader#drag - Instance of {@link TK.DragValue} used for the handle + * interaction. + */ + this.drag = new TK.DragValue(this, { + node: this._handle, + classes: this.element, + direction: O.direction, + limit: true, + }); + /** + * @member {TK.ScrollValue} TK.Fader#scroll - Instance of {@link TK.ScrollValue} used for the + * handle interaction. + */ + this.scroll = new TK.ScrollValue(this, { + node: this.element, + classes: this.element, + limit: true, + }); + + this.set("bind_click", O.bind_click); + this.set("bind_dblclick", O.bind_dblclick); + this.set("tooltip", O.tooltip); + }, + + redraw: function () { + TK.Widget.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + var E = this.element; + var value; + var tmp; + + if (I.layout) { + I.layout = false; + value = O.layout; + TK.remove_class(E, "toolkit-vertical", "toolkit-horizontal", "toolkit-left", + "toolkit-right", "toolkit-top", "toolkit-bottom"); + TK.add_class(E, vert(O) ? "toolkit-vertical" : "toolkit-horizontal"); + TK.add_class(E, "toolkit-"+value); + + if (TK.supports_transform) + this._handle.style.transform = null; + else { + if (vert(O)) + this._handle.style.left = null; + else + this._handle.style.bottom = null; + } + I.value = false; + } + + if (I.validate.apply(I, Object.keys(TK.Ranged.prototype._options)) || I.value) { + I.value = false; + // TODO: value is snapped already in set(). This is not enough for values which are set during + // initialization. + tmp = this.val2px(this.snap(O.value)) + "px" + + if (vert(O)) { + if (TK.supports_transform) + this._handle.style.transform = "translateY(-"+tmp+")"; + else + this._handle.style.bottom = tmp; + } else { + if (TK.supports_transform) + this._handle.style.transform = "translateX("+tmp+")"; + else + this._handle.style.left = tmp; + } + } + }, + resize: function () { + var O = this.options; + var T = this._track, H = this._handle; + var basis; + + TK.Widget.prototype.resize.call(this); + + this._padding = TK.css_space(T, "padding", "border"); + + if (vert(O)) { + this._handle_size = TK.outer_height(H, true); + basis = TK.inner_height(T) - this._handle_size; + } else { + this._handle_size = TK.outer_width(H, true); + basis = TK.inner_width(T) - this._handle_size; + } + + this.set("basis", basis); + }, + destroy: function () { + this._handle.remove(); + TK.Widget.prototype.destroy.call(this); + TK.tooltip.remove(0, this.tooltip_by_value); + TK.tooltip.remove(1, this.tooltip_by_position); + }, + + /** + * Resets the fader value to options.reset. + * + * @method TK.Fader#reset + */ + reset: function() { + this.set("value", this.options.reset); + }, + + // GETTER & SETTER + set: function (key, value) { + if (key === "value") { + if (value > this.options.max || value < this.options.min) + this.warning(this.element); + value = this.snap(value); + } + + return TK.Widget.prototype.set.call(this, key, value); + }, + userset: function (key, value) { + if (key == "value") { + if (value > this.options.max || value < this.options.min) + this.warning(this.element); + value = this.snap(value); + } + return TK.Widget.prototype.userset.call(this, key, value); + } +}); +/** + * @member {TK.Scale} TK.Fader#scale - A {@link TK.Scale} to display a scale next to the fader. + */ +TK.ChildWidget(TK.Fader, "scale", { + create: TK.Scale, + show: true, + inherit_options: true, + toggle_class: true, + static_events: { + set: function(key, value) { + /** + * Is fired when the scale was changed. + * + * @event TK.Fader#scalechanged + * + * @param {string} key - The key of the option. + * @param {mixed} value - The value to which it was set. + */ + if (this.parent) + this.parent.fire_event("scalechanged", key, value); + }, + }, +}); +/** + * @member {TK.Label} TK.Fader#label - A {@link TK.label} to display a title. + */ +TK.ChildWidget(TK.Fader, "label", { + create: TK.Label, + show: false, + toggle_class: true, + option: "label", + map_options: { + label: "label", + }, +}); +/** + * @member {TK.Label} TK.Fader#value - A {@link TK.Value} to display the current value, offering a way to enter a value via keyboard. + */ +TK.ChildWidget(TK.Fader, "value", { + create: TK.Value, + show: false, + static_events: { + "valueset" : function (v) { this.parent.set("value", v); } + }, + map_options: { + value: "value", + format: "format", + }, + toggle_class: true, + userset_delegate: true, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/frame.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/frame.js new file mode 100644 index 0000000000..cc65ea2be6 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/frame.js @@ -0,0 +1,59 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +/** + * Frame is a {@link TK.Container} with a {@link TK.Label} on top. + * + * @extends TK.Container + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String|Boolean} [options.label=false] - The label of the frame. Set to `false` to remove it from the DOM. + * + * @class TK.Frame + */ +TK.Frame = TK.class({ + Extends: TK.Container, + _class: "Frame", + _options: Object.create(TK.Container.prototype._options), + options: { + label: false, + }, + initialize: function (options) { + TK.Container.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Frame#element - The main DIV container. + * Has class toolkit-frame. + */ + TK.add_class(this.element, "toolkit-frame"); + }, +}); +/** + * @member {TK.Label} TK.Frame#label - The {@link TK.Label} of the frame. + */ +TK.ChildWidget(TK.Frame, "label", { + create: TK.Label, + option: "label", + inherit_options: true, + default_options: { + class: "toolkit-frame-label" + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/frequencyresponse.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/frequencyresponse.js new file mode 100644 index 0000000000..63975ba831 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/frequencyresponse.js @@ -0,0 +1,160 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function calculate_grid(range, step) { + var min = range.get("min"); + var max = range.get("max"); + var grid = []; + var i, cls; + + + for (i = min; i <= max; i += step) { + if (i == 0) { + cls = "toolkit-highlight"; + } else { + cls = ""; + } + grid.push({ + pos: i, + label: i === min ? "" : i + "dB", + "class": cls + }); + } + + return grid; +} +TK.FrequencyResponse = TK.class({ + /** + * TK.FrequencyResponse is a {@link TK.Chart} drawing frequencies on the x axis and dB + * values on the y axis. This widget automatically draws a {@link TK.Grid} depending + * on the ranges. + * + * @class TK.FrequencyResponse + * + * @extends TK.Chart + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.db_grid=12] - Distance in decibels between y axis grid lines. + * @property {Object} [options.range_x={min:20, max:20000, scale:"frequency"}] - Either a function returning a {@link TK.Range} + * or an object containing options for a new {@link TK.Range} + * @property {Object} [options.range_y={min:-36, max: 36, scale: "linear"}] - Either a function returning a {@link TK.Range} + * or an object containing options for a new {@link TK.Range} + * @property {Array} [options.grid_x=[{pos: 20, label: "20 Hz"}, {pos: 30}, {pos: 40}, {pos: 50}, {pos: 60}, {pos: 70}, {pos: 80}, {pos: 90}, {pos: 100, label: "100 Hz"}, {pos: 200}, {pos: 300}, {pos: 400}, {pos: 500}, {pos: 600}, {pos: 700}, {pos: 800}, {pos: 900}, {pos: 1000, label: "1000 Hz"}, {pos: 2000}, {pos: 3000}, {pos: 4000}, {pos: 5000}, {pos: 6000}, {pos: 7000}, {pos: 8000}, {pos: 9000}, {pos: 10000, label: "10000 Hz"}, {pos: 20000, label: "20000 Hz"}]] - An array containing objects with the following optional members: + * @property {Number} [options.grid_x.pos] - The value where to draw grid line and corresponding label. + * @property {String} [options.grid_x.color] - A valid CSS color string to colorize the elements. + * @property {String} [options.grid_x.class] - A class name for the elements. + * @property {String} [options.grid_x.label] - A label string. + * @property {String} [options.scale="linear"] - The type of the decibels scale. See {@link TK.Range} for more details. + * @property {Number} [options.depth=0] - The depth of the z axis (basis of options.range_z) + */ + _class: "FrequencyResponse", + Extends: TK.Chart, + _options: Object.assign(Object.create(TK.Chart.prototype._options), { + db_grid: "number", + grid_x: "array", + scale: "boolean", + depth: "number", + }), + options: { + db_grid: 12, // dB grid distance + range_x: {min:20, max:20000, scale:"frequency"}, // TK.Range x options + range_y: {min:-36, max: 36, scale: "linear"}, // TK.Range y options + range_z: {min:0.1, max:10, scale:"linear"}, + grid_x: [ + {pos: 20, label: "20Hz"}, + {pos: 30}, + {pos: 40}, + {pos: 50}, + {pos: 60}, + {pos: 70}, + {pos: 80}, + {pos: 90}, + {pos: 100, label: "100Hz"}, + {pos: 200}, + {pos: 300}, + {pos: 400}, + {pos: 500}, + {pos: 600}, + {pos: 700}, + {pos: 800}, + {pos: 900}, + {pos: 1000, label: "1kHz"}, + {pos: 2000}, + {pos: 3000}, + {pos: 4000}, + {pos: 5000}, + {pos: 6000}, + {pos: 7000}, + {pos: 8000}, + {pos: 9000}, + {pos: 10000, label: "10kHz"}, + {pos: 20000, label: "20kHz"}], // Frequency grid + scale: false, // the mode of the + // dB scale + depth: 0, // the depth of the z axis (basis of range_z) + }, + static_events: { + set_scale: function(value, key) { + this.range_y.set("scale", value); + }, + set_db_grid: function(value) { + this.set("grid_y", calculate_grid(this.range_y, value)); + }, + }, + initialize: function (options) { + if (options.scale) + this.set("scale", options.scale, true); + TK.Chart.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.FrequencyResponse#element - The main DIV container. + * Has class toolkit-frequency-response. + */ + TK.add_class(this.element, "toolkit-frequency-response"); + /** + * @member {SVGGroup} TK.Chart#_handles - The SVG group containing all handles. + * Has class toolkit-response-handles. + */ + TK.add_class(this._handles, "toolkit-response-handles"); + + // do not overwrite custom grids, please + if (this.options.db_grid && !this.options.grid_y.length) + this.set("db_grid", this.options.db_grid); + this.range_y.add_event("set", function (key, value) { + if (key === "scale") + this.options.scale = value; + }.bind(this)); + if (this.options.depth) this.set("depth", this.options.depth); + }, + + redraw: function() { + var I = this.invalid; + var O = this.options; + + if (I.scale) { + I.scale = false; + // tell chart to redraw + I.ranges = true; + } + + TK.Chart.prototype.redraw.call(this); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/gauge.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/gauge.js new file mode 100644 index 0000000000..29356ec5f8 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/gauge.js @@ -0,0 +1,171 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function _get_coords_single(deg, inner, pos) { + deg = deg * Math.PI / 180; + return { + x: Math.cos(deg) * inner + pos, + y: Math.sin(deg) * inner + pos + } +} +var format_translate = TK.FORMAT("translate(%f, %f)"); +var format_viewbox = TK.FORMAT("0 0 %d %d"); +/** + * TK.Gauge draws a single {@link TK.Circular} into a SVG image. It inherits + * all options of {@link TK.Circular}. + * + * @class TK.Gauge + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.x=0] - Displacement of the {@link TK.Circular} + * in horizontal direction. This allows drawing gauges which are only + * represented by a segment of a circle. + * @property {Number} [options.y=0] - Displacement of the {@link TK.Circular} + * in vertical direction. + * @property {Object} [options.title] - Optional gauge title. + * @property {Number} [options.title.pos] - Position inside of the circle in + * degrees. + * @property {String} [options.title.title] - Title string. + * @property {Number} [options.title.margin] - Margin of the title string. + * @property {String} [options.title.align] - Alignment of the title, either + * inner or outer. + */ +TK.Gauge = TK.class({ + _class: "Gauge", + Extends: TK.Widget, + _options: Object.assign(Object.create(TK.Circular.prototype._options), { + width: "number", + height: "number", + title: "object", + }), + options: Object.assign({}, TK.Circular.prototype.options, { + width: 120, // width of the element + height: 120, // height of the svg + size: 120, + title: {pos: 90, margin: 0, align: "inner", title:""} + }), + initialize: function (options) { + if (typeof options.title === "string") + options.title = {title: options.title}; + TK.Widget.prototype.initialize.call(this, options); + var O = this.options; + var E, S; + if (!(E = this.element)) this.element = E = TK.element("div"); + + /** + * @member {SVGImage} TK.Gauge#svg - The main SVG image. + */ + this.svg = S = TK.make_svg("svg"); + + /** + * @member {HTMLDivElement} TK.Gauge#element - The main DIV container. + * Has class toolkit-gauge. + */ + TK.add_class(E, "toolkit-gauge"); + this.widgetize(E, true, true, true); + + /** + * @member {SVGText} TK.Gauge#_title - The title of the gauge. + * Has class toolkit-title. + */ + this._title = TK.make_svg("text", {"class": "toolkit-title"}); + S.appendChild(this._title); + + var co = TK.object_and(O, TK.Circular.prototype._options); + co = TK.object_sub(co, TK.Widget.prototype._options); + co.container = S; + + /** + * @member {TK.Circular} TK.Gauge#circular - The {@link TK.Circular} module. + */ + this.circular = new TK.Circular(co); + this.add_child(this.circular); + this.widgetize(this.element); + E.appendChild(S); + }, + resize: function() { + TK.Widget.prototype.resize.call(this); + this.invalid.title = true; + this.trigger_draw(); + }, + redraw: function() { + var I = this.invalid, O = this.options; + var S = this.svg; + + TK.Widget.prototype.redraw.call(this); + + if (I.validate("width", "height")) { + S.setAttribute("viewBox", format_viewbox(O.width, O.height)); + } + + if (I.validate("title", "size", "x", "y")) { + var _title = this._title; + _title.textContent = O.title.title; + + if (O.title.title) { + TK.S.add(function() { + var t = O.title; + var outer = O.size / 2; + var margin = t.margin; + var align = t.align === "inner"; + var bb = _title.getBoundingClientRect(); + var angle = t.pos % 360; + var outer_p = outer - margin; + var coords = _get_coords_single(angle, outer_p, outer); + + var mx = ((coords.x - outer) / outer_p) + * (bb.width + bb.height / 2.5) / (align ? -2 : 2); + var my = ((coords.y - outer) / outer_p) + * bb.height / (align ? -2 : 2); + + mx += O.x; + my += O.y; + + TK.S.add(function() { + _title.setAttribute("transform", format_translate(coords.x + mx, coords.y + my)); + _title.setAttribute("text-anchor", "middle"); + }.bind(this), 1); + /** + * Is fired when the title changed. + * + * @event TK.Gauge#titledrawn + */ + this.fire_event("titledrawn"); + }.bind(this)); + } + } + }, + + // GETTERS & SETTERS + set: function (key, value) { + if (key === "title") { + if (typeof value === "string") value = {title: value}; + value = Object.assign(this.options.title, value); + } + // TK.Circular does the snapping + if (!TK.Widget.prototype._options[key] && TK.Circular.prototype._options[key]) + value = this.circular.set(key, value); + return TK.Widget.prototype.set.call(this, key, value); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/icon.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/icon.js new file mode 100644 index 0000000000..a17b1759d4 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/icon.js @@ -0,0 +1,88 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +"use strict"; +(function(w, TK){ + +TK.Icon = TK.class({ + /** + * TK.Icon represents a <DIV> element showing either + * icons from the toolkit font or dedicated image files as CSS background. + * + * @class TK.Icon + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String} [options.icon] - The icon to show. It can either be + * a string which is interpreted as class name (if [A-Za-z0-9_\-]) or as URI. + */ + _class: "Icon", + Extends: TK.Widget, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + icon: "string", + }), + options: { + icon: false, + }, + initialize: function (options) { + var E; + TK.Widget.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Icon#element - The main DIV element. Has class toolkit-icon + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-icon"); + this.widgetize(E, true, true, true); + this._icon_old = []; + }, + redraw: function() { + var O = this.options; + var I = this.invalid; + var E = this.element; + + TK.Widget.prototype.redraw.call(this); + + if (I.icon) { + I.icon = false; + var old = this._icon_old; + for (var i = 0; i < old.length; i++) { + if (old[i] && TK.is_class_name(old[i])) { + TK.remove_class(E, old[i]); + } + } + this._icon_old = []; + if (TK.is_class_name(O.icon)) { + E.style["background-image"] = null; + if (O.icon) + TK.add_class(E, O.icon); + } else if (O.icon) { + E.style["background-image"] = "url(\"" + O.icon + "\")"; + } + } + }, + set: function (key, val) { + if (key == "icon") { + this._icon_old.push(this.options.icon); + } + return TK.Widget.prototype.set.call(this, key, val); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/knob.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/knob.js new file mode 100644 index 0000000000..6283596379 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/knob.js @@ -0,0 +1,212 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +/** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the option value. + * + * @event TK.Knob#useraction + * + * @param {string} name - The name of the option which was changed due to the users action + * @param {mixed} value - The new value of the option + */ + +"use strict"; +(function(w, TK){ +var format_viewbox = TK.FORMAT("0 0 %d %d"); +function dblclick() { + this.userset("value", this.options.reset); + /** + * Is fired when the knob receives a double click in order to reset to initial value. + * + * @event TK.Knob#doubleclick + * + * @param {number} value - The value of the widget. + */ + this.fire_event("doubleclick", this.options.value); +} +function module_range() { + return this.parent.circular; +} +/** + * TK.Knob is a {@link TK.Circular} inside of an SVG which can be + * modified both by dragging and scrolling utilizing {@link TK.DragValue} + * and {@link TK.ScrollValue}. + * It inherits all options of {@link TK.Circular} and {@link TK.DragValue}. + * The options listed below consist of options from the contained widgets, + * only showing the default values. + * + * @class TK.Knob + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Object} [options.hand={width: 1, length: 12, margin: 24}] + * @property {Number} [options.margin=13] + * @property {Number} [options.thickness=6] + * @property {Number} [options.step=1] + * @property {Number} [options.shift_up=4] + * @property {Number} [options.shift_down=0.25] + * @property {Object} [options.dot={length: 6, margin: 13, width: 2}] + * @property {Object} [options.marker={thickness: 6, margin: 13}] + * @property {Object} [options.label={margin: 10, align: "outer", format: function(val){return val;}}] + * @property {Number} [options.basis=300] - Distance to drag between min and max. + + */ +TK.Knob = TK.class({ + _class: "Knob", + Extends: TK.Widget, + _options: Object.assign(Object.create(TK.Widget.prototype._options), TK.Circular.prototype._options, + TK.DragValue.prototype._options, { + size: "number", + reset: "number", + }), + options: Object.assign({}, TK.Circular.prototype.options, { + size: 100, + hand: {width: 1, length: 12, margin: 24}, + margin: 13, + thickness: 6, + step: 1, + shift_up: 4, + shift_down: 0.25, + dot: {length: 6, margin: 13, width: 2}, + marker: {thickness: 6, margin: 13}, + label: {margin: 12, align: "outer", format: function(val){return val;}}, + direction: "polar", + rotation: 45, + blind_angle: 20, + basis: 300, + }), + static_events: { + dblclick: dblclick, + }, + initialize: function (options) { + TK.Widget.prototype.initialize.call(this, options); + options = this.options; + var E, S; + /** + * @member {HTMLDivElement} TK.Knob#element - The main DIV container. + * Has class toolkit-knob. + */ + if (!(E = this.element)) this.element = E = TK.element("div") + TK.add_class(E, "toolkit-knob"); + + /** + * @member {SVGImage} TK.Knob#svg - The main SVG image. + */ + this.svg = S = TK.make_svg("svg"); + + var co = TK.object_and(options, TK.Circular.prototype._options); + co = TK.object_sub(co, TK.Widget.prototype._options); + co.container = S; + + /** + * @member {TK.Circular} TK.Knob#circular - The {@link TK.Circular} module. + */ + this.circular = new TK.Circular(co); + + this.widgetize(E, true, true, true); + + /** + * @member {TK.DragValue} TK.Knob#drag - Instance of {@link TK.DragValue} used for the + * interaction. + */ + this.drag = new TK.DragValue(this, { + node: S, + range: module_range, + direction: options.direction, + rotation: options.rotation, + blind_angle: options.blind_angle, + limit: true, + }); + /** + * @member {TK.ScrollValue} TK.Knob#scroll - Instance of {@link TK.ScrollValue} used for the + * interaction. + */ + this.scroll = new TK.ScrollValue(this, { + node: S, + range: module_range, + limit: true, + }); + + E.appendChild(S); + this.set("base", options.base); + if (options.reset === void(0)) + options.reset = options.value; + this.add_child(this.circular); + }, + + get_range: function() { + return this.circular; + }, + + destroy: function () { + this.drag.destroy(); + this.scroll.destroy(); + this.circular.destroy(); + TK.Widget.prototype.destroy.call(this); + }, + + redraw: function() { + var I = this.invalid; + var O = this.options; + + if (I.size) { + I.size = false; + this.svg.setAttribute("viewBox", format_viewbox(O.size, O.size)); + } + + TK.Widget.prototype.redraw.call(this); + }, + /** + * This is an alias for {@link TK.Circular#add_label} of the internal + * circular instance. + * + * @method TK.Knob#add_label + */ + add_label: function(x) { + return this.circular.add_label(x); + }, + + /** + * This is an alias for {@link TK.Circular#remove_label} of the internal + * circular instance. + * + * @method TK.Knob#remove_label + */ + remove_label: function(x) { + this.circular.remove_label(x); + }, + + set: function(key, value) { + if (key === "base") { + if (value === false) value = this.options.min; + } + // TK.Circular does the snapping + if (!TK.Widget.prototype._options[key]) { + if (TK.Circular.prototype._options[key]) + value = this.circular.set(key, value); + if (TK.DragValue.prototype._options[key]) + this.drag.set(key, value); + } + return TK.Widget.prototype.set.call(this, key, value); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/label.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/label.js new file mode 100644 index 0000000000..343f91c371 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/label.js @@ -0,0 +1,69 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +TK.Label = TK.class({ + /** + * TK.Label is a simple text field displaying strings. + * + * @class TK.Label + * + * @extends TK.Widget + * + * @property {Object} options + * + * @param {Mixed} [options.label=""] - The content of the label. Can be formatted via `options.format`. + * @param {Function|Boolean} [options.format=false] - Optional format function. + */ + _class: "Label", + Extends: TK.Widget, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + label: "string", + format: "function|boolean" + }), + options: { + label: "", + format: false, + }, + initialize: function (options) { + var E; + TK.Widget.prototype.initialize.call(this, options); + /** @member {HTMLDivElement} TK.Label#element - The main DIV container. + * Has class toolkit-label. + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-label"); + this._text = document.createTextNode(""); + E.appendChild(this._text); + this.widgetize(E, true, true, true); + }, + + redraw: function () { + var I = this.invalid; + var O = this.options; + + TK.Widget.prototype.redraw.call(this); + + if (I.label || I.format) { + I.label = I.format = false; + this._text.data = O.format ? O.format.call(this, O.label) : O.label; + } + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/levelmeter.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/levelmeter.js new file mode 100644 index 0000000000..94d9709f5e --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/levelmeter.js @@ -0,0 +1,542 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function vert(O) { + return O.layout === "left" || O.layout === "right"; +} +function clip_timeout() { + var O = this.options; + if (!O.auto_clip || O.auto_clip < 0) return false; + if (this.__cto) return; + if (O.clip) + this.__cto = window.setTimeout(this._reset_clip, O.auto_clip); +} +function peak_timeout() { + var O = this.options; + if (!O.auto_peak || O.auto_peak < 0) return false; + if (this.__pto) window.clearTimeout(this.__pto); + var value = +this.effective_value(); + if (O.peak > O.base && value > O.base || + O.peak < O.base && value < O.base) + this.__pto = window.setTimeout(this._reset_peak, O.auto_peak); +} +function label_timeout() { + var O = this.options; + var peak_label = (0 | O.peak_label); + var base = +O.base; + var label = +O.label; + var value = +this.effective_value(); + + if (peak_label <= 0) return false; + + if (this.__lto) window.clearTimeout(this.__lto); + + if (label > base && value > base || + label < base && value < base) + + this.__lto = window.setTimeout(this._reset_label, peak_label); +} +function top_timeout() { + var O = this.options; + if (!O.auto_hold || O.auto_hold < 0) return false; + if (this.__tto) window.clearTimeout(this.__tto); + if (O.top > O.base) + this.__tto = window.setTimeout( + this._reset_top, + O.auto_hold); + else + this.__tto = null; +} +function bottom_timeout() { + var O = this.options; + if (!O.auto_hold || O.auto_hold < 0) return false; + if (this.__bto) window.clearTimeout(this.__bto); + if (O.bottom < O.base) + this.__bto = window.setTimeout(this._reset_bottom, O.auto_hold); + else + this.__bto = null; +} + +TK.LevelMeter = TK.class({ + /** + * TK.LevelMeter is a fully functional meter bar displaying numerical values. + * TK.LevelMeter is an enhanced {@link TK.MeterBase}'s containing a clip LED, + * a peak pin with value label and hold markers. + * In addition, LevelMeter has an optional falling animation, top and bottom peak + * values and more. + * + * @class TK.LevelMeter + * + * @extends TK.MeterBase + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Boolean} [options.show_clip=false] - If set to true, show the clipping LED. + * @property {Number} [options.clipping=0] - If clipping is enabled, this is the threshold for the + * clipping effect. + * @property {Integer|Boolean} [options.auto_clip=false] - This is the clipping timeout. If set to + * false automatic clipping is disabled. If set to n the clipping effect + * times out after n ms, if set to -1 it remains forever. + * @property {Boolean} [options.clip=false] - If clipping is enabled, this option is set to + * true when clipping happens. When automatic clipping is disabled, it can be set to + * true to set the clipping state. + * @property {Object} [options.clip_options={}] - Additional options for the {@link TK.State} clip LED. + * @property {Boolean} [options.show_hold=false] - If set to true, show the hold value LED. + * @property {Integer} [options.hold_size=1] - Size of the hold value LED in the number of segments. + * @property {Number|boolean} [options.auto_hold=false] - If set to false the automatic + * hold LED is disabled, if set to n the hold value is reset after n ms and + * if set to -1 the hold value is not reset automatically. + * @property {Number} [options.top=false] - The top hold value. If set to false it will + * equal the meter level. + * @property {Number} [options.bottom=false] - The bottom hold value. This only exists if a + * base value is set and the value falls below the base. + * @property {Boolean} [options.show_peak=false] - If set to true, show the peak label. + * @property {Integer|Boolean} [options.peak_label=false] - If set to false the automatic peak + * label is disabled, if set to n the peak label is reset after n ms and + * if set to -1 it remains forever. + * @property {Function} [options.format_peak=TK.FORMAT("%.2f")] - Formatting function for the peak label. + * @property {Number} [options.falling=0] - If set to a positive number, activates the automatic falling + * animation. The meter level will fall by this amount per frame. + * @property {Number} [options.falling_fps=24] - This is the number of frames of the falling animation. + * It is not an actual frame rate, but instead is used to determine the actual speed of the falling + * animation together with the option falling. + * @property {Number} [options.falling_init=2] - Initial falling delay in number of frames. This option + * can be used to delay the start of the falling animation in order to avoid flickering if internal + * and external falling are combined. + */ + _class: "LevelMeter", + Extends: TK.MeterBase, + _options: Object.assign(Object.create(TK.MeterBase.prototype._options), { + falling: "number", + falling_fps: "number", + falling_init: "number", + peak: "number", + top: "number", + bottom: "number", + hold_size: "int", + show_hold: "boolean", + clipping: "number", + auto_clip: "int|boolean", + auto_peak: "int|boolean", + peak_label: "int", + auto_hold: "int|boolean", + format_peak: "function", + clip_options: "object", + }), + options: { + clip: false, + falling: 0, + falling_fps: 24, + falling_init: 2, + peak: false, + top: false, + bottom: false, + hold_size: 1, + show_hold: false, + clipping: 0, + auto_clip: false, + auto_peak: false, + peak_label: false, + auto_hold: false, + format_peak: TK.FORMAT("%.2f"), + clip_options: {} + }, + static_events: { + set_label: label_timeout, + set_bottom: bottom_timeout, + set_top: top_timeout, + set_peak: peak_timeout, + set_clip: function(value) { + if (value) { + clip_timeout.call(this); + } + }, + set_show_peak: peak_timeout, + set_auto_clip: function(value) { + if (this.__cto && 0|value <=0) + window.clearTimeout(this.__cto); + }, + set_auto_peak: function(value) { + if (this.__pto && 0|value <=0) + window.clearTimeout(this.__pto); + }, + set_peak_label: function(value) { + if (this.__lto && 0|value <=0) + window.clearTimeout(this.__lto); + }, + set_auto_hold: function(value) { + if (this.__tto && 0|value <=0) + window.clearTimeout(this.__tto); + if (this.__bto && 0|value <=0) + window.clearTimeout(this.__bto); + }, + }, + + initialize: function (options) { + /* track the age of the value option */ + this.track_option("value"); + TK.MeterBase.prototype.initialize.call(this, options); + this._reset_label = this.reset_label.bind(this); + this._reset_clip = this.reset_clip.bind(this); + this._reset_peak = this.reset_peak.bind(this); + this._reset_top = this.reset_top.bind(this); + this._reset_bottom = this.reset_bottom.bind(this); + + /** + * @member {HTMLDivElement} TK.LevelMeter#element - The main DIV container. + * Has class toolkit-level-meter. + */ + TK.add_class(this.element, "toolkit-level-meter"); + + var O = this.options; + + if (O.peak === false) + O.peak = O.value; + if (O.top === false) + O.top = O.value; + if (O.bottom === false) + O.bottom = O.value; + if (O.falling < 0) + O.falling = -O.falling; + }, + + redraw: function () { + var O = this.options; + var I = this.invalid; + var E = this.element; + + if (I.show_hold) { + I.show_hold = false; + TK.toggle_class(E, "toolkit-has-hold", O.show_hold); + } + + if (I.top || I.bottom) { + /* top and bottom require a meter redraw, so lets invalidate + * value */ + I.top = I.bottom = false; + I.value = true; + } + + if (I.base) + I.value = true; + + TK.MeterBase.prototype.redraw.call(this); + + if (I.clip) { + I.clip = false; + TK.toggle_class(E, "toolkit-clipping", O.clip); + } + }, + destroy: function () { + TK.MeterBase.prototype.destroy.call(this); + }, + /** + * Resets the peak label. + * + * @method TK.LevelMeter#reset_peak + * + * @emits TK.LevelMeter#resetpeak + */ + reset_peak: function () { + if (this.__pto) clearTimeout(this.__pto); + this.__pto = false; + this.set("peak", this.effective_value()); + /** + * Is fired when the peak was reset. + * + * @event TK.LevelMeter#resetpeak + */ + this.fire_event("resetpeak"); + }, + /** + * Resets the label. + * + * @method TK.LevelMeter#reset_label + * + * @emits TK.LevelMeter#resetlabel + */ + reset_label: function () { + if (this.__lto) clearTimeout(this.__lto); + this.__lto = false; + this.set("label", this.effective_value()); + /** + * Is fired when the label was reset. + * + * @event TK.LevelMeter#resetlabel + */ + this.fire_event("resetlabel"); + }, + /** + * Resets the clipping LED. + * + * @method TK.LevelMeter#reset_clip + * + * @emits TK.LevelMeter#resetclip + */ + reset_clip: function () { + if (this.__cto) clearTimeout(this.__cto); + this.__cto = false; + this.set("clip", false); + /** + * Is fired when the clipping LED was reset. + * + * @event TK.LevelMeter#resetclip + */ + this.fire_event("resetclip"); + }, + /** + * Resets the top hold. + * + * @method TK.LevelMeter#reset_top + * + * @emits TK.LevelMeter#resettop + */ + reset_top: function () { + this.set("top", this.effective_value()); + /** + * Is fired when the top hold was reset. + * + * @event TK.LevelMeter#resettop + */ + this.fire_event("resettop"); + }, + /** + * Resets the bottom hold. + * + * @method TK.LevelMeter#reset_bottom + * + * @emits TK.LevelMeter#resetbottom + */ + reset_bottom: function () { + this.set("bottom", this.effective_value()); + /** + * Is fired when the bottom hold was reset. + * + * @event TK.LevelMeter#resetbottom + */ + this.fire_event("resetbottom"); + }, + /** + * Resets all hold features. + * + * @method TK.LevelMeter#reset_all + * + * @emits TK.LevelMeter#resetpeak + * @emits TK.LevelMeter#resetlabel + * @emits TK.LevelMeter#resetclip + * @emits TK.LevelMeter#resettop + * @emits TK.LevelMeter#resetbottom + */ + reset_all: function () { + this.reset_label(); + this.reset_peak(); + this.reset_clip(); + this.reset_top(); + this.reset_bottom(); + }, + + effective_value: function() { + var O = this.options; + var falling = +O.falling; + if (O.falling <= 0) return O.value; + var value = +O.value, base = +O.base; + + var age = +this.value_time.value; + + if (!(age > 0)) age = Date.now(); + else age = +(Date.now() - age); + + var frame_length = 1000.0 / +O.falling_fps; + + if (age > O.falling_init * frame_length) { + if (value > base) { + value -= falling * (age / frame_length); + if (value < base) value = base; + } else { + value += falling * (age / frame_length); + if (value > base) value = base; + } + } + + return value; + }, + + /* + * This is an _internal_ method, which calculates the non-filled regions + * in the overlaying canvas as pixel positions. The canvas is only modified + * using this information when it has _actually_ changed. This can save a lot + * of performance in cases where the segment size is > 1 or on small devices where + * the meter has a relatively small pixel size. + */ + calculate_meter: function(to, value, i) { + var O = this.options; + var falling = +O.falling; + var base = +O.base; + value = +value; + + // this is a bit unelegant... + if (falling) { + value = this.effective_value(); + // continue animation + if (value !== base) { + this.invalid.value = true; + // request another frame + this.trigger_draw_next(); + } + } + + i = TK.MeterBase.prototype.calculate_meter.call(this, to, value, i); + + if (!O.show_hold) return i; + + // shorten things + var hold = +O.top; + var segment = O.segment|0; + var hold_size = (O.hold_size|0) * segment; + var base = +O.base; + var pos; + + if (hold > base) { + /* TODO: lets snap in set() */ + pos = this.val2px(hold)|0; + if (segment !== 1) pos = segment*(Math.round(pos/segment)|0); + + to[i++] = pos; + to[i++] = pos+hold_size; + } + + hold = +O.bottom; + + if (hold < base) { + pos = this.val2px(hold)|0; + if (segment !== 1) pos = segment*(Math.round(pos/segment)|0); + + to[i++] = pos; + to[i++] = pos+hold_size; + } + + return i; + }, + + // GETTER & SETTER + set: function (key, value) { + if (key === "value") { + var O = this.options; + var base = O.base; + + // snap will enforce clipping + value = this.snap(value); + + if (O.falling) { + var v = this.effective_value(); + if (v >= base && value >= base && value < v || + v <= base && value <= base && value > v) { + /* NOTE: we are doing a falling animation, but maybe its not running */ + if (!this.invalid.value) { + this.invalid.value = true; + this.trigger_draw(); + } + return; + } + } + if (O.auto_clip !== false && value > O.clipping && !this.has_base()) { + this.set("clip", true); + } + if (O.show_label && O.peak_label !== false && + (value > O.label && value > base || value < O.label && value < base)) { + this.set("label", value); + } + if (O.auto_peak !== false && + (value > O.peak && value > base || value < O.peak && value < base)) { + this.set("peak", value); + } + if (O.auto_hold !== false && O.show_hold && value > O.top) { + this.set("top", value); + } + if (O.auto_hold !== false && O.show_hold && value < O.bottom && this.has_base()) { + this.set("bottom", value); + } + } else if (key === "top" || key === "bottom") { + value = this.snap(value); + } + return TK.MeterBase.prototype.set.call(this, key, value); + } +}); + +/** + * @member {TK.State} TK.LevelMeter#clip - The {@link TK.State} instance for the clipping LED. + * @member {HTMLDivElement} TK.LevelMeter#clip.element - The DIV element of the clipping LED. + * Has class toolkit-clip. + */ +TK.ChildWidget(TK.LevelMeter, "clip", { + create: TK.State, + show: false, + map_options: { + clip: "state", + }, + default_options: { + "class": "toolkit-clip" + }, + toggle_class: true, +}); +/** + * @member {HTMLDivElement} TK.LevelMeter#_peak - The DIV element for the peak marker. + * Has class toolkit-peak. + */ +TK.ChildElement(TK.LevelMeter, "peak", { + show: false, + create: function() { + var peak = TK.element("div","toolkit-peak"); + peak.appendChild(TK.element("div","toolkit-peak-label")); + return peak; + }, + append: function() { + this._bar.appendChild(this._peak); + }, + toggle_class: true, + draw_options: [ "peak" ], + draw: function (O) { + if (!this._peak) return; + var n = this._peak.firstChild; + TK.set_text(n, O.format_peak(O.peak)); + if (O.peak > O.min && O.peak < O.max && O.show_peak) { + this._peak.style.display = "block"; + var pos = 0; + if (vert(O)) { + pos = O.basis - this.val2px(this.snap(O.peak)); + pos = Math.min(O.basis, pos); + this._peak.style.top = pos + "px"; + } else { + pos = this.val2px(this.snap(O.peak)); + pos = Math.min(O.basis, pos) + this._peak.style.left = pos + "px"; + } + } else { + this._peak.style.display = "none"; + } + /** + * Is fired when the peak was drawn. + * + * @event TK.LevelMeter#drawpeak + */ + this.fire_event("drawpeak"); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/list.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/list.js new file mode 100644 index 0000000000..e67f18e2bf --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/list.js @@ -0,0 +1,73 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +TK.List = TK.class({ + /** + * TK.List is a sortable {@link TK.Container} for {@TK.ListItems}s. + * the element is a UL instead of a DIV. + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Function|Boolean} [options.sort=false] - A function + * expecting arguments A and B, returning a number <0, if A comes first and >0, + * if B comes first. 0 keeps both elements in place. Please refer to the + * compareFunction at W3Schools + * for any further information. + * + * @class TK.List + * + * @extends TK.Container + */ + _options: Object.assign(Object.create(TK.Container.prototype._options), { + sort: "function", + }), + _class: "List", + Extends: TK.Container, + + initialize: function (options) { + this.element = TK.element("ul", "toolkit-list"); + TK.Container.prototype.initialize.call(this, options); + }, + static_events: { + set_sort: function(f) { + if (typeof(f) === "function") { + var C = this.children.slice(0); + C.sort(f); + for (var i = 0; i < C.length; i++) { + this.element.appendChild(C[i].element); + } + } + }, + }, + append_child: function(w) { + TK.Container.prototype.append_child.call(this, w); + var O = this.options; + var C = this.children; + if (O.sort) { + C.sort(O.sort); + var pos = C.indexOf(w); + if (pos !== C.length - 1) + this.element.insertBefore(w.element, C[pos+1].element); + } + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/listitem.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/listitem.js new file mode 100644 index 0000000000..895d3db59d --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/listitem.js @@ -0,0 +1,40 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +TK.ListItem = TK.class({ + /** + * TK.ListItem is a member {@link TK.Container} of {@link TK.List}s. The + * element is a `LI` instead of a `DIV`. + * + * @class TK.ListItem + * + * @extends TK.Container + */ + _class: "ListItem", + Extends: TK.Container, + + initialize: function (options) { + this.element = TK.element("li", "toolkit-list-item"); + TK.Container.prototype.initialize.call(this, options); + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/meterbase.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/meterbase.js new file mode 100644 index 0000000000..6aa54f215c --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/meterbase.js @@ -0,0 +1,534 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function vert(O) { + return O.layout === "left" || O.layout === "right"; +} +function fill_interval(ctx, w, h, a, is_vertical) { + var i; + if (is_vertical) { + for (i = 0; i < a.length; i+= 2) { + ctx.fillRect(0, h - a[i+1], w, a[i+1]-a[i]); + } + } else { + for (i = 0; i < a.length; i+= 2) { + ctx.fillRect(a[i], 0, a[i+1]-a[i], h); + } + } +} +function clear_interval(ctx, w, h, a, is_vertical) { + var i; + if (is_vertical) { + for (i = 0; i < a.length; i+= 2) { + ctx.clearRect(0, h - a[i+1], w, a[i+1]-a[i]); + } + } else { + for (i = 0; i < a.length; i+= 2) { + ctx.clearRect(a[i], 0, a[i+1]-a[i], h); + } + } +} +function draw_full(ctx, w, h, a, is_vertical) { + + ctx.fillRect(0, 0, w, h); + clear_interval(ctx, w, h, a, is_vertical); +} +function make_interval(a) { + var i, tmp, again, j; + + do { + again = false; + for (i = 0; i < a.length-2; i+=2) { + if (a[i] > a[i+2]) { + tmp = a[i]; + a[i] = a[i+2]; + a[i+2] = tmp; + + tmp = a[i+1]; + a[i+1] = a[i+3]; + a[i+3] = tmp; + again = true; + } + } + } while (again); + + for (i = 0; i < a.length-2; i+= 2) { + if (a[i+1] > a[i+2]) { + if (a[i+3] > a[i+1]) { + a[i+1] = a[i+3]; + } + for (j = i+3; j < a.length; j++) a[j-1] = a[j]; + a.length = j-2; + i -=2; + continue; + } + } +} +function cmp_intervals(a, b) { + var ret = 0; + var i; + + for (i = 0; i < a.length; i+=2) { + if (a[i] === b[i]) { + if (a[i+1] === b[i+1]) continue; + ret |= (a[i+1] < b[i+1]) ? 1 : 2; + } else if (a[i+1] === b[i+1]) { + ret |= (a[i] > b[i]) ? 1 : 2; + } else return 4; + } + return ret; +} +function subtract_intervals(a, b) { + var i; + var ret = []; + + for (i = 0; i < a.length; i+=2) { + if (a[i] === b[i]) { + if (a[i+1] <= b[i+1]) continue; + ret.push(b[i+1], a[i+1]); + } else { + if (a[i] > b[i]) continue; + ret.push(a[i], b[i]); + } + } + + return ret; +} +TK.MeterBase = TK.class({ + /** + * TK.MeterBase is a base class to build different meters such as {@link TK.LevelMeter} from. + * TK.MeterBase uses {@link TK.Gradient} and contains a {@link TK.Scale} widget. + * TK.MeterBase inherits all options from {@link TK.Scale}. + * + * Note that the two options format_labels and + * scale_base have different names here. + * + * Note that level meters with high update frequencies can be very demanding when it comes + * to rendering performance. These performance requirements can be reduced by increasing the + * segment size using the segment option. Using a segment, the different level + * meter positions are reduced. This widget will take advantage of that by avoiding rendering those + * changes to the meter level, which fall into the same segment. + * + * @class TK.MeterBase + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String} [options.layout="left"] - A string describing the layout of the meter. + * Possible values are "left", "right", "top" and + * "bottom". "left" and "right" are vertical + * layouts, where the meter is on the left or right of the scale, respectively. Similarly, + * "top" and "bottom" are horizontal layouts in which the meter + * is at the top or the bottom, respectively. + * @property {Integer} [options.segment=1] - Segment size. Pixel positions of the meter level are + * rounded to multiples of this size. This can be used to give the level meter a LED effect and to + * reduce processor load. + * @property {Number} [options.value=0] - Level value. + * @property {Number} [options.base=false] - The base value of the meter. If set to false, + * the base will coincide with the minimum value options.min. The meter level is drawn + * starting from the base to the value. + * @property {Number} [options.label=0] - Value to be displayed on the label. + * @property {Function} [options.format_label=TK.FORMAT("%.2f")] - Function for formatting the + * label. + * @property {Boolean} [options.show_label=false] - If set to true a label is displayed. + * @property {Number} [options.title=false] - The title of the TK.MeterBase. Set to `false` to hide it. + * @property {Boolean} [options.show_scale=true] - Set to false to hide the scale. + * @property {Number|Boolean} [options.scale_base=false] - Base of the meter scale, see {@link TK.Scale} for more information. + * @property {Boolean} [options.show_labels=true] - If true, display labels on the + * scale. + * @property {Function} [options.format_labels=TK.FORMAT("%.2f")] - Function for formatting the + * scale labels. This is passed to TK.Scale as option labels. + * + */ + + _class: "MeterBase", + Extends: TK.Widget, + Implements: [TK.Gradient], + _options: Object.assign(Object.create(TK.Widget.prototype._options), + TK.Gradient.prototype._options, TK.Scale.prototype._options, { + layout: "string", + segment: "number", + value: "number", + base: "number|boolean", + min: "number", + max: "number", + label: "number", + title: "string|boolean", + show_labels: "boolean", + format_label: "function", + scale_base: "number|boolean", + format_labels: "function", + background: "string|boolean", + gradient: "object|boolean" + }), + options: { + layout: "left", + segment: 1, + value: 0, + base: false, + label: 0, + title: false, + show_labels: true, + format_label: TK.FORMAT("%.2f"), + levels: [1, 5, 10], // array of steps where to draw labels + scale_base: false, + format_labels: TK.FORMAT("%.2f"), + background: false, + gradient: false + }, + static_events: { + set_label: function(value) { + /** + * Is fired when the label changed. + * The argument is the actual label value. + * + * @event TK.MeterBase#labelchanged + * + * @param {string} label - The label of the {@link TK.MeterBase}. + */ + this.fire_event("labelchanged", value); + }, + set_title: function(value) { + /** + * Is fired when the title changed. + * The argument is the actual title. + * + * @event TK.MeterBase#titlechanged + * + * @param {string} title - The title of the {@link TK.MeterBase}. + */ + this.fire_event("titlechanged", value); + }, + set_segment: function(value) { + // what is this supposed to do? + // -> probably invalidate the value to force a redraw + this.set("value", this.options.value); + }, + set_value: function(value) { + /** + * Is fired when the value changed. + * The argument is the actual value. + * + * @event TK.MeterBase#valuechanged + * + * @param {number} value - The value of the {@link TK.MeterBase}. + */ + this.fire_event("valuechanged", value); + }, + set_base: function(value) { + if (value === false) { + var O = this.options; + O.base = value = O.min; + } + /** + * Is fired when the base value changed. + * The argument is the actual base value. + * + * @event TK.MeterBase#basechanged + * + * @param {number} base - The value of the base. + */ + this.fire_event("basechanged", value); + }, + set_layout: function () { + var O = this.options; + this.set("value", O.value); + this.set("min", O.min); + this.trigger_resize(); + }, + rangedchanged: function() { + var gradient = this.options.gradient; + if (gradient) { + this.set("gradient", gradient); + } + }, + }, + + initialize: function (options) { + var E; + TK.Widget.prototype.initialize.call(this, options); + var O = this.options; + /** + * @member {HTMLDivElement} TK.MeterBase#element - The main DIV container. + * Has class toolkit-meter-base. + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-meter-base"); + this.widgetize(E, false, true, true); + + this._bar = TK.element("div", "toolkit-bar"); + /** + * @member {HTMLCanvas} TK.MeterBase#_canvas - The canvas element drawing the mask. + * Has class toolkit-mask. + */ + this._canvas = document.createElement("canvas"); + TK.add_class(this._canvas, "toolkit-mask"); + + this._fillstyle = false; + + E.appendChild(this._bar); + + this._bar.appendChild(this._canvas); + + /** + * @member {HTMLDivElement} TK.MeterBase#_bar - The DIV element containing the masks + * and drawing the background. Has class toolkit-bar. + */ + this.delegate(this._bar); + this._last_meters = []; + this._current_meters = []; + + this.set("label", O.value); + this.set("base", O.base); + }, + + destroy: function () { + this._bar.remove(); + TK.Widget.prototype.destroy.call(this); + }, + redraw: function () { + var I = this.invalid; + var O = this.options; + var E = this.element; + + if (this._fillstyle === false) { + this._canvas.style.removeProperty("background-color"); + TK.S.add(function() { + this._fillstyle = TK.get_style(this._canvas, "background-color"); + TK.S.add(function() { + this._canvas.getContext("2d").fillStyle = this._fillstyle; + this._canvas.style.setProperty("background-color", "transparent", "important"); + this.trigger_draw(); + }.bind(this), 3); + }.bind(this), 2); + } + + if (I.reverse) { + I.reverse = false; + TK.toggle_class(E, "toolkit-reverse", O.reverse); + } + if (I.gradient || I.background) { + I.gradient = I.background = false; + this.draw_gradient(this._bar, O.gradient, O.background); + } + + TK.Widget.prototype.redraw.call(this); + + if (I.layout) { + I.layout = false; + TK.remove_class(E, "toolkit-vertical", + "toolkit-horizontal", "toolkit-left", + "toolkit-right", "toolkit-top", "toolkit-bottom"); + var scale = this.scale ? this.scale.element : null; + var bar = this._bar; + switch (O.layout) { + case "left": + TK.add_class(E, "toolkit-vertical", "toolkit-left"); + if (scale) TK.insert_after(scale, bar); + break; + case "right": + TK.add_class(E, "toolkit-vertical", "toolkit-right"); + if (scale) TK.insert_after(bar, scale); + break; + case "top": + TK.add_class(E, "toolkit-horizontal", "toolkit-top"); + if (scale) TK.insert_after(scale, bar); + break; + case "bottom": + TK.add_class(E, "toolkit-horizontal", "toolkit-bottom"); + if (scale) TK.insert_after(bar, scale); + break; + default: + throw("unsupported layout"); + } + } + + if (this._fillstyle === false) return; + + if (I.basis && O._height > 0 && O._width > 0) { + this._canvas.setAttribute("height", Math.round(O._height)); + this._canvas.setAttribute("width", Math.round(O._width)); + /* FIXME: I am not sure why this is even necessary */ + this._canvas.style.width = O._width + "px"; + this._canvas.style.height = O._height + "px"; + this._canvas.getContext("2d").fillStyle = this._fillstyle; + } + + if (I.value && O.show_label) { + this.label.set("label", O.format_label(O.value)); + } + + if (I.value || I.basis || I.min || I.max) { + I.basis = I.value = I.min = I.max = false; + this.draw_meter(); + } + }, + + resize: function() { + var O = this.options; + TK.Widget.prototype.resize.call(this); + var w = TK.inner_width(this._bar); + var h = TK.inner_height(this._bar); + this.set("_width", w); + this.set("_height", h); + var i = vert(O) ? h : w; + this.set("basis", i); + this._last_meters.length = 0; + this._fillstyle = false; + }, + + calculate_meter: function(to, value, i) { + var O = this.options; + // Set the mask elements according to options.value to show a value in + // the meter bar + var base = O.base; + var segment = O.segment|0; + var reverse = !!O.reverse; + var size = O.basis|0; + + /* At this point the whole meter bar is filled. We now want + * to clear the area between base and value. + */ + + /* canvas coordinates are reversed */ + var v1 = this.val2px(base)|0; + var v2 = this.val2px(value)|0; + + if (segment !== 1) v2 = segment*(Math.round(v2/segment)|0); + + if (v2 < v1) { + to[i++] = v2; + to[i++] = v1; + } else { + to[i++] = v1; + to[i++] = v2; + } + + return i; + }, + + draw_meter: function () { + var O = this.options; + var w = Math.round(O._width); + var h = Math.round(O._height); + var i, j; + + if (!(w > 0 && h > 0)) return; + + var a = this._current_meters; + var tmp = this._last_meters; + + var i = this.calculate_meter(a, O.value, 0); + if (i < a.length) a.length = i; + make_interval(a); + + this._last_meters = a; + this._current_meters = tmp; + + var diff; + + if (tmp.length === a.length) { + diff = cmp_intervals(tmp, a)|0; + } else diff = 4; + + if (!diff) return; + + // FIXME: this is currently broken for some reason + if (diff == 1) + diff = 4; + + var ctx = this._canvas.getContext("2d"); + ctx.fillStyle = this._fillstyle; + var is_vertical = vert(O); + + if (diff === 1) { + /* a - tmp is non-empty */ + clear_interval(ctx, w, h, subtract_intervals(a, tmp), is_vertical); + return; + } + if (diff === 2) { + /* tmp - a is non-empty */ + fill_interval(ctx, w, h, subtract_intervals(tmp, a), is_vertical); + return; + } + + draw_full(ctx, w, h, a, is_vertical); + }, + + // HELPERS & STUFF + _val2seg: function (val) { + // rounds values to fit in the segments size + // always returns values without taking options.reverse into account + var s = +this.val2px(this.snap(val)); + s -= s % +this.options.segment; + if (this.options.reverse) + s = +this.options.basis - s; + return s; + }, + + has_base: function() { + var O = this.options; + return O.base > O.min; + }, + +}); +/** + * @member {TK.Scale} TK.MeterBase#scale - The {@link TK.Scale} of the meter. + */ +TK.ChildWidget(TK.MeterBase, "scale", { + create: TK.Scale, + map_options: { + format_labels: "labels", + scale_base: "base", + }, + inherit_options: true, + show: true, + toggle_class: true, + static_events: { + set: function(key, value) { + var p = this.parent; + if (p) + p.fire_event("scalechanged", key, value); + }, + }, +}); +/** + * @member {TK.Label} TK.MeterBase#title - The {@link TK.Label} displaying the title. + * Has class toolkit-title. + */ +TK.ChildWidget(TK.MeterBase, "title", { + create: TK.Label, + show: false, + option: "title", + default_options: { "class" : "toolkit-title" }, + map_options: { "title" : "label" }, + toggle_class: true, +}); +/** + * @member {TK.Label} TK.MeterBase#label - The {@link TK.Label} displaying the label. + */ +TK.ChildWidget(TK.MeterBase, "label", { + create: TK.Label, + show: false, + default_options: { "class" : "toolkit-value" }, + toggle_class: true, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/multimeter.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/multimeter.js new file mode 100644 index 0000000000..2c0c1646cb --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/multimeter.js @@ -0,0 +1,247 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ + +function add_meters (cnt, options) { + for (var i = 0; i < cnt; i++) + this.add_meter(options); +} +function add_meter (options) { + var l = this.meters.length; + var O = options; + var opt = extract_child_options(O, l); + var m = new TK.LevelMeter(opt); + + this.meters.push(m); + this.append_child(m); +} +function remove_meter (meter) { + /* meter can be int or meter instance */ + var I = this.invalid; + var M = this.meters; + + var m = -1; + if (typeof meter == "number") { + m = meter; + } else { + for (var i = 0; i < M.length; i++) { + if (M[i] == meter) { + m = i; + break; + } + } + } + if (m < 0 || m > M.length - 1) return; + this.remove_child(M[m]); + M[m].set("container", null); + // TODO: no destroy function in levelmeter at this point? + //this.meters[m].destroy(); + M = M.splice(m, 1); +} + + +TK.MultiMeter = TK.class({ + /** + * TK.MultiMeter is a collection of {@link TK.LevelMeter}s to show levels of channels + * containing multiple audio streams. It offers all options of {@link TK.LevelMeter} and + * {@link TK.MeterBase} which are passed to all instantiated level meters. + * + * @class TK.MultiMeter + * + * @extends TK.Container + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.count=2] - The amount of level meters. + * @property {String} [options.title=""] - The title of the multi meter. Set to `false` to hide the title from the DOM. + * @property {Array} [options.titles=["L", "R"]] - An Array containing titles for the level meters. Their order is the same as the meters. + * @property {Array} [options.values=[]] - An Array containing values for the level meters. Their order is the same as the meters. + * @property {Array} [options.labels=[]] - An Array containing label values for the level meters. Their order is the same as the meters. + * @property {Array} [options.clips=[]] - An Array containing clippings for the level meters. Their order is the same as the meters. + * @property {Array} [options.peaks=[]] - An Array containing peak values for the level meters. Their order is the same as the meters. + * @property {Array} [options.tops=[]] - An Array containing values for top for the level meters. Their order is the same as the meters. + * @property {Array} [options.bottoms=[]] - An Array containing values for bottom for the level meters. Their order is the same as the meters. + */ + _class: "MultiMeter", + Extends: TK.Container, + + /* TODO: The following sucks cause we need to maintain it according to + LevelMeters and MeterBases options. */ + _options: Object.assign(Object.create(TK.Container.prototype._options), { + count: "int", + title: "boolean|string", + titles: "array", + layout: "string", + show_scale: "boolean", + }), + options: { + count: 2, + title: false, + titles: ["L", "R"], + layout: "left", + show_scale: true, + }, + initialize: function (options) { + TK.Container.prototype.initialize.call(this, options, true); + /** + * @member {HTMLDivElement} TK.MultiMeter#element - The main DIV container. + * Has class toolkit-multi-meter. + */ + TK.add_class(this.element, "toolkit-multi-meter"); + this.meters = []; + var O = this.options; + }, + + redraw: function () { + var O = this.options; + var I = this.invalid; + var E = this.element; + var M = this.meters; + + if (I.count) { + while (M.length > O.count) + remove_meter.call(this, M[M.length-1]); + while (M.length < O.count) + add_meter.call(this, O); + E.setAttribute("class", E.getAttribute("class").replace(/toolkit-count-[0-9]*/g, "")); + E.setAttribute("class", E.getAttribute("class").replace(/ +/g, " ")); + TK.add_class(E, "toolkit-count-" + O.count); + } + + if (I.layout || I.count) { + I.count = I.layout = false; + TK.remove_class(E, "toolkit-vertical", "toolkit-horizontal", "toolkit-left", + "toolkit-right", "toolkit-top", "toolkit-bottom"); + switch (O.layout) { + case "left": + TK.add_class(E, "toolkit-vertical", "toolkit-left"); + break; + case "right": + TK.add_class(E, "toolkit-vertical", "toolkit-right"); + break; + case "top": + TK.add_class(E, "toolkit-horizontal", "toolkit-top"); + break; + case "bottom": + TK.add_class(E, "toolkit-horizontal", "toolkit-bottom"); + break; + default: + throw("unsupported layout"); + } + switch (O.layout) { + case "top": + case "left": + for (var i = 0; i < M.length - 1; i++) + M[i].set("show_scale", false); + if (M.length) + M[this.meters.length - 1].set("show_scale", O.show_scale); + break; + case "bottom": + case "right": + for (var i = 0; i < M.length; i++) + M[i].set("show_scale", false); + if (M.length) + M[0].set("show_scale", O.show_scale); + break; + } + } + + TK.Container.prototype.redraw.call(this); + }, +}); + +/** + * @member {HTMLDivElement} TK.MultiMeter#title - The {@link TK.Label} widget displaying the meters title. + */ +TK.ChildWidget(TK.MultiMeter, "title", { + create: TK.Label, + show: false, + option: "title", + default_options: { "class" : "toolkit-title" }, + map_options: { "title" : "label" }, + toggle_class: true, +}); + + + +/* + * This could be moved into TK.ChildWidgets(), + * which could in similar ways be used in the buttonarray, + * pager, etc. + * + */ + +var mapped_options = { + titles: "title", + layout: "layout", +}; + +function map_child_option_simple(value, key) { + var M = this.meters, i; + for (i = 0; i < M.length; i++) M[i].set(key, value); +} + +function map_child_option(value, key) { + var M = this.meters, i; + if (Array.isArray(value)) { + for (i = 0; i < M.length && i < value.length; i++) M[i].set(key, value[i]); + } else { + for (i = 0; i < M.length; i++) M[i].set(key, value); + } +} + +TK.add_static_event(TK.MultiMeter, "set_titles", function(value, key) { + map_child_option.call(this, value, "title"); +}); + +for (var key in TK.object_sub(TK.LevelMeter.prototype._options, TK.Container.prototype._options)) { + if (TK.MultiMeter.prototype._options[key]) continue; + var type = TK.LevelMeter.prototype._options[key]; + if (type.search("array") !== -1) { + TK.MultiMeter.prototype._options[key] = type; + mapped_options[key] = key; + TK.add_static_event(TK.MultiMeter, "set_"+key, map_child_option_simple); + } else { + TK.MultiMeter.prototype._options[key] = "array|"+type; + mapped_options[key] = key; + TK.add_static_event(TK.MultiMeter, "set_"+key, map_child_option); + } + if (key in TK.LevelMeter.prototype.options) + TK.MultiMeter.prototype.options[key] = TK.LevelMeter.prototype.options[key]; +} + +function extract_child_options(O, i) { + var o = {}, value, type; + + for (var key in mapped_options) { + var ckey = mapped_options[key]; + if (!O.hasOwnProperty(key)) continue; + value = O[key]; + type = TK.LevelMeter.prototype._options[key] || ""; + if (Array.isArray(value) && type.search("array") === -1) { + if (i < value.length) o[ckey] = value[i]; + } else { + o[ckey] = value; + } + } + + return o; +} +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/notification.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/notification.js new file mode 100644 index 0000000000..8a594fec2b --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/notification.js @@ -0,0 +1,156 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +function close_clicked (e) { + /** + * Is fired when the user clicks on the close button. + * + * @event TK.Notification#closeclicked + */ + this.fire_event("closeclicked"); + close.call(this.parent); +} + +function after_hide() { + TK.S.after_frame(function() { + if (this.is_destructed()) return; + this.destroy(); + }.bind(this)); +} + +function close () { + this.add_event("hide", after_hide); + this.hide(); + /** + * Is fired when the notification was removed from the DOM after the hiding animation. + * + * @event TK.Notification#closed + */ + this.fire_event("closed"); +} + +function timeout() { + this._timeout = void(0); + close.call(this); +} + +/** + * TK.Notification is a {@link TK.Container} to be used in {@link TK.Notifications}. + * + * @class TK.Notification + * + * @extends TK.Container + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.timeout=5000] - Time in milliseconds + * after the notification disappears automatically. + * Set to 0 for permanent notification. + * @property {String} [options.icon=false] - Show an icon. Set to + * false to hide it from the DOM. + * @property {Boolean} [options.show_close=false] - Show a close button. + */ + +TK.Notification = TK.class({ + + _class: "Notification", + Extends: TK.Container, + + _options: Object.assign(TK.Container.prototype._options, { + timeout: "number", + icon: "string", + show_close: "boolean", + }), + options: { + timeout: 5000, + icon: false, + show_close: false, + }, + + initialize: function (options) { + TK.Container.prototype.initialize.call(this, options); + var O = this.options; + /** + * @member {HTMLDivElement} TK.Notification#element - The main DIV container. + * Has class toolkit-notification. + */ + TK.add_class(this.element, "toolkit-notification"); + this._timeout = void(0); + this.set("timeout", O.timeout); + }, + redraw: function () { + var I = this.invalid; + var O = this.options; + var i = I.content; + TK.Container.prototype.redraw.call(this); + if (i && this.icon) + this.element.insertBefore(this.icon.element, this.element.firstChild); + if (i && this.close) + this.element.insertBefore(this.close.element, this.element.firstChild); + }, + + remove: close, + destroy: function() { + if (this._timeout !== void(0)) + window.clearTimeout(this._timeout); + TK.Container.prototype.destroy.call(this); + }, + set: function(key, val) { + TK.Container.prototype.set.call(this, key, val); + if (key === "timeout") { + if (this._timeout !== void(0)) + window.clearTimeout(this._timeout); + if (val > 0) + this._timeout = window.setTimeout(timeout.bind(this), val); + } + }, +}); + +/** + * @member {TK.Button} TK.Notification#close - The TK.Button for closing the notification. + */ +TK.ChildWidget(TK.Notification, "close", { + create: TK.Button, + show: false, + toggle_class: true, + static_events: { + click: close_clicked, + }, + default_options: { + "icon" : "close", + "class" : "toolkit-close", + }, +}); + +/** + * @member {TK.Icon} TK.Notification#icon - The TK.Icon widget. + */ +TK.ChildWidget(TK.Notification, "icon", { + create: TK.Icon, + show: false, + toggle_class: true, + option: "icon", + map_options: { + icon: "icon", + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/notifications.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/notifications.js new file mode 100644 index 0000000000..67b600bf06 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/notifications.js @@ -0,0 +1,72 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +/** + * TK.Notifications is a {@link TK.Container} displaying {@link TK.Notification} + * popups. + * + * @class TK.Notifications + * + * @extends TK.Container + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String} [options.stack="end"] - Define the position a new {@link TK.Notification} + * is appended to the container, either `end` or `start`. + */ + +TK.Notifications = TK.class({ + + _class: "Notifications", + Extends: TK.Container, + + _options: Object.assign(TK.Container.prototype._options, { + stack: "string", + }), + options: { + stack: "start", + }, + + initialize: function (options) { + TK.Container.prototype.initialize.call(this, options); + TK.add_class(this.element, "toolkit-notifications"); + }, + + notify: function (options) { + /** + * Create and show a new notification. + * + * @method TK.Notifications#notify + * + * @param {Object} [options={ }] - An object containing initial options. - Options for the {@link TK.Notification} to add + * + */ + var n = new TK.Notification(options); + this.add_child(n); + if (this.options.stack == "start") + this.element.insertBefore(n.element, this.element.firstChild); + else + this.element.appendChild(n.element); + return n; + } +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/pager.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/pager.js new file mode 100644 index 0000000000..206fef2f6e --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/pager.js @@ -0,0 +1,461 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + + /** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the option show. + * + * @event TK.Pager#useraction + * + * @param {string} name - The name of the option which was changed due to the users action + * @param {mixed} value - The new value of the option + */ + +"use strict"; +(function(w, TK){ +TK.Pager = TK.class({ + /** + * TK.Pager, also known as Notebook in other UI toolkits, provides + * multiple containers for displaying contents which are switchable + * via a {@link TK.ButtonArray}. + * + * @class TK.Pager + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String} [options.position="top"] - The position of the ButtonArray. Can either be `top`, `right`, `left` or `bottom`. + * @property {Array} [options.pages=[]] - + * An array of objects with the following members: + * @property {String|Object} [options.pages.label=""] - A string + * used as the buttons label or an object containing options for + * a {@link TK.Button}. + * @property {TK.Container|DOMNode|String} [options.pages.content] + * - The content of the page. Can be + * either an instance of {@link TK.Container} (or derivate), + * a DOMNode or a string which gets wrapped in a new {@link TK.Container}. + * @property {Integer} [options.show=-1] - The page to show. + * + * @extends TK.Container + * + * @example + * var pager = new TK.Pager({ + * pages: [ + * { + * label: "Empty Page 1", + * content: document.createElement("span") + * }, + * { + * label: { label:"Foobar", class:"foobar" }, + * content: "

Foobar

Lorem ipsum dolor sit amet

" + * } + * ] + * }); + */ + _class: "Pager", + Extends: TK.Container, + _options: Object.assign(Object.create(TK.Container.prototype._options), { + position: "string", + direction: "string", + pages: "array", + show: "int", + resized: "boolean", + }), + options: { + position: "top", + direction: "forward", + pages: [], + show: -1, + resized: false, + }, + static_events: { + set_show: function(value) { + var page = this.current(); + + if (page) { + page.set("active", true); + this.show_child(page); + /** + * The page to show has changed. + * + * @param {TK.Container} page - The {@link TK.Container} instance of the newly selected page. + * @param {number} id - The ID of the page. + * + * @event TK.Pager#changed + */ + this.fire_event("changed", page, value); + } + }, + set_pages: function(value) { + for (var i = 0; i < this.pages.length; i++) + this.pages[i].destroy(); + this.pages = []; + this.add_pages(value); + }, + set_position: function(value) { + var badir; + if (value === "top" || value === "bottom") { + badir = "horizontal"; + } else { + badir = "vertical"; + } + this.buttonarray.set("direction", badir); + }, + }, + + initialize: function (options) { + this.pages = []; + TK.Container.prototype.initialize.call(this, options); + /** + * The main DIV element. Has the class toolkit-pager. + * + * @member TK.Pager#element + */ + /** + * @member {HTMLDivElement} TK.Pager#_buttonarray_wrapper - An internal container for layout purposes containing the #TK.ButtonArray. + * Has classes toolkit-buttonarray-wrapper and toolkit-wrapper. + */ + /** + * @member {HTMLDivElement} TK.Pager#_container_wrapper - An internal container for layout purposes containing the _clip element. + * Has classes toolkit-wrapper and toolkit-container-wrapper. + */ + /** + * @member {HTMLDivElement} TK.Pager#_clip - The clipping area containing the pages. + * Has class toolkit-clip. + */ + TK.add_class(this.element, "toolkit-pager"); + /** + * The {@link TK.ButtonArray} instance acting as the menu. + * + * @member TK.Pager#buttonarray + */ + this.buttonarray = new TK.ButtonArray({ + container: this.element, + }); + this.buttonarray.add_event("userset", function(key, value) { + this.parent.userset(key, value); + return false; + }); + + var b_o = this.buttonarray._options; + var _o = this._options; + for (var k in b_o) { + if (!b_o.hasOwnProperty(k)) continue; + var o = "buttonarray." + k; + _o[o] = b_o[k]; + this.add_event("set_" + o, (function (t,k) { + return function (v) { + t.buttonarray.set(k, v); + } + })(this, k)); + if (typeof this.options[o] !== "undefined") + this.set(o, this.options[o]); + } + /** + * @member {HTMLDivElement} TK.Pager#_clip - The clipping of the pages. + * Has class toolkit-clip. + */ + this._clip = TK.element("div", "toolkit-clip"); + this.element.appendChild(this._clip); + + this.add_child(this.buttonarray); + this.add_pages(this.options.pages); + this.set("position", this.options.position); + this.set("show", this.options.show); + }, + + redraw: function () { + TK.Container.prototype.redraw.call(this); + var O = this.options; + var I = this.invalid; + var E = this.element; + + if (I.overlap) + TK[(O.overlap ? "add_" : "remove_") + "class"](E, "toolkit-overlap"); + + if (I.direction) { + I.direction = false; + TK.remove_class(E, "toolkit-forward", "toolkit-backward"); + TK.add_class(E, "toolkit-" + O.direction); + } + + if (I.position) { + I.position = false; + TK.remove_class(E, "toolkit-top", "toolkit-right", "toolkit-bottom", + "toolkit-left", "toolkit-vertical", "toolkit-horizontal"); + switch (O.position) { + case "top": + TK.add_class(E, "toolkit-top", "toolkit-vertical"); + break; + case "bottom": + TK.add_class(E, "toolkit-bottom", "toolkit-vertical"); + break; + case "left": + TK.add_class(E, "toolkit-left", "toolkit-horizontal"); + break; + case "right": + TK.add_class(E, "toolkit-right", "toolkit-horizontal"); + break; + default: + TK.warn("Unsupported position", O.position); + } + I.layout = true; + } + + if (I.show) { + I.show = false; + for (var i = 0; i < this.pages.length; i ++) { + var page = this.pages[i]; + if (i === O.show) + page.add_class("toolkit-active"); + else + page.remove_class("toolkit-active"); + } + } + }, + + /** + * Adds an array of pages. + * + * @method TK.Pager#add_pages + * + * @param {Array} options - An Array of objects with members + * `label` and `content`. `label` is a string with the {@link TK.Button}s label or + * an object containing options for the {@link TK.Button} instance. + * `content` is either a {@link TK.Container} (or derivate) widget, + * a DOMNode (needs option `options` to be set) or a string which + * gets wrapped in a new {@link TK.Container} with options from + * argument `options`. + * + * @example + * var p = new TK.Pager(); + * p.add_pages([ + * { + * label: "Page 1", + * content: "

Page1

", + * } + * ]); + * + */ + add_pages: function (options) { + for (var i = 0; i < options.length; i++) + this.add_page(options[i].label, options[i].content); + }, + + /** + * Adds a {@link TK.Container} to the pager and a corresponding {@link TK.Button} + * to the pagers {@link TK.ButtonArray}. + * + * @method TK.Pager#add_page + * + * @param {string|Object} button - A string with the {@link TK.Button}s label or + * an object containing options for the {@link TK.Button} instance. + * @param {TK.Widget|Class|string} content - The content of the page. + * Either a {@link TK.Container} (or derivate) widget, + * a DOMNode (needs option `options` to be set) or a string which + * gets wrapped in a new {@link TK.Container} with options from + * argument `options`. + * @param {Object} [options={ }] - An object containing options for + * the {@link TK.Container} to be added as page if `content` is + * either a string or a DOMNode. + * @param {integer|undefined} position - The position to add the new + * page to. If undefined, the page is added at the end. + * @emits TK.Pager#added + */ + add_page: function (button, content, position, options) { + var p; + if (typeof button === "string") + button = {label: button}; + this.buttonarray.add_button(button, position); + + if (typeof content === "string" || TK.is_dom_node(content)) { + if (!options) options = {}; + options.content = content; + p = new TK.Container(options); + } else if (typeof content === "function") { + // assume here content is a subclass of Container + p = new content(options); + } else { + p = content; + } + + p.add_class("toolkit-page"); + p.set("container", this._clip); + + var len = this.pages.length; + + if (position >= 0 && position < len - 1) { + this.pages.splice(position, 0, p); + this._clip.insertBefore(p.element, this._clip.childNodes[position]); + } else { + position = len; + this.pages.push(p); + this._clip.appendChild(p.element); + } + /** + * A page was added to the TK.Pager. + * + * @event TK.Pager#added + * + * @param {TK.Container} page - The {@link TK.Container} which was added as a page. + */ + this.fire_event("added", p); + + this.add_child(p); + + // TODO: not always necessary + if (this.current() === p) { + this.options.show = position; + this.buttonarray.set("show", position); + p.set("active", true); + p.set("display_state", "show"); + } else { + /* do not use animation */ + p.force_hide(); + this.hide_child(p); + } + this.invalid.layout = true; + this.trigger_draw(); + return p; + }, + + /** + * Removes a page from the TK.Pager. + * + * @method TK.Pager#remove_page + * + * @param {integer|TK.Container} page - The container to remove. Either a + * position or the {@link TK.Container} widget generated by add_page. + * + * @emits TK.Pager#removed + */ + remove_page: function (page) { + if (typeof page === "object") + page = this.pages.indexOf(page); + if (page < 0 || page >= this.pages.length) + return; + this.buttonarray.remove_button(page); + if (page < this.options.show) + this.set("show", this.options.show-1); + else if (page === this.options.show) + this.set("show", this.options.show); + var p = this.pages[page]; + this.pages.splice(page, 1); + p.destroy(); + this.remove_child(p); + this.invalid.layout = true; + this.trigger_draw(); + /** + * A page was removed from the Pager + * + * @event TK.Pager#removed + * + * @param {TK.Container} page - The {@link TK.Container} which was removed. + */ + this.fire_event("removed", p); + }, + + current: function() { + /** + * Returns the currently displayed page or null. + * + * @method TK.Pager#current + */ + var n = this.options.show; + if (n >= 0 && n < this.pages.length) { + return this.pages[n]; + } + return null; + }, + + /** + * Opens the first page of the pager. Returns true if a + * first page exists, false otherwise. + * + * @method TK.Pager#first + */ + first: function() { + if (this.pages.length) { + this.set("show", 0); + return true; + } + return false; + }, + /** + * Opens the last page of the pager. Returns true if a + * last page exists, false otherwise. + * + * @method TK.Pager#last + */ + last: function() { + if (this.pages.length) { + this.set("show", this.pages.length-1); + return true; + } + return false; + }, + + /** + * Opens the next page of the pager. Returns true if a + * next page exists, false otherwise. + * + * @method TK.Pager#next + */ + next: function() { + var c = this.options.show; + return this.set("show", c+1) !== c; + }, + /** + * Opens the previous page of the pager. Returns true if a + * previous page exists, false otherwise. + * + * @method TK.Pager#prev + */ + prev: function() { + var c = this.options.show; + return this.set("show", c-1) !== c; + }, + + set: function (key, value) { + var page; + if (key === "show") { + if (value < 0) value = 0; + else if (value >= this.pages.length) value = this.pages.length - 1; + + if (value === this.options.show) return value; + if (value > this.options.show) { + this.set("direction", "forward"); + } else { + this.set("direction", "backward"); + } + page = this.current(); + if (page) { + this.hide_child(page); + page.set("active", false); + } + + this.buttonarray.set("show", value); + } + return TK.Container.prototype.set.call(this, key, value); + }, + get: function (key) { + if (key === "pages") return this.pages; + return TK.Container.prototype.get.call(this, key); + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/responsehandler.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/responsehandler.js new file mode 100644 index 0000000000..656af6643a --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/responsehandler.js @@ -0,0 +1,51 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +"use strict"; +(function(w, TK){ + +TK.ResponseHandler = TK.class({ + /** + * TK.ResponseHandler is a {@link TK.FrequencyResponse} adding some {@link TK.ResponseHandle}s. It is + * meant as a universal user interface for equalizers and the like. + * + * This class is deprecated since all relevant functionality went into + * the base class TK.Graph. Use {@link TK.FrequencyResponse} instead. + * + * @class TK.ResponseHandler + * + * @extends TK.FrequencyResponse + */ + _class: "ResponseHandler", + Extends: TK.FrequencyResponse, + initialize: function (options) { + TK.FrequencyResponse.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.ResponseHandler#element - The main DIV container. + * Has class toolkit-response-handler. + */ + TK.add_class(this.element, "toolkit-response-handler"); + /** + * @member {SVGImage} TK.ResponseHandler#_handles - An SVG group element containing all {@link TK.ResponseHandle} graphics. + * Has class toolkit-response-handles. + */ + TK.add_class(this._handles, "toolkit-response-handles"); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/root.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/root.js new file mode 100644 index 0000000000..f585ef1304 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/root.js @@ -0,0 +1,76 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function visibility_change() { + if (document.hidden) { + this.disable_draw(); + } else { + this.enable_draw(); + } +} +function resized() { + if (!this.resize_event) { + this.resize_event = true; + this.trigger_resize(); + } +} +/** + * TK.Root is used to force a resize on all its child widgets + * as soon as the browser window is resized. It also toggles drawing + * of its children depending on the state of visibility of the + * browser tab or browser window. + * + * @extends TK.Container + * + * @class TK.Root + */ +TK.Root = TK.class({ + Extends: TK.Container, + _class: "Root", + _options: Object.create(TK.Container.prototype._options), + static_events: { + initialized: function () { + window.addEventListener("resize", this._resize_cb); + document.addEventListener("visibilitychange", this._visibility_cb, false); + this.enable_draw(); + }, + destroy: function() { + window.removeEventListener("resize", this._resize_cb); + document.removeEventListener("visibilitychange", this._visibility_cb) + this._resize_cb = this._visibility_cb = null; + }, + redraw: function() { + if (this.resize_event) + this.resize_event = false; + }, + }, + initialize: function (options) { + TK.Container.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Root#element - The main DIV container. + * Has class toolkit-root. + */ + TK.add_class(this.element, "toolkit-root"); + this._resize_cb = resized.bind(this); + this._visibility_cb = visibility_change.bind(this); + this.resize_event = false; + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/select.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/select.js new file mode 100644 index 0000000000..a5f41f1963 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/select.js @@ -0,0 +1,739 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + + /** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the options selected and value. + * + * @event TK.Select#useraction + * + * @param {string} name - The name of the option which was changed due to the users action + * @param {mixed} value - The new value of the option + */ + +"use strict"; +(function(w, TK){ + +function hide_list() { + this.__transition = false; + this.__timeout = false; + if (!this.__open) { + this._list.remove(); + } else { + document.addEventListener("touchstart", this._global_touch_start); + document.addEventListener("mousedown", this._global_touch_start); + } +} +function show_list(show) { + if (show) { + var ew = TK.outer_width(this.element, true); + document.body.appendChild(this._list); + var cw = TK.width(); + var ch = TK.height(); + var sx = TK.scroll_left(); + var sy = TK.scroll_top(); + TK.set_styles(this._list, { + "opacity": "0", + "maxHeight": ch+"px", + "maxWidth": cw+"px", + "minWidth": ew+"px" + }); + var lw = TK.outer_width(this._list, true); + var lh = TK.outer_height(this._list, true); + TK.set_styles(this._list, { + "top": Math.min(TK.position_top(this.element) + TK.outer_height(this.element, true), ch + sy - lh) + "px", + "left": Math.min(TK.position_left(this.element), cw + sx - lw) + "px", + }); + } else { + document.removeEventListener("touchstart", this._global_touch_start); + document.removeEventListener("mousedown", this._global_touch_start); + } + TK.set_style(this._list, "opacity", show ? "1" : "0"); + this.__transition = true; + this.__open = show; + if (this.__timeout !== false) window.clearTimeout(this.__timeout); + var dur = TK.get_duration(this._list); + this.__timeout = window.setTimeout(hide_list.bind(this), dur); +} + +function low_remove_entry(entry) { + var li = entry.element; + var entries = this.entries; + var id = entries.indexOf(entry); + + if (id === -1) throw new Error("Entry removed twice."); + + // remove from DOM + if (li.parentElement == this._list) + this._list.removeChild(li); + // remove from list + entries.splice(id, 1); + // selection + var sel = this.options.selected; + if (sel !== false) { + if (sel > id) { + this.options.selected --; + } else if (sel === id) { + this.options.selected = false; + this.set("label", ""); + } + } + this.invalid.entries = true; + this.select(this.options.selected); + /** + * Is fired when a new entry is added to the list. + * + * @event TK.Select.entryremoved + * + * @param {Object} entry - An object containing the members title and value. + */ + this.fire_event("entryremoved", entry); +} + +TK.Select = TK.class({ + /** + * TK.Select provides a {@link TK.Button} with a select list to choose from + * a list of {@TK.SelectEntry}. + * + * @class TK.Select + * + * @extends TK.Button + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Integer|Boolean} [options.selected=false] - The index of the selected {@TK.SelectEntry}. + * Set to `false` to unselect any already selected entries. + * @property {mixed} [options.value] - The value of the selected entry. + * @property {Boolean} [options.auto_size=true] - If `true`, the TK.Select is + * auto-sized to be as wide as the widest {@TK.SelectEntry}. + * @property {Array} [options.entries=[]] - The list of {@TK.SelectEntry}. Each member is an + * object with the two properties title and value, a string used + * as label for constructing a {@TK.SelectEntry} or an instance of {@TK.SelectEntry}. + * + */ + _class: "Select", + Extends: TK.Button, + _options: Object.assign(Object.create(TK.Button.prototype._options), { + entries: "array", + selected: "int", + value: "mixed", + auto_size: "boolean", + show_list: "boolean", + sort: "function", + resized: "boolean", + }), + options: { + entries: [], // A list of strings or objects {title: "Title", value: 1} or SelectEntry instance + selected: false, + value: false, + auto_size: true, + show_list: false, + icon: "arrowdown", + }, + static_events: { + click: function(e) { this.set("show_list", !this.options.show_list); }, + "set_show_list": function (v) {this.set("icon", (v ? "arrowup" : "arrowdown"));}, + }, + initialize: function (options) { + this.__open = false; + + this.__timeout = -1; + + /** + * @member {Array} TK.Select#entries - An array containing all entry objects with members title and value. + */ + this.entries = []; + this._active = null; + TK.Button.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Select#element - The main DIV container. + * Has class toolkit-select. + */ + TK.add_class(this.element, "toolkit-select"); + + /** + * @member {HTMLListElement} TK.Select#_list - A HTML list for displaying the entry titles. + * Has class toolkit-select-list. + */ + this._list = TK.element("ul", "toolkit-select-list"); + this._global_touch_start = function (e) { + if (this.__open && !this.__transition && + !this._list.contains(e.target) && + !this.element.contains(e.target)) { + + this.show_list(false); + } + }.bind(this); + var sel = this.options.selected; + var val = this.options.value; + this.set("entries", this.options.entries); + if (sel === false && val !== false) { + this.set("value", val); + } else { + this.set("selected", sel); + } + }, + destroy: function () { + this.clear(); + this._list.remove(); + TK.Button.prototype.destroy.call(this); + }, + + /** + * Show or hide the select list + * + * @method TK.Select#show_list + * + * @param {boolean} show - `true` to show and `false` to hide the list + * of {@link TK.SelectEntry}. + */ + show_list: function (s) { + this.set("show_list", !!s); + }, + + /** + * Select a {@link TK.SelectEntry} by its index. + * + * @method TK.Select#select + * + * @param {Integer} index - The index of the {@link TK.SelectEntry} to select. + */ + select: function (id) { + this.set("selected", id); + }, + /** + * Select a {@link TK.SelectEntry} by its value. + * + * @method TK.Select#select_value + * + * @param {mixed} value - The value of the {@link TK.SelectEntry} to select. + */ + select_value: function (value) { + var id = this.index_by_value.call(this, value); + this.set("selected", id); + }, + /** + * Select a {@link TK.SelectEntry} by its title. + * + * @method TK.Select#select_title + * + * @param {mixed} title - The title of the {@link TK.SelectEntry} to select. + */ + select_title: function (title) { + var id = this.index_by_title.call(this, title); + this.set("selected", id); + }, + /** + * Replaces the list of {@link TK.SelectEntry} to select from with an entirely new one. + * + * @method TK.Select#set_entries + * + * @param {Array} entries - An array of {@link TK.SelectEntry} to set as the new list to select from. + * Please refer to {@link TK.Select#add_entry} for more details. + */ + set_entries: function (entries) { + // Replace all entries with a new options list + this.clear(); + this.add_entries(entries); + this.select(this.index_by_value.call(this, this.options.value)); + }, + /** + * Adds new {@link TK.SelectEntry} to the end of the list to select from. + * + * @method TK.Select#add_entries + * + * @param {Array} entries - An array of {@link TK.SelectEntry} to add to the end of the list + * of {@link TK.SelectEntry} to select from. Please refer to {@link TK.Select#add_entry} + * for more details. + */ + add_entries: function (entries) { + for (var i = 0; i < entries.length; i++) + this.add_entry(entries[i]); + }, + /** + * Adds a single {@link TK.SelectEntry} to the end of the list. + * + * @method TK.Select#add_entry + * + * @param {mixed} entry - A string to be displayed and used as the value, + * an object with members title and value + * or an instance of {@link TK.SelectEntry}. + * + * @emits TK.Select.entryadded + */ + add_entry: function (ent) { + var O = this.options; + var entry; + var entries = this.entries; + + if (TK.SelectEntry.prototype.isPrototypeOf(ent)) { + entry = ent; + } else { + entry = new TK.SelectEntry({ + value: (typeof ent === "string") ? ent : ent.value, + title: (typeof ent === "string") + ? ent : (ent.title !== void(0)) + ? ent.title : ent.value.toString() + }); + } + this.add_child(entry); + entries.push(entry); + entry.set("container", this._list) + + var id; + + if (O.sort) { + entries.sort(O.sort); + id = entries.indexOf(entry); + if (id !== entries.length - 1) + this._list.insertBefore(entry.element, entries[id+1].element); + } else { + id = entries.length - 1; + } + + this.invalid.entries = true; + + if (this.options.selected === id) { + this.invalid.selected = true; + this.trigger_draw(); + } else if (this.options.selected > id) { + this.set("selected", this.options.selected+1); + } else { + this.trigger_draw(); + } + /** + * Is fired when a new {@link TK.SelectEntry} is added to the list. + * + * @event TK.Select#entryadded + * + * @param {TK.SelectEntry} entry - A new {@link TK.SelectEntry}. + */ + this.fire_event("entryadded", entry); + }, + /** + * Remove a {@link TK.SelectEntry} from the list by its index. + * + * @method TK.Select#remove_id + * + * @param {Integer} index - The index of the {@link TK.SelectEntry} to be removed from the list. + * + * @emits TK.Select#entryremoved + */ + remove_index: function (index) { + var entry = this.entries[index]; + this.remove_child(entry); + }, + /** + * Remove a {@link TK.SelectEntry} from the list by its value. + * + * @method TK.Select#remove_value + * + * @param {mixed} value - The value of the {@link TK.SelectEntry} to be removed from the list. + * + * @emits TK.Select#entryremoved + */ + remove_value: function (val) { + this.remove_id(this.index_by_value.call(this, val)); + }, + /** + * Remove an entry from the list by its title. + * + * @method TK.Select#remove_title + * + * @param {string} title - The title of the entry to be removed from the list. + * + * @emits TK.Select#entryremoved + */ + remove_title: function (title) { + this.remove_id(this.index_by_title.call(this, title)); + }, + /** + * Remove an entry from the list. + * + * @method TK.Select#remove_entry + * + * @param {TK.SelectEntry} entry - The {@link TK.SelectEntry} to be removed from the list. + * + * @emits TK.Select#entryremoved + */ + remove_entry: function (entry) { + this.remove_child(entry); + }, + remove_entries: function (a) { + for (var i = 0; i < a.length; i++) + this.remove_entry(a[i]); + }, + remove_child: function(child) { + TK.Button.prototype.remove_child.call(this, child); + if (TK.SelectEntry.prototype.isPrototypeOf(child)) { + low_remove_entry.call(this, child); + } + }, + /** + * Get the index of a {@link TK.SelectEntry} by its value. + * + * @method TK.Select#index_by_value + * + * @param {Mixed} value - The value of the {@link TK.SelectEntry}. + * + * @returns {Integer|Boolean} The index of the entry or `false`. + */ + index_by_value: function (val) { + var entries = this.entries; + for (var i = 0; i < entries.length; i++) { + if (entries[i].options.value === val) + return i; + } + return false; + }, + /** + * Get the index of a {@link TK.SelectEntry} by its title/label. + * + * @method TK.Select#index_by_title + * + * @param {String} title - The title/label of the {@link TK.SelectEntry}. + * + * @returns {Integer|Boolean} The index of the entry or `false`. + */ + index_by_title: function (title) { + var entries = this.entries; + for (var i = 0; i < entries.length; i++) { + if (entries[i].options.title === title) + return i; + } + return false; + }, + /** + * Get the index of a {@link TK.SelectEntry} by the {@link TK.SelectEntry} itself. + * + * @method TK.Select#index_by_entry + * + * @param {TK.SelectEntry} entry - The {@link TK.SelectEntry}. + * + * @returns {Integer|Boolean} The index of the entry or `false`. + */ + index_by_entry: function (entry) { + var pos = this.entries.indexOf(entry); + return pos === -1 ? false : pos; + }, + /** + * Get a {@link TK.SelectEntry} by its value. + * + * @method TK.Select#entry_by_value + * + * @param {Mixed} value - The value of the {@link TK.SelectEntry}. + * + * @returns {TK.SelectEntry|False} The {@link TK.SelectEntry} or `false`. + */ + entry_by_value: function (val) { + var entries = this.entries; + for (var i = 0; i < entries.length; i++) { + if (entries[i].options.value === val) + return entries[i]; + } + return false; + }, + /** + * Get a {@link TK.SelectEntry} by its title/label. + * + * @method TK.Select#entry_by_title + * + * @param {String} title - The title of the {@link TK.SelectEntry}. + * + * @returns {TK.SelectEntry|Boolean} The {@link TK.SelectEntry} or `false`. + */ + entry_by_title: function (title) { + var entries = this.entries; + for (var i = 0; i < entries.length; i++) { + if (entries[i].options.title === title) + return entries[i]; + } + return false; + }, + /** + * Get a {@link TK.SelectEntry} by its index. + * + * @method TK.Select#entry_by_index + * + * @param {Integer} index - The index of the {@link TK.SelectEntry}. + * + * @returns {TK.SelectEntry|Boolean} The {@link TK.SelectEntry} or `false`. + */ + entry_by_index: function (index) { + if (index >= 0 && index < entries.length && entries[index]) + return entries[i]; + return false; + }, + /** + * Get a value by its {@link TK.SelectEntry} index. + * + * @method TK.Select#value_by_index + * + * @param {Integer} index - The index of the {@link TK.SelectEntry}. + * + * @returns {Mixed|Boolean} The value of the {@link TK.SelectEntry} or `false`. + */ + value_by_index: function(index) { + var entries = this.entries; + if (index >= 0 && index < entries.length && entries[index]) { + return entries[index].options.value; + } + return false; + }, + /** + * Get the value of a {@link TK.SelectEntry}. + * + * @method TK.Select#value_by_entry + * + * @param {TK.SelectEntry} entry - The {@link TK.SelectEntry}. + * + * @returns {mixed} The value of the {@link TK.SelectEntry}. + */ + value_by_entry: function(entry) { + return entry.options.value; + }, + /** + * Get the value of a {@link TK.SelectEntry} by its title/label. + * + * @method TK.Select#value_by_title + * + * @param {String} title - The title of the {@link TK.SelectEntry}. + * + * @returns {Mixed|Boolean} The value of the {@link TK.SelectEntry} or `false`. + */ + value_by_title: function (title) { + var entries = this.entries; + for (var i = 0; i < entries.length; i++) { + if (entries[i].options.title === title) + return entries[i].options.value; + } + return false; + }, + /** + * Remove all {@link TK.SelectEntry} from the list. + * + * @method TK.Select#clear + * + * @emits TK.Select#cleared + */ + clear: function () { + TK.empty(this._list); + this.select(false); + var entries = this.entries.slice(0); + for (var i = 0; i < entries.length; i++) { + this.remove_child(entries[i]); + } + /** + * Is fired when the list is cleared. + * + * @event TK.Select.cleared + */ + this.fire_event("cleared"); + }, + + redraw: function() { + TK.Button.prototype.redraw.call(this); + + var I = this.invalid; + var O = this.options; + var E = this.element; + + if (I.selected || I.value) { + I.selected = I.value = false; + if (this._active) { + TK.remove_class(this._active, "toolkit-active"); + } + var entry = this.entries[O.selected]; + + if (entry) { + this._active = entry.element; + TK.add_class(entry.element, "toolkit-active"); + } else { + this._active = null; + } + } + + if (I.validate("entries", "auto_size")) { + + I.show_list = true; + + var L; + + if (O.auto_size && (L = this._label)) { + var width = 0; + E.style.width = "auto"; + var orig_content = document.createDocumentFragment(); + while (L.firstChild) orig_content.appendChild(L.firstChild); + var entries = this.entries; + for (var i = 0; i < entries.length; i++) { + L.appendChild(document.createTextNode(entries[i].options.title)); + L.appendChild(document.createElement("BR")); + } + TK.S.add(function() { + width = TK.outer_width(E, true); + TK.S.add(function() { + while (L.firstChild) L.removeChild(L.firstChild); + L.appendChild(orig_content); + TK.outer_width(E, true, width); + }, 1); + }); + } + } + + if (I.validate("show_list", "resized")) { + show_list.call(this, O.show_list); + } + }, + /** + * Get the currently selected {@link TK.SelectEntry}. + * + * @method TK.Select#current + * + * @returns {TK.SelectEntry|Boolean} The currently selected {@link TK.SelectEntry} or `false`. + */ + current: function() { + if (this.options.selected !== false) + return this.entries[this.options.selected]; + return false; + }, + /** + * Get the currently selected {@link TK.SelectEntry}'s index. Just for the sake of completeness, this + * function abstracts `options.selected`. + * + * @method TK.Select#current_index + * + * @returns {Integer|Boolean} The index of the currently selected {@link TK.SelectEntry} or `false`. + */ + current_index: function() { + return this.options.selected; + }, + /** + * Get the currently selected {@link TK.SelectEntry}'s value. + * + * @method TK.Select#current_value + * + * @returns {Mixed|Boolean} The value of the currently selected {@link TK.SelectEntry} or `false`. + */ + current_value: function() { + var w = this.current(); + if (w) return w.get("value"); + return false; + }, + set: function (key, value) { + if (key === "value") { + this.set("selected", this.index_by_value.call(this, value)); + return this.current_value(); + } + + value = TK.Button.prototype.set.call(this, key, value); + + switch (key) { + case "selected": + var entry = this.current(); + if (entry !== false) { + TK.Button.prototype.set.call(this, "value", entry.options.value); + this.set("label", entry.options.label); + } else { + TK.Button.prototype.set.call(this, "value", void 0); + this.set("label", false); + } + break; + case "entries": + this.set_entries(value); + break; + } + return value; + } +}); + + +function on_select(e) { + var w = this.parent; + var id = w.index_by_entry(this); + var entry = this; + e.stopPropagation(); + e.preventDefault(); + + if (w.userset("selected", id) === false) return false; + w.userset("value", this.options.value); + /** + * Is fired when a selection was made by the user. The arguments + * are the value of the currently selected {@link TK.SelectEntry}, its index, its title and the {@link TK.SelectEntry} instance. + * + * @event TK.Select#select + * + * @param {mixed} value - The value of the selected entry. + * @param {number} value - The ID of the selected entry. + * @param {string} value - The title of the selected entry. + * @param {string} value - The title of the selected entry. + */ + w.fire_event("select", entry.options.value, id, entry.options.title); + w.show_list(false); + + return false; +} + +TK.SelectEntry = TK.class({ + /** + * TK.SelectEntry provides a {@link TK.Label} as an entry for {@link TK.Select}. + * + * @class TK.SelectEntry + * + * @extends TK.Label + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String} [options.title=""] - The title of the entry. Kept for backward compatibility, deprecated, use label instead. + * @property {mixed} [options.value] - The value of the selected entry. + * + */ + _class: "SelectEntry", + Extends: TK.Label, + + _options: Object.assign(Object.create(TK.Label.prototype._options), { + value: "Mixed", + title: "String", + }), + options: { + title: "", + value: null + }, + initialize: function (options) { + var E = this.element = TK.element("li", "toolkit-option"); + TK.Label.prototype.initialize.call(this, options); + this.set("title", this.options.title); + }, + static_events: { + touchstart: on_select, + mousedown: on_select, + }, + set: function (key, value) { + switch (key) { + case "title": + this.set("label", value); + break; + case "label": + this.options.title = value; + break; + } + return TK.Label.prototype.set.call(this, key, value); + } +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/slider.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/slider.js new file mode 100644 index 0000000000..c046b7dabb --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/slider.js @@ -0,0 +1,207 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +/** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the option value. + * + * @event TK.Slider#useraction + * + * @param {string} name - The name of the option which was changed due to the users action + * @param {mixed} value - The new value of the option + */ + +"use strict"; +(function(w, TK){ +function dblclick() { + this.userset("value", this.options.reset); + /** + * Is fired when the slider receives a double click in order to reset to initial value. + * + * @event TK.Slider#doubleclick + * + * @param {number} value - The value of the widget. + */ + this.fire_event("doubleclick", this.options.value); +} + +function set_background(horiz, vert, size) { + var E = this.element; + E.style["background-position"] = "-"+horiz+"px -"+vert+"px"; + + E.style["-webkit-background-size"] = size; + E.style["-moz-background-size"] = size; + E.style["-ms-background-size"] = size; + E.style["-o-background-size"] = size; + E.style["background-size"] = size; +} +/** + * TK.Slider is a {@link TK.Widget} moving its background image + * according to its value. It can be used to show strips of + * e.g. 3D-rendered faders or knobs. It's important to set the + * width and height of the widget in CSS according to the frames in + * the background file. If alignment is `horizontal` the background image + * is as height as the widget, the width keeps the ratio intact. Overall + * width of the image should be frames * width. If alignment is `vertical` + * the background image is as wide as the widget and the height of the + * image keeps the ratio intact. The height should be height of widget + * times the amount of frames. + * TK.Slider uses {@link TK.DragValue} and {@link TK.ScrollValue} + * for setting its value. + * It inherits all options of {@link TK.DragValue}, {@link TK.Ranged} and {@link TK.Warning}. + * + * @class TK.Slider + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.value=0] - The current value. + * @property {Integer} [options.frames=1] - The amount of frames contained + * in the background image. + * @property {String} [options.alignment="horizontal"] - The direction + * of the frames in the image, next to (`horizontal`) or among each other (`vertical`). + * @property {String|Booelan} [options.image=false] - The image containing all frames for the slider. + * Set to `false` to set the background image via external CSS. + * + */ +TK.Slider = TK.class({ + _class: "Slider", + Extends: TK.Widget, + Implements: [TK.Ranged, TK.Warning], + _options: Object.assign(Object.create(TK.Widget.prototype._options), + TK.Ranged.prototype._options, + TK.DragValue.prototype._options, { + value: "number", + frames: "int", + alignment: "string", + image: "string|boolean", + _width: "number", + _height: "number", + + }), + options: { + value: 0, + frames: 1, + alignment: "horizontal", + image: false, + + direction: "polar", + rotation: 45, + blind_angle: 20, + basis: 300, + }, + static_events: { + dblclick: dblclick, + }, + initialize: function (options) { + TK.Widget.prototype.initialize.call(this, options); + var E; + /** + * @member {HTMLDivElement} TK.Slider#element - The main DIV container. + * Has class toolkit-slider. + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-slider"); + this.widgetize(E, true, true, true); + /** + * @member {TK.DragValue} TK.Knob#drag - Instance of {@link TK.DragValue} used for + * interaction. + */ + this.drag = new TK.DragValue(this, { + node: E, + classes: E, + direction: this.options.direction, + rotation: this.options.rotation, + blind_angle: this.options.blind_angle, + }); + /** + * @member {TK.ScrollValue} TK.Knob#scroll - Instance of {@link TK.ScrollValue} used for + * interaction. + */ + this.scroll = new TK.ScrollValue(this, { + node: E, + classes: E, + }); + + if (options.reset === void(0)) + options.reset = options.value; + }, + + destroy: function () { + this.drag.destroy(); + this.scroll.destroy(); + TK.Widget.prototype.destroy.call(this); + }, + + redraw: function() { + var I = this.invalid; + var O = this.options; + var E = this.element; + + if (I.image) { + I.image = false; + if (O.image) + this.element.style["background-image"] = "url('" + O.image + "')"; + else + this.element.style["background-image"] = void 0; + I.value = true; + } + + if (I.value || I.alignment || O.frames) { + I.value = false; + I.alignment = false; + I.frames = false; + var coef = this.val2coef(O.value); + var frame = Math.round(Math.max(0, O.frames - 1) * coef); + switch (O.alignment) { + default: + TK.warn("Unknown alignment, only 'vertical' and 'horizontal' are allowed"); + break; + case "vertical": + set_background.call(this, 0, frame * O._width, "100% auto"); + break; + case "horizontal": + set_background.call(this, frame * O._height, 0, "auto 100%"); + break; + } + } + + TK.Widget.prototype.redraw.call(this); + }, + + resize: function () { + this.set("_width", TK.outer_width(this.element)); + this.set("_height", TK.outer_height(this.element)); + }, + + set: function(key, value) { + switch (key) { + case "value": + if (value > this.options.max || value < this.options.min) + this.warning(this.element); + value = this.snap(value); + break; + } + if (TK.DragValue.prototype._options[key]) + this.drag.set(key, value); + return TK.Widget.prototype.set.call(this, key, value); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/sortablelist.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/sortablelist.js new file mode 100644 index 0000000000..74ed1dadf8 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/sortablelist.js @@ -0,0 +1,76 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +/* TODO */ + +var build_dragcapture = function () { + this.dragcapture = new TK.DragCapture(this, { + state: true, + onstartcapture: function (state) { + console.log(state, "start"); + return true; + }, + onmovecapture: function (state) { + console.log(state, "move"); + }, + onstopcapture: function () { + console.log("stop"); + } + }); +} + +TK.SortableList = TK.class({ + + _class: "SortableList", + Extends: TK.List, + + _options: Object.assign(Object.create(TK.Container.prototype._options), { + sortable: "boolean", + }), + options: { + sortable: false, + item_class: TK.SortableListItem, + }, + initialize: function (options) { + TK.List.prototype.initialize.call(this, options); + this.element.add_class("toolkit-sortable-list"); + }, + add_item: function (item, pos) { + var O = this.options; + var item = TK.List.prototype.add_item.call(this, item, pos); + item.set("sortable", O.sortable); + }, + set: function (key, value) { + switch (key) { + case "sortable": + var I = this.options.items; + for (var i = 0; i < I.length; i++) + I[i].set("sortable", value); + if (value && !this.dragcapture) + build_dragcapture.call(this); + break; + } + return TK.List.prototype.set.call(this, key, value); + } +}); + + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/sortablelistitem.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/sortablelistitem.js new file mode 100644 index 0000000000..e129f59e30 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/sortablelistitem.js @@ -0,0 +1,65 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +var build_sorter = function () { + this.sorter = new TK.Button({"class":"toolkit-sorter",container:this.element}); + this.add_child(this.sorter); +} + +TK.SortableListItem = TK.class({ + + _class: "SortableListItem", + Extends: TK.ListItem, + + _options: Object.assign(Object.create(TK.ListItem.prototype._options), { + sortable: "boolean", + }), + options: { + sortable: false, + }, + initialize: function (options) { + TK.ListItem.prototype.initialize.call(this, options); + this.element.add_class("toolkit-sortable-list-item"); + }, + redraw: function () { + TK.ListItem.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + if (I.sortable) { + if (O.sortable) { + if (!this.sorter) { + build_sorter.call(this); + } else { + this.element.appendChild(this.sorter.element); + } + } else { + if (this.sorter) + this.element.removeChild(this.sorter.element); + } + } + }, + set: function (key, value) { + return TK.ListItem.prototype.set.call(this, key, value); + } +}); + + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/state.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/state.js new file mode 100644 index 0000000000..a0e96a2c07 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/state.js @@ -0,0 +1,109 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +/** + * The TK.State widget is a multi-functional adaption of a traditional LED. It + * is able to show different colors as well as on/off states. The + * "brightness" can be set seamlessly. Classes can be used to display + * different styles. TK.State extends {@link TK.Widget}. + * + * The LED effect is implemented as a DIV element, which is overlayed by + * a DIV element with class toolkit-mask. `options.state` + * changes the opacity of the mask element. + * + * @class TK.State + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.state=0] - The state. To toggle between `on|off` set to `1|0`. + * Set to fractions of `1` to change "brightness", e.g. `0.5`. Values > 0 trigger setting + * the class `toolkit-state-on`, while a state of `0` results in class `toolkit-state-off`. + * @property {String|Boolean} [options.color=false] - A CSS color string for the state LED or + * `false` to set the background via external CSS. + */ +TK.State = TK.class({ + _class: "State", + Extends: TK.Widget, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + state: "number|boolean", + color: "string|boolean", + }), + options: { + state: 0, // the initial state (0 ... 1) + color: false, // the base color + }, + initialize: function (options) { + TK.Widget.prototype.initialize.call(this, options); + + var E; + /** + * @member {HTMLDivElement} TK.State#element - The main DIV container. + * Has class toolkit-state. + */ + if (!(E = this.element)) this.element = E = toolkit.element("div"); + TK.add_class(E, "toolkit-state"); + this.widgetize(E, true, true, true); + + /** + * @member {HTMLDivElement} TK.State#_mask - A DIV for masking the background. + * Has class toolkit-mask. + */ + this._mask = TK.element("div","toolkit-mask"); + + E.appendChild(this._mask); + }, + destroy: function () { + this._mask.remove(); + TK.Widget.prototype.destroy.call(this); + }, + + redraw: function() { + TK.Widget.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + + if (I.color) { + I.color = false; + if (O.color) + this.element.style["background-color"] = O.color; + else + this.element.style["background-color"] = void 0; + } + + if (I.state) { + I.state = false; + var v = +O.state; + if (!(v >= 0)) v = 0; + if (!(v <= 1)) v = 1; + + if (!O.state) { + this.remove_class("toolkit-state-on"); + this.add_class("toolkit-state-off"); + } else { + this.remove_class("toolkit-state-off"); + this.add_class("toolkit-state-on"); + } + this._mask.style["opacity"] = "" + (1 - v); + } + } +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/tag.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/tag.js new file mode 100644 index 0000000000..5f173d398e --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/tag.js @@ -0,0 +1,135 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +function remove (e, node) { + this.fire_event("remove", node); + if (!this.options.async) + this.remove_node(node); +} + +function colorize (e) { + var that = this; + var c = new TK.ColorPickerDialog({ + autoclose: true, + hex: this.options.color, + onapply: function (rgb, hsl, hex) { + if (!that.options.async) + that.userset("color", hex); + else + that.fire_event("userset", "color", hex); + + }, + container: document.body, + }); + c.open(e.pageX, e.pageY); + c.show(); + this.colorpicker = c; + w.c = c; +} + +TK.Tag = TK.class({ + _class: "Tag", + Extends: TK.Widget, + Implements: TK.Colors, + + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + color: "string|null", + tag: "string", + async: "boolean", + node_class: "constructor", + }), + options: { + color: null, + tag: "", + async: false, + node_class: TK.TagNode, + }, + initialize: function (options) { + TK.Widget.prototype.initialize.call(this, options); + this.nodes = []; + }, + destroy: function () { + var l = this.nodes.length; + for (var i = 0; i < l; i++) + this.remove_node(this.nodes[i]); + TK.Widget.prototype.destroy.call(this); + }, + + redraw: function () { + var I = this.invalid; + var O = this.options; + if (I.color) { + I.color = false; + for (var i = 0; i < this.nodes.length; i++) { + this.nodes[i].element.style.backgroundColor = O.color; + if (O.color) + this.nodes[i].element.style.color = this.rgb2gray(this.hex2rgb(O.color)) > 0.5 ? "black" : "white"; + else + this.nodes[i].element.style.color = null; + } + } + if (I.tag) { + I.tag = false; + for (var i = 0; i < this.nodes.length; i++) + this.nodes[i].children[0].textContent = O.tag; + } + TK.Widget.prototype.redraw.call(this); + }, + remove_node: function (node) { + var O = this.options; + for (var i = 0; i < this.nodes.length; i++) { + if (this.nodes[i] == node) { + node.set("container", false); + this.fire_event("noderemoved", node); + this.nodes.splice(i, 1); + node.destroy(); + return true; + } + } + }, + create_node: function (options) { + var O = this.options; + options = options || {}; + options.color = O.color; + options.tag = O.tag; + var node = new O.node_class(options, this); + node.add_event("colorize", colorize.bind(this)); + node.add_event("remove", remove.bind(this)); + this.nodes.push(node); + this.fire_event("nodecreated", node); + return node; + }, + set: function (key, value) { + switch (key) { + case "color": + for (var i = 0; i < this.nodes.length; i++) + this.nodes[i].set("color", this.options.color); + break; + case "tag": + for (var i = 0; i < this.nodes.length; i++) + this.nodes[i].set("tag", this.options.tag); + break; + } + return TK.Widget.prototype.set.call(this, key, value); + } +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/taggable.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/taggable.js new file mode 100644 index 0000000000..4ab246373e --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/taggable.js @@ -0,0 +1,155 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +function add (e) { + this.fire_event("addtag", e); +} + +function remove (e, tagnode) { + this.fire_event("removetag", tagnode); + if (!this.options.async) + this.remove_tag(tagnode.tag, tagnode); +} + +TK.Taggable = TK.class({ + _options: { + tags: "array", + backend: "object", + add_label: "string", + show_add: "boolean", + async: "boolean", + tag_class: "object", + tag_options: "object", + }, + options: { + tags: [], + backend: false, + add_label: "✚", + show_add: true, + async: false, + tag_class: TK.Tag, + tag_options: {}, + }, + static_events: { + destroy: function() { + this.tags.destroy(); + this.add.destroy(); + }, + }, + initialize: function () { + var O = this.options; + this.taglist = []; + if (!O.backend) + O.backend = new TK.Tags({}); + + this.tags = new TK.Container({ + "class" : "toolkit-tags" + }); + this.append_child(this.tags); + + this.add = new TK.Button({ + container: this.element, + label: O.add_label, + "class": "toolkit-add", + "onclick": add.bind(this), + }); + this.append_child(this.add); + + this.add_tags(O.tags); + }, + + request_tag: function (tag, tag_class, tag_options) { + return this.options.backend.request_tag( + tag, + tag_class || this.options.tag_class, + tag_options || this.options.tag_options); + }, + add_tags: function (tags) { + for (var i = 0; i < tags.length; i++) + this.add_tag(tags[i]); + }, + add_tag: function (tag, options) { + var B = this.options.backend; + tag = B.request_tag(tag, options); + if (this.has_tag(tag)) return; + + var node = tag.create_node(options); + this.tags.append_child(node); + + node.add_event("remove", remove.bind(this)); + + var t = {tag:tag, node:node}; + this.taglist.push(t); + this.fire_event("tagadded", tag, node); + return t; + }, + has_tag: function (tag) { + tag = this.request_tag(tag); + for (var i = 0; i < this.taglist.length; i++) { + if (this.taglist[i].tag === tag) + return true; + } + return false; + }, + remove_tag: function (tag, node, purge) { + var B = this.options.backend; + tag = B.request_tag(tag); + if (!this.has_tag(tag)) return; + for (var i = 0; i < this.taglist.length; i++) { + if (this.taglist[i].tag === tag) { + this.taglist.splice(i, 1); + break; + } + } + + if (!node) { + var c = this.tags.children; + if (c) { + for (var i = 0; i < c.length; i++) { + var tagnode = c[i]; + if (tagnode.tag === tag) { + tag.remove_node(tagnode); + this.remove_child(tagnode); + break; + } + } + } + } else { + tag.remove_node(node); + } + if (purge) + B.remove_tag(tag); + this.fire_event("tagremoved", tag); + }, + empty: function () { + var T = this.taglist; + while (T.length) + this.remove_tag(T[0].tag, T[0].node); + }, + tag_to_string: function (tag) { + return this.options.backend.tag_to_string.call(this, tag); + }, + find_tag: function (tag) { + this.options.backend.find_tag.call(this, tag); + }, +}); + +})(this, TK) diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/taggablelistitem.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/taggablelistitem.js new file mode 100644 index 0000000000..79ecefcfbc --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/taggablelistitem.js @@ -0,0 +1,35 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +TK.TaggableListItem = TK.class({ + + _class: "TaggableListItem", + Extends: TK.ListItem, + Implements: TK.Taggable, + + initialize: function (options) { + TK.ListItem.prototype.initialize.call(this, options); + TK.Taggable.prototype.initialize.call(this); + TK.add_class(this.element, "toolkit-taggable-list-item"); + } +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/taggabletreeitem.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/taggabletreeitem.js new file mode 100644 index 0000000000..0f5771bf8e --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/taggabletreeitem.js @@ -0,0 +1,35 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +TK.TaggableTreeItem = TK.class({ + + _class: "TaggableTreeItem", + Extends: TK.TreeItem, + Implements: TK.Taggable, + + initialize: function (options) { + TK.TreeItem.prototype.initialize.call(this, options); + TK.Taggable.prototype.initialize.call(this); + TK.add_class(this.element, "toolkit-taggable-tree-item"); + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/tagger.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/tagger.js new file mode 100644 index 0000000000..eae1655fec --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/tagger.js @@ -0,0 +1,99 @@ +(function (w, TK) { +"use strict" + +function keyup (e) { + if (e.keyCode != 13) return; + new_tag_from_input.call(this); +} +function addclicked (e) { + new_tag_from_input.call(this); +} +function new_tag_from_input () { + var val = this._input.value; + if (!val) return; + this._input.value = ""; + var t = false; + if (!this.options.async) + t = this.add_tag(val); + this.fire_event("newtag", val, t); + if (this.options.closenew) + this.close(); +} + +TK.Tagger = TK.class({ + + _class: "Tagger", + Extends: TK.Dialog, + Implements: TK.Taggable, + + _options: Object.assign(Object.create(TK.Dialog.prototype._options), { + closenew: "boolean", + add: "boolean", + }), + options: { + closenew: true, + visible: false, + add: true, + }, + initialize: function (options) { + TK.Dialog.prototype.initialize.call(this, options); + TK.add_class(this.element, "toolkit-tagger"); + + TK.Taggable.prototype.initialize.call(this); + this.append_child(this.tags); + this.add_event("addtag", new_tag_from_input.bind(this)); + + this.set("add", this.options.add); + }, + destroy: function (options) { + TK.Taggable.prototype.destroy.call(this); + TK.Dialog.prototype.destroy.call(this); + }, + redraw: function () { + TK.Dialog.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + if (I.add) { + I.add = false; + if (O.add) { + if (!this._input) { + this._input = TK.element("input", "toolkit-input"); + this._input.addEventListener("keyup", keyup.bind(this), true); + this._input.type = "text"; + this._input.placeholder = "New tag"; + this.element.appendChild(this._input); + } + this.add.set("container", this.element); + TK.add_class(this.element, "toolkit-has-input"); + } else if (!O.add) { + if (this._input) { + this.element.removeChild(this._input); + this._input = null; + } + this.add.set("container", false); + TK.remove_class(this.element, "toolkit-has-input"); + } + } + }, + add_tag: function (tag, options) { + var t = TK.Taggable.prototype.add_tag.call(this, tag, options); + if (!t) return; + t.node.label.add_event("click", (function (that, tag) { + return function (e) { + that.fire_event("tagclicked", tag.tag, tag.node); + } + })(this, t)); + if (this.options.visible) + this.reposition(); + return t; + }, + remove_tag: function (tag, node, purge) { + TK.Taggable.prototype.remove_tag.call(this, tag, node, purge); + if (!this.taglist.length) + this.close(); + if (this.options.visible) + this.reposition(); + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/tagnode.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/tagnode.js new file mode 100644 index 0000000000..9255fa9750 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/tagnode.js @@ -0,0 +1,93 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +TK.TagNode = TK.class({ + + Extends: TK.Container, + Implements: TK.Colors, + + _options: Object.assign(Object.create(TK.Container.prototype._options), { + label: "string", + color: "string|null", + confirm: "boolean", + }), + options: { + label: "", + color: null, + confirm: false, + }, + + initialize: function (options, tag) { + TK.Container.prototype.initialize.call(this, options); + this.tag = tag; + this.add_class("toolkit-tag"); + + }, + + redraw: function () { + TK.Container.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + if (I.color) { + I.color = false; + this.element.style.backgroundColor = O.color; + if (O.color) + this.element.style.color = this.rgb2bw(this.hex2rgb(O.color)); + else + this.element.style.color = null; + } + } +}); + +TK.ChildWidget(TK.TagNode, "label", { + create: TK.Label, + show: true, + map_options: { + tag: "label", + }, + toggle_class: true, +}); +TK.ChildWidget(TK.TagNode, "colorize", { + create: TK.Button, + show: false, + toggle_class: true, + static_events: { + click: function (e) { this.parent.fire_event("colorize", e); } + }, + default_options: { + class: "toolkit-colorize" + }, +}); +TK.ChildWidget(TK.TagNode, "remove", { + create: TK.ConfirmButton, + show: true, + toggle_class: true, + static_events: { + confirmed: function (e) { this.parent.fire_event("remove", e, this.parent); } + }, + default_options: { + class: "toolkit-remove", + label : "", + confirm: false, + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/tags.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/tags.js new file mode 100644 index 0000000000..7c309f2a34 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/tags.js @@ -0,0 +1,87 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +TK.Tags = TK.class({ + + Extends: TK.Widget, + + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + tag_class: "object", + }), + options: { + tag_class: TK.Tag, + }, + + initialize: function (options) { + this.tags = new Map(); + this.tag_to_name = new Map(); + TK.Widget.prototype.initialize.call(this, options); + }, + tag_to_string: function (tag) { + if (typeof tag == "string") { + return tag + } else if (TK.Tag.prototype.isPrototypeOf(tag)) { + if (!tag.is_destructed()) { + return tag.options.tag; + } else { + return this.tag_to_name.get(tag); + } + } else { + return tag.tag; + } + }, + find_tag: function (tag) { + return this.tags.get(this.tag_to_string(tag)); + }, + request_tag: function (tag, options) { + var C = this.options.tag_class; + var t = this.tag_to_string(tag); + + if (this.tags.has(t)) { + tag = this.tags.get(t); + if (!tag.is_destructed()) return tag; + } + + if (typeof tag == "string") { + var o = Object.assign(options || {}, {tag:tag}); + tag = new C(o); + } else if (C.prototype.isPrototypeOf(tag)) { + tag = tag; + } else { + tag = new C(tag); + } + tag.show(); + this.tags.set(t, tag); + this.tag_to_name.set(tag, t); + return tag; + }, + remove_tag: function (tag) { + tag = this.find_tag(tag); + this.tags.delete(this.tag_to_string(tag)); + this.tag_to_name.delete(tag); + }, + empty: function() { + this.tags = new Map(); + this.tag_to_name = new Map(); + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/toggle.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/toggle.js new file mode 100644 index 0000000000..ab365467d4 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/toggle.js @@ -0,0 +1,237 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ + +/* Abstract toggle logic */ + +function reset_delay_to () { + window.clearTimeout(this.__delayed_to); + this.__delayed_to = -1; + this.remove_class("toolkit-delayed"); +} + +function toggle(O) { + if (this.userset("state", !O.state) === false) return; + this.fire_event("toggled", O.state); +} +function press_start() { + var O = this.options; + this.__press_start_time = Date.now(); + if (O.delay && this.__delayed_to < 0) { + this.__delayed_to = window.setTimeout((function (t) { + return function () { press_start.call(t); } + })(this), O.delay); + this.add_class("toolkit-delayed"); + return; + } + this.remove_class("toolkit-delayed"); + if (O.delay && this.__delayed_to >= 0) { + toggle.call(this, O); + } + if (O.press) toggle.call(this, O); +} +function press_end() { + var O = this.options; + if (O.delay && this.__delayed_to >= 0) { + reset_delay_to.call(this); + return; + } + var t = Date.now() - this.__press_start_time; + if ((O.toggle && (!O.press || t > O.press)) || (!O.toggle && O.press)) { + toggle.call(this, O); + } +} +function press_cancel() { + var O = this.options; + if (O.delay && this.__delayed_to >= 0) { + reset_delay_to.call(this); + return; + } + /* this is definitely not a click, its a cancel by leaving the + * button with mouse or finger while pressing */ + if (O.press) toggle.call(this, O); +} + +/* MOUSE handling */ +function mouseup(e) { + this.remove_event("mouseup", mouseup); + this.remove_event("mouseleave", mouseleave); + press_end.call(this); +} +function mouseleave(e) { + this.remove_event("mouseup", mouseup); + this.remove_event("mouseleave", mouseleave); + press_cancel.call(this); +} +function mousedown(e) { + /* only left click please */ + if (e.button) return true; + press_start.call(this); + this.add_event("mouseup", mouseup); + this.add_event("mouseleave", mouseleave); +} + +/* TOUCH handling */ +function is_current_touch(ev) { + var id = this.__touch_id; + var i; + for (i = 0; i < ev.changedTouches.length; i++) { + if (ev.changedTouches[i].identifier === id) { + return true; + } + } + + return false; +} + +function touchend(e) { + if (!is_current_touch.call(this, e)) return; + this.__touch_id = false; + e.preventDefault(); + press_end.call(this); + + this.remove_event("touchend", touchend); + this.remove_event("touchcancel", touchleave); + this.remove_event("touchleave", touchleave); +} +function touchleave(e) { + if (!is_current_touch.call(this, e)) return; + this.__touch_id = false; + e.preventDefault(); + press_cancel.call(this); + + this.remove_event("touchend", touchend); + this.remove_event("touchcancel", touchleave); + this.remove_event("touchleave", touchleave); +} +function touchstart(e) { + if (this.__touch_id !== false) return; + this.__touch_id = e.targetTouches[0].identifier; + press_start.call(this); + this.add_event("touchend", touchend); + this.add_event("touchcancel", touchleave); + this.add_event("touchleave", touchleave); + e.preventDefault(); + e.stopPropagation(); + return false; +} +function contextmenu(e) { + e.preventDefault(); + e.stopPropagation(); + return false; +} +var class_regex = /[^A-Za-z0-9_\-]/; +function is_class_name (str) { + return !class_regex.test(str); +} + +TK.Toggle = TK.class({ + /** + * A toggle button. The toggle button can either be pressed (which means that it will + * switch its state as long as it is pressed) or toggled permanently. Its behavior is + * controlled by the two options press and toggle. + * + * @class TK.Toggle + * + * @extends TK.Button + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Boolean} [options.toggle=true] - If true, the button is toggled on click. + * @property {Integer} [options.press=0] - Controls press behavior. If options.toggle + * is false and this option is 0, the toggle button will toggle until + * released. If options.toggle is true and this option is a positive integer, it is + * interpreted as a milliseconds timeout. When pressing a button longer than this timeout, it will + * be toggled until released, otherwise it will be toggled permanently. + * @property {Integer} [options.delay=0] - Delay all actions for n milliseconds. While actions are + * delayed, the widget has class toolkit-delayed. Use to force users to press the button + * for a certain amount of time before it actually gets toggled. + * @property {String|Boolean} [options.icon_active=false] - An optional icon which is only displayed + * when the button toggle state is true. Please note that this option only works if `icon` is also set. + * @property {String|Boolean} [options.label_active=false] - An optional label which is only displayed + * when the button toggle state is true. Please note that this option only works if `label` is also set. + */ + _class: "Toggle", + Extends: TK.Button, + _options: Object.assign(Object.create(TK.Button.prototype._options), { + label_active: "string", + icon_active: "string", + press: "int", + delay: "int", + toggle: "boolean", + }), + options: { + label_active: false, + icon_active: false, + icon_inactive: false, + press: 0, + delay: 0, + toggle: true, + }, + static_events: { + mousedown: mousedown, + touchstart: touchstart, + contextmenu: contextmenu, + }, + + initialize: function (options) { + TK.Button.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Toggle#element - The main DIV container. + * Has class toolkit-toggle. + */ + TK.add_class(this.element, "toolkit-toggle"); + this.__press_start_time = 0; + this.__touch_id = false; + this.__delayed_to = -1; + }, + + redraw: function () { + var O = this.options; + var I = this.invalid; + if (I.state) { + var tmp = (O.state && O.label_active) || O.label; + if (tmp) + this.label.set("label", tmp || ""); + tmp = (O.state && O.icon_active) || O.icon; + if (tmp) + this.icon.set("icon", tmp || ""); + } + TK.Button.prototype.redraw.call(this); + }, + /** + * Toggle the button state. + * + * @method TK.Toggle#toggle + * + * @emits TK.Toggle#toggled + */ + toggle: function () { + toggle.call(this, this.options); + /** + * Is fired when the button was toggled. + * + * @event TK.Toggle#toggled + * + * @param {boolean} state - The state of the {@link TK.Toggle}. + */ + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/tooltips.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/tooltips.js new file mode 100644 index 0000000000..fbc1d941d3 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/tooltips.js @@ -0,0 +1,205 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function(w, TK){ +function get_event(event) { + // return the right event if touch surface is used + // with multiple fingers + return (event.touches && event.touches.length) + ? event.touches[0] : event; +} +/** + * TK.GlobalTooltip is a small text field following the mouse. + * Due to optimization it is implemented as a table with a single cell + * containing the content. + * + * @class TK.Tooltip + */ +TK.GlobalTooltip = function() { + var overlay = TK.element("div", "toolkit-tooltip"); + var table = TK.element("div", "toolkit-table"); + var row = TK.element("div", "toolkit-row"); + var cell = TK.element("div", "toolkit-cell"); + + var tmp = row.cloneNode(); + + var spacer_tl = cell.cloneNode(); + + tmp.appendChild(spacer_tl); + tmp.appendChild(cell.cloneNode()); + tmp.appendChild(cell.cloneNode()); + + table.appendChild(tmp); + + tmp = row.cloneNode(); + + var entry = cell.cloneNode(); + entry.className += " toolkit-entry"; + + var entry_tl = cell.cloneNode(); + entry_tl.className += " toolkit-tt-container"; + entry_tl.appendChild(entry); + + tmp.appendChild(cell.cloneNode()); + tmp.appendChild(entry_tl); + tmp.appendChild(cell.cloneNode()); + + table.appendChild(tmp); + + tmp = row.cloneNode(); + + tmp.appendChild(cell.cloneNode()); + tmp.appendChild(cell.cloneNode()); + tmp.appendChild(cell.cloneNode()); + + table.appendChild(tmp); + overlay.appendChild(table); + + var tooltips = []; + + function current_callback() { + for (var i = 0; i < tooltips.length; i++) { + if (tooltips[i] && tooltips[i].length) { + return tooltips[i][0]; + } + } + } + + var ev = null; + + function redraw() { + var e = ev; + ev = null; + var current = current_callback(); + + if (!current) { + hide(); + return; + } + + var w = e.clientX; + var h = e.clientY / window.innerHeight; + + spacer_tl.style.width = w > 0 ? w + "px" : "0.01%"; + spacer_tl.style.height = h > 0 ? (h * 100).toFixed(2) + "%" : "0.01%"; + + current(e, entry); + } + + function onmove_mouse(e) { + if (!ev) { + TK.S.add(redraw, 1); + } + ev = e; + } + + function onmove_touch(e) { + onmove_mouse(get_event(e)); + } + + var hidden = false; + var num_tooltips = 0; + + /** + * Hide the tooltips + * + * @method TK.Tooltip#hide + */ + function hide() { + document.removeEventListener("mousemove", onmove_mouse); + document.removeEventListener("touchmove", onmove_touch); + overlay.style.display = "none"; + hidden = true; + } + /** + * Show the tooltips + * + * @method TK.Tooltip#show + */ + function show() { + if (!overlay.parentNode) + document.body.appendChild(overlay); + document.addEventListener("mousemove", onmove_mouse); + document.addEventListener("touchmove", onmove_touch); + overlay.style.removeProperty("display"); + hidden = false; + } + /** + * Add a new tooltip. + * + * @method TK.Tooltip#add + * + * @param {int} priority - The priority of the tooltip. States its position in the list. + * @param {Function} onmove - The function which sets the text of the tooltip while moving the mouse. + */ + function add(priority, onmove) { + if (!tooltips[priority]) tooltips[priority] = []; + tooltips[priority].push(onmove); + if (hidden) show(); + num_tooltips++; + } + /** + * Remove a tooltip. + * + * @method TK.Tooltip#remove + * + * @param {int} priority - The priority of the tooltip. + * @param {Function} onmove - The function which sets the text of the tooltip while moving the mouse. + */ + function remove(priority, onmove) { + if (!tooltips[priority]) return; + var i = tooltips[priority].indexOf(onmove); + + if (i === -1) return; + if (tooltips[priority].length === 1) tooltips[priority] = null; + else tooltips[priority].splice(i, 1); + if (--num_tooltips === 0) hide(); + } + + hide(); + + this.show = show; + this.hide = hide; + this.add = add; + this.remove = remove; + this.trigger = onmove_touch; + + /** + * @member {HTMLDivElement} TK.Tooltip#element - The overlay containing the tooltip table. + * Has class toolkit-tooltip. + */ + this.element = overlay; + /** + * @member {HTMLDivElement} TK.Tooltip#_table - The table containing the tooltips. + * Has class toolkit-table. + */ + this._table = table; + /** + * @member {HTMLDivElement} TK.Tooltip#_entry - The element containing the tooltip text. + * Has class toolkit-entry. + */ + this._entry = entry; +}; +TK.GlobalTooltip.prototype = { + destroy: function() { + this.hide(); + }, +}; +TK.tooltip = new TK.GlobalTooltip(); +})(this, TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/treeitem.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/treeitem.js new file mode 100644 index 0000000000..937c01ffdc --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/treeitem.js @@ -0,0 +1,173 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; +(function (w, TK) { + +var toggle_collapsed = function () { + set_collapsed.call(this, !TK.has_class(this.element, "toolkit-collapsed")); +} +var set_collapsed = function (c) { + this.set("collapsed", c); +} + +var reset_size = function (state) { + if (state !== "show") return; + //this.element.style.height = null; +} + +TK.TreeItem = TK.class({ + + _class: "TreeItem", + Extends: TK.ListItem, + + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + collapsed: "boolean", + collapsable: "boolean", + force_collapsable: "boolean", + }), + options: { + collapsed: false, + collapsable: true, + force_collapsable: false, + }, + initialize: function (options) { + this.list = new TK.List({ + "onset_display_state": reset_size + }); + this.flex = new TK.Container({"class":"toolkit-flex"}); + + TK.ListItem.prototype.initialize.call(this, options); + TK.add_class(this.element, "toolkit-tree-item"); + + TK.ListItem.prototype.append_child.call(this, this.flex); + TK.ListItem.prototype.add_child.call(this, this.list); + this.flex.show(); + + this.collapse = new TK.Button({"class":"toolkit-collapse"}); + this.append_child(this.collapse); + this.collapse.add_event("click", toggle_collapsed.bind(this)); + + if (this.options.collapsable && this.options.collapsed) { + this.list.element.style.height = "0px"; + this.list.hide(); + } + }, + append_child: function (child) { + this.invalid._list = true; + this.trigger_resize(); + if (TK.ListItem.prototype.isPrototypeOf(child)) { + return this.list.append_child(child); + } else { + return this.flex.append_child(child); + } + }, + add_child : function(child) { + this.trigger_resize(); + if (TK.ListItem.prototype.isPrototypeOf(child)) { + return this.list.add_child(child); + } else { + return this.flex.add_child(child); + } + }, + remove_child : function(child) { + if (TK.ListItem.prototype.isPrototypeOf(child)) { + var r = this.list.remove_child(child); + return r; + } else { + this.flex.remove_child(child); + } + this.invalid._list = true; + this.trigger_resize(); + }, + redraw: function () { + TK.ListItem.prototype.redraw.call(this); + var I = this.invalid; + var O = this.options; + var E = this.element; + var F = this.flex.element; + if (I._list) { + I.collapsed = true; + if (this.list.children && this.list.children.length) { + if (this.list.element.parentElement != E) + E.appendChild(this.list.element); + this.add_class("toolkit-has-tree"); + } else { + if (this.list.element.parentElement == E) + E.removeChild(this.list.element); + this.remove_class("toolkit-has-tree"); + } + } + if (I._list || I.collapsable || I.force_collapsable) { + if ((this.list.children && this.list.children.length && O.collapsable) || O.force_collapsable) + F.appendChild(this.collapse.element); + else if (this.collapse.element.parentElement == E) + F.removeChild(this.collapse.element); + TK.toggle_class(E, "toolkit-force-collapsable", O.force_collapsable); + } + if (I.collapsed) { + I.collapsed = false; + var s = this.list.element.style; + if (O.collapsed) { + var h = this.list.element.offsetHeight; + s["height"] = h + "px"; + window.requestAnimationFrame(function () {s["height"] = "0px";}); + } else { + var list = this.list.element; + /* This is a train */ + TK.S.add(function() { + var h0 = list.offsetHeight; + var duration = TK.get_duration(list); + TK.S.add(function() { + s["height"] = "auto"; + TK.S.add(function() { + var h = list.offsetHeight; + TK.S.add(function() { + s["height"] = h0 + "px"; + TK.S.add_next(function () { + s["height"] = h + "px"; + + setTimeout(function () { + s.height = null; + }, duration); + }); + }, 1) + }); + }, 1); + }); + } + TK.toggle_class(E, "toolkit-collapsed", O.collapsed); + } + I._list = I.collapsable = I.force_collapsable = false; + }, + set: function (key, value) { + var O = this.options; + switch (key) { + case "collapsed": + if (!this.list) break; + if (!value) + this.list.show(); + else + this.list.hide(); + break; + } + return TK.ListItem.prototype.set.call(this, key, value); + } +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/value.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/value.js new file mode 100644 index 0000000000..4a955be560 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/value.js @@ -0,0 +1,262 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + + /** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the option value. + * + * @event TK.Value#useraction + * + * @param {string} name - The name of the option which was changed due to the users action + * @param {mixed} value - The new value of the option + */ + +"use strict"; +(function(w, TK){ +function value_clicked(e) { + var O = this.options; + if (O.set === false) return; + if (this.__editing) return false; + TK.add_class(this.element, "toolkit-active"); + this._input.setAttribute("value", O.value); + this.__editing = true; + this._input.focus(); + if (O.auto_select) + this._input.setSelectionRange(0, this._input.value.length) + /** + * Is fired when the value was clicked. + * + * @event TK.Value#valueclicked + * + * @param {number} value - The value of the widget. + */ + this.fire_event("valueclicked", O.value); +} +function value_typing(e) { + var O = this.options; + if (O.set === false) return; + if (!this.__editing) return; + switch (e.keyCode) { + case 27: + // ESC + value_done.call(this); + /** + * Is fired when the ESC key was pressed while editing the value. + * + * @event TK.Value#valueescape + * + * @param {string} value - The new value of the widget. + */ + this.fire_event("valueescape", O.value); + break; + case 13: + // ENTER + this.userset("value", O.set ? O.set(this._input.value) : this._input.value); + value_done.call(this); + /** + * Is fired after the value has been set and editing has ended. + * + * @event TK.Value#valueset + * + * @param {string} value - The new value of the widget. + */ + this.fire_event("valueset", O.value); + + e.preventDefault(); + return false; + break; + } + /** + * Is fired when the user hits a key while editing the value. + * + * @event TK.Value#valuetyping + * + * @param {DOMEvent} event - The native DOM event. + * @param {string} value - The new value of the widget. + */ + this.fire_event("valuetyping", e, O.value); +} +function value_input() { + var O = this.options; + if (O.set === false) return; + if (!this.__editing) return; + if (O.editmode == "immediate") + this.userset("value", O.set ? O.set(this._input.value) : this._input.value); +} +function value_done() { + if (!this.__editing) return; + this.__editing = false; + TK.remove_class(this.element, "toolkit-active"); + this._input.blur(); + /** + * Is fired when editing of the value ends. + * + * @event TK.Value#valuedone + * + * @param {string} value - The new value of the widget. + */ + this.fire_event("valuedone", this.options.value); + this.invalid.value = true; + this.trigger_draw(); +} + +function submit_cb(e) { + e.preventDefault(); + return false; +} +/** + * TK.Value is a formatted text field displaying values and providing + * an input field for editing the value. + * + * @class TK.Value + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.value=0] - The value. + * @property {Function} [options.format=TK.FORMAT("%.2f")] - A formatting + * function used to display the value. + * @property {Integer} [options.size=5] - Size attribute of the INPUT element. + * @property {Integer} [options.maxlength] - Maxlength attribute of the INPUT element. + * @property {Function} [options.set=function (val) { return parseFloat(val || 0); }] - + * A function which is called to parse user input. + * @property {boolean} [options.auto_select=false] - Select the entire text in the entry field if clicked (new in v1.3). + * @property {boolean} [options.readonly=false] - Sets the readonly attribute (new in v1.3). + * @property {string} [options.placeholder=""] - Sets the placeholder attribute (new in v1.3). + * @property {string} [options.type="text"] - Sets the type attribute. Type can be either `text` or `password` (new in v1.3). + * @property {string} [options.editmode="onenter"] - Sets the event to trigger the userset event. Can be one out of `onenter` or `immediate`. + * + */ +TK.Value = TK.class({ + _class: "Value", + Extends: TK.Widget, + _options: Object.assign(Object.create(TK.Widget.prototype._options), { + value: "number|string", + format: "function", + size: "number", + maxlength: "int", + set: "object|function|boolean", + auto_select: "boolean", + readonly: "boolean", + placeholder: "string", + type: "string", + editmode: "string", + }), + options: { + value: 0, + format: TK.FORMAT("%.2f"), + size: 5, + container: false, + // set a callback function if value is editable or + // false to disable editing. A function has to return + // the value treated by the parent widget. + set: function (val) { return parseFloat(val || 0); }, + auto_select: false, + readonly: false, + placeholder: "", + type: "text", + editmode: "onenter", + }, + static_events: { + submit: submit_cb, + click: value_clicked, + }, + initialize: function (options) { + var E; + TK.Widget.prototype.initialize.call(this, options); + /** + * @member {HTMLDivElement} TK.Value#element - The main DIV container. + * Has class toolkit-value. + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-value"); + + this.widgetize(E, true, true, true); + + /** + * @member {HTMLInputElement} TK.Value#_input - The text input. + * Has class toolkit-input. + */ + this._input = TK.element("input", "toolkit-input"); + this._input.type = "text"; + E.appendChild(this._input); + + this._value_typing = value_typing.bind(this); + this._value_done = value_done.bind(this); + this._value_input = value_input.bind(this); + + this._input.addEventListener("keyup", this._value_typing); + this._input.addEventListener("input", this._value_input); + this._input.addEventListener("blur", this._value_done); + this.__editing = false; + }, + + redraw: function () { + var I = this.invalid; + var O = this.options; + var E = this._input; + + TK.Widget.prototype.redraw.call(this); + + if (I.size) { + I.size = 0; + E.setAttribute("size", O.size); + } + + if (I.maxlength) { + I.maxlength = 0; + if (O.maxlength) E.setAttribute("maxlength", O.maxlength); + else E.removeAttribute("maxlength"); + } + + if (I.placeholder) { + I.placeholder = 0; + if (O.placeholder) E.setAttribute("placeholder", O.placeholder); + else E.removeAttribute("placeholder"); + } + + if ((I.value || I.format) && !this.__editing) { + I.format = I.value = false; + E.value = O.format(O.value); + } + + if (I.readonly) { + I.readonly = 0; + if (O.readonly) + E.setAttribute("readonly", "readonly"); + else + E.removeAttribute("readonly"); + } + + if (I.type) { + I.type = 0; + E.setAttribute("type", O.type); + } + }, + + destroy: function () { + this._input.removeEventListener("keyup", this._value_typing); + this._input.removeEventListener("blur", this._value_done); + this._input.removeEventListener("input", this._value_input); + this._input.remove(); + TK.Widget.prototype.destroy.call(this); + }, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/valuebutton.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/valuebutton.js new file mode 100644 index 0000000000..778d29087c --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/valuebutton.js @@ -0,0 +1,218 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +/** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the option value. + * + * @event TK.ValueButton#useraction + * + * @param {string} name - The name of the option which was changed due to the users action + * @param {mixed} value - The new value of the option + */ + +"use strict"; +(function(w, TK){ +TK.ValueButton = TK.class({ + /** + * This widget combines a {@link TK.Button}, a {@link TK.Scale} and a {@link TK.Value}. + * TK.ValueButton uses {@link TK.DragValue} and {@link TK.ScrollValue} + * for setting its value. + * It inherits all options of {@link TK.DragValue} and {@link TK.Scale}. + * + * @class TK.ValueButton + * + * @extends TK.Button + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.value=0] - The value of the widget. + * @property {Number} [options.value_format=function (val) { return val.toFixed(2); }] - Callback to format the value label. + * @property {Number} [options.value_size=5] - Amount of digits of the value input. + * @property {String} [options.direction="polar"] - Direction for changing the value. + * Can be "polar", "vertical" or "horizontal". See {@link TK.DragValue} for more details. + * @property {Number} [options.blind_angle=20] - If `options.direction` is "polar", + * this is the angle of separation between positive and negative value changes. See {@link TK.DragValue} for more details. + * @property {Number} [options.rotation=45] - Defines the angle of the center of the positive value + * changes. 0 means straight upward. For instance, a value of 45 leads to increasing value when + * moving towards top and right. See {@link TK.DragValue} for more details. + * @property {Number} [options.snap=0.01] - Snap value while dragging. + * @property {Number} [options.basis=300] - Distance to drag between min and max in pixels. + */ + _class: "ValueButton", + Extends: TK.Button, + Implements: [TK.Warning, TK.Ranged], + _options: Object.assign(Object.create(TK.Button.prototype._options), TK.Ranged.prototype._options, { + value: "number", + value_format: "function", + value_size: "number", + drag_direction: "string", + rotation: "number", + blind_angle: "number", + snap: "number", + reset: "number", + }), + options: { + layout: "bottom", + value: 0, + value_format: function (val) { return val.toFixed(2); }, + value_size: 5, + drag_direction: "polar", + rotation: 45, + blind_angle: 20, + snap: 0.01, + basis: 300, + labels: TK.FORMAT("%d"), + }, + static_events: { + set_drag_direction: function(value) { + this.drag.set("direction", value); + }, + set_drag_rotation: function(value) { + this.drag.set("rotation", value); + }, + set_blind_angle: function(value) { + this.drag.set("blind_angle", value); + }, + }, + initialize: function (options) { + TK.Button.prototype.initialize.call(this, options); + + /** + * @member {HTMLDivElement} TK.ValueButton#element - The main DIV container. + * Has class toolkit-valuebutton. + */ + TK.add_class(this.element, "toolkit-valuebutton"); + + /** + * @member {TK.DragValue} TK.ValueButton#drag - The {@link TK.DragValue} module. + */ + this.drag = new TK.DragValue(this, { + node: this.element, + direction: this.options.drag_direction, + rotation: this.options.rotation, + blind_angle: this.options.blind_angle, + }); + /** + * @member {TK.ScrollValue} TK.ValueButton#scroll - The {@link ScrollValue} module. + */ + this.scroll = new TK.ScrollValue(this, { + node: this.element, + }); + + if (this.options.reset === void(0)) + this.options.reset = this.options.value; + this.element.addEventListener("dblclick", function () { + this.userset("value", this.options.reset); + /** + * Is fired when the user doubleclicks the valuebutton in order to to reset to initial value. + * The Argument is the new value. + * + * @event TK.ValueButton#doubleclick + * + * @param {number} value - The value of the widget. + */ + this.fire_event("doubleclick", this.options.value); + }.bind(this)); + }, + destroy: function () { + this.drag.destroy(); + this.scroll.destroy(); + this.scale.destroy(); + TK.Button.prototype.destroy.call(this); + }, + // GETTERS & SETTERS + set: function (key, value) { + switch (key) { + case "value": + if (value > this.options.max || value < this.options.min) + this.warning(this.element); + value = this.snap(value); + break; + } + return TK.Button.prototype.set.call(this, key, value); + } +}); +function value_clicked() { + var self = this.parent; + self.scroll.set("active", false); + self.drag.set("active", false); + /** + * Is fired when the user starts editing the value manually + * + * @event TK.ValueButton#valueedit + * + * @param {number} value - The value of the widget. + */ + self.fire_event("valueedit", self.options.value); +} +function value_done() { + var self = this.parent; + self.scroll.set("active", true); + self.drag.set("active", true); + /** + * Is fired when the user finished editing the value manually + * + * @event TK.ValueButton#valueset + * + * @param {number} value - The value of the widget. + */ + self.fire_event("valueset", self.options.value); +} +/** + * @member {TK.Value} TK.ValueButton#value - The value widget for editing the value manually. + */ +TK.ChildWidget(TK.ValueButton, "value", { + create: TK.Value, + show: true, + map_options: { + value: "value", + value_format: "format", + value_size: "size", + }, + userset_delegate: true, + static_events: { + dblclick: function(e) { + e.stopPropagation(); + }, + valueclicked: value_clicked, + valuedone: value_done, + }, +}); + +/** + * @member {TK.Scale} TK.ValueButton#scale - The {@link TK.Scale} showing the value. + */ +TK.ChildWidget(TK.ValueButton, "scale", { + create: TK.Scale, + show: true, + toggle_class: true, + inherit_options: true, + map_options: { + value: "bar", + }, + static_events: { + "set_layout" : function (v) { + if (v == "horizontal") this.scale.set("layout", "bottom"); + if (v == "vertical") this.scale.set("layout", "left"); + }, + }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/valueknob.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/valueknob.js new file mode 100644 index 0000000000..d6321ded67 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/valueknob.js @@ -0,0 +1,144 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + /** + * The useraction event is emitted when a widget gets modified by user interaction. + * The event is emitted for the option value. + * + * @event TK.ValueKnob#useraction + * + * @param {string} name - The name of the option which was changed due to the users action + * @param {mixed} value - The new value of the option + */ + +"use strict"; +(function(w, TK){ +function value_clicked() { + var self = this.parent; + var knob = self.knob; + knob.scroll.set("active", false); + knob.drag.set("active", false); + /** + * Is fired when the user starts editing the value manually. + * + * @event TK.ValueButton#valueedit + * + * @param {number} value - The value of the widget. + */ + self.fire_event("valueedit", this.options.value); +} +function value_done() { + var self = this.parent; + var knob = self.knob; + knob.scroll.set("active", true); + knob.drag.set("active", true); + /** + * Is fired when the user finished editing the value manually. + * + * @event TK.ValueButton#valueset + * + * @param {number} value - The value of the widget. + */ + self.fire_event("valueset", this.options.value); +} +TK.ValueKnob = TK.class({ + /** + * This widget combines a {@link TK.Knob}, a {@link TK.Label} and a {@link TK.Value} whose + * value is synchronized. It inherits all options from {@link TK.Knob} and {@link TK.Value}. + * + * @class TK.ValueKnob + * + * @extends TK.Widget + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String} [options.title=false] - Title of the knob. Set to `false` to hide the element from the DOM. + * @property {Function} [options.value_format=TK.FORMAT("%.2f")] - Callback to format the value. + * @property {Number} [options.value_set=function (val) { return parseFloat(val || 0); }] - A function which is called to parse user input in the {@link TK.Value} + * @property {Number} [options.value_size=5] - Amount of digits for the value input. + * @property {Number} [options.show_value=true] - Set to `false` to hide the {@link TK.Value}. + * @property {Number} [options.show_knob=true] - Set to `false` to hide the {@link TK.Knob}. + */ + _class: "ValueKnob", + Extends: TK.Widget, + _options: Object.create(TK.Widget.prototype._options), + options: { }, + initialize: function (options) { + TK.Widget.prototype.initialize.call(this, options); + var E; + /** + * @member {HTMLDivElement} TK.ValueKnob#element - The main DIV container. + * Has class toolkit-valueknob. + */ + if (!(E = this.element)) this.element = E = TK.element("div"); + TK.add_class(E, "toolkit-valueknob"); + + this.widgetize(E, true, true, true); + }, + get_range: function() { + return this.knob.get_range(); + }, + set: function (key, value) { + /* this gets triggered twice, but we need it in order to make the snapping work */ + if (key === "value" && this.knob) + value = this.knob.set("value", value); + + return TK.Widget.prototype.set.call(this, key, value); + }, +}); +/** + * @member {TK.Label} TK.ValueKnob#label - The {@link TK.Label} widget. + */ +TK.ChildWidget(TK.ValueKnob, "label", { + create: TK.Label, + option: "title", + toggle_class: true, + map_options: { + title: "label", + }, +}); +/** + * @member {TK.Knob} TK.ValueKnob#knob - The {@link TK.Knob} widget. + */ +TK.ChildWidget(TK.ValueKnob, "knob", { + create: TK.Knob, + show: true, + inherit_options: true, + toggle_class: true, + userset_delegate: true, +}); +/** + * @member {TK.Value} TK.ValueKnob#value - The {@link TK.Value} widget. + */ +TK.ChildWidget(TK.ValueKnob, "value", { + create: TK.Value, + show: true, + inherit_options: true, + map_options: { + value_format: "format", + value_set: "set", + value_size: "size", + }, + static_events: { + valueclicked: value_clicked, + valuedone: value_done, + }, + toggle_class: true, + userset_delegate: true, +}); +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/widget.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/widget.js new file mode 100644 index 0000000000..ab598d9e79 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/widget.js @@ -0,0 +1,799 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +"use strict"; + +(function(w, TK){ + +function Invalid(options) { + for (var key in options) this[key] = true; +}; +Invalid.prototype = { + validate : function() { + var i = 0, key; + var ret = false; + for (i = 0; i < arguments.length; i++) { + key = arguments[i]; + if (this.hasOwnProperty(key) && this[key]) { + this[key] = false; + ret = true; + } + } + + return ret; + }, + test : function() { + var i = 0, key; + for (i = 0; i < arguments.length; i++) { + key = arguments[i]; + if (this.hasOwnProperty(key) && this[key]) { + return true; + } + } + } +}; +function redraw(fun) { + if (!this._drawn) return; + this.needs_redraw = false; + /** + * Is fired when a redraw is executed. + * + * @event TK.Widget#redraw + */ + this.fire_event("redraw"); + fun.call(this); +} +function resize() { + if (this.is_destructed()) return; + this.resize(); +} +function dblclick (e) { + /** + * Is fired after a double click appeared. Set `dblclick` to 0 to + * disable click event handling. + * + * @event TK.Widget#doubleclick + * + * @param {string} event - The browsers `MouseEvent`. + * + */ + var O = this.options; + var dbc = O.dblclick; + if (!dbc) return; + var d = + new Date(); + if (this.__lastclick + dbc > d) { + e.lastclick = this.__lastclick; + this.fire_event("doubleclick", e); + this.__lastclick = 0; + } else { + this.__lastclick = d; + } +} + +TK.Widget = TK.class({ + /** + * TK.Widget is the base class for all widgets drawing DOM elements. It + * provides basic functionality like delegating events, setting options and + * firing some events. + * + * @class TK.Widget + * + * @extends TK.Base + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {String} [options.class=""] - A class to add to the class attribute of the main element. + * @property {HTMLElement} [options.container] - A container the main element shall be added to. + * @property {String} [options.id=""] - A string to be set as id attribute on the main element. + * @property {Object} [options.styles=""] - An object containing CSS declarations to be added directly to the main element. + * @property {Boolean} [options.disabled=false] - Toggles the class toolkit-disabled. + * @property {HTMLElement} [options.element] - An element to be used as the main element. + * @property {Boolean} [options.active] - Toggles the class toolkit-inactive. + * @property {Boolean} [options.needs_resize=true] - Set to true if the resize function shall be called before the next redraw. + * @property {Boolean} [options.dblclick=400] - Set a time in milliseconds for triggering double click event. If 0, no double click events are fired. + */ + /** + * The set event is emitted when an option was set using the {@link TK.Widget#set} + * method. The arguments are the option name and its new value. + * + * Note that this happens both for user interaction and programmatical option changes. + * + * @event TK.Widget#set + */ + /** + * The redraw event is emitted when a widget is redrawn. This can be used + * to do additional DOM modifications to a Widget. + * + * @event TK.Widget#redraw + */ + /** + * The resize event is emitted whenever a widget is being resized. This event can + * be used to e.g. measure its new size. Note that some widgets do internal adjustments after + * the resize event. If that is relevant, the {@link TK.Widget#resized} event can + * be used, instead. + * + * @event TK.Widget#resize + */ + /** + * The resized event is emitted after each rendering frame, which was triggered by + * a resize event. + * + * @event TK.Widget#resized + */ + /** + * The hide event is emitted when a widget is hidden and is not rendered anymore. + * This happens both with browser visibility changes and also internally when using layout widgets + * such as {@link TK.Pager}. + * + * @event TK.Widget#hide + */ + /** + * The show event is emitted when a widget is shown and is being rendered. This is the + * counterpart to {@link TK.Widget#hide}. + * + * @event TK.Widget#show + */ + Extends : TK.Base, + _class: "Widget", + _options: { + // A CSS class to add to the main element + class: "string", + // A DOM element as container to inject the element + // into + container: "object", + // a id to set on the element. If omitted a random + // string is generated. + id: "string", + // If an element was stylized, styles can be applied + styles: "object", + disabled: "boolean", + element: "object", + active: "boolean", + needs_resize: "boolean", + dblclick: "number", + }, + options: { + // these options are of less use and only here to show what we need + disabled: false, // Widgets can be disabled by setting this to true + needs_resize: true, + dblclick: 0, + }, + static_events: { + set_container: function(value) { + if (value && this.element) { + value.appendChild(this.element); + } else if (!value && this.element.parentElement) { + this.element.parentElement.removeChild(this.element); + } + }, + set_dblclick: function (val) { + if (!this.__delegated) return; + if (!!val) + this.__delegated.addEventListener("click", this.__dblclick_cb); + else + this.__delegated.removeEventListener("click", this.__dblclick_cb); + }, + initialized: function () { + var v = this.options.dblclick; + if (v > 0) + this.set("dblclick", v); + }, + }, + initialize: function (options) { + // Main actions every widget needs to take + if (!options) options = {}; + /** @property {HTMLElement} TK.Widget#element - The main element. */ + if (options.element) + this.element = options.element; + TK.Base.prototype.initialize.call(this, options); + this.__classified = null; + this.__stylized = null; + this.__delegated = null; + this.__widgetized = null; + this.invalid = new Invalid(this.options); + if (!this.value_time) this.value_time = null; + this.needs_redraw = false; + this._redraw = redraw.bind(this, this.redraw); + this.__resize = resize.bind(this); + this._schedule_resize = this.schedule_resize.bind(this); + this._drawn = false; + this.parent = null; + this.children = null; + this.draw_queue = null; + this.__lastclick = 0; + this.__dblclick_cb = dblclick.bind(this); + }, + + is_destructed: function() { + return this.options === null; + }, + + invalidate_all: function() { + for (var key in this.options) { + if (!this._options[key]) { + if (key.charCodeAt(0) !== 95) + TK.warn("%O %s: unknown option %s", this, this._class, key); + } else this.invalid[key] = true; + } + }, + + assert_none_invalid: function() { + var warn = []; + for (var key in this.invalid) { + if (this.invalid[key] === true) { + warn.push(key); + } + } + + if (warn.length) { + TK.warn("found", warn.length, "invalid in", this, ":", warn); + } + }, + + trigger_resize: function() { + if (!this.options.needs_resize) { + if (this.is_destructed()) { + // This object was destroyed but trigger resize was still scheduled for the next frame. + // FIXME: fix this whole problem properly + return; + } + + this.set("needs_resize", true); + + var C = this.children; + + if (!C) return; + + for (var i = 0; i < C.length; i++) { + C[i].trigger_resize(); + } + } + }, + + trigger_resize_children: function() { + var C = this.children; + + if (!C) return; + + for (var i = 0; i < C.length; i++) { + C[i].trigger_resize(); + } + }, + + schedule_resize: function() { + TK.S.add(this.__resize, 0); + }, + + resize: function() { + /** + * Is fired when a resize is requested. + * + * @event TK.Widget#resize + */ + this.fire_event("resize"); + + if (this._options.resized) + this.set("resized", true); + + /** + * Is fired after the resize was executed and the DOM is updated. + * + * @event TK.Widget#resized + */ + if (this.has_event_listeners("resized")) { + TK.S.after_frame(this.fire_event.bind(this, "resized")); + } + }, + + trigger_draw: function() { + if (!this.needs_redraw) { + this.needs_redraw = true; + if (this._drawn) TK.S.add(this._redraw, 1); + } + }, + + trigger_draw_next : function() { + if (!this.needs_redraw) { + this.needs_redraw = true; + if (this._drawn) TK.S.add_next(this._redraw, 1); + } + }, + + initialized: function () { + // Main actions every widget needs to take + /** + * Is fired when a widget is initialized. + * + * @event TK.Widget#initialized + */ + this.fire_event("initialized"); + this.trigger_draw(); + }, + draw_once: function(fun) { + var q = this.draw_queue; + + if (q === null) { + this.draw_queue = [ fun ]; + } else { + for (var i = 0; i < q.length; i++) if (q[i] === fun) return; + q[i] = fun; + } + this.trigger_draw(); + }, + redraw: function () { + var I = this.invalid; + var O = this.options; + var E = this.element; + + if (E) { + if (I.id) { + I.id = false; + if (O.id) E.setAttribute("id", O.id); + } + } + + E = this.__stylized; + + if (E) { + if (I.active) { + I.active = false; + TK.toggle_class(E, "toolkit-inactive", !O.active); + } + + if (I.disabled) { + I.disabled = false; + TK.toggle_class(E, "toolkit-disabled", O.disabled); + } + + if (I.styles) { + I.styles = false; + if (O.styles) TK.set_styles(E, O.styles); + } + } + + if (I.needs_resize) { + I.needs_resize = false; + + if (O.needs_resize) { + O.needs_resize = false; + + TK.S.after_frame(this._schedule_resize); + } + } + + var q = this.draw_queue; + + this.draw_queue = null; + + if (q) for (var i = 0; i < q.length; i++) { + q[i].call(this, O); + } + }, + destroy: function () { + /** + * Is fired when a widget is destroyed. + * + * @event TK.Widget#destroy + */ + if (this.is_destructed()) { + TK.warn("destroy called twice on ", this); + return; + } + this.fire_event("destroy"); + + this.disable_draw(); + if (this.parent) this.parent.remove_child(this); + + TK.Base.prototype.destroy.call(this); + + this._redraw = null; + this.__resize = null; + this._schedule_resize = null; + this.children = null; + this.options = null; + this.parent = null; + + if (this.element) { + this.element.remove(); + this.element = null; + } + }, + delegate: function (element) { + this.delegate_events(element); + this.__delegated = element; + /** + * Is fired when a widget gets delegated. + * + * @event TK.Widget#initialized + * + * @param {HTMLElement} element - The element all native DOM events are delegated to. + */ + this.fire_event("delegated", element); + return element; + }, + add_class: function (cls) { + TK.add_class(this.__classified, cls); + }, + remove_class: function (cls) { + TK.remove_class(this.__classified, cls); + }, + has_class: function (cls) { + return TK.has_class(this.__classified, cls); + }, + classify: function (element) { + // Takes a DOM element and adds its CSS functionality to the + // widget instance + this.__classified = element; + if (this.options.class && element) + TK.add_class(element, this.options.class); + /** + * Is fired when a widget is classified. + * + * @event TK.Widget#classified + * + * @param {HTMLElement} element - The element which receives all further class changes. + */ + this.fire_event("classified", element); + return element; + }, + set_style: function (name, value) { + TK.set_style(this.__stylized, name, value); + }, + /** + * Sets a CSS style property in this widgets DOM element. + * + * @method TK.Widget#set_style + */ + set_styles: function (styles) { + TK.set_styles(this.__stylized, styles); + }, + /** + * Returns the computed style of this widgets DOM element. + * + * @method TK.Widget#get_style + */ + get_style: function (name) { + return TK.get_style(this.__stylized, name); + }, + stylize: function (element) { + // Marks a DOM element as receiver for the "styles" options + this.__stylized = element; + if (this.options.styles) { + TK.set_styles(element, this.options.styles); + } + /** + * Is fired when a widget is stylized. + * + * @event TK.Widget#stylized + * + * @param {HTMLElement} element - The element which receives all further style changes. + */ + this.fire_event("stylized", element); + return element; + }, + widgetize: function (element, delegate, classify, stylize) { + /** + * Set the DOM elements of this widgets. This method is usually only used internally. + * Basically it means to add the id from options and set a basic CSS class. + * If delegate is true, basic events will be delegated from the element to the widget instance + * if classify is true, CSS functions will be bound to the widget instance. + * + * @method TK.Widget#widgetize + * @emits TK.Widget#widgetize + */ + var O = this.options; + + // classify? + TK.add_class(element, "toolkit-widget"); + if (typeof O.id !== "string") { + O.id = element.getAttribute("id"); + if (!O.id) { + O.id = TK.unique_id() + element.setAttribute("id", O.id); + } + } else element.setAttribute("id", O.id); + + if (O.class) { + var c = O.class.split(" "); + for (var i = 0; i < c.length; i++) + TK.add_class(element, c[i]); + } + if (O.container) + O.container.appendChild(element); + if (delegate) + this.delegate(element); + if (classify) + this.classify(element); + if (stylize) + this.stylize(element); + this.__widgetized = element; + /** + * Is fired when a widget is widgetized. + * + * @event TK.Widget#widgetize + * + * @param {HTMLElement} element - The element which got widgetized. + */ + this.fire_event("widgetized", element); + return element; + }, + + // GETTER & SETTER + /** + * Sets an option. + * + * @method TK.Widget#set + * + * @param {string} key - The option name. + * @param value - The option value. + */ + set: function (key, value) { + /* These options are special and need to be handled immediately, in order + * to preserve correct ordering */ + if (key === "class" && this.__classified) { + if (this.options.class) TK.remove_class(this.__classified, this.options.class); + if (value) TK.add_class(this.__classified, value); + } + if (this._options[key]) { + this.invalid[key] = true; + if (this.value_time && this.value_time[key]) + this.value_time[key] = Date.now(); + this.trigger_draw(); + } else if (key.charCodeAt(0) !== 95) { + TK.warn("%O: %s.set(%s, %O): unknown option.", this, this._class, key, value); + } + TK.Base.prototype.set.call(this, key, value); + return value; + }, + track_option: function(key) { + if (!this.value_time) this.value_time = {}; + this.value_time[key] = Date.now(); + }, + /** + * Schedules this widget for drawing. + * + * @method TK.Widget#enable_draw + * + * @emits TK.Widget#show + */ + enable_draw: function () { + if (this._drawn) return; + this._drawn = true; + if (this.needs_redraw) { + TK.S.add(this._redraw, 1); + } + /** + * Is fired when a widget gets enabled for drawing. + * + * @event TK.Widget#show + */ + this.fire_event("show"); + this.fire_event("visibility", true); + var C = this.children; + if (C) for (var i = 0; i < C.length; i++) C[i].enable_draw(); + }, + /** + * Stop drawing this widget. + * + * @method TK.Widget#enable_draw + * + * @emits TK.Widget#hide + */ + disable_draw: function () { + if (!this._drawn) return; + this._drawn = false; + if (this.needs_redraw) { + TK.S.remove(this._redraw, 1); + TK.S.remove_next(this._redraw, 1); + } + /** + * Is fired when a widget is hidden and not rendered anymore. + * + * @event TK.Widget#hide + */ + /** + * Is fired when the visibility state changes. The first argument + * is the visibility state, which is either true + * or false. + * + * @event TK.Widget#visibility + */ + this.fire_event("hide"); + this.fire_event("visibility", false); + var C = this.children; + if (C) for (var i = 0; i < C.length; i++) C[i].disable_draw(); + }, + /** + * Make the widget visible. This does not modify the DOM, instead it will only schedule + * the widget for rendering. + * + * @method TK.Widget#show + */ + show: function () { + this.enable_draw(); + }, + /** + * This is an alias for hide, which may be overloaded. + * See {@link TK.Container} for an example. + * + * @method TK.Widget#force_show + */ + force_show: function() { + this.enable_draw(); + }, + /** + * Make the widget hidden. This does not modify the DOM, instead it will stop rendering + * this widget. Options changed after calling hide will only be rendered (i.e. applied + * to the DOM) when the widget is made visible again using {@link TK.Widget#show}. + * + * @method TK.Widget#hide + */ + hide: function () { + this.disable_draw(); + }, + /** + * This is an alias for hide, which may be overloaded. + * See {@link TK.Container} for an example. + * + * @method TK.Widget#force_hide + */ + force_hide: function () { + this.disable_draw(); + }, + show_nodraw: function() { }, + hide_nodraw: function() { }, + /** + * Returns the current hidden status. + * + * @method TK.Widget#hidden + */ + hidden: function() { + return !this._drawn; + }, + is_drawn: function() { + return this._drawn; + }, + /** + * TK.Toggle the hidden status. This is equivalent to calling hide() or show(), depending on + * the current hidden status of this widget. + * + * @method TK.Widget#toggle_hidden + */ + toggle_hidden: function() { + if (this.hidden()) this.show(); + else this.hide(); + }, + set_parent: function(parent) { + if (this.parent) { + this.parent.remove_child(this); + } + this.parent = parent; + }, + /** + * Registers a widget as a child widget. This method is used to build up the widget tree. It does not modify the DOM tree. + * + * @method TK.Widget#add_child + * + * @param {TK.Widget} child - The child to add. + * + * @see TK.Container#append_child + */ + add_child: function(child) { + var C = this.children; + if (!C) this.children = C = []; + child.set_parent(this); + C.push(child); + if (!this.hidden()) { + child.enable_draw(); + } else { + child.disable_draw(); + } + child.trigger_resize(); + }, + /** + * Removes a child widget. Note that this method only modifies + * the widget tree and does not change the DOM. + * + * @method TK.Widget#remove_child + * + * @param {TK.Widget} child - The child to remove. + */ + remove_child : function(child) { + child.disable_draw(); + child.parent = null; + var C = this.children; + if (C === null) return; + var i = C.indexOf(child); + if (i !== -1) { + C.splice(i, 1); + } + if (!C.length) this.children = null; + }, + /** + * Removes an array of children. + * + * @method TK.Widget#remove_children + * + * @param {Array.} a - An array of Widgets. + */ + remove_children : function(a) { + a.map(this.remove_child, this); + }, + /** + * Registers an array of widgets as children. + * + * @method TK.Widget#add_children + * + * @param {Array.} a - An array of Widgets. + */ + add_children : function (a) { + a.map(this.add_child, this); + }, + + /** + * Returns an array of all visible children. + * + * @method TK.Widget#visible_children + */ + visible_children: function(a) { + if (!a) a = []; + var C = this.children; + if (C) for (var i = 0; i < C.length; i++) { + a.push(C[i]); + C[i].visible_children(a); + } + return a; + }, + + /** + * Returns an array of all children. + * + * @method TK.Widget#all_children + */ + all_children: function(a) { + if (!a) a = []; + var C = this.children; + if (C) for (var i = 0; i < C.length; i++) { + a.push(C[i]); + C[i].all_children(a); + } + return a; + }, +}); + +TK.Module = TK.class({ + Extends: TK.Base, + initialize: function(widget, options) { + this.parent = widget; + TK.Base.prototype.initialize.call(this, options); + }, + destroy: function() { + this.parent = null; + TK.Base.prototype.destroy.call(this); + }, +}); +})(this, this.TK); + +/** + * Generic DOM events. Please refer to + * + * W3Schools + * for further details. + * + * @event TK.Widget##GenericDOMEvents + */ diff --git a/share/web_surfaces/builtin/mixer/toolkit/widgets/window.js b/share/web_surfaces/builtin/mixer/toolkit/widgets/window.js new file mode 100644 index 0000000000..65895e7662 --- /dev/null +++ b/share/web_surfaces/builtin/mixer/toolkit/widgets/window.js @@ -0,0 +1,819 @@ +/* + * This file is part of Toolkit. + * + * Toolkit is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Toolkit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General + * Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +"use strict"; +(function(w, TK){ +function header_action() { + var that = this.parent; + switch (that.options.header_action) { + case "shrink": that.toggle_shrink(); break; + case "maximize": that.toggle_maximize(); break; + case "maximizehorizontal": that.toggle_maximize_horizontal(); break; + case "maximizevertical": that.toggle_maximize_vertical(); break; + case "minimize": that.toggle_minimize(); break; + case "close": that.destroy(); break; + } + /** + * The user double-clicked on the header. + * @event TK.Window.headeraction + * @param {string} action - The function which was executed, e.g. shrink, maximize or close. + */ + that.fire_event("headeraction", that.options.header_action); +} +function mout(e) { + if(this.options.auto_active && !this.dragging && !this.resizing) + TK.remove_class(this.element, "toolkit-active"); +} +function mover(e) { + if(this.options.auto_active) + TK.add_class(this.element, "toolkit-active"); +} +function max_height() { + // returns the max height of the window + return (this.options.max_height < 0 ? Number.MAX_SAFE_INTEGER : this.options.max_height); +} +function max_width() { + // returns the max width of the window + return (this.options.max_width < 0 ? Number.MAX_SAFE_INTEGER : this.options.max_width); +} +function close(e) { + /** + * The user clicked the close button. + * @event TK.Window.closeclicked + */ + this.fire_event("closeclicked"); + if (this.options.auto_close) + this.destroy(); +} +function maximize(e) { + if (this.options.auto_maximize) this.toggle_maximize(); + /** + * The user clicked the maximize button. + * @event TK.Window.maximizeclicked + * @param {Object} maximize - The maximize option. + */ + this.fire_event("maximizeclicked", this.options.maximize); +} +function maximizevertical(e) { + if (this.options.auto_maximize) this.toggle_maximize_vertical(); + /** + * The user clicked the maximize-vertical button. + * @event TK.Window.maximizeverticalclicked + * @param {Object} maximize - The maximize option. + */ + this.fire_event("maximizeverticalclicked", this.options.maximize.y); +} +function maximizehorizontal(e) { + if (this.options.auto_maximize) this.toggle_maximize_horizontal(); + /** + * The user clicked the maximize-horizontal button. + * @event TK.Window.maximizehorizontalclicked + * @param {Object} maximize - The maximize option. + */ + this.fire_event("maximizehorizontalclicked", this.options.maximize.x); +} +function minimize(e) { + if (this.options.auto_minimize) this.toggle_minimize(); + /** + * The user clicked the minimize button. + * @event TK.Window.minimizeclicked + * @param {Object} minimize - The minimize option. + */ + this.fire_event("minimizeclicked", this.options.minimize); +} +function shrink(e) { + if (this.options.auto_shrink) this.toggle_shrink(); + /** + * The user clicked the shrink button. + * @event TK.Window.shrinkclicked + * @param {Object} shrink - The shrink option. + */ + this.fire_event("shrinkclicked", this.options.shrink); +} +function start_resize(el, ev) { + this.global_cursor("se-resize"); + this.resizing = true; + TK.add_class(this.element, "toolkit-resizing"); + /** + * The user starts resizing the window. + * @event TK.Window.startresize + * @param {DOMEvent} event - The DOM event. + */ + this.fire_event("startresize", ev); +} +function stop_resize(el, ev) { + this.remove_cursor("se-resize"); + this.resizing = false; + TK.remove_class(this.element, "toolkit-resizing"); + this.trigger_resize_children(); + calculate_dimensions.call(this); + /** + * The user stops resizing the window. + * @event TK.Window.stopresize + * @param {DOMEvent} event - The DOM event. + */ + this.fire_event("stopresize", ev); +} +function resizing(el, ev) { + if (this.options.resizing === "continuous") { + this.trigger_resize_children(); + calculate_dimensions.call(this); + } + /** + * The user resizes the window. + * @event TK.Window.resizing + * @param {DOMEvent} event - The DOM event. + */ + this.fire_event("resizing", ev); +} +function calculate_dimensions() { + var x = TK.outer_width(this.element, true); + var y = TK.outer_height(this.element, true); + this.dimensions.width = this.options.width = x; + this.dimensions.height = this.options.height = y; + this.dimensions.x2 = x + this.dimensions.x1; + this.dimensions.y2 = y + this.dimensions.y1; +} +function calculate_position() { + var posx = TK.position_left(this.element); + var posy = TK.position_top(this.element); + var pos1 = this.translate_anchor(this.options.anchor, posx, posy, + this.options.width, this.options.height); + this.dimensions.x = this.options.x = pos1.x; + this.dimensions.y = this.options.y = pos1.y; + this.dimensions.x1 = posx; + this.dimensions.y1 = posy; + this.dimensions.x2 = posx + this.dimensions.width; + this.dimensions.y2 = posy + this.dimensions.height; +} +function horiz_max() { + // returns true if maximized horizontally + return this.options.maximize.x; +} +function vert_max() { + // returns if maximized vertically + return this.options.maximize.y; +} +function start_drag(ev, el) { + this.global_cursor("move"); + TK.add_class(this.element, "toolkit-dragging"); + // if window is maximized, we have to replace the window according + // to the position of the mouse + var x = y = 0; + if (vert_max.call(this)) { + var y = (!this.options.fixed ? window.scrollY : 0); + } + if (horiz_max.call(this)) { + var x = ev.clientX - (ev.clientX / TK.width()) + * this.options.width; + x += (!this.options.fixed ? window.scrollX : 0); + } + var pos = this.translate_anchor( + this.options.anchor, x, y, this.options.width, this.options.height); + + if (horiz_max.call(this)) this.options.x = pos.x; + if (vert_max.call(this)) this.options.y = pos.y; + + this.Drag._xpos += x; + this.Drag._ypos += y; + + /** + * The user starts dragging the window. + * @event TK.Window.startdrag + * @param {DOMEvent} event - The DOM event. + */ + this.fire_event("startdrag", ev); +} +function stop_drag(ev, el) { + this.dragging = false; + calculate_position.call(this); + this.remove_cursor("move"); + /** + * The user stops dragging the window. + * @event TK.Window.stopdrag + * @param {DOMEvent} event - The DOM event. + */ + this.fire_event("stopdrag", ev); +} +function dragging(ev, el) { + if (!this.dragging) { + this.dragging = true; + // un-maximize + if (horiz_max.call(this)) { + this.set("maximize", {x: false}); + } + if (vert_max.call(this)) { + this.set("maximize", {y: false}); + } + } + calculate_position.call(this); + /** + * The user is dragging the window. + * @event TK.Window.dragging + * @param {DOMEvent} event - The DOM event. + */ + this.fire_event("dragging", ev); +} +function init_position(pos) { + var O = this.options; + if (pos) { + var x0 = O.fixed ? 0 : window.scrollX; + var y0 = O.fixed ? 0 : window.scrollY; + var pos1 = this.translate_anchor( + O.open, x0, y0, + window.innerWidth - O.width, + window.innerHeight - O.height); + var pos2 = this.translate_anchor( O.anchor, pos1.x, pos1.y, O.width, O.height); + O.x = pos2.x; + O.y = pos2.y; + } + set_dimensions.call(this); + set_position.call(this); +} +function set_position() { + var O = this.options; + var D = this.dimensions; + var width = TK.inner_width(this.element); + var height = TK.inner_height(this.element); + var pos = this.translate_anchor(O.anchor, O.x, O.y, -width, -height); + if (horiz_max.call(this)) { + this.element.style.left = (O.fixed ? 0 : window.scrollX) + "px"; + } else { + this.element.style.left = pos.x + "px"; + } + if (vert_max.call(this)) { + this.element.style.top = (O.fixed ? 0 : window.scrollY) + "px"; + } else { + this.element.style.top = pos.y + "px"; + } + D.x = O.x; + D.y = O.y; + D.x1 = pos.x; + D.y1 = pos.y; + D.x2 = pos.x + D.width; + D.y2 = pos.y + D.height; + /** + * The position of the window changed. + * @event TK.Window.positionchanged + * @param {Object} event - The {@link TK.Window#dimensions} dimensions object. + */ + this.fire_event("positionchanged", D); +} +function set_dimensions() { + var O = this.options; + var D = this.dimensions; + if (O.width >= 0) { + O.width = Math.min(max_width.call(this), Math.max(O.width, O.min_width)); + if (horiz_max.call(this)) { + TK.outer_width(this.element, true, TK.width()); + D.width = TK.width(); + } else { + TK.outer_width(this.element, true, O.width); + D.width = O.width; + } + } else { + D.width = TK.outer_width(this.element); + } + if (O.height >= 0) { + O.height = Math.min(max_height.call(this), Math.max(O.height, O.min_height)); + if (vert_max.call(this)) { + TK.outer_height(this.element, true, TK.height()); + D.height = TK.height(); + } else { + TK.outer_height(this.element, true, O.height); + D.height = O.height; + } + } else { + D.height = TK.outer_height(this.element, true); + } + D.x2 = D.x1 + D.width; + D.y2 = D.y1 + D.height; + /** + * The dimensions of the window changed. + * @event TK.Window.dimensionschanged + * @param {Object} event - The {@link TK.Window#dimensions} dimensions object. + */ + this.fire_event("dimensionschanged", this.dimensions); +} +function build_header() { + build_from_const.call(this, "header"); + if (!this.Drag) { + this.Drag = new TK.Drag({ + node : this.element, + handle : this.header.element, + onStartdrag : start_drag.bind(this), + onStopdrag : stop_drag.bind(this), + onDragging : dragging.bind(this), + min : {x: 0 - this.options.width + 20, y: 0}, + max : {x: TK.width() - 20, y: TK.height() - 20}, + }); + //this.header.add_event("dblclick", header_action.bind(this)); + } + /** + * The header changed. + * @event TK.Window.headerchanged + */ + this.fire_event("headerchanged"); +} +function build_footer() { + build_from_const.call(this, "footer"); + /** + * The footer changed. + * @event TK.Window.footerchanged + */ + this.fire_event("footerchanged"); +} +function build_from_const(element) { + var E = this[element].element; + var L = this.options[element]; + var O = this.options; + var targ; + while (E.firstChild) E.firstChild.remove(); + if (!L) return; + for (var i = 0; i < L.length; i++) { + if (L[i] !== "spacer") { + this.set("show_" + L[i], true); + E.appendChild(this[L[i]].element); + if (L[i] == "size" && !this.Resize && this.size) { + this.Resize = new TK.Resize({ + node : this.element, + handle : this.size.element, + min : {x: O.min_width, y: O.min_height}, + max : {x: max_width.call(this), y: max_height.call(this)}, + onResizestart : start_resize.bind(this), + onResizestop : stop_resize.bind(this), + onResizing : resizing.bind(this), + active : O.resizable + }); + } + } else { + E.appendChild(TK.element("div", "toolkit-spacer")); + } + } +} + +function status_timeout () { + var O = this.options; + if (this.__status_to !== false) + window.clearTimeout(this.__status_to); + if (!O.hide_status) return; + if (O.status) + this.__status_to = window.setTimeout(function () { + this.set("status", ""); + this.__status_to = false; + }.bind(this), O.hide_status); +} + +TK.Window = TK.class({ + /** + * This widget is a flexible overlay window. + * + * @class TK.Window + * + * @extends TK.Container + * @implments TK.Anchor TK.GlobalCursor + * + * @param {Object} [options={ }] - An object containing initial options. + * + * @property {Number} [options.width=500] - Initial width, can be a CSS length or an integer (pixels). + * @property {Number} [options.height=200] - Initial height, can be a CSS length or an integer (pixels). + * @property {Number} [options.x=0] - X position of the window. + * @property {Number} [options.y=0] - Y position of the window. + * @property {Number} [options.min_width=64] - Minimum width of the window. + * @property {Number} [options.max_width=-1] - Maximum width of the window, -1 ~ infinite. + * @property {Number} [options.min_height=64] - Minimum height of the window. + * @property {Number} [options.max_height=-1] - Maximum height of the window, -1 ~ infinite. + * @property {String} [options.anchor="top-left"] - Anchor of the window, can be one out of + * `top-left`, `top`, `top-right`, `left`, `center`, `right`, `bottom-left`, `bottom`, `bottom-right` + * @property {Boolean} [options.modal=false] - If modal window blocks all other elements + * @property {String} [options.dock=false] - Docking of the window, can be one out of + * `top-left`, `top`, `top-right`, `left`, `center`, `right`, `bottom-left`, `bottom`, `bottom-right` + * @property {Object|Boolean} [options.maximize=false] - Boolean or object with members x and y as boolean to determine the maximized state. + * @property {Boolean} [options.minimize=false] - Minimize window (does only make sense with a + * window manager application to keep track of it) + * @property {Boolean} [options.shrink=false] - Shrink rolls the window up into the title bar. + * @property {String|HTMLElement|TK.Container} [options.content=""] - The content of the window. + * Can be either a string, a HTMLElement or a {@link TK.Container} to be appended to the content area. + * @property {String} [options.open="center"] - initial position of the window, can be one out of + * `top-left`, `top`, `top-right`, `left`, `center`, `right`, `bottom-left`, `bottom`, `bottom-right` + * @property {Integer} [options.z_index=10000] - Z index for piling windows. does make more sense + * when used together with a window manager + * @property {String|Array} [options.header=["title", "maximize", "close"]] - Single element or array of + * `title`, `icon`, `close`, `minimize`, `shrink`, `maximize`, `maximizevertical`, `maximizehorizontal`, `status`, `resize`, `spacer`. + * @property {String|Array} [options.footer=false] - Single element or array of + * `title`, `icon`, `close`, `minimize`, `shrink`, `maximize`, `maximizevertical`, `maximizehorizontal`, `status`, `resize`, `spacer`. + * @property {String} [options.title=false] - Window title. + * @property {String} [options.status=false] Window status. + * @property {String} [options.icon=false] URL to window icon. + * @property {Boolean} [options.fixed=true] - Whether the window sticks to the viewport rather than the document + * @property {Boolean} [options.auto_active=false] - Auto-toggle the active-class when mouseovered + * @property {Boolean} [options.auto_close=true] - Set whether close destroys the window or not + * @property {Boolean} [options.auto_maximize=true] - Set whether maximize toggles the window or not + * @property {Boolean} [options.auto_minimize=true] - Set whether minimize toggles the window or not + * @property {Boolean} [options.auto_shrink=true] - Set whether shrink toggles the window or not + * @property {Boolean} [options.draggable=true] - Set whether the window is draggable + * @property {Boolean} [options.resizable=true] - Set whether the window is resizable + * @property {String} [options.resizing="continuous"] - Resizing policy, `continuous` or `stop`. + * The first one resizes all children continuously while resizing. + * @property {String} [options.header_action="maximize"] - Action for double clicking the window header, one out of + * `close`, `minimize`, `shrink`, `maximize`, `maximizevertical`, `maximizehorizontal` + * @property {Boolean} [options.active=true] - Active state of the window. + * @property {Integer} [options.hide_status=0] - If set to !0 status message hides after [n] milliseconds. + */ + + /** + * @member {TK.Drag} TK.Window#Drag - The {TK.Drag} module. + */ + /** + * @member {TK.Resize} TK.Window#Resize - The {TK.Resize} module. + */ + _class: "Window", + Extends: TK.Container, + Implements: [TK.Anchor, TK.GlobalCursor], + _options: Object.assign(Object.create(TK.Container.prototype._options), { + width: "number", + height: "number", + x: "number", + y: "number", + min_width: "number", + max_width: "number", + min_height: "number", + max_height: "number", + anchor: "string", + modal: "boolean", + dock: "boolean", + maximize: "boolean", + minimize: "boolean", + shrink: "boolean", + open: "int", + z_index: "int", + header: "array", + footer: "array", + title: "string", + status: "string", + icon: "string", + fixed: "boolean", + auto_active: "boolean", + auto_close: "boolean", + auto_maximize: "boolean", + auto_minimize: "boolean", + auto_shrink: "boolean", + draggable: "boolean", + resizable: "boolean", + resizing: "int", + header_action: "int", + active: "boolean", + hide_status: "int", + }), + options: { + width: 500, + height: 200, + x: 0, + y: 0, + min_width: 64, + max_width: -1, + min_height: 64, + max_height: -1, + anchor: "top-left", + modal: false, + dock: false, + maximize: false, + minimize: false, + shrink: false, + content: "", + open: "center", + z_index: 10000, + header: ["title", "maximize", "close"], + footer: false, + title: false, + status: false, + icon: false, + fixed: true, + auto_active: false, + auto_close: true, + auto_maximize: true, + auto_minimize: true, + auto_shrink: true, + draggable: true, + resizable: true, + resizing: "continuous", + header_action: "maximize", + active: true, + hide_status: 0, + }, + static_events: { + mouseenter: mover, + mouseleave: mout, + }, + initialize: function (options) { + this.dimensions = {anchor: "top-left", x: 0, x1: 0, x2: 0, y: 0, y1: 0, y2: 0, width: 0, height: 0}; + TK.Container.prototype.initialize.call(this, options); + var O = this.options; + TK.add_class(this.element, "toolkit-window"); + this.__status_to = false; + init_position.call(this, this.options.open); + this.set("maximize", this.options.maximize); + this.set("minimize", this.options.minimize); + }, + + /** + * Appends a new child to the window content area. + * @method TK.Window#append_child + * @param {TK.Widget} child - The child widget to add to the windows content area. + */ + append_child : function(child) { + child.set("container", this.content.element); + this.add_child(child); + }, + + /** + * Toggles the overall maximize state of the window. + * @method TK.Window#toggle_maximize + * @param {Boolean} maximize - State of maximization. If window is already + * maximized in one or both directions it is un-maximized, otherwise maximized. + */ + toggle_maximize: function () { + if (!vert_max.call(this) || !horiz_max.call(this)) + this.set("maximize", {x: true, y: true}); + else + this.set("maximize", {x: false, y: false}); + }, + /** + * Toggles the vertical maximize state of the window. + * @method TK.Window#toggle_maximize_vertical + * @param {Boolean} maximize - The new vertical maximization. + */ + toggle_maximize_vertical: function () { + this.set("maximize", {y: !this.options.maximize.y}); + }, + /** + * Toggles the horizontal maximize state of the window. + * @method TK.Window#toggle_maximize_horizontal + * @param {Boolean} maximize - The new horizontal maximization. + */ + toggle_maximize_horizontal: function () { + this.set("maximize", {x: !this.options.maximize.x}); + }, + /** + * Toggles the minimize state of the window. + * @method TK.Window#toggle_minimize + * @param {Boolean} minimize - The new minimization. + */ + toggle_minimize: function () { + this.set("minimize", !this.options.minimize); + }, + /** + * Toggles the shrink state of the window. + * @method TK.Window#toggle_shrink + * @param {Boolean} shrink - The new shrink state. + */ + toggle_shrink: function () { + this.set("shrink", !this.options.shrink); + }, + + resize: function () { + this.Drag.set("min", {x: 0 - this.options.width + 20, y: 0}); + this.Drag.set("max", {x: TK.width() - 20, y: TK.height() - 20}); + TK.Container.prototype.resize.call(this); + }, + + redraw: function () { + var I = this.invalid; + var O = this.options; + var E = this.element; + + var setP = false; + var setD = false; + + if (I.maximize) { + I.maximize = false; + if (O.shrink) { + O.shrink = false; + I.shrink = true; + } + TK.toggle_class(this.element, "toolkit-maximized-horizontal", O.maximize.x); + TK.toggle_class(this.element, "toolkit-maximized-vertical", O.maximize.y); + setD = true; + } + if (I.anchor) { + I.anchor = false; + this.dimensions.anchor = O.anchor; + setP = setD = true; + } + if (I.width || I.height) { + I.width = I.height = false; + setD = true; + } + if (I.x || I.y) { + I.x = I.y = false; + setP = true; + } + if (I.z_index) { + I.z_index = false; + this.element.style.zIndex = O.z_index; + } + if (I.header) { + I.header = false; + this.set("show_header", !!O.header); + if (O.header) build_header.call(this); + } + if (I.footer) { + I.footer = false; + this.set("show_footer", !!O.footer); + if (O.footer) build_footer.call(this); + } + if (I.status) { + I.status = false; + status_timeout.call(this); + } + if (I.fixed) { + this.element.style.position = O.fixed ? "fixed" : "absolute"; + setP = true; + } + if (I.active) { + I.active = false; + TK.toggle_class(this.element, "toolkit-active", O.active); + } + if (I.shrink) { + I.shrink = false; + this.options.maximize.y = false; + TK.toggle_class(this.element, "toolkit-shrinked", O.shrink); + } + if (I.draggable) { + I.draggable = false; + TK.toggle_class(this.element, "toolkit-draggable", O.draggable); + } + if (I.resizable) { + I.resizable = false; + TK.toggle_class(this.element, "toolkit-resizable", O.resizable); + } + if (I.content) { + I.content = false; + if (O.content) { + if (TK.Container.prototype.isPrototypeOf(O.content)) { + TK.set_content(this.content.element, ""); + this.append_child(O.content); + } else { + TK.set_content(this.content.element, O.content); + } + } + setD = true; + setP = true; + } + + if (setD) set_dimensions.call(this); + if (setP) set_position.call(this); + TK.Container.prototype.redraw.call(this); + }, + + set: function (key, value) { + var O = this.options; + var E = this.element; + + if (key == "maximize") { + if (value === false) value = this.options.maximize = {x: false, y: false}; + else if (value === true) value = this.options.maximize = {x: true, y: true}; + else value = Object.assign(this.options.maximize, value); + } + O[key] = value; + + switch (key) { + case "shrink": + O.maximize.y = false; + break; + case "minimize": + if (value) { + if (!this.options.container && E.parentElement) + O.container = E.parentElement; + E.remove(); + } else if (O.container) { + this.set("container", O.container) + } + break; + case "resizable": + this.Resize.set("active", value); + break; + + } + return TK.Container.prototype.set.call(this, key, value); + } +}); + +/** + * @member {TK.Icon} TK.Window#icon - A {@link TK.Icon} widget to display the window icon. + */ +TK.ChildWidget(TK.Window, "icon", { + create: TK.Icon, + map_options: { icon : "icon" }, + toggle_class: true, +}); +/** + * @member {TK.Label} TK.Window#title - A {@link TK.Label} to display the window title. + */ +TK.ChildWidget(TK.Window, "title", { + create: TK.Label, + default_options: { "class" : "toolkit-title" }, + map_options: { title : "label" }, + toggle_class: true, +}); +/** + * @member {TK.Label} TK.Window#status - A {@link TK.Label} to display the window status. + */ +TK.ChildWidget(TK.Window, "status", { + create: TK.Label, + default_options: { "class" : "toolkit-status" }, + map_options: { status : "label" }, + toggle_class: true, +}); +/** + * @member {TK.Button} TK.Window#close - The close button. + */ +/** + * @member {TK.Button} TK.Window#minimize - The minimize button. + */ +/** + * @member {TK.Button} TK.Window#maximize - The maximize button. + */ +/** + * @member {TK.Button} TK.Window#maximizevertical - The maximizevertical button. + */ +/** + * @member {TK.Button} TK.Window#maximizehorizontal - The maximizehorizontal button. + */ +/** + * @member {TK.Button} TK.Window#shrink - The shrink button. + */ + +var bfactory = function (name) { + TK.ChildWidget(TK.Window, name, { + create: TK.Button, + default_options: { + "class" : "toolkit-" + name, + "icon" : "window" + name, + }, + static_events: { + "click" : function (e) { (eval(name)).call(this.parent, e); }, + "mousedown" : function (e) { e.stopPropagation(); }, + }, + }); +} +var b = ["close", "minimize", "maximize", "maximizevertical", "maximizehorizontal", "shrink"]; + +b.map(bfactory); +/** + * @member {TK.Icon} TK.Window#size - A {@link TK.Icon} acting as handle for window resize. + */ +TK.ChildWidget(TK.Window, "size", { + create: TK.Icon, + default_options: { "icon" : "windowresize", "class" : "toolkit-size" }, +}); +/** + * @member {TK.Container} TK.Window#content - A {@link TK.Container} for the window content. + */ +TK.ChildWidget(TK.Window, "content", { + create: TK.Container, + toggle_class: true, + show: true, + default_options: { "class" : "toolkit-content" }, +}); +/** + * @member {TK.Container} TK.Window#header - The top header bar. + */ +TK.ChildWidget(TK.Window, "header", { + create: TK.Container, + toggle_class: true, + show: true, + default_options: { "class" : "toolkit-header" }, + static_events: { + "dblclick" : header_action, + }, + append: function () { build_header.call(this); this.element.appendChild(this.header.element); }, +}); +/** + * @member {TK.Container} TK.Window#footer - The bottom footer bar. + */ +TK.ChildWidget(TK.Window, "footer", { + create: TK.Container, + toggle_class: true, + show: false, + default_options: { "class" : "toolkit-footer" }, + append : function () { build_footer.call(this); this.element.appendChild(this.footer.element); }, +}); + +})(this, this.TK); diff --git a/share/web_surfaces/builtin/mixer-demo/img/ardour-icon.svg b/share/web_surfaces/builtin/protocol/ardour-icon.svg similarity index 100% rename from share/web_surfaces/builtin/mixer-demo/img/ardour-icon.svg rename to share/web_surfaces/builtin/protocol/ardour-icon.svg diff --git a/share/web_surfaces/builtin/protocol/index.html b/share/web_surfaces/builtin/protocol/index.html new file mode 100644 index 0000000000..22294674bf --- /dev/null +++ b/share/web_surfaces/builtin/protocol/index.html @@ -0,0 +1,19 @@ + + + + + Ardour WebSockets Protocol + + + + +
+
+ + +
+
+
+ + + diff --git a/share/web_surfaces/builtin/protocol/main.css b/share/web_surfaces/builtin/protocol/main.css new file mode 100644 index 0000000000..0a954f6875 --- /dev/null +++ b/share/web_surfaces/builtin/protocol/main.css @@ -0,0 +1,67 @@ +html { + height: 100%; +} + +body { + background: rgb(62,61,61); + color: rgb(248,248,242); + font-family: Helvetica, Arial, sans-serif; + height: 100%; + width: 100%; + position: fixed; + margin: 0; +} + +#main { + display: flex; + flex-direction: column; + height: 100%; +} + +#top { + display: flex; + align-items: center; + padding: 0.25em 0.5em; + box-shadow: 0px 0px 10px #000; + z-index: 10; +} + +#top > img { + height: 24px; +} + +#top > span { + margin-left: 12px; +} + +#log { + padding: 1em; + display: flex; + flex-direction: column; + flex-grow: 1; + overflow: scroll; + overflow-x: hidden; + background: rgba(0,0,0,0.4); +} + +#log pre { + margin: 0; + font-family: Menlo, monospace; + font-size: 1em; +} + +.message-in { + color: rgb(248,248,242); +} + +.message-out { + color: rgb(172,128,255); +} + +.info { + color: rgb(99,208,230); +} + +.error { + color: rgb(249,36,114); +} diff --git a/share/web_surfaces/builtin/protocol/main.js b/share/web_surfaces/builtin/protocol/main.js new file mode 100644 index 0000000000..d88fc6612e --- /dev/null +++ b/share/web_surfaces/builtin/protocol/main.js @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 Luciano Iam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import ArdourClient from '/shared/ardour.js'; + +(() => { + + const MAX_LOG_LINES = 1000; + + const ardour = new ArdourClient(); + + async function main () { + ardour.on('connected', (connected) => { + if (connected) { + log('Client connected', 'info'); + } else { + log('Client disconnected', 'error'); + } + }); + + ardour.on('message', (msg, inbound) => { + if (inbound) { + log(`↙ ${msg}`, 'message-in'); + } else { + log(`↗ ${msg}`, 'message-out'); + } + }); + + await ardour.connect(); + + const manifest = await ardour.getSurfaceManifest(); + document.getElementById('manifest').innerHTML = manifest.name.toUpperCase() + + ' v' + manifest.version + ' — ' + manifest.description; + } + + function log (message, className) { + const output = document.getElementById('log'); + + if (output.childElementCount > MAX_LOG_LINES) { + output.removeChild(output.childNodes[0]); + } + + const pre = document.createElement('pre'); + pre.innerHTML = message; + pre.className = className; + + output.appendChild(pre); + output.scrollTop = output.scrollHeight; + } + + main(); + +})(); diff --git a/share/web_surfaces/builtin/protocol/manifest.xml b/share/web_surfaces/builtin/protocol/manifest.xml new file mode 100644 index 0000000000..8775c48dfb --- /dev/null +++ b/share/web_surfaces/builtin/protocol/manifest.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/share/web_surfaces/builtin/transport/manifest.xml b/share/web_surfaces/builtin/transport/manifest.xml index 93d7fcd075..e45b147bcd 100644 --- a/share/web_surfaces/builtin/transport/manifest.xml +++ b/share/web_surfaces/builtin/transport/manifest.xml @@ -1,6 +1,6 @@ - +