%`
148 | - New options dialog
149 |
150 | ### Changed
151 | - Send to new playlist now checks playlist lock status
152 | - Smooth scroll: enhanced smoothness when using scrollbar
153 | - Refactored code
154 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | License
2 |
3 | Copyright (c) 2021-2023 WilB
4 |
5 | The above copyright notice shall be included in all
6 | copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
11 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
12 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
13 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
14 | SOFTWARE.
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Library Tree
2 |
3 | Feature rich library viewer and browser for [foobar2000](https://www.foobar2000.org).
4 |
5 | ### FEATURES
6 | - Tree viewer
7 | - Album art browser
8 | - New facets
9 | - Statistics
10 | - Album art flow mode
11 | - Library and playlist sources
12 | - Single panel and multiple panel modes
13 | - Mode presets
14 | - browser: keep playing playlist
15 | - player: play without a playlist
16 | - default: choice of all actions
17 |
18 | ### REQUIREMENTS:
19 | - [foobar2000](https://www.foobar2000.org)
20 | - [Spider Monkey Panel 1.5.2+](https://www.foobar2000.org/components)
21 | - IE8 or later
22 | - [FontAwesome](https://github.com/FortAwesome/Font-Awesome/blob/fa-4/fonts/fontawesome-webfont.ttf?raw=true)
23 | - Segoe UI Symbol
24 | - should be present on most windows systems
25 | - if you're on win7 and symbols don't render properly you can download an updated version that includes newer symbols [here](https://www.stephanpringle.com/corrupted-segoe-ui-symbol-font/)
26 |
27 | ### INSTALLATION
28 | Install as a package as follows.
29 |
30 | New install or update:
31 | 1) Add a spider monkey panel to foobar2000 if required
32 | 2) Close any instances of windows explorer using foobar2000 folders or subfolders
33 | 3) Right click the spider monkey panel while pressing the windows key + shift
34 | 4) Choose configure panel
35 | 5) On the script tab ensure package is selected
36 | 6) Open package manager if it doesn't open automatically
37 | 7) Import the package
38 |
39 | Tip: check out Quick setup for a flavour of capabilities
40 |
41 | ### SUPPORT
42 | The official discussion thread for Library Tree is located at [HydrogenAudio](https://hydrogenaud.io/index.php?topic=111060.0) and that's a great place to go for questions and other support issues.
43 |
44 | ## Screenshots
45 |
46 | #### Two panel mode with artist images and covers
47 |
48 | The screenshot is using the dark theme and columns UI with dividing splitter hidden. Left pane: quick setup: artist photos (labels right). Right pane: quick setup: album covers (labels bottom)
49 |
50 | #### Flow mode (upper) and tree modes (lower)
51 |
52 | Tree modes shows various node styles with, left to right: user interface theme; dark theme; blend theme; album art background
53 |
54 | #### Two panel mode with alphabet index and covers
55 |
56 | To set up the above, position two Spider Monkey Panels side by side. Add library tree to each. The screenshot is using the dark theme (display tab) and columns UI with the dividing splitter hidden.
57 | - Right panel: set source to panel & follow instructions on pop-up
58 | - Left panel: on display tab, tick 'List view (tree)'. Use a view pattern something like:
59 | ```
60 | $cut(%artist%,1)|%artist%|$if2(%album%,εXtra)|[[%discnumber%.]%tracknumber%. ][%track artist% - ]%title%
61 | ```
62 |
63 | #### Dark mode colours (left and right) + album art background (middle)
64 |
65 | - LEFT: Quick setup: covers (labels right)
66 | - MIDDLE: Tree with jump search and cover as background. Setup: display tab > theme > cover and adjust cover opacity according to taste
67 | - RIGHT: Tree with item durations, item counts and sort menu. Quick setup: ultra modern
68 | - Display of durations can be enabled for any tree or album art view on the display tab
69 |
70 | ### Credits
71 | - Original Jscript library search (2013): thanhdat1710
72 | - Original JS smooth browser design (2015): Br3tt (aka falstaff)
73 | - [TT-ReBORN](https://github.com/TT-ReBORN) for clean preset inspiration and collaborative effort with new sort code
74 |
--------------------------------------------------------------------------------
/assets/html/confirm.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
19 |
20 |
21 |
22 |
23 |
24 | Yes
25 | No
26 |
27 |
76 |
77 |
--------------------------------------------------------------------------------
/assets/html/input.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 | OK
27 | Cancel
28 |
29 |
87 |
88 |
--------------------------------------------------------------------------------
/assets/html/input.notUsed:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 | OK
27 | Cancel
28 |
29 |
82 |
83 |
--------------------------------------------------------------------------------
/assets/html/inputApply.notUsed:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
21 |
22 |
23 |
24 |
25 |
26 | OK
27 | Apply
28 | Cancel
29 |
30 |
86 |
87 |
--------------------------------------------------------------------------------
/assets/html/messageBox.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
18 | Panel and Playlist Sources
19 |
20 |
21 | Panel source
22 | This enables a multiple panel mode that supports a New Facets style. Panels can also contain a tree view or album art.
23 |
24 | See help on the "Views" tab for setup guidance.
25 | Playlist source
26 | Active playlist
27 | The sort order of the playlist is used, except for multi-value tag or soft splitter views where sorting is necessary. Items are highlighted in the playlist, not sent.
28 |
29 | Other playlists
30 | These behave the same as the library source.
31 |
32 | OK
33 |
34 |
94 |
95 |
--------------------------------------------------------------------------------
/assets/html/styles10.css:
--------------------------------------------------------------------------------
1 | body {color:WindowText; background-color:Menu; font:caption;}
2 | input {font:caption; border:1px solid #7A7A7A; width:100%;}
3 | input:focus {outline:none !important; border:1px solid #0078D7;}
4 | input:hover:focus {outline:none !important; border:1px solid #0078D7;}
5 | input:hover {outline:none !important; border:1px solid #000000;}
6 | label {font:caption;}
7 | button {font:caption; background:#E1E1E1; color:ButtonText; border:1px solid #ADADAD; margin:5px; padding:3px; width:auto; min-width:70px; display:inline-block;}
8 | button:focus {outline:none !important; border:2px solid #0078D7; padding:2px;}
9 | button:hover {background:#e5f1fb; outline:none !important; border:1px solid #0078D7; padding:3px;}div {overflow:hidden;}
10 | button:focus:hover {background:#e5f1fb; outline:none !important; border:2px solid #0078D7; padding:2px;} span {display:block; overflow:hidden; padding-right:10px;}
11 | button[disabled] {background:#CCCCCC; color:#EBEBE4;}
12 | /* --Suppress button:hover manually, since not() is not working =( */
13 | button[disabled]:hover {border:1px solid #ADADAD; padding:3px;}
--------------------------------------------------------------------------------
/assets/html/styles7.css:
--------------------------------------------------------------------------------
1 | body {color:WindowText; background-color:Menu; font:caption;}
2 | input {font:caption; border:1px solid #7A7A7A; width:100%;}
3 | input:focus {outline:none !important; border:1px solid #0078D7;}
4 | input:hover:focus {outline:none !important; border:1px solid #0078D7;}
5 | input:hover {outline:none !important; border:1px solid #000000;}
6 | label {font:caption;}
7 | button {font:caption; background:#E1E1E1; color:ButtonText; border:1px solid #ADADAD; margin:5px; padding:3px; width:auto; min-width:70px; display:inline-block;}
8 | button:focus {border:1px solid #0078D7; padding:3px;}
9 | button:hover {background:#e5f1fb; border:1px solid #0078D7; padding:3px;}
10 | button:focus:hover {background:#e5f1fb; border:1px solid #0078D7; padding:3px;}
11 | button[disabled] {background:#CCCCCC; color:#EBEBE4;}
12 | /* --Suppress button:hover manually, since not() is not working =( */
13 | button[disabled]:hover {border:1px solid #ADADAD; padding:3px;}
--------------------------------------------------------------------------------
/assets/images/noArtist/Blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/Blue.png
--------------------------------------------------------------------------------
/assets/images/noArtist/Dark 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/Dark 1.png
--------------------------------------------------------------------------------
/assets/images/noArtist/Grey 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/Grey 1.png
--------------------------------------------------------------------------------
/assets/images/noArtist/Grey 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/Grey 2.png
--------------------------------------------------------------------------------
/assets/images/noArtist/Grey 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/Grey 3.png
--------------------------------------------------------------------------------
/assets/images/noArtist/Grey 4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/Grey 4.png
--------------------------------------------------------------------------------
/assets/images/noArtist/Grey 5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/Grey 5.png
--------------------------------------------------------------------------------
/assets/images/noArtist/Grey 6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/Grey 6.png
--------------------------------------------------------------------------------
/assets/images/noArtist/small/Blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/small/Blue.png
--------------------------------------------------------------------------------
/assets/images/noArtist/small/Dark 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/small/Dark 1.png
--------------------------------------------------------------------------------
/assets/images/noArtist/small/Grey 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/small/Grey 1.png
--------------------------------------------------------------------------------
/assets/images/noArtist/small/Grey 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/small/Grey 2.png
--------------------------------------------------------------------------------
/assets/images/noArtist/small/Grey 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/small/Grey 3.png
--------------------------------------------------------------------------------
/assets/images/noArtist/small/Grey 4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/small/Grey 4.png
--------------------------------------------------------------------------------
/assets/images/noArtist/small/Grey 5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/small/Grey 5.png
--------------------------------------------------------------------------------
/assets/images/noArtist/small/Grey 6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noArtist/small/Grey 6.png
--------------------------------------------------------------------------------
/assets/images/noCover/CD 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/CD 1.png
--------------------------------------------------------------------------------
/assets/images/noCover/CD 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/CD 2.png
--------------------------------------------------------------------------------
/assets/images/noCover/CD 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/CD 3.png
--------------------------------------------------------------------------------
/assets/images/noCover/Dark 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/Dark 1.png
--------------------------------------------------------------------------------
/assets/images/noCover/Dark 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/Dark 2.png
--------------------------------------------------------------------------------
/assets/images/noCover/Dark 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/Dark 3.png
--------------------------------------------------------------------------------
/assets/images/noCover/Grey 1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/Grey 1.jpg
--------------------------------------------------------------------------------
/assets/images/noCover/Grey 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/Grey 2.png
--------------------------------------------------------------------------------
/assets/images/noCover/Grey 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/Grey 3.png
--------------------------------------------------------------------------------
/assets/images/noCover/Light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/Light.png
--------------------------------------------------------------------------------
/assets/images/noCover/Multi-coloured 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/Multi-coloured 1.png
--------------------------------------------------------------------------------
/assets/images/noCover/Multi-coloured 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/Multi-coloured 2.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/CD 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/CD 1.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/CD 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/CD 2.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/CD 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/CD 3.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/Dark 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/Dark 1.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/Dark 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/Dark 2.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/Dark 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/Dark 3.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/Grey 1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/Grey 1.jpg
--------------------------------------------------------------------------------
/assets/images/noCover/small/Grey 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/Grey 2.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/Grey 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/Grey 3.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/Light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/Light.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/Multi-coloured 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/Multi-coloured 1.png
--------------------------------------------------------------------------------
/assets/images/noCover/small/Multi-coloured 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/noCover/small/Multi-coloured 2.png
--------------------------------------------------------------------------------
/assets/images/root/Black 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Black 1.png
--------------------------------------------------------------------------------
/assets/images/root/Blue 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Blue 1.png
--------------------------------------------------------------------------------
/assets/images/root/Blue 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Blue 2.png
--------------------------------------------------------------------------------
/assets/images/root/Collage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Collage.png
--------------------------------------------------------------------------------
/assets/images/root/Crystal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Crystal.png
--------------------------------------------------------------------------------
/assets/images/root/Glow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Glow.png
--------------------------------------------------------------------------------
/assets/images/root/Green 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Green 1.png
--------------------------------------------------------------------------------
/assets/images/root/Green 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Green 2.png
--------------------------------------------------------------------------------
/assets/images/root/Grey 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Grey 1.png
--------------------------------------------------------------------------------
/assets/images/root/Grey 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Grey 2.png
--------------------------------------------------------------------------------
/assets/images/root/Grey 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Grey 3.png
--------------------------------------------------------------------------------
/assets/images/root/Grey 4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Grey 4.png
--------------------------------------------------------------------------------
/assets/images/root/Multi-coloured 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Multi-coloured 1.png
--------------------------------------------------------------------------------
/assets/images/root/Multi-coloured 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Multi-coloured 2.png
--------------------------------------------------------------------------------
/assets/images/root/Multi-coloured 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Multi-coloured 3.png
--------------------------------------------------------------------------------
/assets/images/root/Purple 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Purple 1.png
--------------------------------------------------------------------------------
/assets/images/root/Purple 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Purple 2.png
--------------------------------------------------------------------------------
/assets/images/root/Red 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Red 1.png
--------------------------------------------------------------------------------
/assets/images/root/Red 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Red 2.png
--------------------------------------------------------------------------------
/assets/images/root/Red 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/Red 3.png
--------------------------------------------------------------------------------
/assets/images/root/small/Black 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Black 1.png
--------------------------------------------------------------------------------
/assets/images/root/small/Blue 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Blue 1.png
--------------------------------------------------------------------------------
/assets/images/root/small/Blue 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Blue 2.png
--------------------------------------------------------------------------------
/assets/images/root/small/Collage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Collage.png
--------------------------------------------------------------------------------
/assets/images/root/small/Crystal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Crystal.png
--------------------------------------------------------------------------------
/assets/images/root/small/Glow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Glow.png
--------------------------------------------------------------------------------
/assets/images/root/small/Green 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Green 1.png
--------------------------------------------------------------------------------
/assets/images/root/small/Green 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Green 2.png
--------------------------------------------------------------------------------
/assets/images/root/small/Grey 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Grey 1.png
--------------------------------------------------------------------------------
/assets/images/root/small/Grey 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Grey 2.png
--------------------------------------------------------------------------------
/assets/images/root/small/Grey 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Grey 3.png
--------------------------------------------------------------------------------
/assets/images/root/small/Grey 4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Grey 4.png
--------------------------------------------------------------------------------
/assets/images/root/small/Multi-coloured 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Multi-coloured 1.png
--------------------------------------------------------------------------------
/assets/images/root/small/Multi-coloured 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Multi-coloured 2.png
--------------------------------------------------------------------------------
/assets/images/root/small/Multi-coloured 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Multi-coloured 3.png
--------------------------------------------------------------------------------
/assets/images/root/small/Purple 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Purple 1.png
--------------------------------------------------------------------------------
/assets/images/root/small/Purple 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Purple 2.png
--------------------------------------------------------------------------------
/assets/images/root/small/Red 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Red 1.png
--------------------------------------------------------------------------------
/assets/images/root/small/Red 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Red 2.png
--------------------------------------------------------------------------------
/assets/images/root/small/Red 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Wil-B/Library-Tree/e78b7e1d003c3507ff8ef97872cc6edc4d874ec8/assets/images/root/small/Red 3.png
--------------------------------------------------------------------------------
/assets/licences/licences.txt:
--------------------------------------------------------------------------------
1 | ========== YaMD5 ==========
2 |
3 | The MIT License (MIT)
4 |
5 | Copyright (C) 2014 Raymond Hill
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS INTHE SOFTWARE.
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | if (typeof my_utils === 'undefined') include('utils.js');
4 |
5 | const loadAsync = window.GetProperty('Load Library Tree Asynchronously', true);
6 |
7 | async function readFiles(files) {
8 | for (const file of files) {
9 | if (window.ID) { // fix pss issue
10 | await include(my_utils.getScriptPath + file);
11 | }
12 | }
13 | }
14 |
15 | const files = [
16 | 'helpers.js',
17 | 'properties.js',
18 | 'interface.js',
19 | 'panel.js',
20 | 'scrollbar.js',
21 | 'library.js',
22 | 'populate.js',
23 | 'search.js',
24 | 'buttons.js',
25 | 'popupbox.js',
26 | 'timers.js',
27 | 'menu.js',
28 | 'initialise.js',
29 | 'images.js',
30 | 'callbacks.js'
31 | ];
32 |
33 | if (loadAsync) {
34 | readFiles(files).then(() => {
35 | if (!window.ID) return; // fix pss issue
36 | on_size();
37 | window.Repaint();
38 | });
39 | } else {
40 | files.forEach(v => include(my_utils.getScriptPath + v));
41 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "WilB",
3 | "description": "Feature rich media library viewer for foobar2000.\r\n\r\n• Tree viewer\r\n• Album art browser\r\n• New facets\r\n• Statistics\r\n• Album art flow mode\r\n\r\nFor guidance on setting up new facets / multiple panel mode, see help on views tab\r\n\r\nCredits\r\n\r\n• Original Jscript library search (2013): thanhdat1710\r\n• Original JS smooth browser design (2015): Br3tt (aka falstaff)",
4 | "enableDragDrop": true,
5 | "id": "{E85C9EF0-778B-46DD-AF20-F4BE831360DD}",
6 | "name": "Library Tree",
7 | "shouldGrabFocus": true,
8 | "version": "2.4.0"
9 | }
--------------------------------------------------------------------------------
/scripts/buttons.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class Buttons {
4 | constructor() {
5 | this.alpha = 255;
6 | this.arc = 1;
7 | this.cur = null;
8 | this.Dn = false;
9 | this.hoverArea = 4;
10 | this.hot_h = 4;
11 | this.margin = Math.max(ppt.margin * 2 + 2, 12) / 4;
12 | this.trace = false;
13 | this.transition;
14 | this.vertical = true;
15 |
16 | this.b = {}
17 | this.btns = {}
18 | this.s = {}
19 |
20 | this.cross = {
21 | hover: null,
22 | normal: null
23 | }
24 |
25 | this.q = {
26 | s_img: null
27 | }
28 |
29 | this.scr = {
30 | bg: null,
31 | btns: ['scrollUp', 'scrollDn'],
32 | iconFontName: 'Segoe UI Symbol',
33 | iconFontStyle: 0,
34 | img: null,
35 | opaque: ui.getOpaque(),
36 | pad: $.clamp(ppt.sbarButPad / 100, -0.5, 0.3)
37 | }
38 |
39 | this.tooltip = {
40 | delay: true,
41 | show: true,
42 | start: Date.now() - 2000
43 | }
44 |
45 | this.setSbarIcon();
46 | this.createImages();
47 | }
48 |
49 | // Methods
50 |
51 | checkScrollBtns(x, y, hover_btn) {
52 | if (sbar.timer_but) {
53 | if ((this.btns['scrollUp'].down || this.btns['scrollDn'].down) && !this.btns['scrollUp'].trace(x, y) && !this.btns['scrollDn'].trace(x, y)) {
54 | this.btns['scrollUp'].cs('normal');
55 | this.btns['scrollDn'].cs('normal');
56 | clearTimeout(sbar.timer_but);
57 | sbar.timer_but = false;
58 | sbar.count = -1;
59 | }
60 | } else if (hover_btn) this.scr.btns.forEach(v => {
61 | if (hover_btn.name == v && hover_btn.down) {
62 | this.btns[v].cs('down');
63 | hover_btn.l_dn();
64 | }
65 | });
66 | }
67 |
68 | clear() {
69 | this.Dn = false;
70 | Object.values(this.btns).forEach(v => v.down = false);
71 | }
72 |
73 | createImages() {
74 | let sz = this.scr.arrow == 0 ? Math.max(Math.round(ui.sbar.but_h * 1.666667), 1) : 100;
75 | const sc = sz / 100;
76 | const iconFont = gdi.Font(this.scr.iconFontName, sz, this.scr.iconFontStyle);
77 | this.alpha = !ui.sbar.col ? [75, 192, 228] : [68, 153, 255];
78 | const hovAlpha = (!ui.sbar.col ? 75 : (!ui.sbar.type ? 68 : 51)) * 0.4;
79 | this.scr.hover = !ui.sbar.col ? RGBA(ui.col.t, ui.col.t, ui.col.t, hovAlpha) : ui.col.text & RGBA(255, 255, 255, hovAlpha);
80 | this.q.s_img = $.gr(100, 100, true, g => {
81 | g.SetSmoothingMode(2);
82 | g.DrawLine(59, 59, 90, 90, 10, !ui.id.local ? ui.col.txt_box_h : ui.col.txt_box);
83 | g.DrawEllipse(10, 10, 54, 54, 10, !ui.id.local ? ui.col.txt_box_h : ui.col.txt_box);
84 | g.FillEllipse(16, 16, 42, 42, 0x0AFAFAFA);
85 | g.SetSmoothingMode(0);
86 | });
87 | this.q.s_img.RotateFlip(4);
88 | this.scr.img = $.gr(sz, sz, true, g => {
89 | g.SetTextRenderingHint(3);
90 | g.SetSmoothingMode(2);
91 | if (ui.sbar.col) {
92 | this.scr.arrow == 0 ? g.FillPolygon(ui.col.text, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : g.DrawString(this.scr.arrow, iconFont, ui.col.text, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1));
93 | } else {
94 | this.scr.arrow == 0 ? g.FillPolygon(RGBA(ui.col.t, ui.col.t, ui.col.t, 255), 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) :
95 | g.DrawString(this.scr.arrow, iconFont, RGBA(ui.col.t, ui.col.t, ui.col.t, 255), 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1));
96 | }
97 | g.SetSmoothingMode(0);
98 | });
99 | this.scr.bg = $.gr(sz, sz, true, g => {
100 | g.SetTextRenderingHint(3);
101 | g.SetSmoothingMode(2);
102 | if (ui.sbar.col) {
103 | this.scr.arrow == 0 ? g.FillPolygon(ui.col.bg, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) : g.DrawString(this.scr.arrow, iconFont, ui.col.bg, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1));
104 | } else {
105 | this.scr.arrow == 0 ? g.FillPolygon(ui.col.bg, 1, [50 * sc, 0, 100 * sc, 76 * sc, 0, 76 * sc]) :
106 | g.DrawString(this.scr.arrow, iconFont, ui.col.bg, 0, sz * this.scr.pad, sz, sz, StringFormat(1, 1));
107 | }
108 | g.SetSmoothingMode(0);
109 | });
110 | sz = 100;
111 | this.cross.normal = $.gr(sz, sz, true, g => {
112 | g.SetTextRenderingHint(3);
113 | g.SetSmoothingMode(2);
114 | let nn = 31;
115 | let offset1 = 12;
116 | let offset2 = 2;
117 | g.DrawLine(offset1, nn - offset2, 100 - nn * 2 + offset1, 100 - nn - offset2, 5, !ui.id.local ? ui.col.txt_box_h : ui.col.txt_box);
118 | g.DrawLine(offset1, 100 - nn - offset2, 100 - nn * 2 + offset1, nn - offset2, 5, !ui.id.local ? ui.col.txt_box_h : ui.col.txt_box);
119 | g.SetSmoothingMode(0);
120 | });
121 | this.cross.hover = $.gr(sz, sz, true, g => {
122 | g.SetTextRenderingHint(3);
123 | g.SetSmoothingMode(2);
124 | let nn = 28;
125 | let offset1 = 9;
126 | let offset2 = 2;
127 | g.DrawLine(offset1, nn - offset2, 100 - nn * 2 + offset1, 100 - nn - offset2, 5, !ui.id.local ? ui.col.txt_box_h : ui.col.txt_box);
128 | g.DrawLine(offset1, 100 - nn - offset2, 100 - nn * 2 + offset1, nn - offset2, 5, !ui.id.local ? ui.col.txt_box_h : ui.col.txt_box);
129 | g.SetSmoothingMode(0);
130 | });
131 | }
132 |
133 | draw(gr) {
134 | Object.values(this.btns).forEach(v => {
135 | if (!v.hide) v.draw(gr);
136 | });
137 | }
138 |
139 | lbtn_dn(x, y) {
140 | this.move(x, y);
141 | if (!this.cur || this.cur.hide) {
142 | this.Dn = false;
143 | return false
144 | } else this.Dn = this.cur.name;
145 | this.cur.down = true;
146 | this.cur.cs('down');
147 | this.cur.lbtn_dn(x, y);
148 | return true;
149 | }
150 |
151 | lbtn_up(x, y) {
152 | if (!this.cur || this.cur.hide || this.Dn != this.cur.name) {
153 | this.clear();
154 | return false;
155 | }
156 | this.clear();
157 | if (this.cur.trace(x, y)) this.cur.cs('hover');
158 | this.cur.lbtn_up(x, y);
159 | return true;
160 | }
161 |
162 | leave() {
163 | if (this.cur) {
164 | this.cur.cs('normal');
165 | if (!this.cur.hide) this.transition.start();
166 | }
167 | this.cur = null;
168 | }
169 |
170 | move(x, y) {
171 | const hover_btn = Object.values(this.btns).find(v => {
172 | if (!this.Dn || this.Dn == v.name) return v.trace(x, y);
173 | });
174 | let hand = false;
175 | this.checkScrollBtns(x, y, hover_btn);
176 | if (hover_btn) {
177 | hand = hover_btn.hand;
178 | }
179 | pop.hand = hand;
180 | if (hover_btn && hover_btn.hide) {
181 | if (this.cur) {
182 | this.cur.cs('normal');
183 | this.transition.start();
184 | }
185 | this.cur = null;
186 | return null;
187 | } // btn hidden, ignore
188 | if (this.cur === hover_btn) return this.cur;
189 | if (this.cur) {
190 | this.cur.cs('normal');
191 | this.transition.start();
192 | } // return prev btn to normal state
193 | if (hover_btn && !(hover_btn.down && hover_btn.type < 4)) {
194 | hover_btn.cs('hover');
195 | if (this.tooltip.show && hover_btn.tiptext) hover_btn.tt.show(hover_btn.tiptext());
196 | this.transition.start();
197 | }
198 | this.cur = hover_btn;
199 | return this.cur;
200 | }
201 |
202 | on_script_unload() {
203 | this.tt('');
204 | }
205 |
206 | reset() {
207 | this.transition.stop();
208 | }
209 |
210 | setSbarIcon() {
211 | switch (ppt.sbarButType) {
212 | case 0:
213 | this.scr.iconFontName = 'Segoe UI Symbol';
214 | this.scr.iconFontStyle = 0;
215 | if (!ui.sbar.type) {
216 | this.scr.arrow = ui.sbar.but_w < Math.round(14 * $.scale) ? '\uE018' : '\uE0A0';
217 | this.scr.pad = ui.sbar.but_w < Math.round(15 * $.scale) ? -0.3 : -0.22;
218 | } else {
219 | this.scr.arrow = ui.sbar.but_w < Math.round(14 * $.scale) ? '\uE018' : '\uE0A0';
220 | this.scr.pad = ui.sbar.but_w < Math.round(14 * $.scale) ? -0.26 : -0.22;
221 | }
222 | break;
223 | case 1:
224 | this.scr.arrow = 0;
225 | break;
226 | case 2:
227 | this.scr.iconFontName = ppt.butCustIconFont;
228 | this.scr.iconFontStyle = 0;
229 | this.scr.arrow = ppt.arrowSymbol.charAt().trim();
230 | if (!this.scr.arrow.length) this.scr.arrow = 0;
231 | this.scr.pad = $.clamp(ppt.sbarButPad / 100, -0.5, 0.3);
232 | break;
233 | }
234 | }
235 |
236 | setScrollBtnsHide(set, autoHide) {
237 | if (autoHide) {
238 | this.scr.btns.forEach(v => {
239 | if (this.btns[v]) this.btns[v].hide = set;
240 | });
241 | panel.treePaint();
242 | } else {
243 | if (!this.btns || (!ppt.sbarShow && !set)) return;
244 | this.scr.btns.forEach(v => {
245 | if (this.btns[v]) this.btns[v].hide = sbar.scrollable_lines < 1 || !ppt.sbarShow;
246 | });
247 | }
248 | }
249 |
250 | setSearchBtnsHide() {
251 | const noShow = !ppt.searchShow;
252 | const searching = (ppt.filterShow || ppt.settingsShow) && panel.search.txt;
253 | const o1 = this.btns.s_img;
254 | if (o1) o1.hide = noShow || searching;
255 | const o2 = this.btns.cross2;
256 | if (o2) o2.hide = noShow || !searching;
257 | }
258 |
259 | refresh(upd) {
260 | if (upd) {
261 | this.b.x = ui.w - ui.sz.marginSearch - Math.round(ui.row.h * 0.59);
262 | this.b.h = ui.row.h;
263 | this.b.y = Math.round((panel.search.sp - this.b.h * 0.4) / 2 - this.b.h * 0.27);
264 | this.scr.opaque = ui.getOpaque();
265 | this.vertical = !panel.imgView || img.style.vertical;
266 | switch (true) {
267 | case this.vertical:
268 | this.scr.x1 = panel.sbar_x;
269 | this.scr.yUp1 = sbar.y;
270 | this.scr.yDn1 = sbar.y + sbar.h - ui.sbar.but_h;
271 | if (ui.sbar.type != 2) {
272 | this.scr.x1 -= 1;
273 | this.scr.hotOffset = this.scr.yUp1 - panel.search.h;
274 | this.scr.x2 = (ui.sbar.but_h - ui.sbar.but_w) / 2;
275 | this.scr.yUp2 = -ui.sbar.arrowPad + this.scr.yUp1 + (ui.sbar.but_h - 1 - ui.sbar.but_w) / 2;
276 | this.scr.yDn2 = ui.sbar.arrowPad + this.scr.yDn1 + (ui.sbar.but_h - 1 - ui.sbar.but_w) / 2;
277 | }
278 | break;
279 | case !this.vertical:
280 | this.scr.x1 = sbar.x;
281 | this.scr.x3 = sbar.x + sbar.w - ui.sbar.but_h;
282 | this.scr.xLeft1 = sbar.x;
283 | this.scr.xRight1 = sbar.x + sbar.w - ui.sbar.but_h;
284 | this.scr.y1 = panel.sbar_y;
285 | if (ui.sbar.type != 2) {
286 | this.scr.y1 -= 1;
287 | this.scr.hotOffset = this.scr.xLeft1 - 0;
288 | this.scr.y2 = (ui.sbar.but_h - ui.sbar.but_w) / 2;
289 | this.scr.xLeft2 = -ui.sbar.arrowPad + this.scr.xLeft1 + (ui.sbar.but_h - 1 - ui.sbar.but_w) / 2;
290 | this.scr.xRight2 = ui.sbar.arrowPad + this.scr.xRight1 + (ui.sbar.but_h - 1 - ui.sbar.but_w) / 2;
291 | }
292 | break;
293 | }
294 |
295 |
296 | this.q.x = ui.sz.marginSearch;
297 | this.q.y = (panel.search.sp - ui.row.h * 0.6) / 2 + (ui.row.h - ui.font.main.Size) % 2;
298 | this.q.h = ui.row.h * 0.6;
299 | this.hoverArea = Math.round(panel.search.sp / 8);
300 | this.hot_h = Math.max(panel.search.sp - this.hoverArea * 2, 4);
301 | this.margin = Math.max(ppt.margin * 2 + 2, 12) / 4;
302 | this.arc = Math.max(Math.round(Math.min(panel.search.sp - this.hoverArea * 2, panel.settings.w + this.margin / 2) / 4), 1);
303 | this.s.w1 = panel.settings.w + but.margin;
304 | this.s.w2 = ui.w - ui.sz.marginSearch - 1 + panel.settings.offset;
305 | this.s.x = this.s.w2 - panel.settings.w - but.margin / 2;
306 | }
307 | if (ppt.sbarShow) {
308 | switch (ui.sbar.type) {
309 | case 2:
310 | switch (true) {
311 | case this.vertical:
312 | this.btns.scrollUp = new Btn(this.scr.x1, this.scr.yUp1, ui.sbar.but_h, ui.sbar.but_h, 3, '', '', '', {
313 | normal: 1,
314 | hover: 2,
315 | down: 3
316 | }, ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(1), '', '', false, 'scrollUp');
317 | this.btns.scrollDn = new Btn(this.scr.x1, this.scr.yDn1, ui.sbar.but_h, ui.sbar.but_h, 3, '', '', '', {
318 | normal: 5,
319 | hover: 6,
320 | down: 7
321 | }, ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(-1), '', '', false, 'scrollDn');
322 | break;
323 | case !this.vertical:
324 | this.btns.scrollUp = new Btn(this.scr.xLeft1, this.scr.y1, ui.sbar.but_h, ui.sbar.but_h, 3, '', '', '', {
325 | normal: 9,
326 | hover: 10,
327 | down: 11
328 | }, ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(1), '', '', false, 'scrollUp');
329 | this.btns.scrollDn = new Btn(this.scr.xRight1, this.scr.y1, ui.sbar.but_h, ui.sbar.but_h, 3, '', '', '', {
330 | normal: 13,
331 | hover: 14,
332 | down: 15
333 | }, ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(-1), '', '', false, 'scrollDn');
334 | break;
335 | }
336 | break;
337 | default:
338 | switch (true) {
339 | case this.vertical:
340 | this.btns.scrollUp = new Btn(this.scr.x1, this.scr.yUp1 - this.scr.hotOffset, ui.sbar.but_h, ui.sbar.but_h + this.scr.hotOffset, 1, this.scr.x2, this.scr.yUp2, ui.sbar.but_w, '', ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(1), '', '', false, 'scrollUp');
341 | this.btns.scrollDn = new Btn(this.scr.x1, this.scr.yDn1, ui.sbar.but_h, ui.sbar.but_h + this.scr.hotOffset, 2, this.scr.x2, this.scr.yDn2, ui.sbar.but_w, '', ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(-1), '', '', false, 'scrollDn');
342 | break;
343 | case !this.vertical:
344 | this.btns.scrollUp = new Btn(this.scr.xLeft1 - this.scr.hotOffset, this.scr.y1, ui.sbar.but_h, ui.sbar.but_h + this.scr.hotOffset, 1, this.scr.y2, this.scr.xLeft2, ui.sbar.but_w, '', ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(1), '', '', false, 'scrollUp');
345 | this.btns.scrollDn = new Btn(this.scr.xRight1, this.scr.y1, ui.sbar.but_h, ui.sbar.but_h + this.scr.hotOffset, 2, this.scr.y2, this.scr.xRight2, ui.sbar.but_w, '', ppt.sbarShow == 1 && sbar.narrow.show || sbar.scrollable_lines < 1, () => sbar.but(-1), '', '', false, 'scrollDn');
346 | break;
347 | }
348 | break;
349 | }
350 | }
351 | this.transition = new Transition(this.btns, v => v.state !== 'normal');
352 | this.btns.s_img = new Btn(this.q.x - this.margin / 2, this.hoverArea, this.q.h + this.margin, this.hot_h, 4, this.q.x, this.q.y, this.q.h, {
353 | normal: this.q.s_img
354 | }, false, '', () => sMenu.load(this.q.x - this.margin / 2, panel.search.h), () => 'History and query syntax help. Ctrl+E focuses search', true, 's_img');
355 |
356 | this.btns.cross2 = new Btn(this.q.x - this.margin / 2, this.hoverArea, this.q.h + this.margin, this.hot_h, 5, this.q.x, this.b.y, this.b.h, {
357 | normal: this.cross.normal,
358 | hover: this.cross.hover
359 | }, true, '', () => search.clear(), () => panel.search.txt ? 'Clear search text (escape). Double click to show history' : 'No search text to clear', true, 'cross2');
360 | this.btns.filter = new Btn(ppt.searchShow ? panel.filter.x + this.margin / 2 : panel.filter.x - this.margin / 2, 0, ppt.searchShow ? panel.filter.w - this.margin : panel.filter.w + this.margin, panel.search.sp, 6, panel.filter.x, ppt.searchShow ? panel.cc : panel.lc, panel.filter.w, {
361 | normal: ui.col.txt_box,
362 | hover: !ui.id.local ? (!ui.img.blurDark ? ui.col.txt_box_h : ui.col.text) : ui.col.txt_box
363 | }, !ppt.filterShow, '', () => fMenu.load(panel.filter.x, panel.search.h), () => 'Filter', true, 'filter');
364 |
365 | this.btns.settings = new Btn(this.s.x, panel.settings.offset, this.s.w1, panel.search.sp, 7, this.s.w2, panel.search.sp, panel.settings.y, {
366 | normal: ui.col.txt_box,
367 | hover: !ui.id.local ? (!ui.img.blurDark ? ui.col.txt_box_h : ui.col.text) : ui.col.txt_box
368 | }, !ppt.settingsShow, '', () => men.rbtn_up(this.s.x, panel.search.h, true), () => 'Settings', true, 'settings');
369 |
370 | this.btns.cross1 = new Btn(this.b.x - this.margin / 2, this.hoverArea, this.q.h + this.margin, this.hot_h, 5, this.b.x, this.b.y, this.b.h, {
371 | normal: this.cross.normal,
372 | hover: this.cross.hover
373 | }, !ppt.searchShow || ppt.filterShow || ppt.settingsShow, '', () => search.clear(), () => panel.search.txt ? 'Clear search text (escape)' : 'No search text to clear', true, 'cross1');
374 | this.setSearchBtnsHide();
375 | }
376 |
377 | traceBtn(btn, x, y) {
378 | const o = this.btns[btn];
379 | return o && o.trace(x, y);
380 | }
381 |
382 | tt(n, force) {
383 | if (tooltip.Text === n && !force) return;
384 | pop.checkTooltipFont('btn');
385 | tooltip.Text = n;
386 | tooltip.Activate();
387 | }
388 | }
389 |
390 | class Btn {
391 | constructor(x, y, w, h, type, p1, p2, p3, item, hide, l_dn, l_up, tiptext, hand, name) {
392 | this.x = x;
393 | this.y = y;
394 | this.w = w;
395 | this.h = h;
396 | this.type = type;
397 | this.p1 = p1;
398 | this.p2 = p2;
399 | this.p3 = p3;
400 | this.item = item;
401 | this.hide = hide;
402 | this.l_dn = l_dn;
403 | this.l_up = l_up;
404 | this.tt = new Tooltip;
405 | this.tiptext = tiptext;
406 | this.hand = hand;
407 | this.name = name;
408 | this.transition_factor = 0;
409 | this.state = 'normal';
410 | }
411 |
412 | // Methods
413 |
414 | cs(state) {
415 | this.state = state;
416 | if (state === 'down' || state === 'normal') this.tt.clear();
417 | this.repaint();
418 | }
419 |
420 | draw(gr) {
421 | switch (this.type) {
422 | case 1:
423 | case 2:
424 | this.drawScrollBtn(gr);
425 | break;
426 | case 3:
427 | ui.theme.SetPartAndStateID(1, this.item[this.state]);
428 | ui.theme.DrawThemeBackground(gr, this.x, this.y, this.w, this.h);
429 | break;
430 | case 4:
431 | this.drawSearch(gr);
432 | break;
433 | case 5:
434 | this.drawCross(gr);
435 | break;
436 | case 6:
437 | this.drawFilter(gr);
438 | break;
439 | case 7:
440 | this.drawSettings(gr);
441 | break;
442 | }
443 | }
444 |
445 | drawCross(gr) {
446 | const a = !ui.id.local ? panel.search.txt ? (this.state !== 'down' ? Math.min(170 + (255 - 170) * this.transition_factor, 255) : 255) : 170 : 255;
447 | const crossIm = this.state === 'normal' || !panel.search.txt ? this.item.normal : this.item.hover;
448 | const colRect = this.state !== 'down' ? ui.getBlend(ui.col.bg4, ui.col.bg5, this.transition_factor, true) : ui.col.bg4;
449 | gr.SetSmoothingMode(2);
450 | gr.FillRoundRect(this.x, this.y, this.w, this.h, but.arc, but.arc, colRect);
451 | gr.SetSmoothingMode(0);
452 | gr.SetInterpolationMode(2);
453 | if (crossIm) gr.DrawImage(crossIm, this.p1, this.p2, this.p3, this.p3, 0, 0, crossIm.Width, crossIm.Height, 0, a);
454 | gr.SetInterpolationMode(0);
455 | }
456 |
457 | drawFilter(gr) {
458 | const colText = !ui.id.local ? (this.state !== 'down' ? ui.getBlend(this.item.hover, this.item.normal, this.transition_factor, true) : this.item.hover) : this.item.normal;
459 | const colRect = this.state !== 'down' ? ui.getBlend(ui.col.bg4, ui.col.bg5, this.transition_factor, true) : ui.col.bg4;
460 | gr.SetSmoothingMode(2);
461 | gr.FillRoundRect(this.x, but.hoverArea, this.w, but.hot_h, but.arc, but.arc, colRect);
462 | gr.SetSmoothingMode(0);
463 | if (!ui.img.blurDark) gr.GdiDrawText(panel.filter.mode[ppt.filterBy].name, panel.filter.font, colText, this.p1, this.y, this.p3, this.h, this.p2);
464 | else {
465 | gr.SetTextRenderingHint(5);
466 | gr.DrawString(panel.filter.mode[ppt.filterBy].name, panel.filter.font, colText, this.p1 - 1, this.y - 1, this.p3, this.h, StringFormat(1, 1));
467 | }
468 | }
469 |
470 | drawScrollBtn(gr) {
471 | const a = this.state !== 'down' ? Math.min(but.alpha[0] + (but.alpha[1] - but.alpha[0]) * this.transition_factor, but.alpha[1]) : but.alpha[2];
472 | switch (true) {
473 | case but.vertical:
474 | if (this.state !== 'normal' && ui.sbar.type == 1) gr.FillSolidRect(sbar.x, this.y + (this.type == 1 ? but.scr.hotOffset - panel.sbar_o : 0), sbar.w, this.h - but.scr.hotOffset + panel.sbar_o, but.scr.hover);
475 | if (but.scr.opaque && but.scr.bg) gr.DrawImage(but.scr.bg, this.x + this.p1, this.p2, this.p3, this.p3, 0, 0, but.scr.bg.Width, but.scr.bg.Height, this.type == 1 ? 0 : 180);
476 | if (but.scr.img) gr.DrawImage(but.scr.img, this.x + this.p1, this.p2, this.p3, this.p3, 0, 0, but.scr.img.Width, but.scr.img.Height, this.type == 1 ? 0 : 180, a);
477 | break;
478 | case !but.vertical:
479 | if (this.state !== 'normal' && ui.sbar.type == 1) gr.FillSolidRect(this.x + (this.type == 1 ? but.scr.hotOffset - panel.sbar_o : 0), sbar.y, this.w - but.scr.hotOffset + panel.sbar_o, sbar.h, but.scr.hover);
480 | if (but.scr.opaque && but.scr.bg) gr.DrawImage(but.scr.bg, this.p2, this.y + this.p1, this.p3, this.p3, 0, 0, but.scr.bg.Width, but.scr.bg.Height, this.type == 1 ? 270 : 90);
481 | if (but.scr.img) gr.DrawImage(but.scr.img, this.p2, this.y + this.p1, this.p3, this.p3, 0, 0, but.scr.img.Width, but.scr.img.Height, this.type == 1 ? 270 : 90, a);
482 | break;
483 | }
484 | }
485 |
486 | drawSearch(gr) {
487 | const a = !ui.id.local ? (this.state !== 'down' ? Math.min(170 + (255 - 170) * this.transition_factor, 255) : 255) : 255;
488 | const colRect = this.state !== 'down' ? ui.getBlend(ui.col.bg4, ui.col.bg5, this.transition_factor, true) : ui.col.bg4;
489 | gr.SetSmoothingMode(2);
490 | gr.FillRoundRect(this.x, this.y, this.w, this.h, but.arc, but.arc, colRect);
491 | gr.SetSmoothingMode(0);
492 | gr.SetInterpolationMode(2);
493 | if (this.item.normal) gr.DrawImage(this.item.normal, this.p1, this.p2, this.p3, this.p3, 0, 0, this.item.normal.Width, this.item.normal.Height, 0, a);
494 | gr.SetInterpolationMode(0);
495 | }
496 |
497 | drawSettings(gr) {
498 | const colText = !ui.id.local ? (this.state !== 'down' ? ui.getBlend(this.item.hover, this.item.normal, this.transition_factor, true) : this.item.hover) : this.item.normal;
499 | const colRect = this.state !== 'down' ? ui.getBlend(ui.col.bg4, ui.col.bg5, this.transition_factor, true) : ui.col.bg4;
500 | gr.SetSmoothingMode(2);
501 | gr.FillRoundRect(this.x, but.hoverArea, this.w, but.hot_h, but.arc, but.arc, colRect);
502 | gr.SetSmoothingMode(0);
503 | if (!ui.img.blurDark) gr.GdiDrawText(panel.settings.icon, panel.settings.font, colText, 0, this.y, this.p1, this.p2, panel.rc);
504 | else {
505 | gr.SetTextRenderingHint(5);
506 | gr.DrawString(panel.settings.icon, panel.settings.font, colText, 0, this.y - 1, this.p1, this.p2, StringFormat(2, 1));
507 | }
508 | }
509 |
510 | lbtn_dn(x, y) {
511 | if (!but.Dn) return;
512 | this.l_dn && this.l_dn(x, y);
513 | }
514 |
515 | lbtn_up() {
516 | if (this.l_up) this.l_up();
517 | }
518 |
519 | repaint() {
520 | const expXY = 2;
521 | const expWH = 4;
522 | window.RepaintRect(this.x - expXY, this.y - expXY, this.w + expWH, this.h + expWH);
523 | }
524 |
525 | trace(x, y) {
526 | but.trace = !this.hide && x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h;
527 | return but.trace;
528 | }
529 | }
530 |
531 | class Tooltip {
532 | constructor() {
533 | this.id = Math.ceil(Math.random().toFixed(8) * 1000);
534 | this.tt_timer = new TooltipTimer();
535 | }
536 |
537 | // Methods
538 |
539 | clear() {
540 | this.tt_timer.stop(this.id);
541 | }
542 |
543 | show(text) {
544 | if (Date.now() - but.tooltip.start > 2000 && but.tooltip.delay) this.showDelayed(text);
545 | else this.showImmediate(text);
546 | but.tooltip.start = Date.now();
547 | }
548 |
549 | showDelayed(text) {
550 | this.tt_timer.start(this.id, text);
551 | }
552 |
553 | showImmediate(text) {
554 | this.tt_timer.set_id(this.id);
555 | this.tt_timer.stop(this.id);
556 | but.tt(text);
557 | }
558 |
559 | stop() {
560 | this.tt_timer.forceStop();
561 | }
562 | }
563 |
564 | class TooltipTimer {
565 | constructor() {
566 | this.delay_timer;
567 | this.tt_caller = undefined;
568 | }
569 |
570 | // Methods
571 |
572 | forceStop() {
573 | but.tooltip.delay = true;
574 | but.tt('');
575 | if (this.delay_timer) {
576 | clearTimeout(this.delay_timer);
577 | this.delay_timer = null;
578 | }
579 | }
580 |
581 | set_id(id) {
582 | this.tt_caller = id;
583 | }
584 |
585 | start(id, text) {
586 | const old_caller = this.tt_caller;
587 | this.tt_caller = id;
588 | if (!this.delay_timer && tooltip.Text) but.tt(text, old_caller !== this.tt_caller);
589 | else {
590 | this.forceStop();
591 | if (!this.delay_timer) {
592 | this.delay_timer = setTimeout(() => {
593 | but.tt(text);
594 | this.delay_timer = null;
595 | }, 500);
596 | }
597 | }
598 | }
599 |
600 | stop(id) {
601 | if (this.tt_caller === id) this.forceStop();
602 | }
603 | }
604 |
605 | class Transition {
606 | constructor(items, hover) {
607 | this.hover = hover;
608 | this.items = items;
609 | this.transition_timer = null;
610 | }
611 |
612 | // Methods
613 |
614 | start() {
615 | const hover_in_step = 0.2;
616 | const hover_out_step = 0.06;
617 | if (!this.transition_timer) {
618 | this.transition_timer = setInterval(() => {
619 | Object.values(this.items).forEach(v => {
620 | const saved = v.transition_factor;
621 | if (this.hover(v)) v.transition_factor = Math.min(1, v.transition_factor += hover_in_step);
622 | else v.transition_factor = Math.max(0, v.transition_factor -= hover_out_step);
623 | if (saved !== v.transition_factor) {
624 | v.repaint();
625 | }
626 | });
627 | const running = Object.values(this.items).some(v => v.transition_factor > 0 && v.transition_factor < 1);
628 | if (!running) this.stop();
629 | }, 25);
630 | }
631 | }
632 |
633 | stop() {
634 | if (this.transition_timer) {
635 | clearInterval(this.transition_timer);
636 | this.transition_timer = null;
637 | }
638 | }
639 | }
--------------------------------------------------------------------------------
/scripts/callbacks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | function on_colours_changed(keepCache) {
4 | ui.getColours();
5 |
6 | if (panel.colMarker) {
7 | panel.getFields(ppt.viewBy, ppt.filterBy);
8 | if (lib) {
9 | lib.getLibrary();
10 | lib.rootNodes(true, true);
11 | }
12 | }
13 | sbar.setCol();
14 | pop.createImages();
15 | but.createImages();
16 | if (!keepCache) img.clearCache();
17 | img.createImages();
18 | but.refresh(true);
19 | sbar.resetAuto();
20 | ui.createImages();
21 | if (!ppt.themed) ui.blurReset();
22 | window.Repaint();
23 | }
24 |
25 | function on_font_changed() {
26 | sbar.logScroll();
27 | pop.deactivateTooltip();
28 | ui.getFont();
29 | panel.on_size(true);
30 | if (ui.style.topBarShow || ppt.sbarShow) but.refresh(true);
31 | sbar.resetAuto();
32 | window.Repaint();
33 | sbar.setScroll();
34 | }
35 |
36 | function on_char(code) {
37 | pop.on_char(code);
38 | find.on_char(code);
39 | if (!ppt.searchShow) return;
40 | search.on_char(code);
41 | }
42 |
43 | function on_focus(is_focused) {
44 | if (!is_focused) {
45 | timer.clear(timer.cursor);
46 | panel.search.cursor = false;
47 | panel.searchPaint();
48 | }
49 | pop.on_focus(is_focused);
50 | }
51 |
52 | function on_get_album_art_done(handle, art_id, image, image_path) {
53 | ui.on_get_album_art_done(handle, image, image_path);
54 | }
55 |
56 | function on_item_focus_change(playlistIndex) {
57 | lib.checkFilter();
58 | if (!pop.setFocus) {
59 | if (ppt.followPlaylistFocus && playlistIndex == $.pl_active && !ppt.libSource) {
60 | setSelection(fb.GetFocusItem());
61 | }
62 | } else pop.setFocus = false;
63 | ui.focus_changed();
64 | }
65 |
66 | function on_key_down(vkey) {
67 | pop.on_key_down(vkey);
68 | img.on_key_down(vkey);
69 | if (!ppt.searchShow) return;
70 | search.on_key_down(vkey);
71 | }
72 |
73 | function on_key_up(vkey) {
74 | img.on_key_up(vkey);
75 | if (!ppt.searchShow) return;
76 | search.on_key_up(vkey)
77 | }
78 |
79 | function on_library_items_added(handleList) {
80 | if (ppt.libSource == 2) return;
81 | if (lib.v2_init) {
82 | lib.v2_init = false;
83 | if (ui.w < 1 || !window.IsVisible) return;
84 | lib.initialise(handleList);
85 | return;
86 | }
87 | if (!ppt.libAutoSync || ppt.fixedPlaylist || !ppt.libSource) return;
88 | lib.treeState(false, 2, handleList, 0);
89 | }
90 |
91 | function on_library_items_removed(handleList) {
92 | if (!ppt.libAutoSync || ppt.fixedPlaylist || !ppt.libSource) return;
93 | if (ppt.libSource == 2) {
94 | const libList = lib.list.Clone();
95 | libList.Sort();
96 | handleList.Sort();
97 | handleList.MakeIntersection(libList);
98 | }
99 | lib.treeState(false, 2, handleList, 2);
100 | }
101 |
102 | function on_library_items_changed(handleList) {
103 | if (!ppt.libAutoSync || ppt.fixedPlaylist || !ppt.libSource) return;
104 | if (ppt.libSource == 2) {
105 | const libList = lib.list.Clone();
106 | libList.Sort();
107 | handleList.Sort();
108 | handleList.MakeIntersection(libList);
109 | }
110 | lib.treeState(false, 2, handleList, 1);
111 | }
112 |
113 | function on_main_menu(index) {
114 | pop.on_main_menu(index);
115 | }
116 |
117 | function on_metadb_changed(handleList, isDatabase) {
118 | if (isDatabase && !panel.statistics || lib.list.Count != lib.libNode.length) return;
119 | if (ppt.fixedPlaylist || !ppt.libSource) {
120 | handleList.Convert().some(h => {
121 | const i = lib.full_list.Find(h);
122 | if (i != -1) {
123 | const isMainChanged = lib.isMainChanged(handleList);
124 | if (isMainChanged) lib.treeState(false, 2);
125 | ui.focus_changed();
126 | return true;
127 | }
128 | });
129 | }
130 | }
131 |
132 | function on_mouse_lbtn_dblclk(x, y) {
133 | but.lbtn_dn(x, y);
134 | if (ppt.searchShow) search.lbtn_dblclk(x, y);
135 | pop.lbtn_dblclk(x, y);
136 | sbar.lbtn_dblclk(x, y);
137 | }
138 |
139 | function on_mouse_lbtn_down(x, y) {
140 | if (ppt.touchControl) panel.last_pressed_coord = {
141 | x: x,
142 | y: y
143 | };
144 | if (ui.style.topBarShow || ppt.sbarShow) but.lbtn_dn(x, y);
145 | if (ppt.searchShow) search.lbtn_dn(x, y);
146 | pop.lbtn_dn(x, y);
147 | sbar.lbtn_dn(x, y);
148 | ui.sz.y_start = y;
149 | }
150 |
151 | function on_mouse_lbtn_up(x, y) {
152 | pop.lbtn_up(x, y);
153 | if (ppt.searchShow) search.lbtn_up();
154 | but.lbtn_up(x, y);
155 | sbar.lbtn_up();
156 | }
157 |
158 | function on_mouse_leave() {
159 | if (ui.style.topBarShow || ppt.sbarShow) but.leave();
160 | sbar.leave();
161 | pop.leave();
162 | }
163 |
164 | function on_mouse_mbtn_dblclk(x, y, mask) {
165 | pop.mbtnDblClickOrAltDblClick(x, y, mask, 'mbtn');
166 | }
167 |
168 | function on_mouse_mbtn_down(x, y) {
169 | pop.mbtn_dn(x, y);
170 | }
171 |
172 | function on_mouse_mbtn_up(x, y, mask) {
173 | // hacks at default settings blocks on_mouse_mbtn_up, at least in windows; workaround configure hacks: main window > move with > caption only & ensure pseudo-caption doesn't overlap buttons
174 | pop.mbtnUpOrAltClickUp(x, y, mask, 'mbtn');
175 | }
176 |
177 | function on_mouse_move(x, y) {
178 | if (panel.m.x == x && panel.m.y == y) return;
179 | pop.hand = false;
180 | if (ui.style.topBarShow || ppt.sbarShow) but.move(x, y);
181 | if (ppt.searchShow) search.move(x, y);
182 | pop.move(x, y);
183 | pop.dragDrop(x, y);
184 | sbar.move(x, y);
185 | ui.zoomDrag(x, y);
186 | panel.m.x = x;
187 | panel.m.y = y;
188 | }
189 |
190 | function on_mouse_rbtn_up(x, y) {
191 | if (y < panel.search.h && x > panel.search.x && x < panel.search.x + panel.search.w) {
192 | if (ppt.searchShow) search.rbtn_up(x, y);
193 | } else men.rbtn_up(x, y);
194 | return true;
195 | }
196 |
197 | function on_mouse_wheel(step) {
198 | pop.deactivateTooltip();
199 | if (!vk.k('zoom')) sbar.wheel(step);
200 | else ui.wheel(step);
201 | }
202 |
203 | function on_notify_data(name, info) {
204 | if (ppt.libSource == 2 && name != 'bio_imgChange') {
205 | const panelSelectionPlaylists = ppt.panelSelectionPlaylist.split(/\s*\|\s*/);
206 | panelSelectionPlaylists.some(v=> {
207 | if (name == v) {
208 | lib.list = new FbMetadbHandleList(info);
209 | if ($.equalHandles(lib.list.Convert(), lib.full_list.Convert())) return;
210 | lib.full_list = lib.list.Clone();
211 | ppt.lastPanelSelectionPlaylist = `${v} Cache`;
212 | const pln = plman.FindOrCreatePlaylist(`${v} Cache`, false);
213 | plman.ClearPlaylist(pln);
214 | plman.InsertPlaylistItems(pln, 0, lib.list);
215 | lib.searchCache = {};
216 | pop.clearTree();
217 | pop.cache = {
218 | 'standard': {},
219 | 'search': {},
220 | 'filter': {}
221 | }
222 | lib.treeState(false, 2, null, 3);
223 | ui.expandHandle = lib.list.Count ? lib.list[0] : null;
224 | ui.on_playback_new_track();
225 | lib.treeState(false, ppt.rememberTree);
226 | return;
227 | }
228 | });
229 | }
230 |
231 | switch (name) {
232 | case '!!.tags update':
233 | lib.treeState(false, 2);
234 | break;
235 | case 'newThemeColours':
236 | if (!ppt.themed) break;
237 | ppt.theme = info.theme;
238 | ppt.themeBgImage = info.themeBgImage;
239 | ppt.themeColour = info.themeColour;
240 | on_colours_changed(true);
241 | break;
242 | case 'Sync col': {
243 | if (!ppt.themed) break;
244 | const themeLight = ppt.themeLight;
245 | if (themeLight != info.themeLight) {
246 | ppt.themeLight = info.themeLight;
247 | on_colours_changed(true);
248 | }
249 | break;
250 | }
251 | case 'Sync image':
252 | if (!ppt.themed) break;
253 | sync.image(new GdiBitmap(info.image), info.id);
254 | break;
255 | }
256 | if (ui.id.local && name.startsWith('opt_')) {
257 | const clone = typeof info === 'string' ? String(info) : info;
258 | on_notify(name, clone);
259 | }
260 | }
261 |
262 | function on_paint(gr) {
263 | if (!lib.initialised) {
264 | lib.initialise();
265 | }
266 | ui.draw(gr);
267 | lib.checkTree();
268 | img.draw(gr);
269 | ui.drawLine(gr);
270 | search.draw(gr);
271 | pop.draw(gr);
272 | sbar.draw(gr);
273 | but.draw(gr);
274 | find.draw(gr);
275 | }
276 |
277 | function on_playback_new_track(handle) {
278 | lib.checkFilter();
279 | pop.getNowplaying(handle);
280 | if (!ppt.recItemImage || ppt.libSource != 2) ui.on_playback_new_track(handle);
281 | }
282 |
283 | function on_playback_stop(reason) {
284 | if (reason == 2) return;
285 | pop.getNowplaying('', true);
286 | on_item_focus_change();
287 | }
288 |
289 | function on_playback_queue_changed() {
290 | on_queue_changed();
291 | }
292 |
293 | function on_playlists_changed() {
294 | men.playlists_changed();
295 | if ($.pl_active != plman.ActivePlaylist) $.pl_active = plman.ActivePlaylist;
296 | let fixedPlaylistIndex = -1;
297 | if (ppt.fixedPlaylist) {
298 | fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName);
299 | if (fixedPlaylistIndex == -1) {
300 | ppt.fixedPlaylist = false;
301 | ppt.libSource = 0;
302 | if (panel.imgView) img.clearCache();
303 | lib.playlist_update();
304 | }
305 | }
306 | }
307 |
308 | function on_playlist_items_added(playlistIndex) {
309 | if (ppt.fixedPlaylist) {
310 | const fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName);
311 | if (playlistIndex == fixedPlaylistIndex) {
312 | lib.playlist_update(playlistIndex);
313 | return
314 | }
315 | }
316 | if (!ppt.libSource && playlistIndex == $.pl_active) {
317 | lib.playlist_update(playlistIndex);
318 |
319 | }
320 | }
321 |
322 | function on_playlist_items_removed(playlistIndex) {
323 | if (ppt.fixedPlaylist) {
324 | const fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName);
325 | if (playlistIndex == fixedPlaylistIndex) {
326 | lib.playlist_update(playlistIndex);
327 | return
328 | }
329 | }
330 |
331 | if (!ppt.libSource && playlistIndex == $.pl_active) {
332 | lib.playlist_update(playlistIndex);
333 | }
334 | }
335 |
336 | function on_playlist_items_reordered(playlistIndex) {
337 | if (!ppt.libSource && playlistIndex == $.pl_active) {
338 | lib.playlist_update(playlistIndex);
339 | }
340 | }
341 |
342 | function on_playlist_switch() {
343 | $.pl_active = plman.ActivePlaylist;
344 | if (!ppt.libSource) {
345 | lib.playlist_update();
346 | }
347 | ui.focus_changed();
348 | }
349 |
350 | const on_queue_changed = $.debounce(() => {
351 | if (ppt.itemShowStatistics != 7) return;
352 | pop.tree.forEach(v => {
353 | v.id = '';
354 | v.count = '';
355 | delete v.statistics;
356 | delete v._statistics;
357 | });
358 | pop.cache = {
359 | 'standard': {},
360 | 'search': {},
361 | 'filter': {}
362 | }
363 | panel.treePaint();
364 | }, 250, {
365 | leading: true,
366 | trailing: true
367 | });
368 |
369 | function on_script_unload() {
370 | but.on_script_unload();
371 | pop.deactivateTooltip();
372 | }
373 |
374 | function on_selection_changed() {
375 | if (!panel.setSelection()) return;
376 | setSelection(fb.GetSelection());
377 | }
378 |
379 | const windowMetricsPath = `${fb.ProfilePath}settings\\themed\\windowMetrics.json`;
380 | function on_size() {
381 | ui.w = window.Width;
382 | ui.h = window.Height;
383 | if (!ui.w || !ui.h) return;
384 |
385 | pop.deactivateTooltip();
386 | tooltip.SetMaxWidth(Math.max(ui.w, 800));
387 | ui.blurReset();
388 | ui.calcText(true)
389 |
390 | if (ppt.themed && ppt.theme) {
391 | const themed_image = `${fb.ProfilePath}settings\\themed\\themed_image.bmp`;
392 | if ($.file(themed_image)) sync.image(gdi.Image(themed_image));
393 | }
394 |
395 | panel.on_size();
396 | if (ui.style.topBarShow || ppt.sbarShow) but.refresh(true);
397 | sbar.resetAuto();
398 | find.on_size();
399 |
400 | if (!ppt.themed) return;
401 | const windowMetrics = $.jsonParse(windowMetricsPath, {}, 'file');
402 | windowMetrics[window.Name] = {
403 | w: ui.w,
404 | h: ui.h
405 | }
406 | $.save(windowMetricsPath, JSON.stringify(windowMetrics, null, 3), true);
407 | }
408 |
409 | function setSelection(handle) {
410 | if (!handle || !panel.list.Count) return;
411 | const item = panel.list.Find(handle);
412 | let idx = -1;
413 | pop.tree.forEach((v, i) => {
414 | if (!v.root && pop.inRange(item, v.item)) idx = i;
415 | });
416 | if (idx != -1) {
417 | if (!panel.imgView) pop.focusShow(idx);
418 | else pop.showItem(idx, 'focus');
419 | }
420 | }
--------------------------------------------------------------------------------
/scripts/helpers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const requiredVersionStr = '1.5.2';
4 |
5 | function is_compatible(requiredVersionStr) {
6 | const requiredVersion = requiredVersionStr.split('.');
7 | const currentVersion = utils.Version.split('.');
8 | if (currentVersion.length > 3) currentVersion.length = 3;
9 | for (let i = 0; i < currentVersion.length; ++i)
10 | if (currentVersion[i] != requiredVersion[i]) return currentVersion[i] > requiredVersion[i];
11 | return true;
12 | }
13 | if (!is_compatible(requiredVersionStr)) fb.ShowPopupMessage(`Library Tree requires v${requiredVersionStr}. Current component version is v${utils.Version}.`);
14 |
15 | const doc = new ActiveXObject('htmlfile');
16 | const fso = new ActiveXObject('Scripting.FileSystemObject');
17 | const tooltip = window.Tooltip;
18 | const WshShell = new ActiveXObject('WScript.Shell');
19 |
20 | class Helpers {
21 | constructor() {
22 | this.pl_active = plman.ActivePlaylist;
23 | this.scale = this.getDpi();
24 | }
25 |
26 | // Methods
27 |
28 | average(arr) {
29 | return arr.reduce((a, b) => a + b, 0) / arr.length;
30 | }
31 |
32 | browser(c) {
33 | if (!this.run(c)) fb.ShowPopupMessage('Unable to launch your default browser.', 'Library Tree');
34 | }
35 |
36 | buildPth(pth) {
37 | let result, tmpFileLoc = '';
38 | let UNC = pth.startsWith('\\\\');
39 | if (UNC) pth = pth.replace('\\\\', '');
40 | const pattern = /(.*?)\\/gm;
41 | while ((result = pattern.exec(pth))) {
42 | tmpFileLoc = tmpFileLoc.concat(result[0]);
43 | if (UNC) {
44 | tmpFileLoc = `\\\\${tmpFileLoc}`;
45 | UNC = false;
46 | }
47 | this.create(tmpFileLoc);
48 | }
49 | }
50 |
51 | clamp(num, min, max) {
52 | num = num <= max ? num : max;
53 | num = num >= min ? num : min;
54 | return num;
55 | }
56 |
57 | create(fo) {
58 | try {
59 | if (!this.folder(fo)) fso.CreateFolder(fo);
60 | } catch (e) {}
61 | }
62 |
63 | debounce(e,r,i) {
64 | var o,u,a,c,v,f,d=0,m=!1,j=!1,n=!0;if('function'!=typeof e)throw new TypeError('debounce: invalid function');function T(i){var n=o,t=u;return o=u=void 0,d=i,c=e.apply(t,n)}function b(i){var n=i-f;return void 0===f||r<=n||n<0||j&&a<=i-d}function l(){var i,n,t=Date.now();if(b(t))return w(t);v=setTimeout(l,(n=r-((i=t)-f),j?Math.min(n,a-(i-d)):n))}function w(i){return v=void 0,n&&o?T(i):(o=u=void 0,c)}function t(){var i,n=Date.now(),t=b(n);if(o=arguments,u=this,f=n,t){if(void 0===v)return d=i=f,v=setTimeout(l,r),m?T(i):c;if(j)return v=setTimeout(l,r),T(f)}return void 0===v&&(v=setTimeout(l,r)),c}return r=parseFloat(r)||0,this.isObject(i)&&(m=!!i.leading,a=((j='maxWait'in i))?Math.max(parseFloat(i.maxWait)||0,r):a,n='trailing'in i?!!i.trailing:n),t.cancel=function(){void 0!==v&&clearTimeout(v),o=f=u=v=void(d=0)},t.flush=function(){return void 0===v?c:w(Date.now())},t;
65 | }
66 |
67 | equal(arr1, arr2) {
68 | if (!this.isArray(arr1) || !this.isArray(arr2)) return false;
69 | let i = arr1.length;
70 | if (i != arr2.length) return false;
71 | while (i--)
72 | if (arr1[i] !== arr2[i]) return false;
73 | return true;
74 | }
75 |
76 | equalHandles(arr1, arr2) {
77 | if (!this.isArray(arr1) || !this.isArray(arr2)) return false;
78 | let i = arr1.length;
79 | if (i != arr2.length) return false;
80 | while (i--)
81 | if (!arr1[i].Compare(arr2[i])) return false;
82 | return true;
83 | }
84 |
85 | file(f) {
86 | return typeof f === 'string' && fso.FileExists(f);
87 | }
88 |
89 | folder(fo) {
90 | return typeof fo === 'string' && fso.FolderExists(fo);
91 | }
92 |
93 | getClipboardData() {
94 | try {
95 | return utils.GetClipboardText();
96 | } catch(e) {
97 | try {
98 | return doc.parentWindow.clipboardData.getData('Text');
99 | } catch (e) {
100 | return null;
101 | }
102 | }
103 | }
104 |
105 | getDpi() {
106 | let dpi = 120;
107 | try {
108 | dpi = WshShell.RegRead('HKCU\\Control Panel\\Desktop\\WindowMetrics\\AppliedDPI');
109 | } catch (e) {}
110 | return Math.max(dpi / 120, 1);
111 | }
112 |
113 | gr(w, h, im, func) {
114 | if (isNaN(w) || isNaN(h)) return;
115 | let i = gdi.CreateImage(Math.max(w, 2), Math.max(h, 2));
116 | let g = i.GetGraphics();
117 | func(g, i);
118 | i.ReleaseGraphics(g);
119 | g = null;
120 | if (im) return i;
121 | else i = null;
122 | }
123 |
124 | isArray(arr) {
125 | return Array.isArray(arr);
126 | }
127 |
128 | isObject(t) {
129 | const e = typeof t;
130 | return null != t && ('object' == e || 'function' == e);
131 | }
132 |
133 | jsonParse(n, defaultVal, type) {
134 | switch (type) {
135 | case 'file':
136 | try {
137 | return JSON.parse(this.open(n));
138 | } catch (e) {
139 | return defaultVal;
140 | }
141 | default:
142 | try {
143 | return JSON.parse(n);
144 | } catch (e) {
145 | return defaultVal;
146 | }
147 | }
148 | }
149 |
150 | objHasOwnProperty(obj, key) {
151 | return Object.prototype.hasOwnProperty.call(obj, key);
152 | }
153 |
154 | open(f) {
155 | try { // handle locked files
156 | return this.file(f) ? utils.ReadTextFile(f) : '';
157 | } catch (e) {
158 | return '';
159 | }
160 | }
161 |
162 | padNumber(num, len, base) {
163 | if (!base) base = 10;
164 | return ('000000' + num.toString(base)).substr(-len);
165 | }
166 |
167 | query(h, q) {
168 | let l = new FbMetadbHandleList();
169 | try {
170 | l = fb.GetQueryItems(h, q);
171 | } catch (e) {}
172 | return l;
173 | }
174 |
175 | range(start, stop, step) {
176 | step = step || 1;
177 | return Array.from({length: (stop - start) / step + 1}, (_, i) => start + i * step);
178 | }
179 |
180 | regexEscape(n) {
181 | return n.replace(/[*+\-?^!:&"~${}()|[\]/\\]/g, '\\$&');
182 | }
183 |
184 | replaceAt(str, pos, chr) {
185 | return str.substring(0, pos) + chr + str.substring(pos + 1);
186 | }
187 |
188 | RGBAtoRGB(c, bg) {
189 | c = this.toRGBA(c);
190 | bg = this.toRGB(bg);
191 | const r = c[0] / 255;
192 | const g = c[1] / 255;
193 | const b = c[2] / 255;
194 | const a = c[3] / 255;
195 | const bgr = bg[0] / 255;
196 | const bgg = bg[1] / 255;
197 | const bgb = bg[2] / 255;
198 | let nR = ((1 - a) * bgr) + (a * r);
199 | let nG = ((1 - a) * bgg) + (a * g);
200 | let nB = ((1 - a) * bgb) + (a * b);
201 | nR = this.clamp(Math.round(nR * 255), 0, 255);
202 | nG = this.clamp(Math.round(nG * 255), 0, 255);
203 | nB = this.clamp(Math.round(nB * 255), 0, 255);
204 | return RGB(nR, nG, nB);
205 | }
206 |
207 | RGBtoRGBA(rgb, a) {
208 | return a << 24 | rgb & 0x00FFFFFF;
209 | }
210 |
211 | run(c) {
212 | try {
213 | WshShell.Run(c);
214 | return true;
215 | } catch (e) {
216 | return false;
217 | }
218 | }
219 |
220 | save(fn, text, bom) {
221 | try {
222 | utils.WriteTextFile(fn, text, bom)
223 | } catch (e) {
224 | this.trace('error saving: ' + fn);
225 | }
226 | }
227 |
228 | setClipboardData(n) {
229 | try {
230 | utils.SetClipboardText(n);
231 | } catch(e) {
232 | try {
233 | doc.parentWindow.clipboardData.setData('Text', n);
234 | } catch(e) {
235 | this.trace('unable to set clipboard text');
236 | }
237 | }
238 | }
239 |
240 | split(n, type) {
241 | switch (type) {
242 | case 0:
243 | return n.replace(/\s+|^,+|,+$/g, '').split(',');
244 | case 1:
245 | return n.replace(/^[,\s]+|[,\s]+$/g, '').split(/[,-]/);
246 | }
247 | }
248 |
249 | throttle(e,i,t) {
250 | var n=!0,r=!0;if('function'!=typeof e)throw new TypeError('throttle: invalid function');return this.isObject(t)&&(n='leading'in t?!!txt.leading:n,r='trailing'in t?!!txt.trailing:r),this.debounce(e,i,{leading:n,maxWait:i,trailing:r});
251 | }
252 |
253 | titlecase(n) {
254 | return n.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-/]*/g, match => {
255 | if (match.substr(1).search(/[A-Z]|\../) > -1) return match;
256 | return match.charAt(0).toUpperCase() + match.substr(1);
257 | });
258 | }
259 |
260 | toRGB(c) {
261 | return [c >> 16 & 0xff, c >> 8 & 0xff, c & 0xff];
262 | }
263 |
264 | toRGBA(c) {
265 | return [c >> 16 & 0xff, c >> 8 & 0xff, c & 0xff, c >> 24 & 0xff];
266 | }
267 |
268 | trace(message) {
269 | console.log('Library Tree' + ': ' + message);
270 | }
271 |
272 | value(num, def, type) {
273 | num = parseFloat(num);
274 | if (isNaN(num)) return def;
275 | switch (type) {
276 | case 0:
277 | return num;
278 | case 1:
279 | if (num !== 1 && num !== 0) return def;
280 | break;
281 | case 2:
282 | if (num > 2 || num < 0) return def;
283 | break;
284 | }
285 | return num;
286 | }
287 |
288 | wshPopup(prompt, caption) {
289 | try {
290 | const ns = WshShell.Popup(prompt, 0, caption, 1);
291 | if (ns == 1) return true;
292 | return false;
293 | } catch (e) {
294 | return true;
295 | }
296 | }
297 | }
298 |
299 | const $ = new Helpers;
300 |
301 | function RGB(r, g, b) {
302 | return 0xff000000 | r << 16 | g << 8 | b;
303 | }
304 |
305 | function RGBA(r, g, b, a) {
306 | return a << 24 | r << 16 | g << 8 | b;
307 | }
308 |
309 | function StringFormat() {
310 | const a = arguments;
311 | const flags = 0;
312 | let h_align = 0;
313 | let v_align = 0;
314 | let trimming = 0;
315 | switch (a.length) {
316 | case 3:
317 | trimming = a[2]; /*fall through*/
318 | case 2:
319 | v_align = a[1]; /*fall through*/
320 | case 1:
321 | h_align = a[0];
322 | break;
323 | default:
324 | return 0;
325 | }
326 | return (h_align << 28 | v_align << 24 | trimming << 20 | flags);
327 | }
328 |
329 | function Bezier(){const i=4,c=.001,o=1e-7,v=10,l=11,s=1/(l-1),n=typeof Float32Array==='function';function e(r,n){return 1-3*n+3*r}function u(r,n){return 3*n-6*r}function a(r){return 3*r}function w(r,n,t){return((e(n,t)*r+u(n,t))*r+a(n))*r}function y(r,n,t){return 3*e(n,t)*r*r+2*u(n,t)*r+a(n)}function h(r,n,t,e,u){let a,f,i=0;do{f=n+(t-n)/2;a=w(f,e,u)-r;if(a>0){t=f}else{n=f}}while(Math.abs(a)>o&&++i=c){return A(r,a,i,o)}else if(f===0){return a}else{return h(r,n,n+s,i,o)}}return function r(n){if(n===0){return 0}if(n===1){return 1}return w(u(n),t,e)}} this.scroll = bezier(0.25, 0.1, 0.25, 1); this.full = this.scroll; this.step = this.scroll; this.bar = bezier(0.165,0.84,0.44,1); this.barFast = bezier(0.19, 1, 0.22, 1); this.inertia = bezier(0.23, 1, 0.32, 1);}
330 | const ease = new Bezier;
331 |
332 | function MD5(){const b=function(l,n){let m=l[0],j=l[1],p=l[2],o=l[3];m+=(j&p|~j&o)+n[0]-680876936|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[1]-389564586|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[2]+606105819|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[3]-1044525330|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[4]-176418897|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[5]+1200080426|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[6]-1473231341|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[7]-45705983|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[8]+1770035416|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[9]-1958414417|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[10]-42063|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[11]-1990404162|0;j=(j<<22|j>>>10)+p|0;m+=(j&p|~j&o)+n[12]+1804603682|0;m=(m<<7|m>>>25)+j|0;o+=(m&j|~m&p)+n[13]-40341101|0;o=(o<<12|o>>>20)+m|0;p+=(o&m|~o&j)+n[14]-1502002290|0;p=(p<<17|p>>>15)+o|0;j+=(p&o|~p&m)+n[15]+1236535329|0;j=(j<<22|j>>>10)+p|0;m+=(j&o|p&~o)+n[1]-165796510|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[6]-1069501632|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[11]+643717713|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[0]-373897302|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[5]-701558691|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[10]+38016083|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[15]-660478335|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[4]-405537848|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[9]+568446438|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[14]-1019803690|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[3]-187363961|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[8]+1163531501|0;j=(j<<20|j>>>12)+p|0;m+=(j&o|p&~o)+n[13]-1444681467|0;m=(m<<5|m>>>27)+j|0;o+=(m&p|j&~p)+n[2]-51403784|0;o=(o<<9|o>>>23)+m|0;p+=(o&j|m&~j)+n[7]+1735328473|0;p=(p<<14|p>>>18)+o|0;j+=(p&m|o&~m)+n[12]-1926607734|0;j=(j<<20|j>>>12)+p|0;m+=(j^p^o)+n[5]-378558|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[8]-2022574463|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[11]+1839030562|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[14]-35309556|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[1]-1530992060|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[4]+1272893353|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[7]-155497632|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[10]-1094730640|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[13]+681279174|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[0]-358537222|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[3]-722521979|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[6]+76029189|0;j=(j<<23|j>>>9)+p|0;m+=(j^p^o)+n[9]-640364487|0;m=(m<<4|m>>>28)+j|0;o+=(m^j^p)+n[12]-421815835|0;o=(o<<11|o>>>21)+m|0;p+=(o^m^j)+n[15]+530742520|0;p=(p<<16|p>>>16)+o|0;j+=(p^o^m)+n[2]-995338651|0;j=(j<<23|j>>>9)+p|0;m+=(p^(j|~o))+n[0]-198630844|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[7]+1126891415|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[14]-1416354905|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[5]-57434055|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[12]+1700485571|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[3]-1894986606|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[10]-1051523|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[1]-2054922799|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[8]+1873313359|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[15]-30611744|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[6]-1560198380|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[13]+1309151649|0;j=(j<<21|j>>>11)+p|0;m+=(p^(j|~o))+n[4]-145523070|0;m=(m<<6|m>>>26)+j|0;o+=(j^(m|~p))+n[11]-1120210379|0;o=(o<<10|o>>>22)+m|0;p+=(m^(o|~j))+n[2]+718787259|0;p=(p<<15|p>>>17)+o|0;j+=(o^(p|~m))+n[9]-343485551|0;j=(j<<21|j>>>11)+p|0;l[0]=m+l[0]|0;l[1]=j+l[1]|0;l[2]=p+l[2]|0;l[3]=o+l[3]|0};const e='0123456789abcdef';const d=[];const c=function(k){const q=e;const o=d;let r,p,l;for(let m=0;m<4;m++){p=m*8;r=k[m];for(l=0;l<8;l+=2){o[p+1+l]=q.charAt(r&15);r>>>=4;o[p+0+l]=q.charAt(r&15);r>>>=4}}return o.join('')};const i=function(){this._dataLength=0;this._state=new Int32Array(4);this._buffer=new ArrayBuffer(68);this._bufferLength=0;this._buffer8=new Uint8Array(this._buffer,0,68);this._buffer32=new Uint32Array(this._buffer,0,17);this.start()};const a=new Int32Array([1732584193,-271733879,-1732584194,271733878]);const h=new Int32Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);i.prototype.appendStr=function(n){const k=this._buffer8;const j=this._buffer32;let o=this._bufferLength;for(let l=0;l>>6)+192;k[o++]=m&63|128}else{if(m<55296||m>56319){k[o++]=(m>>>12)+224;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128}else{m=((m-55296)*1024)+(n.charCodeAt(++l)-56320)+65536;if(m>1114111){throw'Unicode standard supports code points up to U+10FFFF'}k[o++]=(m>>>18)+240;k[o++]=(m>>>12&63)|128;k[o++]=(m>>>6&63)|128;k[o++]=(m&63)|128}}}if(o>=64){this._dataLength+=64;b(this._state,j);o-=64;j[0]=j[16]}}this._bufferLength=o;return this};i.prototype.appendAsciiStr=function(o){const l=this._buffer8;const k=this._buffer32;let p=this._bufferLength;let n=0,m=0;for(;;){n=Math.min(o.length-m,64-p);while(n--){l[p++]=o.charCodeAt(m++)}if(p<64){break}this._dataLength+=64;b(this._state,k);p=0}this._bufferLength=p;return this};i.prototype.start=function(){this._dataLength=0;this._bufferLength=0;this._state.set(a);return this};i.prototype.end=function(){const q=this._bufferLength;this._dataLength+=q;const r=this._buffer8;r[q]=128;r[q+1]=r[q+2]=r[q+3]=0;const k=this._buffer32;const m=(q>>2)+1;k.set(h.subarray(m),m);if(q>55){b(this._state,k);k.set(h)}const j=this._dataLength*8;if(j<=4294967295){k[14]=j}else{const n=j.toString(16).match(/(.*?)(.{0,8})$/);const o=parseInt(n[2],16);const l=parseInt(n[1],16)||0;k[14]=o;k[15]=l}b(this._state,k);return c(this._state)};const f=new i();i.hashStr=function(k){return f.start().appendStr(k).end()};return i} // https://github.com/gorhill/yamd5.js
333 | const md5 = new MD5;
--------------------------------------------------------------------------------
/scripts/initialise.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let pop;
4 | const ui = new UserInterface;
5 | const panel = new Panel;
6 | const sbar = new Scrollbar;
7 | const vk = new Vkeys;
8 | const lib = new Library;
9 | pop = new Populate;
10 | const search = new Search;
11 | const find = new Find;
12 | const but = new Buttons;
13 | const popUpBox = new PopUpBox;
14 | const men = new MenuItems;
15 | const timer = new Timers;
16 |
17 | if (!ppt.get('Software Notice Checked', false)) fb.ShowPopupMessage('License\r\n\r\nCopyright (c) 2021-2022 WilB\r\n\r\nThe above copyright notice shall be included in all copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.', 'Library Tree');
18 | ppt.set('Software Notice Checked', true);
--------------------------------------------------------------------------------
/scripts/menu.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const MF_GRAYED = 0x00000001;
4 | const MF_STRING = 0x00000000;
5 |
6 | class MenuManager {
7 | constructor(name, clearArr, baseMenu) {
8 | this.baseMenu = baseMenu || 'baseMenu';
9 | this.clearArr = clearArr;
10 | this.func = {};
11 | this.idx = 0;
12 | this.menu = {};
13 | this.menuItems = [];
14 | this.menuNames = [];
15 | this.name = name;
16 | }
17 |
18 | // Methods
19 |
20 | addItem(v) {
21 | if (v.separator && !v.str) {
22 | const separator = this.get(v.separator);
23 | if (separator) this.menu[v.menuName].AppendMenuSeparator();
24 | } else {
25 | const hide = this.get(v.hide);
26 | if (hide || !v.str) return;
27 | this.idx++;
28 | if (!this.clearArr) this.executeFunctions(v, ['checkItem', 'checkRadio', 'flags', 'menuName', 'separator', 'str']); // if clearArr, functions redundant & not supported
29 | const a = this.clearArr ? v : this;
30 | const menu = this.menu[a.menuName];
31 | menu.AppendMenuItem(a.flags, this.idx, a.str);
32 | if (a.checkItem) menu.CheckMenuItem(this.idx, a.checkItem);
33 | if (a.checkRadio) menu.CheckMenuRadioItem(this.idx, this.idx, this.idx);
34 | if (a.separator) menu.AppendMenuSeparator();
35 | this.func[this.idx] = v.func;
36 | }
37 | }
38 |
39 | addSeparator({menuName = this.baseMenu, separator = true}) {this.menuItems.push({ menuName: menuName || this.baseMenu, separator: separator});}
40 |
41 | appendMenu(v) {
42 | const a = this.clearArr ? v : this;
43 | if (!this.clearArr) this.executeFunctions(v, ['hide', 'menuName']);
44 | if (a.menuName == this.baseMenu || a.hide) return;
45 | if (!this.clearArr) this.executeFunctions(v, ['appendTo', 'flags', 'separator', 'str']);
46 | const menu = this.menu[a.appendTo || this.baseMenu];
47 | this.menu[a.menuName].AppendTo(menu, a.flags, a.str || a.menuName)
48 | if (a.separator) menu.AppendMenuSeparator();
49 | }
50 |
51 | clear() {
52 | this.menu = {}
53 | this.func = {}
54 | this.idx = 0;
55 | if (this.clearArr) {
56 | this.menuItems = [];
57 | this.menuNames = [];
58 | }
59 | }
60 |
61 | createMenu(menuName = this.baseMenu) {
62 | menuName = this.get(menuName);
63 | this.menu[menuName] = window.CreatePopupMenu();
64 | }
65 |
66 | executeFunctions(v, items) {
67 | let i = 0;
68 | let ln = items.length;
69 | while (i < ln) {
70 | const w = items[i];
71 | this[w] = this.get(v[w])
72 | i++;
73 | }
74 | }
75 |
76 | get(v) {
77 | if (v instanceof Function) return v();
78 | return v;
79 | }
80 |
81 | load(x, y) {
82 | if (!this.menuItems.length) men[this.name]();
83 | let i = 0;
84 | let ln = this.menuNames.length;
85 | while (i < ln) {
86 | this.createMenu(this.menuNames[i])
87 | i++;
88 | }
89 |
90 | i = 0;
91 | ln = this.menuItems.length;
92 | while (i < ln) {
93 | const v = this.menuItems[i];
94 | !v.appendMenu ? this.addItem(v) : this.appendMenu(v)
95 | i++;
96 | }
97 |
98 | let Context;
99 | if (men.show_context) {
100 | Context = fb.CreateContextMenuManager();
101 | Context.InitContext(men.items);
102 | this.menu[this.baseMenu].AppendMenuSeparator();
103 | Context.BuildMenu(this.menu[this.baseMenu], 5000);
104 | }
105 |
106 | const idx = this.menu[this.baseMenu].TrackPopupMenu(x, y);
107 | this.run(idx);
108 |
109 | if (men.show_context) {
110 | if (idx >= 5000 && idx <= 5800) Context.ExecuteByID(idx - 5000);
111 | men.show_context = false;
112 | }
113 |
114 | this.clear();
115 | }
116 |
117 | newItem({str = null, func = null, menuName = this.baseMenu, flags = MF_STRING, checkItem = false, checkRadio = false, separator = false, hide = false}) {this.menuItems.push({str: str, func: func, menuName: menuName, flags: flags, checkItem: checkItem, checkRadio: checkRadio, separator: separator, hide: hide});}
118 |
119 | newMenu({menuName = this.baseMenu, str = '', appendTo = this.baseMenu, flags = MF_STRING, separator = false, hide = false}) {
120 | this.menuNames.push(menuName);
121 | if (menuName != this.baseMenu) this.menuItems.push({menuName: menuName, appendMenu: true, str: str, appendTo: appendTo, flags: flags, separator: separator, hide: hide});
122 | }
123 |
124 | run(idx) {
125 | const v = this.func[idx];
126 | if (v instanceof Function) v();
127 | }
128 | }
129 |
130 | const clearArr = true;
131 | const menu = new MenuManager('mainMenu', clearArr);
132 | const fMenu = new MenuManager('filterMenu', clearArr);
133 | const sMenu = new MenuManager('searchHistoryMenu', clearArr);
134 | const searchMenu = new MenuManager('searchMenu');
135 |
136 | class MenuItems {
137 | constructor() {
138 | this.expandable = false;
139 | this.ix = -1;
140 | this.items = new FbMetadbHandleList();
141 | this.nm = '';
142 | this.pl = [];
143 | this.r_up = false;
144 | this.show_context = false;
145 | this.treeExpandLimit = $.file('C:\\check_local\\1450343922.txt') ? 6000 : $.clamp(ppt.treeExpandLimit, 10, 6000);
146 | this.playlists_changed(true);
147 | this.settingsBtnDn = false;
148 | this.validItem = false;
149 | }
150 |
151 | // Methods
152 |
153 | mainMenu() {
154 | menu.newMenu({hide: !this.settingsBtnDn && ppt.settingsShow && this.validItem});
155 |
156 | if (this.validItem) {
157 | ['Send to current playlist' + '\tEnter', 'Add to current playlist' + '\tShift+enter', 'Send to new playlist' + '\tCtrl+enter', 'Show nowplaying'].forEach((v, i) => menu.newItem({
158 | str: v,
159 | func: () => this.setPlaylist(i),
160 | flags: this.getPaylistFlag(i),
161 | separator: i == 2
162 | }));
163 | }
164 |
165 | if (this.validItem && ppt.albumArtOptionsShow) {
166 | menu.newItem({
167 | str: !panel.imgView ? 'Show album art' : (!ppt.facetView ? 'Show tree' : 'Show text'),
168 | func: () => this.setPlaylist(4),
169 | flags: !panel.pn_h_auto || ppt.pn_h != ppt.pn_h_min ? MF_STRING : MF_GRAYED,
170 | separator: !panel.imgView //|| this.show_context && !ui.style.topBarShow
171 | });
172 | }
173 |
174 | if (this.validItem && panel.imgView) {
175 | menu.newItem({
176 | str: ppt.artId != 4 ? 'Show artists' : 'Show albums',
177 | func: () => {ppt.artId = ppt.artId != 4 ? 4 : 0; this.setPlaylist(5);},
178 | separator: this.show_context && !ui.style.topBarShow
179 | });
180 | }
181 |
182 | if (this.validItem && !panel.imgView) {
183 | ['Collapse all\tNum -', 'Expand\tNum *'].forEach((v, i) => menu.newItem({
184 | str: v,
185 | func: () => this.setTreeState(i),
186 | flags: !i || i == 1 && this.expandable ? MF_STRING : MF_GRAYED,
187 | separator: i == 1 && this.show_context && (!ppt.settingsShow && !ppt.searchShow && !ppt.filterShow || this.shift)
188 | }));
189 | }
190 |
191 | menu.newMenu({menuName: 'Settings', hide: !this.show_context || ui.style.topBarShow && !this.shift});
192 |
193 | const mainMenu = () => this.show_context ? 'Settings' : 'baseMenu';
194 |
195 | menu.newMenu({menuName: 'Views', appendTo: mainMenu(), separator: true});
196 | panel.menu.forEach((v, i) => menu.newItem({
197 | menuName: 'Views',
198 | str: v,
199 | func: () => this.setView(i),
200 | checkRadio: i == ppt.viewBy,
201 | separator: i > panel.menu.length - 3
202 | }));
203 |
204 | const d = {}
205 | this.getSortData(d);
206 | menu.newMenu({menuName: d.menuName, appendTo: 'Views', flags: d.sortType ? MF_STRING : MF_GRAYED, separator: true});
207 | if (d.sortType) {
208 | menu.newItem({
209 | menuName: d.menuName,
210 | str: ['', 'By year', 'Albums by year'][d.sortType],
211 | flags: MF_GRAYED,
212 | separator: true
213 | });
214 | const menuSort = [[], ['Default', 'Ascending', 'Descending'], ['Default', 'Ascending (hide year)', 'Ascending (show year)', 'Descending (hide year)', 'Descending (show year)', 'Action: year after album', 'Action: year before album']][d.sortType];
215 | menuSort.forEach((v, i) => menu.newItem({
216 | menuName: d.menuName,
217 | str: v,
218 | func: () => this.sortByDate(i, d),
219 | flags: i > 4 && (d.sortIX == 1 || d.sortIX == 3) ? MF_GRAYED : MF_STRING,
220 | checkRadio: d.sortIX == -1 && !i || i == d.sortIX || d.sortType == 2 && i == 5 && !ppt.yearBeforeAlbum || i == 6 && ppt.yearBeforeAlbum,
221 | separator: i == 0 || d.sortType == 2 && (i == 2 || i == 4)
222 | }));
223 | }
224 |
225 | menu.newItem({
226 | menuName: 'Views',
227 | str: 'Configure views...',
228 | func: () => panel.open('views')
229 | });
230 |
231 | menu.newMenu({menuName: 'Statistics', appendTo: mainMenu(), separator: true});
232 | [pop.countsRight && !panel.imgView ? ['None', '# Tracks', '# Items'][pop.nodeCounts] : 'None', 'Bitrate', 'Duration', 'Total size', 'Rating', 'Popularity', 'Date', 'Playback queue', 'Playcount', 'First played', 'Last played', 'Added', 'Configure statistics...'].forEach((v, i) => menu.newItem({
233 | menuName: 'Statistics',
234 | str: v,
235 | func: () => this.setStatistics(i),
236 | checkRadio: i == ppt.itemShowStatistics,
237 | separator: !i || i == 7 || i == 11
238 | }));
239 |
240 | menu.newMenu({menuName: 'Album art', appendTo: mainMenu(), hide: !panel.imgView});
241 | ['Front', 'Back', 'Disc', 'Icon', 'Artist', 'Group: auto', 'Group: top level', 'Group: two levels', 'Change group name...', 'Configure album art...'].forEach((v, i) => menu.newItem({
242 | menuName: 'Album art',
243 | str: v,
244 | func: () => this.setAlbumart(i),
245 | flags: i == 8 && (panel.folderView || ppt.rootNode != 3) ? MF_GRAYED : MF_STRING,
246 | checkRadio: i == ppt.artId || i - 5 == ppt.albumArtGrpLevel,
247 | separator: i == 4 || i == 7 || i == 8
248 | }));
249 |
250 | menu.newMenu({menuName: 'Quick setup', appendTo: mainMenu()});
251 | ['Traditional', 'Modern [default]', 'Ultra-Modern', 'Clean', 'Facet'].forEach((v, i) => menu.newItem({
252 | menuName: 'Quick setup',
253 | str: v,
254 | func: () => panel.set('quickSetup', i),
255 | separator: i == 3 || i == 4
256 | }));
257 |
258 | if (ppt.albumArtOptionsShow) {
259 | ['Covers [labels right]', 'Covers [labels bottom]', 'Covers [labels blend]', 'Artist photos [labels right]', 'Album art size +', 'Album art size -', 'Flow mode', 'Always load preset with current \'view\' pattern'].forEach((v, i) => menu.newItem({
260 | menuName: 'Quick setup',
261 | str: v,
262 | func: () => panel.set('quickSetup', i + 5),
263 | flags: i == 4 && (ppt.thumbNailSize == 7 || !panel.imgView || ppt.albumArtFlowMode) || i == 5 && (ppt.thumbNailSize == 0 || !panel.imgView || ppt.albumArtFlowMode) ? MF_GRAYED : MF_STRING,
264 | checkItem: i == 7 && ppt.presetLoadCurView,
265 | separator: i == 2 || i == 3 || i == 5 || i == 6
266 | }));
267 | }
268 |
269 | menu.newMenu({menuName: 'Source', appendTo: mainMenu(), separator: true});
270 | ['Library', 'Panel', 'Playlist'].forEach((v, i) => menu.newItem({
271 | menuName: 'Source',
272 | str: v,
273 | func: () => this.setSource(i),
274 | checkRadio: i == (ppt.libSource - 1 < 0 || ppt.fixedPlaylist ? 2 : ppt.libSource - 1),
275 | separator: i == 2
276 | }));
277 |
278 | menu.newItem({
279 | menuName: 'Source',
280 | str: 'Select source panel',
281 | func: () => this.setSourcePanel(),
282 | flags: ppt.libSource != 2 ? MF_GRAYED : MF_STRING,
283 | separator: true
284 | });
285 |
286 | menu.newMenu({menuName: 'Select playlist', appendTo: 'Source'});
287 | menu.newItem({
288 | menuName: 'Select playlist',
289 | str: 'Active playlist',
290 | func: () => this.setActivePlaylist(),
291 | checkRadio: ppt.libSource == 0,
292 | separator: true
293 | });
294 |
295 | const pl_no = Math.ceil(this.pl.length / 30);
296 | const pl_ix = ppt.fixedPlaylist ? plman.FindPlaylist(ppt.fixedPlaylistName) : -1;
297 | for (let j = 0; j < pl_no; j++) {
298 | const n = '# ' + (j * 30 + 1 + ' - ' + Math.min(this.pl.length, 30 + j * 30) + (30 + j * 30 > pl_ix && ((j * 30) - 1) < pl_ix ? ' >>>' : ''));
299 | menu.newMenu({menuName: n, appendTo: 'Select playlist'});
300 | for (let i = j * 30; i < Math.min(this.pl.length, 30 + j * 30); i++) {
301 | menu.newItem({
302 | menuName: n,
303 | str: this.pl[i].menuName,
304 | func: () => this.setFixedPlaylist(i),
305 | checkRadio: i == pl_ix
306 | });
307 | }
308 | }
309 |
310 | menu.newMenu({menuName: 'Refresh', appendTo: mainMenu(), separator: true});
311 | for (let i = 0; i < 5; i++) menu.newItem({
312 | menuName: 'Refresh',
313 | str: ['Refresh selected images...', 'Refresh all images...', 'Reset zoom...', 'Refresh library...', 'Reload...'][i],
314 | func: () => this.setMode(i),
315 | flags: panel.imgView && !i && this.items.Count || !panel.imgView || i ? MF_STRING : MF_GRAYED,
316 | separator: i == 1 && panel.imgView,
317 | hide: i < 2 && !panel.imgView || i == 3 && ppt.libAutoSync
318 | });
319 |
320 | for (let i = 0; i < 2; i++) menu.newItem({
321 | menuName: mainMenu(),
322 | str: [popUpBox.ok ? 'Options...' : 'Options: see console', 'Configure...'][i],
323 | func: () => !i ? panel.open() : window.EditScript(),
324 | separator: !i && this.shift,
325 | hide: !this.settingsBtnDn && ppt.settingsShow && this.validItem && !this.shift || i && !this.shift
326 | });
327 | }
328 |
329 | filterMenu() {
330 | fMenu.newMenu({});
331 | for (let i = 0; i < panel.filter.menu.length + 1; i++) fMenu.newItem({
332 | str: i != panel.filter.menu.length ? (!i ? 'No filter' : panel.filter.menu[i]) : 'Auto-manage scroll',
333 | func: () => panel.set('Filter', i),
334 | checkItem: i == panel.filter.menu.length && !ppt.reset,
335 | checkRadio: i == ppt.filterBy && i < panel.filter.menu.length,
336 | separator: !i || i == panel.filter.menu.length - 1 || i == panel.filter.menu.length
337 | });
338 | fMenu.newItem({
339 | str: 'Configure filters...',
340 | func: () => panel.open('filters'),
341 | });
342 | }
343 |
344 | searchHistoryMenu() {
345 | sMenu.newMenu({});
346 | for (let i = 0; i < search.menu.length + 2; i++) sMenu.newItem({
347 | str: !i ? 'Query syntax help' : i < search.menu.length + 1 ? search.menu[i - 1].search : 'Clear history',
348 | func: () => this.setSearchHistory(i),
349 | flags: i != 1 || search.menu.length ? MF_STRING : MF_GRAYED,
350 | separator: !i || search.menu.length && i == search.menu.length
351 | });
352 | }
353 |
354 | searchMenu() {
355 | searchMenu.newMenu({});
356 | ['Copy', 'Cut', 'Paste'].forEach((v, i) => searchMenu.newItem({
357 | str: v,
358 | func: () => this.setEdit(i),
359 | flags: () => search.start == search.end && i < 2 || i == 2 && !search.paste ? MF_GRAYED : MF_STRING,
360 | separator: i == 1
361 | }));
362 | }
363 |
364 | getPaylistFlag(i) {
365 | const pln = plman.ActivePlaylist;
366 | const plnIsValid = pln != -1 && pln < plman.PlaylistCount;
367 | const plLockAdd = plnIsValid ? plman.GetPlaylistLockedActions(pln).includes('AddItems') : false;
368 | const plLockRemoveOrAdd = plnIsValid ? plman.GetPlaylistLockedActions(pln).includes('RemoveItems') || plman.GetPlaylistLockedActions(pln).includes('ReplaceItems') || plLockAdd : false;
369 | return !i && !plLockRemoveOrAdd || i == 1 && !plLockAdd || i == 2 || i == 3 && pop.nowp != -1 ? MF_STRING : MF_GRAYED
370 | }
371 |
372 | getSortData(d) {
373 | d.name = panel.propNames[ppt.viewBy];
374 | d.sortAlbumsByYearAfter = [``, `[$nodisplay{$sub(%date%,0#)}]%album%`, `[$nodisplay{$sub(%date%,0)}]%album%[ '['$sub(%date%,0)']']`, `[$nodisplay{$sub(4001,%date%)}]%album%`, `[$nodisplay{$sub(4002,%date%)}]%album%[ '['$sub(%date%,0)']']`];
375 | d.sortAlbumsByYearBefore = [``, `[$nodisplay{$sub(%date%,0)}]%album%`, `['['$sub(%date%,0)']' - ]%album%`, `[$nodisplay{$sub(4003,%date%)}]%album%`, `[$nodisplay{$sub(4004,%date%)}]['['$sub(%date%,0)']' - ]%album%`];
376 | d.sortAlbumByYear = ppt.yearBeforeAlbum ? d.sortAlbumsByYearBefore : d.sortAlbumsByYearAfter;
377 | d.sortIX = -1;
378 | d.sortType = 0;
379 | d.sortYear = [``, `$if2($nodisplay{$sub(%date%,0)},$nodisplay{-4000})`, `$nodisplay{$sub(4000,%date%)}`];
380 | d.value = ppt.get(d.name) || '';
381 | d.valueLength = d.value.length;
382 | let l = d.sortYear.length;
383 | while(l-- && l) {
384 | d.value = d.value.replace(RegExp($.regexEscape(d.sortYear[l]), 'g'), '')
385 | if (d.valueLength != d.value.length) {
386 | d.sortIX = l;
387 | d.valueLength = d.value.length;
388 | }
389 | }
390 | if (d.sortIX == -1) {
391 | l = d.sortAlbumsByYearAfter.length;
392 | while(l-- && l) {
393 | d.value = d.value.replace(RegExp($.regexEscape(d.sortAlbumsByYearAfter[l]), 'g'), '%album%');
394 | if (d.valueLength != d.value.length) {
395 | d.sortIX = l;
396 | d.valueLength = d.value.length;
397 | }
398 | }
399 | }
400 | if (d.sortIX == -1) {
401 | l = d.sortAlbumsByYearBefore.length;
402 | while(l-- && l) {
403 | d.value = d.value.replace(RegExp($.regexEscape(d.sortAlbumsByYearBefore[l]), 'g'), '%album%');
404 | if (d.valueLength != d.value.length) {
405 | d.sortIX = l;
406 | d.valueLength = d.value.length;
407 | }
408 | }
409 | }
410 | if (d.value.includes('//') && /%year%|%date%/.test(d.value)) d.sortType = 1;
411 | else if (d.value.includes('%album%')) d.sortType = 2;
412 |
413 | d.menuName = d.sortType ? 'Sort selected view' : 'Sort N/A for selected view pattern';
414 | }
415 |
416 | loadView(clearCache, view, sel) {
417 | ui.getColours();
418 | sbar.setCol();
419 | but.createImages();
420 | if (clearCache) img.clearCache();
421 | if (sel !== undefined) {
422 | const handle = sel >= panel.list.Count ? null : panel.list[sel];
423 | panel.set('view', view, true);
424 | if (handle) {
425 | const item = panel.list.Find(handle);
426 | let idx = -1;
427 | pop.tree.forEach((v, i) => {
428 | if (pop.inRange(item, v.item)) idx = i;
429 | });
430 | if (idx != -1) {
431 | if (!panel.imgView) pop.focusShow(idx);
432 | else pop.showItem(idx, 'focus');
433 | }
434 | }
435 | } else panel.set('view', view, true);
436 | but.refresh(true);
437 | }
438 |
439 | playlists_changed() {
440 | this.pl = [];
441 | for (let i = 0; i < plman.PlaylistCount; i++) this.pl.push({
442 | menuName: plman.GetPlaylistName(i).replace(/&/g, '&&'),
443 | name: plman.GetPlaylistName(i),
444 | ix: i
445 | });
446 | }
447 |
448 | rbtn_up(x, y, settingsBtnDn) {
449 | this.r_up = true;
450 | this.expandable = false;
451 | this.items = new FbMetadbHandleList();
452 | this.ix = pop.get_ix(x, y, true, false);
453 | this.nm = '';
454 | this.settingsBtnDn = settingsBtnDn;
455 | this.shift = vk.k('shift');
456 | this.show_context = false;
457 |
458 | let item = pop.tree[this.ix];
459 | let row = -1;
460 | const level = pop.tree.length > this.ix && this.ix != -1 ? !pop.inlineRoot ? item.level : Math.max(item.level - 1, 0) : -1;
461 |
462 | this.validItem = this.settingsBtnDn ? false : !panel.imgView ? y < panel.tree.y + pop.rows * sbar.row.h && pop.tree.length > this.ix && this.ix != -1 && (x < Math.round(ppt.treeIndent * level) + ui.icon.w + ppt.margin && (!item.track || item.root) || pop.check_ix(item, x, y, true)) : pop.tree.length > this.ix && this.ix != -1;
463 |
464 | if (!this.validItem && !this.settingsBtnDn && ppt.settingsShow && y > panel.search.sp) {
465 | this.ix = pop.row.i != -1 ? pop.row.i : !panel.imgView ? pop.tree.length - 1 : -1;
466 | if (this.ix < pop.tree.length && this.ix != -1) {
467 | item = pop.tree[this.ix];
468 | this.validItem = true;
469 | }
470 | }
471 |
472 | if (this.validItem) {
473 | if (!item.sel) {
474 | pop.clearSelected();
475 | item.sel = true;
476 | }
477 | pop.getTreeSel();
478 | this.expandable = pop.trackCount(pop.tree[this.ix].item) > this.treeExpandLimit || pop.tree[this.ix].track || panel.imgView ? false : true;
479 | if (this.expandable && pop.tree.length) {
480 | let count = 0;
481 | pop.tree.forEach((v, m, arr) => {
482 | if (m == this.ix || v.sel) {
483 | if (row == -1 || m < row) {
484 | row = m;
485 | this.nm = (v.level ? arr[v.par].srt[0] : '') + v.srt[0];
486 | this.nm = this.nm.toUpperCase();
487 | }
488 | count += pop.trackCount(v.item);
489 | this.expandable = count <= this.treeExpandLimit;
490 | }
491 | });
492 | }
493 | this.items = pop.getHandleList();
494 | this.show_context = true;
495 | } else this.items = pop.getHandleList('newItems');
496 |
497 | menu.load(x, y);
498 | this.r_up = false;
499 | }
500 |
501 | setActivePlaylist() {
502 | ppt.libSource = 0;
503 | ppt.fixedPlaylist = false;
504 | ppt.fixedPlaylistName = 'ActivePlaylist';
505 | if (panel.imgView) img.clearCache();
506 | lib.searchCache = {};
507 | if (ppt.showSource) panel.setRootName();
508 | lib.treeState(false, 2);
509 | }
510 |
511 | setAlbumart(i) {
512 | let clearCache = false;
513 | switch (i) {
514 | case 0:
515 | case 1:
516 | case 2:
517 | case 3:
518 | case 4:
519 | ppt.artId = i;
520 | break;
521 | case 5:
522 | case 6:
523 | case 7:
524 | ppt.albumArtGrpLevel = i - 5;
525 | break;
526 | case 8: {
527 | const key = `${panel.grp[ppt.viewBy].type.trim()}${panel.lines}`;
528 | const ok_callback = (status, input) => {
529 | if (status != 'cancel') {
530 | const albumArtGrpNames = $.jsonParse(ppt.albumArtGrpNames, {});
531 | albumArtGrpNames[key] = input;
532 | ppt.albumArtGrpNames = JSON.stringify(albumArtGrpNames);
533 | }
534 | }
535 | const caption = 'Change group name';
536 | const def = img.groupField;
537 | const prompt = 'Enter SINGULAR name, i.e. not plural\n\nName is pinned to VIEW PATTERN and GROUP LEVEL';
538 | const fallback = popUpBox.isHtmlDialogSupported() ? popUpBox.input(caption, prompt, ok_callback, '', def) : true;
539 | if (fallback) {
540 | let ns = '';
541 | let status = 'ok';
542 | try {
543 | ns = utils.InputBox(0, prompt, caption, def, true);
544 | } catch(e) {
545 | status = 'cancel';
546 | }
547 | ok_callback(status, ns);
548 | }
549 | break;
550 | }
551 | case 9:
552 | panel.open('albumArt');
553 | break;
554 | }
555 | this.loadView(clearCache, ppt.albumArtViewBy);
556 | }
557 |
558 | setEdit(i) {
559 | switch (i) {
560 | case 0:
561 | search.on_char(vk.copy);
562 | break;
563 | case 1:
564 | search.on_char(vk.cut);
565 | break;
566 | case 2:
567 | search.on_char(vk.paste, true);
568 | break;
569 | }
570 | }
571 |
572 | setFixedPlaylist(i) {
573 | ppt.fixedPlaylistName = this.pl[i].name;
574 | ppt.fixedPlaylist = true;
575 | ppt.libSource = 1;
576 | if (panel.imgView) img.clearCache();
577 | if (ppt.showSource) panel.setRootName();
578 | lib.searchCache = {};
579 | lib.treeState(false, 2);
580 | }
581 |
582 | setMode(i) {
583 | switch (i) {
584 | case 0:
585 | img.refresh(this.items);
586 | break;
587 | case 1:
588 | img.refresh('all');
589 | break;
590 | case 2:
591 | panel.zoomReset();
592 | break;
593 | case 3:
594 | lib.treeState(false, 2);
595 | break;
596 | case 4:
597 | window.Reload();
598 | break;
599 | }
600 | }
601 |
602 | setPlaylist(i) {
603 | switch (i) {
604 | case 0:
605 | pop.load(pop.sel_items, true, false, pop.autoPlay.send, false, false);
606 | panel.treePaint();
607 | lib.treeState(false, ppt.rememberTree);
608 | break;
609 | case 1:
610 | pop.load(pop.sel_items, true, true, false, false, false);
611 | lib.treeState(false, ppt.rememberTree);
612 | break;
613 | case 2:
614 | pop.sendToNewPlaylist();
615 | panel.treePaint();
616 | lib.treeState(false, ppt.rememberTree);
617 | break;
618 | case 3:
619 | pop.nowPlayingShow();
620 | break;
621 | case 4:
622 | lib.logTree();
623 | pop.clearTree();
624 | ppt.toggle('albumArtShow');
625 | panel.imgView = ppt.albumArtShow;
626 | this.loadView(false, !panel.imgView ? (ppt.artTreeSameView ? ppt.viewBy : ppt.treeViewBy) : (ppt.artTreeSameView ? ppt.viewBy : ppt.albumArtViewBy), pop.sel_items[0]);
627 | break;
628 | case 5:
629 | lib.logTree();
630 | pop.clearTree();
631 | this.loadView(false, !panel.imgView ? (ppt.artTreeSameView ? ppt.viewBy : ppt.treeViewBy) : (ppt.artTreeSameView ? ppt.viewBy : ppt.albumArtViewBy), pop.sel_items[0]);
632 | break;
633 | }
634 | }
635 |
636 | setSearchHistory(i) {
637 | switch (true) {
638 | case !i: {
639 | let fn = fb.FoobarPath + 'doc\\Query Syntax Help.html';
640 | if (!$.file(fn)) fn = fb.FoobarPath + 'Query Syntax Help.html';
641 | $.browser('"' + fn);
642 | break;
643 | }
644 | case i < search.menu.length + 1:
645 | panel.search.txt = search.menu[i - 1].search;
646 | search.menu[i - 1].accessed = Date.now();
647 | search.focus();
648 | but.setSearchBtnsHide();
649 | lib.search();
650 | break;
651 | case i == search.menu.length + 1:
652 | search.menu = [];
653 | ppt.searchHistory = JSON.stringify([]);
654 | break;
655 | }
656 | }
657 |
658 | setSource(i) {
659 | switch (i) {
660 | case 0:
661 | ppt.libSource = 1;
662 | ppt.fixedPlaylist = false;
663 | break;
664 | case 1:
665 | ppt.libSource = 2;
666 | ppt.fixedPlaylist = false;
667 | if (ppt.panelSourceMsg && popUpBox.isHtmlDialogSupported()) popUpBox.message();
668 | break;
669 | case 2: {
670 | const fixedPlaylistIndex = plman.FindPlaylist(ppt.fixedPlaylistName);
671 | if (fixedPlaylistIndex != -1) ppt.fixedPlaylist = true;
672 | ppt.libSource = ppt.fixedPlaylist ? 1 : 0;
673 | if (ppt.panelSourceMsg && popUpBox.isHtmlDialogSupported()) popUpBox.message();
674 | break;
675 | }
676 | }
677 | if (panel.imgView) img.clearCache();
678 | lib.searchCache = {};
679 | if (ppt.showSource) panel.setRootName();
680 | lib.treeState(false, 2);
681 | }
682 |
683 | setSourcePanel() {
684 | const ok_callback = (status, input) => {
685 | if (status != 'cancel') {
686 | ppt.panelSelectionPlaylist = input;
687 | }
688 | }
689 | const caption = 'Panel source name';
690 | const def = ppt.panelSelectionPlaylist;
691 | const prompt = 'Enter source panel name\n\n• To get the name, go to the library tree panel to be used as source\n• Press shift + windows key and choose configure\n• Paste the panel name or id at the top into here\n• Name is also used for a cache playlist that remembers last open state\n• Edit source panel name if required\n• For more than one source panel, use pipe separator, e.g. Genre|Artist'
692 | const fallback = popUpBox.isHtmlDialogSupported() ? popUpBox.input(caption, prompt, ok_callback, '', def) : true;
693 | if (fallback) {
694 | let ns = '';
695 | let status = 'ok';
696 | try {
697 | ns = utils.InputBox(0, prompt, caption, def, true);
698 | } catch(e) {
699 | status = 'cancel';
700 | }
701 | ok_callback(status, ns);
702 | }
703 | }
704 |
705 | setStatistics(i) {
706 | if (i < 12) {
707 | const curStatisticsShown = ppt.itemShowStatistics > 0;
708 | ppt.itemShowStatistics = i;
709 | ppt.itemShowStatisticsLast = ppt.itemShowStatistics;
710 | pop.tree.forEach(v => {
711 | v.id = '';
712 | v.count = ''; // has to reset parentheses if stats change off/on
713 | delete v.statistics;
714 | delete v._statistics;
715 | });
716 | pop.cache = {
717 | 'standard': {},
718 | 'search': {},
719 | 'filter': {}
720 | }
721 | pop.statisticsShow = ppt.itemShowStatistics;
722 | pop.label = !ppt.labelStatistics || !pop.statisticsShow ? '' : pop.statistics[pop.statisticsShow];
723 | const statisticsShown = ppt.itemShowStatistics > 0;
724 | if (panel.imgView && curStatisticsShown != statisticsShown) {
725 | img.labels = {statistics: ppt.itemShowStatistics ? 1 : 0}
726 | img.clearCache();
727 | panel.set('view', ppt.viewBy);
728 | }
729 | panel.treePaint();
730 | } else panel.open('display');
731 | }
732 |
733 | setTreeState(i) {
734 | switch (i) {
735 | case 0:
736 | pop.collapseAll();
737 | break;
738 | case 1:
739 | pop.expand(this.ix, this.nm);
740 | panel.setHeight(true);
741 | break;
742 | }
743 | pop.checkAutoHeight();
744 | }
745 |
746 | setView(i) {
747 | if (i < panel.menu.length) {
748 | if (ppt.artTreeSameView) {
749 | ppt.treeViewBy = i;
750 | ppt.albumArtViewBy = i;
751 | } else {
752 | if (!panel.imgView) ppt.treeViewBy = i;
753 | else ppt.albumArtViewBy = i;
754 | if (ppt.treeViewBy != ppt.albumArtViewBy) {
755 | ppt.set(panel.imgView ? 'Tree' : 'Tree Image', null);
756 | ppt.set(panel.imgView ? 'Tree Search' : 'Tree Image Search', null);
757 | }
758 | }
759 | panel.set('view', i);
760 | }
761 | }
762 |
763 | sortByDate(i, d) {
764 | let sortByIX = -1;
765 | if (i > 4) {
766 | ppt.toggle('yearBeforeAlbum');
767 | d.sortAlbumByYear = ppt.yearBeforeAlbum ? d.sortAlbumsByYearBefore : d.sortAlbumsByYearAfter;
768 | sortByIX = d.sortIX;
769 | } else sortByIX = i;
770 | if (d.sortType == 1) {
771 | if (i) {
772 | let str = d.value.split('//');
773 | if (str[1]) {
774 | str[1] = str[1].trim().replace(/(\|\s*)(.*?(%year%|%date%))/g, '$1' + d.sortYear[i] + '$2')
775 | if (!/\|.*?(%year%|%date%)/.test(str[1])) str[1] = d.sortYear[i] + str[1];
776 | d.value = str[0].trim() + ' // ' + str[1];
777 | } else d.value = str[0];
778 | }
779 | } else if (d.sortType == 2 && i && sortByIX != -1) {
780 | d.value = d.value.replace(/%album%/g, d.sortAlbumByYear[sortByIX])
781 | }
782 | if (d.sortType == 1 || sortByIX != -1) {
783 | const expanded = [];
784 | const ix = pop.get_ix(!panel.imgView ? 0 : img.panel.x + 1, (!panel.imgView || img.style.vertical ? panel.tree.y : panel.tree.x) + sbar.row.h / 2, true, false);
785 | const curName = ix != -1 ? pop.tree[ix].name : '';
786 | const scrollPos = sbar.scroll;
787 | const selected = [];
788 | pop.tree.forEach((v, i) => {
789 | const level = !ppt.rootNode ? v.level : v.level - 1; // 1 level memory: more is less reliable
790 | if (!level) {
791 | if (v.child.length) expanded.push(i);
792 | if (v.sel) selected.push(i);
793 | }
794 | });
795 | ppt.set(d.name, d.value);
796 | pop.clearTree();
797 | panel.getViews();
798 | this.setView(ppt.viewBy);
799 | if (ix < pop.tree.length) {
800 | const name = ix != -1 ? pop.tree[ix].name : '';
801 | if (name && name == curName) {
802 | expanded.forEach(v => {
803 | if (v < pop.tree.length) {
804 | const item = pop.tree[v];
805 | pop.branch(item, !item.root ? false : true, true);
806 | }
807 | });
808 | selected.forEach(v => {
809 | if (v < pop.tree.length) {
810 | pop.tree[v].sel = true;
811 | }
812 | });
813 | }
814 | }
815 | sbar.checkScroll(scrollPos, 'full', true);
816 | }
817 | }
818 | }
--------------------------------------------------------------------------------
/scripts/popupbox.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class PopUpBox {
4 | constructor() {
5 | this.getHtmlCode();
6 | this.ok = true;
7 | this.soFeat = {clipboard: true, gecko: true}
8 | }
9 |
10 | // Methods
11 |
12 | config(cfg, ppt, cfgWindow, ok_callback) {
13 | utils.ShowHtmlDialog(0, this.configHtmlCode, {
14 | data: [cfg, ppt, cfgWindow, ok_callback],
15 | resizable: true
16 | });
17 | }
18 |
19 | confirm(msg_title, msg_content, btn_yes_label, btn_no_label, height_adjust, h_center, confirm_callback) {
20 | utils.ShowHtmlDialog(0, this.confirmHtmlCode, {
21 | data: [msg_title, msg_content, btn_yes_label, btn_no_label, height_adjust, h_center, confirm_callback]
22 | });
23 | }
24 |
25 | getHtmlCode() {
26 | let cssPath = `${my_utils.packagePath}/assets/html/`;
27 | if (this.getWindowsVersion() === '6.1') {
28 | cssPath += 'styles7.css';
29 | } else {
30 | cssPath += 'styles10.css';
31 | }
32 | this.configHtmlCode = my_utils.getAsset('\\html\\config.html').replace(/href="styles10.css"/i, `href="${cssPath}"`);
33 | this.inputHtmlCode = my_utils.getAsset('\\html\\input.html').replace(/href="styles10.css"/i, `href="${cssPath}"`);
34 | this.messageHtmlCode = my_utils.getAsset('\\html\\messageBox.html').replace(/href="styles10.css"/i, `href="${cssPath}"`);
35 | this.confirmHtmlCode = my_utils.getAsset('\\html\\confirm.html').replace(/href="styles10.css"/i, `href="${cssPath}"`);
36 | }
37 |
38 | getWindowsVersion() {
39 | let version = '';
40 | try {
41 | version = (WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMajorVersionNumber')).toString();
42 | version += '.';
43 | version += (WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentMinorVersionNumber')).toString();
44 | return version;
45 | } catch (e) {}
46 | try {
47 | version = WshShell.RegRead('HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentVersion');
48 | return version;
49 | } catch (e) {}
50 | return '6.1';
51 | }
52 |
53 | input(title, msg, ok_callback, input, def) {
54 | utils.ShowHtmlDialog(0, this.inputHtmlCode, {
55 | data: [title, msg, 'Cancel', ok_callback, input, def]
56 | });
57 | }
58 |
59 | isHtmlDialogSupported() {
60 | if (ppt.isHtmlDialogSupported != 2) return ppt.isHtmlDialogSupported;
61 |
62 | if (typeof doc === 'undefined' || !doc) {
63 | this.soFeat.gecko = false;
64 | }
65 | if (this.soFeat.gecko) {
66 | let cache = null;
67 | let clText = 'test';
68 | try {
69 | cache = doc.parentWindow.clipboardData.getData('Text');
70 | } catch (e) {}
71 | try {
72 | doc.parentWindow.clipboardData.setData('Text', clText);
73 | clText = doc.parentWindow.clipboardData.getData('Text');
74 | } catch (e) {
75 | this.soFeat.clipboard = false;
76 | }
77 | if (cache) { // Just in case previous clipboard data is needed
78 | try {
79 | doc.parentWindow.clipboardData.setData('Text', cache);
80 | } catch (e) {}
81 | }
82 | if (clText !== 'test') {
83 | this.soFeat.clipboard = false;
84 | }
85 | } else {
86 | this.soFeat.clipboard = false;
87 | }
88 |
89 | ppt.isHtmlDialogSupported = this.soFeat.gecko && this.soFeat.clipboard || this.isIEInstalled() ? 1 : 0;
90 | if (!ppt.isHtmlDialogSupported) {
91 | const caption = 'Show HTML Dialog';
92 | const prompt =
93 | `A feature check indicates that Spider Monkey Panel show html dialog isn't supported by the current operating system.
94 |
95 | This is used to display options. The console will show alternatives on closing this dialog.
96 |
97 | Occassionally, the feature check may give the wrong answer.
98 |
99 | If you're using windows and have Internet Explorer support it should work, so enter 1 and press OK.
100 |
101 | The setting is saved in panel properties as the first item and can be changed there later.
102 |
103 | Supported-1; unsupported-0`;
104 | let ns = '';
105 | let status = 'ok'
106 | try {
107 | ns = utils.InputBox(0, prompt, caption, ppt.isHtmlDialogSupported, true);
108 | } catch(e) {
109 | status = 'cancel';
110 | }
111 | if (status != 'cancel') {
112 | ppt.isHtmlDialogSupported = ns == 0 ? 0 : 1;
113 | }
114 | }
115 | return ppt.isHtmlDialogSupported;
116 | }
117 |
118 | isIEInstalled() {
119 | const diskLetters = Array.from(Array(26)).map((e, i) => i + 65).map((x) => `${String.fromCharCode(x)}:\\`);
120 | const paths = ['Program Files\\Internet Explorer\\ieinstal.exe', 'Program Files (x86)\\Internet Explorer\\ieinstal.exe'];
121 | return diskLetters.some(d => {
122 | try { // Needed when permission error occurs and current SMP implementation is broken for some devices....
123 | return utils.IsDirectory(d) ? paths.some(p => utils.IsFile(d + p)) : false;
124 | } catch (e) {return false;}
125 | });
126 | }
127 |
128 | message() {
129 | utils.ShowHtmlDialog(0, this.messageHtmlCode, {
130 | data: [this.window_ok_callback, $.scale],
131 | selection: true
132 | });
133 | }
134 |
135 | window_ok_callback(status, clicked) {
136 | if (clicked) ppt.panelSourceMsg = false;
137 | }
138 | }
--------------------------------------------------------------------------------
/scripts/properties.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class PanelProperty {
4 | constructor(name, default_value) {
5 | this.name = name;
6 | this.default_value = default_value;
7 | this.value = ppt.get(this.name, default_value);
8 | }
9 |
10 | // Methods
11 |
12 | get() {
13 | return this.value;
14 | }
15 | set(new_value) {
16 | if (this.value !== new_value) {
17 | ppt.set(this.name, new_value);
18 | this.value = new_value;
19 | }
20 | }
21 | }
22 |
23 | class PanelProperties {
24 | constructor() {
25 | // this.name_list = {}; debug
26 | }
27 |
28 | // Methods
29 |
30 | init(type, properties, thisArg) {
31 | switch (type) {
32 | case 'auto':
33 | properties.forEach(v => {
34 | // this.validate(v); debug
35 | this.add(v);
36 | });
37 | break;
38 | case 'manual':
39 | properties.forEach(v => thisArg[v[2]] = this.get(v[0], v[1]));
40 | break;
41 | }
42 | }
43 |
44 | validate(item) {
45 | if (!$.isArray(item) || item.length !== 3 || typeof item[2] !== 'string') {
46 | throw (`invalid property: requires array: [string, any, string]`);
47 | }
48 |
49 | if (item[2] === 'add') {
50 | throw (`property_id: ${item[2]}\nThis id is reserved`);
51 | }
52 |
53 | if (this[item[2]] != null || this[item[2] + '_internal'] != null) {
54 | throw (`property_id: ${item[2]}\nThis id is already occupied`);
55 | }
56 |
57 | if (this.name_list[item[0]] != null) {
58 | throw (`property_name: ${item[0]}\nThis name is already occupied`);
59 | }
60 | }
61 |
62 | add(item) {
63 | // this.name_list[item[0]] = 1; debug
64 | this[`${item[2]}_internal`] = new PanelProperty(item[0], item[1]);
65 |
66 | Object.defineProperty(this, item[2], {
67 | get() {
68 | return this[`${item[2]}_internal`].get();
69 | },
70 | set(new_value) {
71 | this[`${item[2]}_internal`].set(new_value);
72 | }
73 | });
74 | }
75 |
76 | get(name, default_value) {
77 | return window.GetProperty(name, default_value);
78 | } // initialisation
79 |
80 | set(name, new_value) {
81 | return window.SetProperty(name, new_value);
82 | }
83 |
84 | toggle(name) {
85 | this[name] = !this[name];
86 | }
87 | }
88 |
89 | let properties = [
90 | ['- Show Html Dialog Unsupported-0 Supported-1 Autocheck-2', 2, 'isHtmlDialogSupported'],
91 | ['Action Mode', 0, 'actionMode'],
92 | ['Alt-Click Action', 0, 'altClickAction'],
93 |
94 | ['Colour Line Dark', false, 'colLineDark'],
95 | ['Colour Swap', false, 'swapCol'],
96 | ['Cover Auto-Fill', true, 'autoFill'],
97 | ['Cover Opacity (0-100)', 10, 'covAlpha'],
98 |
99 | ['Custom Colour Text', '171,171,190', 'text'],
100 | ['Custom Colour Text Highlight', '121,194,255', 'text_h'],
101 | ['Custom Colour Text Selected', '255,255,255', 'textSel'],
102 | ['Custom Colour Text Nowplaying Highlight', 'rgb(121-194-255)', 'nowp'],
103 | ['Custom Colour Search Text', '171,171,190', 'search'],
104 | ['Custom Colour Buttons', '113,125,147', 'txt_box'],
105 | ['Custom Colour Background', '4,39,68', 'bg'],
106 | ['Custom Colour Background Accent', '18,52,85', 'bg_h'],
107 | ['Custom Colour Background Selected', '37,71,108', 'bgSel'],
108 | ['Custom Colour Frame Hover', '35,132,182', 'frame'],
109 | ['Custom Colour Frame Selected', '49,145,198', 'bgSelframe'],
110 | ['Custom Colour Item Counts', '171,171,190', 'counts'],
111 | ['Custom Colour Node Collapse', '171,171,190', 'icon_c'],
112 | ['Custom Colour Node Expand', '171,171,190', 'icon_e'],
113 | ['Custom Colour Node Hover', '121,194,255', 'icon_h'],
114 | ['Custom Colour Node Lines', '48,70,90', 'line'],
115 | ['Custom Colour Separators', '48,71,90', 's_line'],
116 | ['Custom Colour Side Marker', '121,194,255', 'sideMarker'],
117 | ['Custom Colour Transparent Fill', '0,0,0,0.06', 'bgTrans'],
118 |
119 | ['Custom Colour Text Use', false, 'textUse'],
120 | ['Custom Colour Text Highlight Use', false, 'text_hUse'],
121 | ['Custom Colour Text Selected Use', false, 'textSelUse'],
122 | ['Custom Colour Text Nowplaying Highlight Use', false, 'nowpUse'],
123 | ['Custom Colour Search Text Use', false, 'searchUse'],
124 | ['Custom Colour Buttons Use', false, 'txt_boxUse'],
125 | ['Custom Colour Background Use', false, 'bgUse'],
126 | ['Custom Colour Background Accent Use', false, 'bg_hUse'],
127 | ['Custom Colour Background Selected Use', false, 'bgSelUse'],
128 | ['Custom Colour Frame Hover Use', false, 'frameUse'],
129 | ['Custom Colour Frame Selected Use', false, 'bgSelframeUse'],
130 | ['Custom Colour Item Counts Use', false, 'countsUse'],
131 | ['Custom Colour Node Collapse Use', false, 'icon_cUse'],
132 | ['Custom Colour Node Expand Use', false, 'icon_eUse'],
133 | ['Custom Colour Node Hover Use', false, 'icon_hUse'],
134 | ['Custom Colour Node Lines Use', false, 'lineUse'],
135 | ['Custom Colour Separators Use', false, 's_lineUse'],
136 | ['Custom Colour Side Marker Use', false, 'sideMarkerUse'],
137 | ['Custom Colour Transparent Fill Use', false, 'bgTransUse'],
138 |
139 | ['Custom Font', 'Segoe UI,16,0', 'custFont'],
140 | ['Custom Font Album Art Line 1', 'Segoe UI,1', 'custAlbumArtGrpFont'],
141 | ['Custom Font Album Art Line 2', 'Segoe UI Semibold,0', 'custAlbumArtLotFont'],
142 | ['Custom Font Album Art Line 3', 'Segoe UI,0', 'custAlbumArtDurFont'],
143 |
144 | ['Custom Font Use', false, 'custFontUse'],
145 | ['Custom Font Album Art Line 1 Use', false, 'custAlbumArtGrpFontUse'],
146 | ['Custom Font Album Art Line 2 Use', false, 'custAlbumArtLotFontUse'],
147 | ['Custom Font Album Art Line 3 Use', false, 'custAlbumArtDurFontUse'],
148 |
149 | ['Custom Font Node Icon', 'Segoe UI Symbol', 'custIconFont'],
150 | ['Custom Font Scroll Icon', 'Segoe UI Symbol', 'butCustIconFont'],
151 |
152 | ['Double-Click Action', 1, 'dblClickAction'],
153 | ['Facet View', false, 'facetView'],
154 | ['Filter By', 0, 'filterBy'],
155 | ['Font Size', 16, 'baseFontSize'],
156 | ['Full Line Selection', true, 'fullLineSelection'],
157 | ['Height', 578, 'pn_h'],
158 | ['Height Auto [Expand/Collapse With Root]', false, 'pn_h_auto'],
159 | ['Height Auto-Collapse', 100, 'pn_h_min'],
160 | ['Height Auto-Expand', 578, 'pn_h_max'],
161 | ['Highlight Row', 2, 'highLightRow'],
162 | ['Highlight Frame Image', false, 'frameImage'],
163 | ['Highlight Text', false, 'highLightText'],
164 | ['Hot Key [Focus Not Needed]: 1-10 // Assign Spider Monkey Panel index in keyboard shortcuts', 'CollapseAll,0,PlaylistAdd,0,PlaylistInsert,0,PlaylistNew,0,Search,0,SearchClear,0', 'hotKeys'],
165 |
166 | ['Image Blur Background Auto-Fill', false, 'blurAutofill'],
167 | ['Image Blur Background Level (%)', 90, 'blurTemp'],
168 | ['Image Blur Background Opacity (%)', 30, 'blurAlpha'],
169 | ['Image Current Root', 17, 'curRootImg'],
170 | ['Image Current No Artist', 2, 'curNoArtistImg'],
171 | ['Image Current No Cover', 6, 'curNoCoverImg'],
172 | ['Image Disk Cache Enabled', true, 'albumArtDiskCache'],
173 | ['Image Drop Shadow', false, 'albumArtDropShadow'],
174 | ['Image Group Level', 0, 'albumArtGrpLevel'],
175 | ['Image Group Names', JSON.stringify({}), 'albumArtGrpNames'],
176 | ['Image Flip Labels', false, 'albumArtFlipLabels'],
177 | ['Image Flow Mode', false, 'albumArtFlowMode'],
178 | ['Image Follow Selection Flow Mode', true, 'flowModeFollowSelection'],
179 | ['Image Follow Selection Standard Mode', false, 'stndModeFollowSelection'],
180 | ['Image Item Overlay', 1, 'itemOverlayType'],
181 | ['Image Label', 1, 'albumArtLabelType'],
182 | ['Image Memory Limit MB (0 = default)', 0, 'memoryLimit'],
183 | ['Image No Artist Images', JSON.stringify([]), 'noArtistImages'],
184 | ['Image No Cover Images', JSON.stringify([]), 'noCoverImages'],
185 | ['Image Preload Images In Disk Cache', false, 'albumArtPreLoad'],
186 | ['Image Root Images', JSON.stringify([]), 'rootImages'],
187 | ['Image Show Album Art', false, 'albumArtShow'],
188 | ['Image Show Index Letter', true, 'albumArtLetter'],
189 | ['Image Show Index Number', 0, 'albumArtLetterNo'],
190 | ['Image Show Index Year Auto', true, 'albumArtYearAuto'],
191 | ['Image Show Options', true, 'albumArtOptionsShow'],
192 | ['Image Style [Front] Regular-0 Auto-Fill-1 Circular-2', 1, 'imgStyleFront'],
193 | ['Image Style [Back] Regular-0 Auto-Fill-1 Circular-2', 1, 'imgStyleBack'],
194 | ['Image Style [Disc] Regular-0 Auto-Fill-1 Circular-2', 1, 'imgStyleDisc'],
195 | ['Image Style [Icon] Regular-0 Auto-Fill-1 Circular-2', 1, 'imgStyleIcon'],
196 | ['Image Style [Artist] Regular-0 Auto-Fill-1 Circular-2', 2, 'imgStyleArtist'],
197 | ['Image Thumbnail Gap Standard', 0, 'thumbNailGapStnd'],
198 | ['Image Thumbnail Gap Compact', 3, 'thumbNailGapCompact'],
199 | ['Image Thumbnail Size', 2, 'thumbNailSize'],
200 | ['Image Type', 0, 'artId'],
201 | ['Image View By: Same As Tree', true, 'artTreeSameView'],
202 |
203 | ['Initial Load Filters', true, 'initialLoadFilters'],
204 | ['Initial Load Views', true, 'initialLoadViews'],
205 | ['Key: Send to Playlist', 0, 'keyAction'],
206 | ['Library Auto-Sync', true, 'libAutoSync'],
207 | ['Library Sort Date Before Album', true, 'yearBeforeAlbum'],
208 |
209 | ['Library Source', 1, 'libSource'],
210 | ['Library Source: Active Playlist Follow Focus', true, 'followPlaylistFocus'],
211 | ['Library Source: Fixed Playlist', false, 'fixedPlaylist'],
212 | ['Library Source: Fixed Playlist Name', '', 'fixedPlaylistName'],
213 |
214 | ['Limit Menu Expand: 10-6000', 500, 'treeExpandLimit'],
215 | ['Limit Tree Auto Expand: 10-1000', 350, 'autoExpandLimit'],
216 | ['Line Padding', 5, 'verticalPad'],
217 | ['Line Padding Album Art', 2, 'verticalAlbumArtPad'],
218 | ['Margin', Math.round(8 * $.scale), 'margin'],
219 | ['Margin Override Top/Bottom (No Top Bar)', Math.round(8 * $.scale), 'marginTopBottom'],
220 | ['Middle-Click Action', 1, 'mbtnClickAction'],
221 | ['Mouse: Always Pointer (no hand)', false, 'mousePointerOnly'],
222 |
223 | ['Node: Auto Collapse', false, 'autoCollapse'],
224 | ['Node: Highlight on Hover', true, 'highLightNode'],
225 | ['Node: Item Counts Align Right', true, 'countsRight'],
226 | ['Node: Item Counts Hide-0 Tracks-1 Sub-Items-2', 1, 'nodeCounts'],
227 | ['Node: Root Hide-0 All Music-1 View Name-2', 1, 'rootNode'],
228 | ['Node: Root Inline Style', true, 'inlineRoot'],
229 | ['Node: Root Show Source', false, 'showSource'],
230 | ['Node: Show Lines', true, 'nodeLines'],
231 | ['Node: Show Tracks', true, 'showTracks'],
232 | ['Node: Style', 2, 'nodeStyle'],
233 | ['Node [Squares]: Windows', false, 'winNode'],
234 | ['Node Custom Icon: +|-', '\uE013|\uE015', 'iconCustom'],
235 | ['Node Custom Icon: Vertical Offset (%)', -2, 'iconVerticalPad'],
236 |
237 | ['Nowplaying Highlight', false, 'highLightNowplaying'],
238 | ['Nowplaying Highlight Last', false, 'highLightNowplayinglast'],
239 | ['Nowplaying Indicator', false, 'nowPlayingIndicator'],
240 | ['Nowplaying Indicator Last', false, 'nowPlayingIndicatorLast'],
241 | ['Nowplaying Sidemarker', false, 'nowPlayingSidemarker'],
242 | ['Nowplaying Sidemarker Last', false, 'nowPlayingSidemarkerLast'],
243 |
244 | ['Play on Enter or Send from Menu', false, 'autoPlay'],
245 | ['Playlist: Custom Sort', '', 'customSort'],
246 | ['Playlist: Default', 'Library View', 'libPlaylist'],
247 | ['Playlist: Default Activate on Change', true, 'activateOnChange'],
248 | ['Playlist: Panel Selection', 'Library Tree Panel Selection', 'panelSelectionPlaylist'],
249 | ['Playlist: Last Panel Selection', 'Library Tree Panel Selection', 'lastPanelSelectionPlaylist'],
250 | ['Playlist: Send to Current', false, 'sendToCur'],
251 | ['Prefixes to Strip or Swap (| Separator)', 'A|The', 'prefix'],
252 | ['Preset: Load Current View', false, 'presetLoadCurView'],
253 | ['Remember.PreSearch', true, 'rememberPreSearch'],
254 | ['Remember.Proc', false, 'process'],
255 | ['Remember.Tree', true, 'rememberTree'],
256 | ['Remember.View', false, 'rememberView'],
257 | ['Reset Tree', false, 'reset'],
258 | ['Row Stripes', true, 'rowStripes'],
259 |
260 | ['Scroll Step 0-10 (0 = Page)', 3, 'scrollStep'],
261 | ['Scroll Smooth Duration 0-5000 msec (Max)', 500, 'durationScroll'],
262 | ['Scroll Touch Flick Distance 0-10', 0.8, 'flickDistance'],
263 | ['Scroll Touch Flick Duration 0-5000 msec (Max)', 3000, 'durationTouchFlick'],
264 | ['Scroll: Smooth Scroll', true, 'smooth'],
265 | ['Scrollbar Arrow Custom Icon', '\uE0A0', 'arrowSymbol'],
266 | ['Scrollbar Arrow Custom Icon: Vertical Offset (%)', -24, 'sbarButPad'],
267 | ['Scrollbar Arrow Width', Math.round(11 * $.scale), 'sbarArrowWidth'],
268 | ['Scrollbar Button Type', 0, 'sbarButType'],
269 | ['Scrollbar Colour Grey-0 Blend-1', 1, 'sbarCol'],
270 | ['Scrollbar Grip MinHeight', Math.round(20 * $.scale), 'sbarGripHeight'],
271 | ['Scrollbar Height Prefer Full', true, 'sbarFullHeight'],
272 | ['Scrollbar Narrow Bar Width (0 = Auto)', 0, 'narrowSbarWidth'],
273 | ['Scrollbar Padding', 0, 'sbarPad'],
274 | ['Scrollbar Show', 1, 'sbarShow'],
275 | ['Scrollbar Type Default-0 Styled-1 Windows-2', 1, 'sbarType'],
276 | ['Scrollbar Width', Math.round(11 * $.scale), 'sbarWidth'],
277 | ['Scrollbar Width Bar', 11, 'sbarBase_w'],
278 | ['Scrollbar Windows Metrics', false, 'sbarWinMetrics'],
279 |
280 | ['Search Enter', false, 'searchEnter'],
281 | ['Search History', JSON.stringify([]), 'searchHistory'],
282 | ['Search Send', 1, 'searchSend'],
283 |
284 | ['Show Filter', true, 'filterShow'],
285 | ['Show Panel Source Message', true, 'panelSourceMsg'],
286 | ['Show Search', true, 'searchShow'],
287 | ['Show Settings', true, 'settingsShow'],
288 | ['Side Marker Width', 0, 'sideMarkerWidth'],
289 | ['Single-Click Action', 1, 'clickAction'],
290 | ['Statistics Show', 0, 'itemShowStatistics'],
291 | ['Statistics Show Last', 0, 'itemShowStatisticsLast'],
292 | ['Statistics Label Show', true, 'labelStatistics'],
293 | ['Statistics Titleformat Added', '[$date(%added%)]', 'tfAdded'],
294 | ['Statistics Titleformat Date', '[$year(%date%)]', 'tfDate'],
295 | ['Statistics Titleformat First Played', '[$date(%first_played%)]', 'tfFirstPlayed'],
296 | ['Statistics Titleformat Last Played', '[$date(%last_played%)]', 'tfLastPlayed'],
297 | ['Statistics Titleformat Playcount DataPinningScheme|Field', '%artist%%album%%discnumber%%tracknumber%%title%|%play_count%', 'tfPc'],
298 | ['Statistics Titleformat Rating', '[%rating%]', 'tfRating'],
299 | ['Statistics Titleformat Popularity', '[$meta(Track Statistics Last.fm,5[score])]', 'tfPopularity'],
300 | ['Statistics Tooltips Show', true, 'tooltipStatistics'],
301 |
302 | ['Theme', 0, 'theme'],
303 | ['Theme Panel Source Use Received Item Image', false, 'recItemImage'],
304 | ['Theme Background Image', false, 'themeBgImage'],
305 | ['Theme Colour', 3, 'themeColour'],
306 | ['Theme Light', false, 'themeLight'],
307 | ['Themed', false, 'themed'], // reserved: don't enable
308 | ['Touch Step 1-10', 1, 'touchStep'],
309 | ['Tree Auto Expand', false, 'treeAutoExpand'],
310 | ['Tree Auto Expand Single Items', false, 'treeAutoExpandSingle'],
311 | ['Tree Indent', Math.round(19 * $.scale), 'treeIndent'],
312 | ['Touch Control', false, 'touchControl'],
313 | ['View By', 1, 'viewBy'],
314 | ['View By Album Art', 1, 'albumArtViewBy'],
315 | ['View By Tree', 1, 'treeViewBy'],
316 | ['Zoom Filter Size (%)', 100, 'zoomFilter'],
317 | ['Zoom Font Size (%)', 100, 'zoomFont'],
318 | ['Zoom Node Size (%)', 100, 'zoomNode'],
319 | ['Zoom Image Size (%)', 100, 'zoomImg'],
320 | ['Zoom Tooltip [Button] (%)', 100, 'zoomTooltipBut']
321 | ];
322 |
323 | const ppt = new PanelProperties;
324 | ppt.init('auto', properties);
325 | if (!$.file('C:\\check_local\\1450343922.txt')) ppt.themed = false;
326 |
327 | if (ppt.get('Tree List View')) {
328 | ppt.facetView = ppt.get('Tree List View');
329 | ppt.set('Tree List View', null);
330 | }
331 | ppt.set('Image Pre-Load Images In Disk Cache', null);
332 | ppt.set('Image Root Collage', null);
333 | ppt.set('Image Show Index Number', null);
334 | ppt.set('Image Show Index Year Auto', null);
335 | ppt.set('Node: Item Show Duration', null);
336 | ppt.set('Node [Squares]: Windows 0 or 1', null);
337 | properties = undefined;
--------------------------------------------------------------------------------
/scripts/scrollbar.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class Scrollbar {
4 | constructor() {
5 | this.active = true;
6 | this.alpha = 255;
7 | this.alpha1 = this.alpha;
8 | this.alpha2 = 255;
9 | this.but_h = 11;
10 | this.clock = Date.now();
11 | this.col = {};
12 | this.count = -1;
13 | this.cur_active = true;
14 | this.cur_hover = false;
15 | this.delta = 0;
16 | this.drag_distance_per_row = 0;
17 | this.draw_timer = null;
18 | this.drawBar = true;
19 | this.elap = 0;
20 | this.event = 'scroll';
21 | this.hover = false;
22 | this.init = true;
23 | this.inStep = 18;
24 | this.max_scroll = 0;
25 | this.ratio = 1;
26 | this.rows_drawn = 0;
27 | this.scroll = 0;
28 | this.scrollable_lines = 0;
29 | this.scrollIX = 0;
30 | this.scrollStep = 3;
31 | this.start = 0;
32 | this.timer_but = null;
33 | this.vertical = true;
34 | this.x = 0;
35 | this.y = 0;
36 | this.w = 0;
37 | this.h = 0;
38 |
39 | this.bar = {
40 | isDragging: false,
41 | h: 0,
42 | timer: null,
43 | y: 0
44 | }
45 |
46 | this.initial = {
47 | drag: {
48 | x: 0,
49 | y: 0
50 | },
51 | scr: 1,
52 | x: -1,
53 | y: -1
54 | }
55 |
56 | this.narrow = {
57 | show: ppt.sbarShow == 1 ? true : false,
58 | x: 0
59 | }
60 |
61 | this.row = {
62 | count: 0,
63 | h: 0
64 | }
65 |
66 | this.scrollbar = {
67 | cur_zone: false,
68 | height: 0,
69 | travel: 0,
70 | zone: false
71 | }
72 |
73 | this.touch = {
74 | dn: false,
75 | end: 0,
76 | start: 0,
77 | amplitude: 0,
78 | counter: 0,
79 | frame: 0,
80 | lastDn: Date.now(),
81 | min: 10 * $.scale,
82 | diff: 2 * $.scale,
83 | offset: 0,
84 | reference: -1,
85 | startTime: 0,
86 | ticker: null,
87 | timestamp: 0,
88 | velocity: 1
89 | }
90 |
91 | this.duration = {
92 | drag: 200,
93 | inertia: ppt.durationTouchFlick,
94 | full: ppt.durationScroll
95 | }
96 |
97 | this.duration.scroll = Math.round(this.duration.full * 0.8)
98 | this.duration.step = Math.round(this.duration.full * 2 / 3)
99 | this.duration.bar = this.duration.full;
100 | this.duration.barFast = this.duration.step;
101 |
102 | this.pageThrottle = $.throttle(dir => this.checkScroll(Math.round((this.scroll + dir * -(this.rows_drawn - 1) * this.row.h) / this.row.h) * this.row.h, 'full'), 100);
103 |
104 | this.scrollThrottle = $.throttle(() => {
105 | this.delta = this.scroll;
106 | this.scrollTo();
107 | }, 16);
108 |
109 | this.hideDebounce = $.debounce(() => {
110 | if ((ppt.countsRight || ppt.itemShowStatistics) && !panel.imgView && !ppt.facetView && (!ppt.rootNode || pop.inlineRoot)) return;
111 | if (this.scrollbar.zone) return;
112 | this.active = false;
113 | this.cur_active = this.active;
114 | this.hover = false;
115 | this.cur_hover = false;
116 | this.alpha = this.alpha1;
117 | panel.treePaint();
118 | }, 5000);
119 |
120 | this.minimiseDebounce = $.debounce(() => {
121 | if (this.scrollbar.zone) return panel.treePaint();
122 | this.narrow.show = true;
123 | if (ppt.sbarShow == 1) but.setScrollBtnsHide(true, true);
124 | this.scrollbar.cur_zone = this.scrollbar.zone;
125 | this.hover = false;
126 | this.cur_hover = false;
127 | this.alpha = this.alpha1;
128 | panel.treePaint();
129 | }, 1000);
130 |
131 | this.updDebounce = $.debounce(() => lib.treeState(false, ppt.rememberTree), 400);
132 |
133 | this.setCol();
134 | }
135 |
136 | // Methods
137 |
138 | but(dir) {
139 | this.checkScroll(Math.round((this.scroll + dir * -this.row.h) / this.row.h) * this.row.h, 'step');
140 | if (!this.timer_but) {
141 | this.timer_but = setInterval(() => {
142 | if (this.count > 6) {
143 | this.checkScroll(this.scroll + dir * -this.row.h, 'step');
144 | } else this.count++;
145 | }, 40);
146 | }
147 | }
148 |
149 | calcItem_y() {
150 | const ix = Math.round(this.delta / this.row.h + 0.4);
151 | panel.tree.x = Math.round(this.row.h * ix - this.delta);
152 | panel.tree.y = Math.round(this.row.h * ix + panel.search.h - this.delta);
153 | }
154 |
155 | checkScroll(new_scroll, type, memory) {
156 | const b = $.clamp(new_scroll, 0, this.max_scroll);
157 | if (b == this.scroll) return;
158 | this.scroll = b;
159 | if (ppt.smooth && !memory) {
160 | this.elap = 16;
161 | this.event = type || 'scroll';
162 | panel.tree.x = 0;
163 | panel.tree.y = panel.search.h;
164 | this.start = this.delta;
165 | if (this.event != 'drag') {
166 | if (this.bar.isDragging && Math.abs(this.delta - this.scroll) > (!panel.imgView ? this.scrollbar.height : this.scrollbar.height * 3)) this.event = 'barFast';
167 | this.clock = Date.now();
168 | if (!this.draw_timer) {
169 | this.scrollTimer();
170 | this.smoothScroll();
171 | }
172 | } else this.scrollDrag();
173 | } else {
174 | this.scrollThrottle();
175 | this.updDebounce();
176 | }
177 | }
178 |
179 | draw(gr) {
180 | if (!ppt.sbarShow) return;
181 | if (this.drawBar && this.active) {
182 | let sbar_x = this.x;
183 | let sbar_w = this.w;
184 | let sbar_y = this.y;
185 | let sbar_h = this.h;
186 | if (ppt.sbarShow == 1) {
187 | sbar_x = !this.narrow.show ? this.x : this.narrow.x;
188 | sbar_w = !this.narrow.show ? this.w : ui.sbar.narrowWidth;
189 | sbar_y = !this.narrow.show ? this.y : this.narrow.y;
190 | sbar_h = !this.narrow.show ? this.h : ui.sbar.narrowWidth;
191 | }
192 | switch (ui.sbar.type) {
193 | case 0:
194 | if (ppt.rowStripes && ppt.sbarShow == 2 && !this.vertical) gr.FillSolidRect(this.x, this.y, this.w, this.h, ui.col.bg1);
195 | if (this.vertical) gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, this.narrow.show ? this.col[this.alpha2] : !this.bar.isDragging ? this.col[this.alpha] : this.col['max']);
196 | else gr.FillSolidRect(this.x + this.bar.x, sbar_y, this.bar.h, sbar_h, this.narrow.show ? this.col[this.alpha2] : !this.bar.isDragging ? this.col[this.alpha] : this.col['max']);
197 | break;
198 | case 1:
199 | if (this.vertical) {
200 | if (!this.narrow.show || ppt.sbarShow != 1) gr.FillSolidRect(sbar_x, this.y - panel.sbar_o, this.w, this.h + panel.sbar_o * 2, this.col['bg']);
201 | gr.FillSolidRect(sbar_x, this.y + this.bar.y, sbar_w, this.bar.h, this.narrow.show ? this.col[this.alpha2] : !this.bar.isDragging ? this.col[this.alpha] : this.col['max']);
202 | } else {
203 | if (!this.narrow.show || ppt.sbarShow != 1) gr.FillSolidRect(this.x - panel.sbar_o, sbar_y, this.w + panel.sbar_o * 2, this.h, this.col['bg']);
204 | gr.FillSolidRect(this.x + this.bar.x, sbar_y, this.bar.h, sbar_h, this.narrow.show ? this.col[this.alpha2] : !this.bar.isDragging ? this.col[this.alpha] : this.col['max']);
205 | }
206 | break;
207 | case 2:
208 | if (this.vertical) {
209 | ui.theme.SetPartAndStateID(6, 1);
210 | if (!this.narrow.show || ppt.sbarShow != 1) ui.theme.DrawThemeBackground(gr, sbar_x, this.y, sbar_w, this.h);
211 | ui.theme.SetPartAndStateID(3, this.narrow.show ? 2 : !this.hover && !this.bar.isDragging ? 1 : this.hover && !this.bar.isDragging ? 2 : 3);
212 | ui.theme.DrawThemeBackground(gr, sbar_x, this.y + this.bar.y, sbar_w, this.bar.h);
213 | } else {
214 | ui.theme.SetPartAndStateID(4, 1);
215 | if (!this.narrow.show || ppt.sbarShow != 1) ui.theme.DrawThemeBackground(gr, this.x, sbar_y, this.w, sbar_h);
216 | ui.theme.SetPartAndStateID(2, this.narrow.show ? 2 : !this.hover && !this.bar.isDragging ? 1 : this.hover && !this.bar.isDragging ? 2 : 3);
217 | ui.theme.DrawThemeBackground(gr, this.x + this.bar.x, sbar_y, this.bar.h, sbar_h);
218 | }
219 | break;
220 | }
221 |
222 | if (!panel.imgView || !img.letter.show || !this.bar.isDragging) return;
223 | const ix = img.style.vertical ? (Math.ceil((panel.m.y + sbar.delta - img.panel.y) / img.row.h) - 1) * (!ppt.albumArtFlowMode ? img.columns : 1) : Math.ceil((panel.m.x + sbar.delta - img.panel.x) / img.columnWidth) - 1;
224 | if (ix < 0 || ix > pop.tree.length - 1) return;
225 | let letter = panel.lines == 1 || !ppt.albumArtFlipLabels ? pop.tree[ix].grp : pop.tree[ix].lot;
226 | if (panel.colMarker) letter = letter.replace(/@!#.*?@!#/g, '');
227 | if (img.letter.no != 0) {
228 | if (img.letter.albumArtYearAuto) {
229 | let sub = letter.substring(0, 4);
230 | if (/\d{4}/.test(sub)) letter = sub;
231 | else {
232 | sub = letter.substring(0, 6);
233 | if (/(\[|\()\d{4}(\]|\))/.test(sub)) letter = sub;
234 | else {
235 | letter = letter.substring(0, img.letter.no);
236 | }
237 | }
238 | } else letter = letter.substring(0, img.letter.no);
239 | }
240 | const letter_w = gr.CalcTextWidth(letter, ui.font.main) + img.letter.w;
241 | const w1 = Math.min(letter_w, ui.w - img.panel.x - img.letter.w);
242 | const w2 = Math.min(letter_w, ui.w - img.panel.x) + 1;
243 | if (img.style.vertical) gr.FillSolidRect(0, this.y + this.bar.y + this.bar.h / 2 - img.text.h / 2, w2, img.text.h + 2, ui.col.bg6);
244 | if (img.style.vertical) gr.FillSolidRect(0, this.y + this.bar.y + this.bar.h / 2 - img.text.h / 2, w2, img.text.h + 2, ui.col.bg3);
245 | if (img.style.vertical) gr.GdiDrawText(letter, ui.font.main, ui.col.text, ui.l.w + img.letter.w / 2, this.y + this.bar.y + this.bar.h / 2 - img.text.h / 2, w1, img.text.h, panel.lc);
246 | else gr.GdiDrawText(letter, ui.font.main, ui.col.text, this.x + this.bar.x + this.bar.h / 2 - w1 / 2, sbar_y - img.text.h, w1, img.text.h, panel.cce);
247 | }
248 | }
249 |
250 | lbtn_dblclk(p_x, p_y) {
251 | const x = p_x - this.x;
252 | const y = p_y - this.y;
253 | let dir;
254 | switch (true) {
255 | case this.vertical:
256 | if (x < 0 || x > this.w || y < 0 || y > this.h || this.row.count <= this.rows_drawn) return;
257 | if (y < this.but_h || y > this.h - this.but_h) return;
258 | if (y < this.bar.y) dir = 1; // above bar
259 | else if (y > this.bar.y + this.bar.h) dir = -1; // below bar
260 | if (y < this.bar.y || y > this.bar.y + this.bar.h) this.shiftPage(dir, this.nearestY(y));
261 | break;
262 | case !this.vertical:
263 | if (y < 0 || y > this.h || x < 0 || x > this.w || this.row.count <= this.rows_drawn) return;
264 | if (x < this.but_h || x > this.w - this.but_h) return;
265 | if (x < this.bar.x) dir = 1; // above bar
266 | else if (x > this.bar.x + this.bar.h) dir = -1; // below bar
267 | if (x < this.bar.x || x > this.bar.x + this.bar.h) this.shiftPage(dir, this.nearestX(x));
268 | break;
269 | }
270 | }
271 |
272 | lbtn_dn(p_x, p_y) {
273 | if (!ppt.sbarShow && ppt.touchControl) return this.tap(p_x, p_y);
274 | const x = p_x - this.x;
275 | const y = p_y - this.y;
276 | let dir;
277 | switch (true) {
278 | case this.vertical:
279 | if (x > this.w || y < 0 || y > this.h || this.row.count <= this.rows_drawn) return;
280 | if (x < 0) {
281 | if (!ppt.touchControl) return;
282 | else return this.tap(p_x, p_y);
283 | }
284 | if (y < this.but_h || y > this.h - this.but_h) return;
285 | if (y < this.bar.y) dir = 1; // above bar
286 | else if (y > this.bar.y + this.bar.h) dir = -1; // below bar
287 | if (y < this.bar.y || y > this.bar.y + this.bar.h) this.shiftPage(dir, this.nearestY(y));
288 | else { // on bar
289 | this.bar.isDragging = true;
290 | but.Dn = true;
291 | window.RepaintRect(this.x, this.y, this.w, this.h);
292 | this.initial.drag.y = y - this.bar.y + this.but_h;
293 | }
294 | break;
295 | case !this.vertical:
296 | if (y > this.h || x < 0 || x > this.w || this.row.count <= this.rows_drawn) return;
297 | if (y < 0) {
298 | if (!ppt.touchControl) return;
299 | else return this.tap(p_x, p_y);
300 | }
301 | if (x < this.but_h || x > this.w - this.but_h) return;
302 | if (x < this.bar.x) dir = 1; // above bar
303 | else if (x > this.bar.x + this.bar.h) dir = -1; // below bar
304 | if (x < this.bar.x || x > this.bar.x + this.bar.h) this.shiftPage(dir, this.nearestX(x));
305 | else { // on bar
306 | this.bar.isDragging = true;
307 | but.Dn = true;
308 | window.RepaintRect(this.x, this.y, this.w, this.h);
309 | this.initial.drag.x = x - this.bar.x + this.but_h;
310 | }
311 | break;
312 | }
313 | }
314 |
315 | lbtn_up() {
316 | if (this.touch.dn) {
317 | this.touch.dn = false;
318 | clearInterval(this.touch.ticker);
319 | if (!this.touch.counter) this.track(true);
320 | if (Math.abs(this.touch.velocity) > this.touch.min && Date.now() - this.touch.startTime < 300) {
321 | this.touch.amplitude = ppt.flickDistance * this.touch.velocity * ppt.touchStep;
322 | this.touch.timestamp = Date.now();
323 | this.checkScroll(Math.round((this.scroll + this.touch.amplitude) / this.row.h) * this.row.h, 'inertia');
324 | }
325 | }
326 | if (!this.hover && this.bar.isDragging) this.paint();
327 | else window.RepaintRect(this.x, this.y, this.w, this.h);
328 | if (this.bar.isDragging) {
329 | this.bar.isDragging = false;
330 | img.loadThrottle();
331 | but.Dn = false;
332 | }
333 | this.initial.drag.x = 0;
334 | this.initial.drag.y = 0;
335 | if (this.timer_but) {
336 | clearTimeout(this.timer_but);
337 | this.timer_but = null;
338 | }
339 | this.count = -1;
340 | }
341 |
342 | leave() {
343 | if (this.touch.dn) this.touch.dn = false;
344 | if (!men.r_up) this.scrollbar.zone = false;
345 | if (this.bar.isDragging || ppt.sbarShow == 1) return;
346 | this.hover = !this.hover;
347 | this.paint();
348 | this.hover = false;
349 | this.cur_hover = false;
350 | }
351 |
352 | logScroll() {
353 | this.scrollIX = $.clamp(Math.round(sbar.scroll / this.row.h + 0.4), 0, pop.tree.length - 1);
354 | }
355 |
356 | metrics(x, y, w, h, rows_drawn, row_h, vertical) {
357 | this.vertical = vertical;
358 | if (this.vertical) {
359 | this.x = x;
360 | this.y = Math.round(y);
361 | } else {
362 | this.x = Math.round(x);
363 | this.y = y;
364 | }
365 | this.w = w;
366 | this.h = h;
367 | this.rows_drawn = rows_drawn;
368 | this.row.h = row_h;
369 | this.but_h = ui.sbar.but_h;
370 | this.scrollStep = $.clamp(ppt.scrollStep, 0, 10);
371 | if (panel.imgView && this.scrollStep != 0) this.scrollStep = Math.max(Math.round(this.scrollStep /= 3), 1);
372 | // draw info
373 | this.scrollbar.height = this.vertical ? Math.round(this.h - this.but_h * 2) : Math.round(this.w - this.but_h * 2);
374 | this.bar.h = Math.max(Math.round(this.scrollbar.height * this.rows_drawn / this.row.count), $.clamp(this.scrollbar.height / 2, 5, ppt.sbarShow == 2 ? ppt.sbarGripHeight : ppt.sbarGripHeight * 2));
375 | this.scrollbar.travel = this.scrollbar.height - this.bar.h;
376 | // scrolling info
377 | this.scrollable_lines = this.rows_drawn > 0 ? this.row.count - this.rows_drawn : 0;
378 | this.drawBar = this.scrollable_lines > 0 && this.scrollbar.height > 1;
379 | this.ratio = this.row.count / this.scrollable_lines;
380 | this.bar.x = this.bar.y = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h);
381 | this.drag_distance_per_row = this.scrollbar.travel / this.scrollable_lines;
382 | // panel info
383 | if (this.vertical) this.narrow.x = this.x + this.w - $.clamp(ui.sbar.narrowWidth, 5, this.w);
384 | else this.narrow.y = this.y + this.h - $.clamp(ui.sbar.narrowWidth, 5, this.h);
385 | panel.tree.w = ui.w -
386 | Math.max(ppt.sbarShow && this.scrollable_lines > 0 ? (!ppt.countsRight && !ppt.itemShowStatistics) || ppt.facetView ? ui.sbar.sp + ui.sz.sel :
387 | ppt.sbarShow == 2 ? ui.sbar.sp + ui.sz.margin :
388 | ppt.sbarShow == 1 ? (ui.w - this.narrow.x) + ui.sz.marginRight + Math.max(this.w - 11, 0) : ui.sz.sel : ui.sz.sel, ui.sz.margin);
389 | pop.id = ui.id.tree + ppt.fullLineSelection + panel.tree.w + panel.imgView + ppt.albumArtLabelType + ppt.albumArtFlipLabels + ppt.albumArtFlowMode;
390 | panel.tree.stripe.w = ppt.sbarShow == 2 && this.scrollable_lines > 0 ? ui.w - ui.sbar.sp - ui.sz.pad : ui.w;
391 | panel.tree.sel.w = ppt.sbarShow == 2 && this.scrollable_lines > 0 ? ui.w - ui.sbar.sp - ui.sz.pad * 2 : ui.w - ui.sz.pad * 2;
392 | this.max_scroll = this.scrollable_lines * this.row.h;
393 | if (panel.imgView && this.vertical && this.row.h > img.panel.h) this.max_scroll -= this.row.h;
394 | if (panel.imgView && !this.vertical && this.row.h > ui.w) this.max_scroll -= this.row.h;
395 | if (ppt.sbarShow != 1) but.setScrollBtnsHide();
396 | }
397 |
398 | move(p_x, p_y) {
399 | this.active = true;
400 | if (p_x > this.x && p_y > this.y) {
401 | this.scrollbar.zone = true;
402 | this.narrow.show = false;
403 | if (ppt.sbarShow == 1 && this.scrollbar.zone != this.scrollbar.cur_zone) {
404 | but.setScrollBtnsHide(!this.scrollbar.zone || this.scrollable_lines < 1, true);
405 | this.scrollbar.cur_zone = this.scrollbar.zone;
406 | }
407 | } else this.scrollbar.zone = false;
408 | if (ppt.sbarShow == 1) {
409 | this.minimiseDebounce();
410 | this.hideDebounce();
411 | }
412 | if (ppt.touchControl) {
413 | const delta = this.touch.reference - (this.vertical ? p_y : p_x);
414 | if (delta > this.touch.diff || delta < -this.touch.diff) {
415 | this.touch.reference = this.vertical ? p_y : p_x;
416 | if (ppt.flickDistance) this.touch.offset = $.clamp(this.touch.offset + delta, 0, this.max_scroll);
417 | if (this.touch.dn) {
418 | ui.id.dragDrop = ui.id.touch_dn = -1;
419 | }
420 | }
421 | }
422 | if (this.touch.dn && !vk.k('zoom')) {
423 | const now = Date.now();
424 | if (now - this.touch.startTime > 300) this.touch.startTime = now;
425 | this.touch.lastDn = now;
426 | this.checkScroll(this.initial.scr + (this.vertical ? this.initial.y - p_y : this.initial.x - p_x) * ppt.touchStep, ppt.touchStep == 1 ? 'drag' : 'scroll');
427 | return;
428 | }
429 | const x = p_x - this.x;
430 | const y = p_y - this.y;
431 | if (this.vertical) {
432 | if (x < 0 || x > this.w || y > this.bar.y + this.bar.h || y < this.bar.y || but.Dn) this.hover = false;
433 | else this.hover = true;
434 | } else {
435 | if (y < 0 || y > this.h || x > this.bar.x + this.bar.h || x < this.bar.x || but.Dn) this.hover = false;
436 | else this.hover = true;
437 | }
438 | if (!this.bar.timer && (this.hover != this.cur_hover || this.active != this.cur_active)) {
439 | this.init = false;
440 | this.paint();
441 | this.cur_active = this.active;
442 | }
443 | if (!this.bar.isDragging || this.row.count <= this.rows_drawn) return;
444 | this.checkScroll(Math.round(this.vertical ? y - this.initial.drag.y : x - this.initial.drag.x) / this.drag_distance_per_row * this.row.h, 'bar');
445 | }
446 |
447 | nearestY(y) {
448 | y = (y - this.but_h) / this.scrollbar.height * this.max_scroll;
449 | y = y / this.row.h;
450 | y = Math.round(y) * this.row.h;
451 | return y;
452 | }
453 |
454 | nearestX(x) {
455 | x = (x - this.but_h) / this.scrollbar.height * this.max_scroll;
456 | x = x / this.row.h;
457 | x = Math.round(x) * this.row.h;
458 | return x;
459 | }
460 |
461 | paint() {
462 | if (this.init) return;
463 | this.alpha = this.hover ? this.alpha1 : this.alpha2;
464 | clearTimeout(this.bar.timer);
465 | this.bar.timer = null;
466 | this.bar.timer = setInterval(() => {
467 | this.alpha = this.hover ? Math.min(this.alpha += this.inStep, this.alpha2) : Math.max(this.alpha -= 3, this.alpha1);
468 | window.RepaintRect(this.x, this.y, this.w, this.h);
469 | if (this.hover && this.alpha == this.alpha2 || !this.hover && this.alpha == this.alpha1) {
470 | this.cur_hover = this.hover;
471 | clearTimeout(this.bar.timer);
472 | this.bar.timer = null;
473 | }
474 | }, 25);
475 | }
476 |
477 | position(Start, End, Elapsed, Duration, Event) {
478 | if (Elapsed > Duration) return End;
479 | if (Event == 'drag') return;
480 | const n = Elapsed / Duration;
481 | return Start + (End - Start) * ease[Event](n);
482 | }
483 |
484 | reset() {
485 | this.delta = this.scroll = 0;
486 | this.metrics(this.x, this.y, this.w, this.h, this.rows_drawn, this.row.h, this.vertical);
487 | lib.treeState(false, ppt.rememberTree);
488 | }
489 |
490 | resetAuto() {
491 | this.minimiseDebounce.cancel();
492 | this.hideDebounce.cancel();
493 | if (!ppt.sbarShow) but.setScrollBtnsHide(true);
494 | if (ppt.sbarShow == 1) {
495 | but.setScrollBtnsHide(true, true);
496 | this.narrow.show = true;
497 | this.scrollbar.cur_zone = false;
498 | }
499 | }
500 |
501 | scrollDrag() {
502 | this.delta = this.scroll;
503 | this.scrollTo();
504 | this.calcItem_y();
505 | this.updDebounce();
506 | }
507 |
508 | scrollFinish() {
509 | if (!this.draw_timer) return;
510 | this.delta = this.scroll;
511 |
512 | if (this.vertical) this.bar.y = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h);
513 | else this.bar.x = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h);
514 | ppt.rememberTree ? lib.treeState(false, ppt.rememberTree) : panel.treePaint();
515 | this.calcItem_y();
516 | clearTimeout(this.draw_timer);
517 | this.draw_timer = null;
518 | }
519 |
520 | scrollRound() {
521 | if (this.vertical) {
522 | if (panel.tree.y == panel.search.h) return;
523 | this.checkScroll((panel.tree.y < panel.search.h ? Math.floor(this.scroll / this.row.h) : Math.ceil(this.scroll / this.row.h)) * this.row.h);
524 | } else {
525 | if (panel.tree.x == 0) return;
526 | this.checkScroll((panel.tree.x < 0 ? Math.floor(this.scroll / this.row.h) : Math.ceil(this.scroll / this.row.h)) * this.row.h);
527 | }
528 | }
529 |
530 | setRows(row_count) {
531 | if (!row_count) {
532 | panel.tree.x = 0;
533 | panel.tree.y = panel.search.h;
534 | }
535 | this.row.count = row_count;
536 | this.metrics(this.x, this.y, this.w, this.h, this.rows_drawn, this.row.h, this.vertical);
537 | }
538 |
539 | scrollMemory(h, j) {
540 | let scroll = !h ? j * this.row.h : (j - 3) * this.row.h;
541 | if (panel.imgView && img.style.vertical) {
542 | scroll /= img.columns;
543 | scroll = scroll - scroll % this.row.h;
544 | }
545 | this.checkScroll(scroll, 'full', true);
546 | }
547 |
548 | setScroll() {
549 | const b = $.clamp(this.scrollIX * this.row.h, 0, this.max_scroll);
550 | if (b == this.scroll) return;
551 | this.scroll = b;
552 | panel.tree.x = 0;
553 | panel.tree.y = panel.search.h;
554 | this.scrollThrottle();
555 | this.updDebounce();
556 | }
557 |
558 | scrollTimer() {
559 | this.draw_timer = setInterval(() => {
560 | this.smoothScroll();
561 | }, 16);
562 | }
563 |
564 | scrollTo() {
565 | if (this.vertical) this.bar.y = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h);
566 | else this.bar.x = this.but_h + this.scrollbar.travel * (this.delta * this.ratio) / (this.row.count * this.row.h);
567 | panel.treePaint();
568 | }
569 |
570 | scrollToEnd() {
571 | this.checkScroll(this.max_scroll, 'full');
572 | }
573 |
574 | setCol() {
575 | let opaque = ui.getOpaque();
576 | this.alpha = !ui.sbar.col ? 75 : (!ui.sbar.type ? 68 : 51);
577 | this.alpha1 = this.alpha;
578 | this.alpha2 = !ui.sbar.col ? 128 : (!ui.sbar.type ? 119 : 85);
579 | this.inStep = ui.sbar.type && ui.sbar.col ? 12 : 18;
580 | switch (ui.sbar.type) {
581 | case 0:
582 | switch (ui.sbar.col) {
583 | case 0:
584 | for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, this.alpha + i), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, this.alpha + i);
585 | this.col.max = opaque ? $.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, 192), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, 192);
586 | break;
587 | case 1:
588 | for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $.RGBAtoRGB(ui.col.text & RGBA(255, 255, 255, this.alpha + i), ui.col.bg) : ui.col.text & RGBA(255, 255, 255, this.alpha + i);
589 | this.col.max = opaque ? $.RGBAtoRGB(ui.col.text & 0x99ffffff, ui.col.bg) : ui.col.text & 0x99ffffff;
590 | break;
591 | }
592 | break;
593 | case 1:
594 | switch (ui.sbar.col) {
595 | case 0:
596 | this.col.bg = opaque ? $.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, 15), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, 15);
597 | for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, this.alpha + i), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, this.alpha + i);
598 | this.col.max = opaque ? $.RGBAtoRGB(RGBA(ui.col.t, ui.col.t, ui.col.t, 192), ui.col.bg) : RGBA(ui.col.t, ui.col.t, ui.col.t, 192);
599 | break;
600 | case 1:
601 | this.col.bg = opaque ? $.RGBAtoRGB(ui.col.text & 0x15ffffff, ui.col.bg) : ui.col.text & 0x15ffffff;
602 | for (let i = 0; i < this.alpha2 - this.alpha + 1; i++) this.col[this.alpha + i] = opaque ? $.RGBAtoRGB(ui.col.text & RGBA(255, 255, 255, this.alpha + i), ui.col.bg) : ui.col.text & RGBA(255, 255, 255, this.alpha + i);
603 | this.col.max = opaque ? $.RGBAtoRGB(ui.col.text & 0x99ffffff, ui.col.bg) : ui.col.text & 0x99ffffff;
604 | break;
605 | }
606 | break;
607 | }
608 | }
609 |
610 | shift(dir, nearest_y) {
611 | let target = Math.round((this.scroll + dir * -(((this.rows_drawn - 1) || 1) * this.row.h)) / this.row.h) * this.row.h;
612 | if (dir == 1) target = Math.max(target, nearest_y);
613 | else target = Math.min(target, nearest_y);
614 | return target;
615 | }
616 |
617 | shiftPage(dir, nearest_y) {
618 | this.checkScroll(this.shift(dir, nearest_y), 'full');
619 | if (!this.timer_but) {
620 | this.timer_but = setInterval(() => {
621 | if (this.count > 1) {
622 | this.checkScroll(this.shift(dir, nearest_y), 'full');
623 | } else this.count++;
624 | }, 100);
625 | }
626 | }
627 |
628 | smoothScroll() {
629 | this.delta = this.position(this.start, this.scroll, Date.now() - this.clock + this.elap, this.duration[this.event], this.event);
630 | if (Math.abs(this.scroll - this.delta) > 0.5) this.scrollTo();
631 | else this.scrollFinish();
632 | }
633 |
634 | tap(p_x, p_y) {
635 | if (this.touch.amplitude) {
636 | this.clock = 0;
637 | // this.scroll = this.delta; // stopping correct scroll on expanding bottom node after touch event
638 | }
639 | this.touch.counter = 0;
640 | this.initial.scr = this.scroll;
641 | this.touch.dn = true;
642 | if (this.vertical) {
643 | this.initial.y = this.touch.reference = p_y;
644 | if (!this.touch.offset) this.touch.offset = p_y;
645 | } else {
646 | this.initial.x = this.touch.reference = p_x;
647 | if (!this.touch.offset) this.touch.offset = p_x;
648 | }
649 | this.touch.velocity = this.touch.amplitude = 0;
650 | if (!ppt.flickDistance) return;
651 | this.touch.frame = this.touch.offset;
652 | this.touch.startTime = this.touch.timestamp = Date.now();
653 | clearInterval(this.touch.ticker);
654 | this.touch.ticker = setInterval(() => this.track, 100);
655 | }
656 |
657 | track(initial) {
658 | let now, elapsed, delta, v;
659 | this.touch.counter++;
660 | now = Date.now();
661 | if (now - this.touch.lastDn < 10000 && this.touch.counter == 4) {
662 | ui.id.touch_dn = -1;
663 | panel.last_pressed_coord = {
664 | x: -1,
665 | y: -1
666 | }
667 | }
668 | elapsed = now - this.touch.timestamp;
669 | if (initial) elapsed = Math.max(elapsed, 32);
670 | this.touch.timestamp = now;
671 | delta = this.touch.offset - this.touch.frame;
672 | this.touch.frame = this.touch.offset;
673 | v = 1000 * delta / (1 + elapsed);
674 | this.touch.velocity = 0.8 * v + 0.2 * this.touch.velocity;
675 | }
676 |
677 | wheel(step) {
678 | this.checkScroll(Math.round((this.scroll + step * -(!this.scrollStep ? this.rows_drawn - 1 : this.scrollStep) * this.row.h) / this.row.h) * this.row.h, this.scrollStep ? 'step' : 'full');
679 | }
680 | }
--------------------------------------------------------------------------------
/scripts/search.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class Search {
4 | constructor() {
5 | this.cx = 0;
6 | this.end = 0;
7 | this.lbtnDn = false;
8 | this.lg = [];
9 | this.log = [];
10 | this.menu = $.jsonParse(ppt.searchHistory, []);
11 | this.offset = 0;
12 | this.paste = false;
13 | this.start = 0;
14 | this.shift = false;
15 | this.shift_x = 0;
16 | this.txt_w = 0;
17 |
18 | this.logHistory = $.debounce(() => {
19 | let item = -1;
20 | const itemPresent = this.menu.some((v, i) => {
21 | item = i;
22 | return v.search == panel.search.txt;
23 | });
24 | if (itemPresent) {
25 | this.menu[item].accessed = Date.now();
26 | return;
27 | }
28 | if (!panel.search.txt) return;
29 | this.menu.push({search: panel.search.txt, accessed: Date.now()});
30 | if (this.menu.length > 25) {
31 | this.menu.sort((a, b) => b.accessed - a.accessed);
32 | this.menu.length = 25;
33 | }
34 | this.menu.sort((a, b) => pop.collator.compare(a.search, b.search));
35 | ppt.searchHistory = JSON.stringify(this.menu);
36 | }, 3000);
37 | }
38 |
39 | // Methods
40 |
41 | calcText() {
42 | $.gr(1, 1, false, g => this.txt_w = g.CalcTextWidth(panel.search.txt.substr(this.offset), ui.font.main, true));
43 | }
44 |
45 | clear() {
46 | if (!panel.search.txt) return;
47 | lib.time.Reset();
48 | pop.cache.search = {};
49 | this.offset = this.start = this.end = this.cx = 0;
50 | panel.search.txt = '';
51 | but.setSearchBtnsHide();
52 | panel.searchPaint();
53 | lib.setNodes(); // comment out to always stop child panels clearing [if memory on & item selected that's used & so won't clear]
54 | pop.checkAutoHeight();
55 | pop.notifySelection();
56 | }
57 |
58 | draw(gr) {
59 | if (!ppt.searchShow) return;
60 | this.start = $.clamp(this.start, 0, panel.search.txt.length);
61 | this.end = $.clamp(this.end, 0, panel.search.txt.length);
62 | this.cx = $.clamp(this.cx, 0, panel.search.txt.length);
63 | if (ui.style.fill) gr.FillSolidRect(0, 1, ui.w, ui.row.h - 4, 0x60000000);
64 | if (ui.style.pen == 2) gr.DrawRoundRect(0, 2, ui.w - 1, ui.row.h - 4, 4, 4, 1, ui.style.pen_c);
65 | if (panel.search.txt) {
66 | this.drawSel(gr);
67 | this.getOffset(gr);
68 | gr.GdiDrawText(panel.search.txt.substr(this.offset), ui.font.main, ui.col.search, panel.search.x, 0, panel.search.w, panel.search.sp, panel.l);
69 | } else {
70 | if (!ui.img.blurDark) gr.GdiDrawText('Search', ui.font.search, ui.col.txt_box, panel.search.x, 0, panel.search.w, panel.search.sp, panel.l);
71 | else {
72 | gr.SetTextRenderingHint(5);
73 | gr.DrawString('Search', ui.font.search, ui.col.txt_box, panel.search.x, -1, panel.search.w, panel.search.sp, panel.s_lc);
74 | }
75 | }
76 | this.drawCursor(gr);
77 | }
78 |
79 | drawCursor(gr) {
80 | if (panel.search.active && panel.search.cursor && this.start == this.end && this.cx >= this.offset) {
81 | const lx = panel.search.x + this.get_cursor_x(this.cx);
82 | gr.DrawLine(lx, panel.search.sp * 0.1, lx, panel.search.sp * 0.85, ui.l.w, ui.col.text);
83 | }
84 | }
85 |
86 | drawSel(gr) {
87 | if (this.start == this.end) return;
88 | const clamp = panel.search.x + panel.search.w;
89 | gr.DrawLine(Math.min(panel.search.x + this.get_cursor_x(this.start), clamp), panel.search.sp / 2, Math.min(panel.search.x + this.get_cursor_x(this.end), clamp), panel.search.sp / 2, ui.row.h - 3, ui.col.searchSel);
90 | }
91 |
92 | get_cursor_x(pos) {
93 | let x = 0;
94 | $.gr(1, 1, false, g => {
95 | if (pos >= this.offset) x = g.CalcTextWidth(panel.search.txt.substr(this.offset, pos - this.offset), ui.font.main, true);
96 | });
97 | return x;
98 | }
99 |
100 | getCursorChrPos(x) {
101 | let i = 0;
102 | $.gr(1, 1, false, g => {
103 | const nx = x - panel.search.x;
104 | let pos = 0;
105 | for (i = this.offset; i < panel.search.txt.length; i++) {
106 | pos += g.CalcTextWidth(panel.search.txt.substr(i, 1), ui.font.main, true);
107 | if (pos >= nx + 3) break;
108 | }
109 | });
110 | return i;
111 | }
112 |
113 | getOffset(gr) {
114 | let j = 0;
115 | let tx = gr.CalcTextWidth(panel.search.txt.substr(this.offset, this.cx - this.offset), ui.font.main, true);
116 | while (tx >= panel.search.w && panel.search.w > 0 && j < 500) {
117 | j++;
118 | this.offset++;
119 | tx = gr.CalcTextWidth(panel.search.txt.substr(this.offset, this.cx - this.offset), ui.font.main, true);
120 | }
121 | }
122 |
123 | lbtn_dblclk(x, y) {
124 | if (y < panel.search.h && x > but.q.h + but.margin && x < panel.search.x + panel.search.w && panel.search.txt.length) {
125 | panel.search.cursor = false;
126 | this.start = 0;
127 | this.end = panel.search.txt.length;
128 | panel.searchPaint();
129 | }
130 | }
131 |
132 | lbtn_dn(x, y) {
133 | panel.searchPaint();
134 | this.lbtnDn = panel.search.active = (y < panel.search.h && x > but.q.x - but.margin / 2 + but.q.h + but.margin && x < panel.search.x + panel.search.w);
135 | if (!this.lbtnDn) {
136 | this.offset = this.start = this.end = this.cx = 0;
137 | timer.clear(timer.cursor);
138 | return;
139 | } else {
140 | if (this.shift) {
141 | this.start = this.cx;
142 | this.end = this.cx = this.getCursorChrPos(x);
143 | } else {
144 | this.cx = this.getCursorChrPos(x);
145 | this.start = this.end = this.cx;
146 | }
147 | timer.searchCursor(true);
148 | }
149 | panel.searchPaint();
150 | }
151 |
152 | lbtn_up() {
153 | if (this.start != this.end) timer.clear(timer.cursor);
154 | this.lbtnDn = false;
155 | }
156 |
157 | move(x, y) {
158 | if (y > panel.search.h || !this.lbtnDn) return;
159 | const cursorChrPos = this.getCursorChrPos(x);
160 | const c_x = this.get_cursor_x(cursorChrPos);
161 | let l;
162 | this.calcText();
163 | if (cursorChrPos < this.start) {
164 | if (cursorChrPos < this.end) {
165 | if (c_x < panel.search.x)
166 | if (this.offset > 0) this.offset--;
167 | } else if (cursorChrPos > this.end) {
168 | if (c_x + panel.search.x > panel.search.x + panel.search.w) {
169 | l = (this.txt_w > panel.search.w) ? this.txt_w - panel.search.w : 0;
170 | if (l > 0) this.offset++;
171 | }
172 | }
173 | this.end = cursorChrPos;
174 | } else if (cursorChrPos > this.start) {
175 | if (c_x + panel.search.x > panel.search.x + panel.search.w) {
176 | l = (this.txt_w > panel.search.w) ? this.txt_w - panel.search.w : 0;
177 | if (l > 0) this.offset++;
178 | }
179 | this.end = cursorChrPos;
180 | }
181 | this.cx = cursorChrPos;
182 | panel.searchPaint();
183 | }
184 |
185 | on_char(code, force) {
186 | let searchDone = false;
187 | let text = String.fromCharCode(code) || '';
188 | if (force) panel.search.active = true;
189 | if (!panel.search.active || code == 5 || code == 9 || code == 12) return;
190 | panel.search.cursor = false;
191 | panel.pos = -1;
192 | switch (code) {
193 | case vk.enter:
194 | if (ppt.searchEnter || ppt.searchSend == 1) {
195 | lib.upd_search = true;
196 | lib.time.Reset();
197 | pop.cache.search = {};
198 | lib.setNodes();
199 | panel.setHeight(true);
200 | if (panel.search.txt.length > 2) window.NotifyOthers(window.Name, !lib.list.Count ? lib.list : panel.list);
201 | else if (!panel.search.txt.length) pop.notifySelection();
202 | lib.search.cancel();
203 | this.logHistory();
204 | searchDone = true;
205 | }
206 | if (ppt.searchSend == 1 || ppt.searchEnter && ppt.searchSend == 2) pop.load(panel.list, false, false, pop.autoPlay.send, !ppt.sendToCur, false);
207 | break;
208 | case vk.escape:
209 | this.clear();
210 | return;
211 | case vk.redo:
212 | this.lg.push(panel.search.txt);
213 | if (this.lg.length > 30) this.lg.shift();
214 | if (this.log.length > 0) {
215 | panel.search.txt = this.log.pop() + '';
216 | this.cx++
217 | }
218 | break;
219 | case vk.undo:
220 | this.log.push(panel.search.txt);
221 | if (this.log.length > 30) this.lg.shift();
222 | if (this.lg.length > 0) panel.search.txt = this.lg.pop() + '';
223 | break;
224 | case vk.selAll:
225 | this.start = 0;
226 | this.end = panel.search.txt.length;
227 | break;
228 | case vk.copy:
229 | if (this.start != this.end) $.setClipboardData(panel.search.txt.substring(this.start, this.end));
230 | break;
231 | case vk.cut:
232 | if (this.start != this.end) $.setClipboardData(panel.search.txt.substring(this.start, this.end)); // fall through
233 | case vk.back:
234 | this.record();
235 | if (this.start == this.end) {
236 | if (this.cx > 0) {
237 | panel.search.txt = panel.search.txt.substr(0, this.cx - 1) + panel.search.txt.substr(this.cx, panel.search.txt.length - this.cx);
238 | if (this.offset > 0) this.offset--;
239 | this.cx--;
240 | }
241 | } else {
242 | if (this.end - this.start == panel.search.txt.length) {
243 | panel.search.txt = '';
244 | this.cx = 0;
245 | } else {
246 | if (this.start > 0) {
247 | const st = this.start;
248 | const en = this.end;
249 | this.start = Math.min(st, en);
250 | this.end = Math.max(st, en);
251 | panel.search.txt = panel.search.txt.substring(0, this.start) + panel.search.txt.substring(this.end, panel.search.txt.length);
252 | this.cx = this.start;
253 | } else {
254 | panel.search.txt = panel.search.txt.substring(this.end, panel.search.txt.length);
255 | this.cx = this.start;
256 | }
257 | }
258 | }
259 | this.calcText();
260 | this.offset = this.offset >= this.end - this.start ? this.offset - this.end + this.start : 0;
261 | this.start = this.cx;
262 | this.end = this.start;
263 | break;
264 | case vk.ctrlBackspace:
265 | this.record();
266 | if (this.start != this.end) this.cx = this.end = this.start;
267 | if (this.cx > 0) {
268 | const initial = panel.search.txt.length;
269 | const leftSide = panel.search.txt.slice(0, this.cx).trimEnd();
270 | let boundary = 0;
271 | for (let k = 0; k < leftSide.length; k++) {
272 | if (panel.search.txt[k] == ' ' && panel.search.txt[k + 1] != ' ') boundary = k + 1;
273 | }
274 | panel.search.txt = leftSide.slice(0, boundary) + panel.search.txt.slice(this.cx).trimStart()
275 | this.cx = boundary;
276 |
277 | if (this.offset > 0) {
278 | this.offset -= initial - panel.search.txt.length;
279 | }
280 | }
281 | this.calcText();
282 | this.offset = this.offset >= this.end - this.start ? this.offset - this.end + this.start : 0;
283 | this.start = this.cx;
284 | this.end = this.start;
285 | break;
286 | case 'delete':
287 | this.record();
288 | if (this.start == this.end) {
289 | if (this.cx < panel.search.txt.length) {
290 | panel.search.txt = panel.search.txt.substr(0, this.cx) + panel.search.txt.substr(this.cx + 1, panel.search.txt.length - this.cx - 1);
291 | }
292 | } else {
293 | if (this.end - this.start == panel.search.txt.length) {
294 | panel.search.txt = '';
295 | this.cx = 0;
296 | } else {
297 | if (this.start > 0) {
298 | const st = this.start;
299 | const en = this.end;
300 | this.start = Math.min(st, en);
301 | this.end = Math.max(st, en);
302 | panel.search.txt = panel.search.txt.substring(0, this.start) + panel.search.txt.substring(this.end, panel.search.txt.length);
303 | this.cx = this.start;
304 | } else {
305 | panel.search.txt = panel.search.txt.substring(this.end, panel.search.txt.length);
306 | this.cx = this.start;
307 | }
308 | }
309 | }
310 | this.calcText();
311 | this.offset = this.offset >= this.end - this.start ? this.offset - this.end + this.start : 0;
312 | this.start = this.cx;
313 | this.end = this.start;
314 | break;
315 | case vk.paste:
316 | text = $.getClipboardData() || '';
317 | text = text.replace(/(\r\n|\n|\r)/gm, ' '); // fall through
318 | default:
319 | this.record();
320 | if (this.start == this.end) {
321 | panel.search.txt = panel.search.txt.substring(0, this.cx) + text + panel.search.txt.substring(this.cx);
322 | this.cx += text.length;
323 | this.end = this.start = this.cx;
324 | } else if (this.end > this.start) {
325 | panel.search.txt = panel.search.txt.substring(0, this.start) + text + panel.search.txt.substring(this.end);
326 | this.calcText();
327 | this.offset = this.offset >= this.end - this.start ? this.offset - this.end + this.start : 0;
328 | this.cx = this.start + text.length;
329 | this.end = this.start = this.cx;
330 | } else {
331 | panel.search.txt = panel.search.txt.substring(0, this.end) + text + panel.search.txt.substring(this.start);
332 | this.calcText();
333 | this.offset = this.offset < this.end - this.start ? this.offset - this.end + this.start : 0;
334 | this.cx = this.end + text.length;
335 | this.end = this.start = this.cx;
336 | }
337 | break;
338 | }
339 | if (code == vk.copy || code == vk.selAll) return;
340 | if (!timer.cursor.id) timer.searchCursor();
341 | but.setSearchBtnsHide();
342 | panel.searchPaint();
343 | if (ppt.searchEnter || searchDone) return;
344 | if ((ppt.searchSend == 2 || lib.list.Count > 200000) && panel.search.txt && panel.search.txt.length < 4) {
345 | lib.upd_search = true;
346 | lib.search500();
347 | this.logHistory();
348 | } else {
349 | lib.search500.cancel();
350 | lib.upd_search = true;
351 | lib.search();
352 | this.logHistory();
353 | }
354 | }
355 |
356 | on_key_down(vkey) {
357 | if (!panel.search.active) return;
358 | switch (vkey) {
359 | case vk.ctrl:
360 | this.ctrl = true;
361 | break;
362 | case vk.left:
363 | case vk.right:
364 | if (vkey == vk.left) {
365 | if (!this.ctrl) {
366 | if (this.offset > 0) {
367 | if (this.cx <= this.offset) {
368 | this.offset--;
369 | this.cx--;
370 | } else this.cx--;
371 | } else if (this.cx > 0) this.cx--;
372 | } else {
373 | let boundary = 0;
374 | for (let k = this.cx - 1; k > 0; k--) {
375 | if (panel.search.txt[k] != ' ' && panel.search.txt[k - 1] == ' ') {
376 | boundary = k;
377 | break;
378 | }
379 | }
380 | if (this.offset > 0) {
381 | this.offset -= (this.cx - boundary);
382 | }
383 | this.cx = boundary;
384 | this.offset = this.offset >= this.end - this.start ? this.offset - this.end + this.start : 0;
385 | }
386 | }
387 | if (vkey == vk.right && this.cx < panel.search.txt.length) {
388 | if (!this.ctrl) this.cx++;
389 | else {
390 | let boundary = panel.search.txt.length;
391 | for (let k = this.cx; k < panel.search.txt.length; k++) {
392 | if (panel.search.txt[k] == ' ' && panel.search.txt[k + 1] != ' ') {
393 | boundary = k + 1;
394 | break;
395 | }
396 | }
397 | this.cx = boundary;
398 | }
399 | }
400 | this.start = this.end = this.cx;
401 | if (this.shift) {
402 | this.start = Math.min(this.cx, this.shift_x);
403 | this.end = Math.max(this.cx, this.shift_x);
404 | }
405 | panel.search.cursor = true;
406 | timer.searchCursor(true);
407 | break;
408 | case vk.home:
409 | case vk.end:
410 | if (vkey == vk.home) this.offset = this.start = this.end = this.cx = 0;
411 | else this.start = this.end = this.cx = panel.search.txt.length;
412 | if (this.shift) {
413 | this.start = Math.min(this.cx, this.shift_x);
414 | this.end = Math.max(this.cx, this.shift_x);
415 | }
416 | panel.search.cursor = true;
417 | timer.searchCursor(true);
418 | break;
419 | case vk.shift:
420 | this.shift = true;
421 | this.shift_x = this.cx;
422 | break;
423 | case vk.del:
424 | if (this.ctrl && !this.shift && this.start == this.end) { // ctrl + delete: delete next word
425 | this.record();
426 | const initial = panel.search.txt.length;
427 | const leftSide = panel.search.txt.slice(0, this.cx);
428 | const rightSide = panel.search.txt.slice(this.cx, panel.search.txt.length).trimStart();
429 | const idx = rightSide.search(/ \b/);
430 | const boundary = idx == -1 ? rightSide.length : idx + 1;
431 | let newRightSide = rightSide.slice(boundary);
432 | if (newRightSide.length && !/\s$/.test(leftSide) && !/^\s/.test(newRightSide)) newRightSide = ' ' + newRightSide;
433 | panel.search.txt = leftSide + newRightSide;
434 | this.cx = !/\s$/.test(leftSide) ? leftSide.length + 1 : leftSide.length;
435 | if (this.offset > 0) {
436 | this.offset -= initial - panel.search.txt.length;
437 | }
438 | this.calcText();
439 | this.offset = this.offset >= this.end - this.start ? this.offset - this.end + this.start : 0;
440 | this.start = this.end = this.cx;
441 | } else this.on_char('delete');
442 | break;
443 | }
444 | panel.searchPaint();
445 | }
446 |
447 | on_key_up(vkey) {
448 | if (!panel.search.active) return;
449 | if (vkey == vk.ctrl) {
450 | this.ctrl = false;
451 | }
452 | if (vkey == vk.shift) {
453 | this.shift = false;
454 | this.shift_x = this.cx;
455 | }
456 | }
457 |
458 | rbtn_up(x, y) {
459 | this.paste = $.getClipboardData() ? true : false;
460 | searchMenu.load(x, y);
461 | }
462 |
463 | record() {
464 | this.lg.push(panel.search.txt);
465 | this.log = [];
466 | if (this.lg.length > 30) this.lg.shift();
467 | }
468 |
469 | focus() {
470 | panel.searchPaint();
471 | panel.search.active = true;
472 | this.shift = false;
473 | this.start = this.end = this.cx = panel.search.x;
474 | panel.search.cursor = true;
475 | timer.searchCursor(true);
476 | panel.searchPaint();
477 | }
478 | }
479 |
480 | class Find {
481 | constructor() {
482 | this.arc1 = 5;
483 | this.arc2 = 4;
484 | this.j = {
485 | x: 5,
486 | y: 5,
487 | w: 50,
488 | h: 30
489 | };
490 | this.jSearch = '';
491 | this.jump_search = true;
492 | this.initials = null;
493 | }
494 |
495 | // Methods
496 |
497 | draw(gr) {
498 | if (this.jSearch) {
499 | gr.SetSmoothingMode(4);
500 | this.j.w = gr.CalcTextWidth(this.jSearch, ui.font.find) + 25;
501 | gr.FillRoundRect(this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, this.arc1, this.arc1, 0x96000000);
502 | gr.DrawRoundRect(this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, this.arc1, this.arc1, 1, 0x64000000);
503 | gr.DrawRoundRect(this.j.x - this.j.w / 2 + 1, this.j.y + 1, this.j.w - 2, this.j.h - 2, this.arc2, this.arc2, 1, 0x28ffffff);
504 | gr.GdiDrawText(this.jSearch, ui.font.find, RGB(0, 0, 0), this.j.x - this.j.w / 2 + 1, this.j.y + 1, this.j.w, this.j.h, panel.cc);
505 | gr.GdiDrawText(this.jSearch, ui.font.find, this.jump_search ? 0xfffafafa : 0xffff4646, this.j.x - this.j.w / 2, this.j.y, this.j.w, this.j.h, panel.cc);
506 | gr.SetSmoothingMode(0);
507 | }
508 | }
509 |
510 | on_char(code) {
511 | const text = String.fromCharCode(code);
512 | let advance = false;
513 | if (panel.pos >= 0 && panel.pos < pop.tree.length) {
514 | const char = pop.tree[panel.pos].name.replace(/@!#.*?@!#/g, '').charAt(0).toLowerCase();
515 | if (pop.tree[panel.pos].sel && char == text) advance = true;
516 | }
517 | switch (true) {
518 | case advance:
519 | if (utils.IsKeyPressed(0x0A) || utils.IsKeyPressed(0x08) || utils.IsKeyPressed(0x09) || utils.IsKeyPressed(0x11) || utils.IsKeyPressed(0x1B) || utils.IsKeyPressed(0x6A) || utils.IsKeyPressed(0x6D)) return;
520 | if (!panel.search.active) {
521 | let init = '';
522 | let cur = 'currentArr';
523 | if (!this.initials) { // reset in buildTree
524 | this.initials = {}
525 | pop.tree.forEach((v, i) => {
526 | if (!v.root) {
527 | const nm = v.name.replace(/@!#.*?@!#/g, '');
528 | init = nm.charAt().toLowerCase();
529 | if (cur != init && !this.initials[init]) {
530 | this.initials[init] = [i];
531 | cur = init;
532 | } else {
533 | this.initials[init].push(i);
534 | }
535 | }
536 | });
537 | }
538 |
539 | this.jump_search = false;
540 | if (panel.pos >= 0 && panel.pos < pop.tree.length) {
541 | this.matches = this.initials[text];
542 | this.ix = this.matches.indexOf(panel.pos);
543 | this.ix++;
544 | if (this.ix >= this.matches.length) this.ix = 0;
545 | panel.pos = this.matches[this.ix];
546 | this.jump_search = true;
547 | }
548 | if (this.jump_search) {
549 | pop.clearSelected();
550 | pop.sel_items = [];
551 | pop.tree[panel.pos].sel = true;
552 | pop.setPos(panel.pos);
553 | pop.getTreeSel();
554 | lib.treeState(false, ppt.rememberTree);
555 | panel.treePaint();
556 | if (panel.imgView) pop.showItem(panel.pos, 'focus');
557 | else {
558 | const row = (panel.pos * ui.row.h - sbar.scroll) / ui.row.h;
559 | if (sbar.rows_drawn - row < 3 || row < 0) sbar.checkScroll((panel.pos + 3) * ui.row.h - sbar.rows_drawn * ui.row.h);
560 | }
561 | if (ppt.libSource) {
562 | if (pop.autoFill.key) pop.load(pop.sel_items, true, false, false, !ppt.sendToCur, false);
563 | pop.track(pop.autoFill.key);
564 | } else if (panel.pos >= 0 && panel.pos < pop.tree.length) pop.setPlaylistSelection(panel.pos, pop.tree[panel.pos]);
565 | } else {
566 | this.jSearch = text;
567 | panel.treePaint();
568 | timer.clear(timer.jsearch2);
569 | timer.jsearch2.id = setTimeout(() => {
570 | this.jSearch = '';
571 | panel.treePaint();
572 | timer.jsearch2.id = null;
573 | }, 1200);
574 | }
575 | }
576 | break;
577 | case !advance:
578 | if (utils.IsKeyPressed(0x09) || utils.IsKeyPressed(0x11) || utils.IsKeyPressed(0x1B) || utils.IsKeyPressed(0x6A) || utils.IsKeyPressed(0x6D)) return;
579 | if (!panel.search.active) {
580 | let found = false;
581 | let pos = -1;
582 | switch (code) {
583 | case vk.back:
584 | this.jSearch = this.jSearch.substr(0, this.jSearch.length - 1);
585 | break;
586 | case vk.enter:
587 | this.jSearch = '';
588 | return;
589 | default:
590 | this.jSearch += text;
591 | break;
592 | }
593 | pop.clearSelected();
594 | if (!this.jSearch) return;
595 | pop.sel_items = [];
596 | this.jump_search = true;
597 | panel.treePaint();
598 | timer.clear(timer.jsearch1);
599 | timer.jsearch1.id = setTimeout(() => {
600 | pop.tree.some((v, i) => {
601 | const name = v.name.replace(/@!#.*?@!#/g, '');
602 | if (name != panel.rootName && name.substring(0, this.jSearch.length).toLowerCase() == this.jSearch.toLowerCase()) {
603 | found = true;
604 | pos = i;
605 | v.sel = true;
606 | pop.setPos(pos);
607 | pop.getTreeSel();
608 | lib.treeState(false, ppt.rememberTree);
609 | return true;
610 | }
611 | });
612 | if (!found) this.jump_search = false;
613 | panel.treePaint();
614 | if (found) pop.showItem(pos, 'focus');
615 | timer.jsearch1.id = null;
616 | }, 500);
617 |
618 | timer.clear(timer.jsearch2);
619 | timer.jsearch2.id = setTimeout(() => {
620 | if (found) {
621 | if (ppt.libSource) {
622 | if (pop.autoFill.key) pop.load(pop.sel_items, true, false, false, !ppt.sendToCur, false);
623 | pop.track(pop.autoFill.key);
624 | } else if (pos >= 0 && pos < pop.tree.length) pop.setPlaylistSelection(pos, pop.tree[pos]);
625 | }
626 | this.jSearch = '';
627 | panel.treePaint();
628 | timer.jsearch2.id = null;
629 | }, 1200);
630 | }
631 | }
632 | }
633 |
634 | on_size() {
635 | this.j.x = Math.round(ui.w / 2);
636 | this.j.h = Math.round(ui.row.h * 1.5);
637 | this.j.y = Math.round((ui.h - this.j.h) / 2);
638 | this.arc1 = Math.min(5, this.j.h / 2);
639 | this.arc2 = Math.min(4, (this.j.h - 2) / 2);
640 | }
641 | }
--------------------------------------------------------------------------------
/scripts/timers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class Timers {
4 | constructor() {
5 | ['cursor', 'jsearch1', 'jsearch2', 'tt'].forEach(v => this[v] = {
6 | id: null
7 | });
8 | }
9 |
10 | // Methods
11 |
12 | clear(timer) {
13 | if (timer) clearTimeout(timer.id);
14 | timer.id = null;
15 | }
16 |
17 | searchCursor(clear) {
18 | if (clear) this.clear(this.cursor);
19 | if (!panel.search.cursor) panel.search.cursor = true;
20 | this.cursor.id = setInterval(() => {
21 | panel.search.cursor = !panel.search.cursor;
22 | panel.searchPaint();
23 | }, 530);
24 | }
25 |
26 | tooltip() {
27 | this.clear(this.tt);
28 | this.tt.id = setTimeout(() => {
29 | pop.deactivateTooltip();
30 | this.tt.id = null;
31 | }, 5000);
32 | }
33 | }
--------------------------------------------------------------------------------
/scripts/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let my_utils = {}
4 |
5 | my_utils.scriptInfo = window.ScriptInfo;
6 | my_utils.packageInfo = utils.GetPackageInfo(my_utils.scriptInfo.PackageId);
7 | my_utils.packagePath = `${my_utils.packageInfo.Directories.Root}/`;
8 |
9 | my_utils.getAsset = assetFile => utils.ReadTextFile(`${my_utils.packageInfo.Directories.Assets}/${assetFile}`);
10 | my_utils.getImageAssets = assetFolder => utils.Glob(`${my_utils.packageInfo.Directories.Assets}/images/${assetFolder}/*`);
11 | my_utils.getScriptPath = `${my_utils.packageInfo.Directories.Scripts}/`;
--------------------------------------------------------------------------------