View difference between Paste ID: rCiZ4xYt and hUPFBbt2
SHOW: | | - or go back to the newest paste.
1
var VERSION = "7.2013-08-01.01"
2-
var VERSION = "7.2013-08-01.01";
2+
3
var DT_CENTER = 0x00000001;
4
var DT_RIGHT = 0x00000002;
5
var DT_VCENTER = 0x00000004;
6
var DT_WORDBREAK = 0x00000010;
7
var DT_CALCRECT = 0x00000400;
8
var DT_NOPREFIX = 0x00000800;
9
var DT_END_ELLIPSIS = 0x00008000;
10
11
var MF_GRAYED = 0x00000001;
12
var MF_STRING = 0x00000000;
13
14
var IDC_ARROW = 32512;
15
var IDC_HAND = 32649;
16
17
var ONE_DAY = 86400000;
18
var ONE_WEEK = 604800000;
19
20
String.prototype.addCommas = function() {
21
	var v = this;
22
	var n = "";
23
	while(v.length > 3) {
24
		n = "," + v.substring(v.length - 3) + n;
25
		v = v.substring(0, v.length - 3);
26
	}
27
	return(v + n);
28
}
29
30
String.prototype.trim = function() {
31
	return this.replace(/^\s+|\s+$/g,"");
32
}
33
34
String.prototype.ucfirst = function() {
35
	return this.charAt(0).toUpperCase() + this.substr(1);
36
}
37
38
function RGB(r, g, b) {
39
	return (0xff000000 | (r << 16) | (g << 8) | (b));
40
}
41
42
function RGBA(r, g, b, a) {
43
	return ((a << 24) | (r << 16) | (g << 8) | (b));
44
}
45
46
function on_colors_changed() {
47
	p.colors_changed();
48
}
49
50
function on_font_changed() {
51
	p.font_changed();
52
}
53
54
function on_selection_changed() {
55
	p.item_focus_change();
56
}
57
58
function on_playlist_switch() {
59
	p.item_focus_change();
60
}
61
62
function on_playback_new_track() {
63
	p.item_focus_change();
64
}
65
66
function on_playback_dynamic_info_track() {
67
	p.item_focus_change();
68
}
69
70
function on_playback_stop() {
71
	p.item_focus_change();
72
}
73
74
function on_item_focus_change() {
75
	p.item_focus_change();
76
}
77
78
function on_mouse_leave() {
79
	if (typeof b == "object") b.leave();
80
	if (typeof r == "object") r.leave();
81
	if (typeof li == "object") li.leave();
82
	if (typeof s == "object") s.leave();
83
}
84
85
function on_mouse_rbtn_up(x, y) {
86
	p.rbtn_up(x, y);
87
	return true;
88
}
89
90
function on_notify_data(name, data) {
91
	if (name == "lastfm" && data == "update" && typeof l == "object") l.notify_data(name, data);
92
	if (name == "autoplaylists" && data == "update" && typeof li == "object" && li.mode == "autoplaylists") li.update();
93
	if (name == "images" && data == "update" && typeof im == "object") im.update();
94
	if (name == "love" && typeof ps == "object") l.post(fb.TitleFormat("%LASTFM_LOVED_DB%").EvalWithMetadb(data) == 1 ? "track.unlove" : "track.love", data);
95
}
96
97
function panel(name, features) {
98
	this.item_focus_change = function() {
99
		switch(this.selection_mode) {
100
			case 0:
101
				this.metadb = fb.GetSelection();
102
				break;
103
			case 1:
104
				this.metadb = fb.IsPlaying ? fb.GetNowPlaying() : fb.GetFocusItem();
105
				break;
106
			case 2:
107
				this.metadb = fb.GetFocusItem();
108
				break;
109
		}
110
		if (this.metadb && typeof on_metadb_changed == "function") on_metadb_changed();
111
	}
112
	
113
	this.colors_changed = function() {
114
		if (this.dui) {
115
			this.textcolour = window.GetColorDUI(0);
116
			this.textcolour_hl = window.GetColorDUI(2);
117
			this.backcolour = window.GetColorDUI(1);
118
		} else {
119
			this.textcolour = window.GetColorCUI(0);
120
			this.textcolour_hl = window.GetColorCUI(0);
121
			//this.backcolour = window.GetColorCUI(3);
122
		}
123
		window.Repaint();
124
	}
125
	
126
	this.font_changed = function() {
127
		this.font = this.dui ? window.GetFontDUI(0) : window.GetFontCUI(0);
128
		try {
129
			this.font_name = this.font.Name;
130
		} catch(e) {
131
			this.console("Unable to use your default font. Using Segoe UI instead.");
132
			this.font_name = "segoe ui";
133
		}
134
		//this.title_font = gdi.Font(this.font_name, 12, 3);
135
		this.normal_font = gdi.Font("segoe ui", 12, 1);
136
		//this.fixed_font = gdi.Font("segoe ui", 9, 3);
137
		//this.list_font = gdi.Font(this.font_name, this.list_font_size);
138
		this.row_height = this.normal_font.Height + 0;
139
		if (typeof t == "object") t.size();
140
		window.Repaint();
141
	}
142
	
143
	this.size = function() {
144
		this.w = window.Width;
145
		this.h = window.Height;
146
	}
147
	
148
	this.move = function(x, y) {
149
		this.mx = x;
150
		this.my = y;
151
	}
152
	
153
	this.rbtn_up = function(x, y) {
154
		var _menu = window.CreatePopupMenu();
155
		var _custom_background = window.CreatePopupMenu();
156
		var _metadb = window.CreatePopupMenu();
157
		var _cover = window.CreatePopupMenu();
158
		var _font = window.CreatePopupMenu();
159
		var _sub1 = window.CreatePopupMenu();
160
		var _sub2 = window.CreatePopupMenu();
161
		var _sub3 = window.CreatePopupMenu();
162
		var _sub4 = window.CreatePopupMenu();
163
		var _sub5 = window.CreatePopupMenu();
164
		var idx;
165
		switch(true) {
166
			case this.check_feature("spectrogram") && s.trace(x, y):
167
				_menu.AppendMenuItem(MF_STRING, 1804, "SoX options...");
168
				_menu.AppendMenuSeparator();
169
				_menu.AppendMenuItem(MF_STRING, 1805, "Marker colour...");
170
				_menu.AppendMenuSeparator();
171
				_menu.AppendMenuItem(MF_STRING, 1803, "Only analyse tracks in library");
172
				_menu.CheckMenuItem(1803, s.library);
173
				_menu.AppendMenuSeparator();
174
				_sub1.AppendMenuItem(MF_STRING, 1800, "Clear all");
175
				_sub1.AppendMenuItem(MF_STRING, 1801, "Clear older than 1 day");
176
				_sub1.AppendMenuItem(MF_STRING, 1802, "Clear older than 1 week");
177
				_sub1.AppendMenuSeparator();
178
				_sub1.AppendMenuItem(MF_GRAYED, 1803, "In use: " + (this.fso.GetFolder(spectrogram_cache).size / 1048576).toFixed(2) + "MB");
179
				_sub1.AppendTo(_menu, MF_STRING, "Cached images");
180
				_menu.AppendMenuSeparator();
181
				break;
182
			case typeof a == "object" && a.trace(x, y):
183
				_menu.AppendMenuItem(MF_STRING, 1, "Refresh");
184
				_menu.AppendMenuSeparator();
185
				_menu.AppendMenuItem(MF_STRING, 2, "Front");
186
				_menu.AppendMenuItem(MF_STRING, 3, "Back");
187
				_menu.AppendMenuItem(MF_STRING, 4, "Disc");
188
				_menu.AppendMenuItem(MF_STRING, 5, "Icon");
189
				_menu.AppendMenuItem(MF_STRING, 6, "Artist");
190
				_menu.CheckMenuRadioItem(2, 6, a.id + 2);
191
				_menu.AppendMenuSeparator();
192
				_menu.AppendMenuItem(MF_STRING, 15, "Crop (focus on centre)");
193
				_menu.AppendMenuItem(MF_STRING, 16, "Crop (focus on top)");
194
				_menu.AppendMenuItem(MF_STRING, 17, "Centre");
195
				_menu.AppendMenuItem(MF_STRING, 18, "Stretch");
196
				_menu.CheckMenuRadioItem(15, 18, a.type == "crop" ? 15 : a.type == "crop top" ? 16 : a.type == "centre" ? 17 : 18);
197
				_menu.AppendMenuSeparator();
198
				break;
199
			case typeof c == "object" && (!this.check_feature("now_playing") || np_cd) && c.trace(x, y):
200
				_menu.AppendMenuItem(MF_STRING, 1, "Refresh");
201
				_menu.AppendMenuSeparator();
202
				_menu.AppendMenuItem(MF_STRING, 11, "Gloss effect");
203
				_menu.CheckMenuItem(11, c.gloss);
204
				_menu.AppendMenuItem(MF_STRING, 12, "Shadow effect");
205
				_menu.CheckMenuItem(12, c.shadow);
206
				_menu.AppendMenuSeparator();
207
				break;
208
			case this.check_feature("simple_tag") && t.trace(x, y):
209
				_menu.AppendMenuItem(MF_STRING, 1700, "Custom title...");
210
				_menu.AppendMenuItem(MF_STRING, 1701, "Custom tag...");
211
				_menu.AppendMenuSeparator();
212
				_menu.AppendMenuItem(MF_STRING, 1702, "Centre text");
213
				_menu.CheckMenuItem(1702, t.centre);
214
				_menu.AppendMenuSeparator();
215
				break;
216
			case this.check_feature("simple_text") && t.trace(x, y):
217
				_menu.AppendMenuItem(MF_STRING, 1700, "Custom title...");
218
				_menu.AppendMenuItem(MF_STRING, 200, "Custom path...");
219
				_menu.AppendMenuSeparator();
220
				_menu.AppendMenuItem(MF_STRING, 201, "Fixed width font");
221
				_menu.CheckMenuItem(201, t.fixed);
222
				_menu.AppendMenuSeparator();
223
				break;
224
			case this.check_feature("lastfm_album") && t.trace(x, y):
225
				_menu.AppendMenuItem(MF_STRING, 53, "Last.fm auto-correct");
226
				_menu.CheckMenuItem(53, l.auto_correct);
227
				break;
228
			case this.check_feature("lastfm_wiki") && (!this.check_feature("now_playing") || np_bio) && t.trace(x, y):
229
				_menu.AppendMenuItem(MF_STRING, 25, "Last.fm");
230
				_menu.AppendMenuItem(MF_STRING, 26, "Wikipedia");
231
				_menu.CheckMenuRadioItem(25, 26, t.source == "last.fm" ? 25 : 26);
232
				//_menu.AppendMenuSeparator();
233
				break;
234
			case this.check_feature("echonest") && li.trace(x, y):
235
				for (i = 0; i < li.echonest_modes.length; i++) {
236
					_menu.AppendMenuItem(MF_STRING, i + 500, li.echonest_modes[i].ucfirst());
237
				}
238
				_menu.CheckMenuRadioItem(500, 502, li.echonest_mode + 500);
239
				_menu.AppendMenuSeparator();
240
				_menu.AppendMenuItem(MF_STRING, 28, "Show timestamp");
241
				_menu.CheckMenuItem(28, li.show_timestamp);
242
				_menu.AppendMenuSeparator();
243
				break;
244
			case this.check_feature("autoplaylists") && li.trace(x, y):
245
				_menu.AppendMenuItem(MF_STRING, 600, "Change title...");
246
				_menu.AppendMenuSeparator();
247
				for (i = 1; i < 9; i++) {
248
					_sub1.AppendMenuItem(MF_STRING, i + 600, i);
249
				}
250
				_sub1.AppendTo(_menu, MF_STRING, "Save slot");
251
				_sub1.CheckMenuRadioItem(601, 608, li.autoplaylists_slot + 600);
252
				_menu.AppendMenuSeparator();
253
				_sub2.AppendMenuItem(MF_STRING, 610, "run query");
254
				_sub2.AppendMenuItem(MF_STRING, 611, "do nothing");
255
				_sub2.CheckMenuRadioItem(610, 611, li.autoplaylists_success ? 610 : 611);
256
				_sub2.AppendTo(_menu, MF_STRING, "On successful creation/edit");
257
				for (i = 0; i < li.autoplaylists_deleted_items.length; i++) {
258
					var temp = li.autoplaylists_deleted_items[i].split("¬");
259
					_sub3.AppendMenuItem(MF_STRING, i + 620, temp[0]);
260
				}
261
				if (li.autoplaylists_deleted_items.length > 0) _sub3.AppendTo(_menu, MF_STRING, "Restore");
262
				_menu.AppendMenuSeparator();
263
				_menu.AppendMenuItem(MF_STRING, 650, "Replace exisiting playlists with the same name");
264
				_menu.CheckMenuItem(650, li.autoplaylists_remove_duplicates);
265
				_menu.AppendMenuSeparator();
266
				break;
267
			case this.check_feature("discogs") && li.trace(x, y):
268
				_menu.AppendMenuItem(MF_STRING, 29, "Show exact artist matches only");
269
				_menu.CheckMenuItem(29, li.discogs_match);
270
				_menu.AppendMenuSeparator();
271
				_menu.AppendMenuItem(MF_STRING, 28, "Show timestamp");
272
				_menu.CheckMenuItem(28, li.show_timestamp);
273
				_menu.AppendMenuSeparator();
274
				break;
275
			case this.check_feature("musicbrainz") && li.trace(x, y):
276
				_menu.AppendMenuItem(MF_STRING, 700, "Releases");
277
				_menu.AppendMenuItem(MF_STRING, 701, "Links");
278
				_menu.CheckMenuRadioItem(700, 701, li.musicbrainz_mode == "releases" ? 700 : 701);
279
				_menu.AppendMenuSeparator();
280
				if (li.musicbrainz_mode == "releases") {
281
					_menu.AppendMenuItem(MF_STRING, 703, "Include albums");
282
					_menu.CheckMenuItem(703, li.musicbrainz_album);
283
					_menu.AppendMenuItem(MF_STRING, 704, "Include EPs");
284
					_menu.CheckMenuItem(704, li.musicbrainz_ep);
285
					_menu.AppendMenuItem(MF_STRING, 705, "Include singles");
286
					_menu.CheckMenuItem(705, li.musicbrainz_single);
287
					_menu.AppendMenuSeparator();
288
					_menu.AppendMenuItem(MF_STRING, 702, "Include 'Live' releases");
289
					_menu.CheckMenuItem(702, li.musicbrainz_live);
290
					_menu.AppendMenuSeparator();
291
					_menu.AppendMenuItem(MF_STRING, 706, "Show release type");
292
					_menu.CheckMenuItem(706, li.musicbrainz_show_release_type);
293
					_menu.AppendMenuSeparator();
294
				}
295
				_menu.AppendMenuItem(MF_STRING, 28, "Show timestamp");
296
				_menu.CheckMenuItem(28, li.show_timestamp);
297
				_menu.AppendMenuSeparator();
298
				break;
299
			case this.check_feature("lastfm") && li.trace(x, y):
300
				for (i = 0; i < li.lastfm_modes.length; i++) {
301
					_menu.AppendMenuItem(MF_STRING, 801 + i, li.lastfm_modes[i].ucfirst());
302
				}
303
				_menu.CheckMenuRadioItem(801, 805, li.lastfm_mode + 801);
304
				_menu.AppendMenuSeparator();
305
				if (li.lastfm_mode == 0) {
306
					if (li.lastfm_button == "spotify") {
307
						_menu.AppendMenuItem(MF_STRING, 810, "Pause playback when launching Spotify");
308
						_menu.CheckMenuItem(810, li.lastfm_pause);
309
						_menu.AppendMenuSeparator();
310
					}
311
					_sub1.AppendMenuItem(MF_STRING, 811, "Spotify");
312
					_sub1.AppendMenuItem(MF_STRING, 812, "Autoplaylist");
313
					_sub1.CheckMenuRadioItem(811, 812, li.lastfm_button == "spotify" ? 811 : 812);
314
					_sub1.AppendTo(_menu, MF_STRING, "Buttons");
315
					_menu.AppendMenuSeparator();
316
				}
317
				_menu.AppendMenuItem(MF_STRING, 28, "Show timestamp");
318
				_menu.CheckMenuItem(28, li.show_timestamp);
319
				_menu.AppendMenuSeparator();
320
				_menu.AppendMenuItem(MF_STRING, 51, "Last.fm auto-correct");
321
				_menu.CheckMenuItem(51, l.auto_correct);
322
				_menu.AppendMenuSeparator();
323
				break;
324
			case this.check_feature("lastfm_charts") && li.trace(x, y):
325
				_menu.AppendMenuItem(l.username.length > 0 ? MF_STRING : MF_GRAYED, 1000, "Refresh");
326
				_menu.AppendMenuSeparator();
327
				for (i = 0; i < li.lastfm_charts_modes.length; i++) {
328
					_menu.AppendMenuItem(MF_STRING, i + 1001, li.lastfm_charts_modes[i].ucfirst());
329
				}
330
				_menu.CheckMenuRadioItem(1001, 1003, li.lastfm_charts_mode + 1001);
331
				_menu.AppendMenuSeparator();
332
				for (i = 0; i < li.lastfm_charts_periods_display.length; i++) {
333
					_menu.AppendMenuItem(MF_STRING, i + 1004, li.lastfm_charts_periods_display[i].ucfirst());
334
				}
335
				_menu.CheckMenuRadioItem(1004, 1009, li.lastfm_charts_period + 1004);
336
				_menu.AppendMenuSeparator();
337
				_menu.AppendMenuItem(MF_STRING, 1010, "Bar colour...");
338
				_menu.AppendMenuSeparator();
339
				_menu.AppendMenuItem(MF_STRING, 28, "Show timestamp");
340
				_menu.CheckMenuItem(28, li.show_timestamp);
341
				_menu.AppendMenuSeparator();
342
				break;
343
			case typeof ps == "object" && b.buttons.ps.trace(x, y):
344
				_sub1.AppendMenuItem(utils.CheckComponent("foo_customdb", true) && !ps.loved_working && !ps.playcount_working && l.username.length > 0 ? 0x00000000 | MF_STRING : MF_GRAYED, 1100, "loved tracks and playcount");
345
				_sub1.AppendMenuItem(utils.CheckComponent("foo_customdb", true) && !ps.loved_working && !ps.playcount_working && l.username.length > 0 ? MF_STRING : MF_GRAYED, 1109, "loved tracks only");
346
				_sub1.AppendMenuSeparator();
347
				_sub1.AppendMenuItem(utils.CheckComponent("foo_customdb", true) && this.fso.FileExists(ps.sql_file) ? MF_STRING : MF_GRAYED, 1101, "from file");
348
				_sub1.AppendMenuSeparator();
349
				_sub1.AppendMenuItem(MF_STRING, 1102, "Show console");
350
				_sub1.CheckMenuItem(1102, ps.show_console);
351
				_sub1.AppendTo(_menu, MF_STRING, "Library import");
352
				_menu.AppendMenuSeparator();
353
				_sub2.AppendMenuItem(MF_STRING, 1103, "Only update tracks in library");
354
				_sub2.CheckMenuItem(1103, ps.library);
355
				_sub2.AppendMenuItem(MF_STRING, 52, "Last.fm auto-correct");
356
				_sub2.CheckMenuItem(52, l.auto_correct);
357
				_sub2.AppendTo(_menu, MF_STRING, "Auto-updates");
358
				_menu.AppendMenuSeparator();
359
				_sub3.AppendMenuItem(MF_STRING, 1104, "Off");
360
				_sub3.AppendMenuItem(MF_STRING, 1105, "On");
361
				_sub3.CheckMenuRadioItem(1104, 1105, ps.auto_love ? 1105 : 1104);
362
				_sub3.AppendMenuSeparator();
363
				_sub3.AppendMenuItem(ps.auto_love ? MF_STRING : MF_GRAYED, 1106, "Condition");
364
				_sub3.AppendTo(_menu, MF_STRING, "Automatically love tracks");
365
				_menu.AppendMenuSeparator();
366
				_sub4.AppendMenuItem(MF_STRING, 1107, "tango");
367
				_sub4.AppendMenuItem(MF_STRING, 1108, "silk");
368
				_sub4.CheckMenuRadioItem(1107, 1108, ps.icon == "tango" ? 1107 : 1108);
369
				_sub4.AppendTo(_menu, MF_STRING, "Icon");
370
				_menu.AppendMenuSeparator();
371
				break;
372
			case typeof th == "object":
373
			case typeof im == "object" && im.trace(x, y):
374
				if (im.mode == "last.fm") {
375
					_menu.AppendMenuItem(this.metadb ? MF_STRING : MF_GRAYED, 450, "Download artist art from Last.fm");
376
					_menu.AppendMenuSeparator();
377
					_sub1.AppendMenuItem(MF_STRING, 451, "Off");
378
					_sub1.AppendMenuItem(MF_STRING, 452, "Library tracks only");
379
					_sub1.AppendMenuItem(MF_STRING, 453, "All tracks");
380
					_sub1.CheckMenuRadioItem(451, 453, im.auto_download == "off" ? 451 : im.auto_download == "library" ? 452 : 453);
381
					_sub1.AppendTo(_menu, MF_STRING, "Automatic download");
382
					_sub2.AppendMenuItem(MF_STRING, 461, "1");
383
					_sub2.AppendMenuItem(MF_STRING, 463, "3");
384
					_sub2.AppendMenuItem(MF_STRING, 465, "5");
385
					_sub2.AppendMenuItem(MF_STRING, 470, "10");
386
					_sub2.AppendMenuItem(MF_STRING, 480, "20");
387
					_sub2.CheckMenuRadioItem(461, 480, im.limit + 460);
388
					_sub2.AppendTo(_menu, MF_STRING, "Limit");
389
				} else {
390
					_menu.AppendMenuItem(MF_STRING, 442, "Set custom folder...");
391
				}
392
				_menu.AppendMenuSeparator();
393
				_menu.AppendMenuItem(MF_STRING, 403, "Re-scan folder");
394
				_menu.AppendMenuSeparator();
395
				_sub3.AppendMenuItem(MF_STRING, 440, "Last.fm artist art");
396
				_sub3.AppendMenuItem(MF_STRING, 441, "Custom folder");
397
				_sub3.CheckMenuRadioItem(440, 441, im.mode == "last.fm" ? 440 : 441);
398
				_sub3.AppendTo(_menu, MF_STRING, "Image source");
399
				_menu.AppendMenuSeparator();
400
				_sub4.AppendMenuItem(MF_STRING, 410, "Off");
401
				_sub4.AppendMenuItem(MF_STRING, 415, "5 seconds");
402
				_sub4.AppendMenuItem(MF_STRING, 420, "10 seconds");
403
				_sub4.AppendMenuItem(MF_STRING, 430, "20 seconds");
404
				_sub4.CheckMenuRadioItem(410, 430, im.cycle + 410);
405
				_sub4.AppendTo(_menu, MF_STRING, "Cycle");
406
				_menu.AppendMenuSeparator();
407
				if (typeof th == "object") {
408
					if (!this.check_feature("now_playing")) _sub5.AppendMenuItem(MF_STRING, 1200, "Grid");
409
					_sub5.AppendMenuItem(MF_STRING, 1201, "Left");
410
					_sub5.AppendMenuItem(MF_STRING, 1202, "Right");
411
					_sub5.AppendMenuItem(MF_STRING, 1203, "Top");
412
					_sub5.AppendMenuItem(MF_STRING, 1204, "Bottom");
413
					_sub5.AppendMenuItem(MF_STRING, 1205, "Off");
414
					_sub5.CheckMenuRadioItem(1200, 1205, th.mode == "grid" ? 1200 : th.mode == "left" ? 1201 : th.mode == "right" ? 1202 : th.mode == "top" ? 1203 : th.mode == "bottom" ? 1204 : 1205);
415
					if (th.mode != "off") {
416
						_sub5.AppendMenuSeparator();
417
						_sub5.AppendMenuItem(MF_STRING, 1275, "75px");
418
						_sub5.AppendMenuItem(MF_STRING, 1300, "100px");
419
						_sub5.AppendMenuItem(MF_STRING, 1350, "150px");
420
						_sub5.AppendMenuItem(MF_STRING, 1400, "200px");
421
						_sub5.AppendMenuItem(MF_STRING, 1450, "250px");
422
						_sub5.AppendMenuItem(MF_STRING, 1500, "300px");
423
						_sub5.CheckMenuRadioItem(1275, 1500, th.px + 1200);
424
					}
425
					_sub5.AppendTo(_menu, MF_STRING, "Thumbs");
426
					_menu.AppendMenuSeparator();
427
				}
428
				if (im.trace(x, y) && im.images.length > 0 && (!this.check_feature("thumbs") || th.mode != "grid")) {
429
					_menu.AppendMenuItem(MF_STRING, 400, "Open containing folder");
430
					_menu.AppendMenuItem(MF_STRING, 401, "Open image");
431
					_menu.AppendMenuItem(MF_STRING, 402, "Delete image");
432
					_menu.AppendMenuSeparator();
433
					_menu.AppendMenuItem(MF_STRING, 404, "Crop (focus on centre)");
434
					_menu.AppendMenuItem(MF_STRING, 405, "Crop (focus on top)");
435
					_menu.AppendMenuItem(MF_STRING, 406, "Centre");
436
					_menu.AppendMenuItem(MF_STRING, 407, "Stretch");
437
					_menu.CheckMenuRadioItem(404, 407, im.type == "crop" ? 404 : im.type == "crop top" ? 405 : im.type == "centre" ? 406 : 407);
438
					_menu.AppendMenuSeparator();
439
				}
440
				break;
441
		}
442
		if ((typeof a == "object" && a.trace(x, y)) || (typeof c == "object" && c.trace(x, y))) {
443
			_cover.AppendMenuItem(this.metadb ? MF_STRING : MF_GRAYED, 21, "Google images (launches browser)");
444
			_cover.AppendMenuItem(this.metadb ? MF_STRING : MF_GRAYED, 22, "Album Art Downloader");
445
			_cover.AppendMenuSeparator();
446
			_cover.AppendMenuItem(MF_STRING, 23, "Help");
447
			_cover.AppendTo(_menu, MF_STRING, "Cover search");
448
			//_menu.AppendMenuSeparator();
449
		}
450
		if (typeof t == "object" && t.trace(x, y)) {
451
			//_font.AppendMenuItem(MF_STRING, 30, 12);
452
			//_font.AppendMenuItem(MF_STRING, 31, 14);
453
			//_font.AppendMenuItem(MF_STRING, 32, 16);
454
			
455
			
456
			//_menu.AppendMenuSeparator();
457
		}
458
		if (this.check_feature("now_playing")) {
459
			_menu.AppendMenuItem(MF_STRING, 44, "Top style");
460
			_menu.AppendMenuItem(MF_STRING, 45, "Bottom style");
461
			_menu.CheckMenuRadioItem(44, 45, np_track_top ? 44 : 45);
462
			_menu.AppendMenuSeparator();
463
			_menu.AppendMenuItem(MF_STRING, 41, "Show CD cover");
464
			_menu.CheckMenuItem(41, np_cd);
465
			_menu.AppendMenuItem(MF_STRING, 42, "Show track info");
466
			_menu.CheckMenuItem(42, np_track);
467
			_menu.AppendMenuItem(MF_STRING, 46, "Show rating");
468
			_menu.CheckMenuItem(46, np_rating);
469
			_menu.AppendMenuItem(MF_STRING, 40, "Show web links");
470
			_menu.CheckMenuItem(40, np_buttons);
471
			_menu.AppendMenuItem(MF_STRING, 43, "Show biography");
472
			_menu.CheckMenuItem(43, np_bio);
473
			_menu.AppendMenuSeparator();
474
		}
475
		if (this.check_feature("custom_background")) {
476
			if (!this.dui) _custom_background.AppendMenuItem(MF_STRING, 100, "None");
477
			_custom_background.AppendMenuItem(MF_STRING, 101, this.dui ? "Use default UI setting" : "Use columns UI setting");
478
			_custom_background.AppendMenuItem(MF_STRING, 102, "Splitter");
479
			_custom_background.AppendMenuItem(MF_STRING, 104, "Pattern");
480
			_custom_background.AppendMenuItem(MF_STRING, 103, "Custom");
481
			_custom_background.AppendMenuSeparator();
482
			_custom_background.AppendMenuItem(this.background_mode == 3 ? MF_STRING : MF_GRAYED, 105, "Set custom colour...");
483
			_custom_background.CheckMenuRadioItem(101, 104, this.background_mode + 100);
484
			_custom_background.AppendTo(_menu, MF_STRING, "Background");
485
			_menu.AppendMenuSeparator();
486
		}
487
		if (typeof l == "object") {
488
			_menu.AppendMenuItem(MF_STRING, 1900, "Last.fm username...");
489
			if (typeof ps == "object") _menu.AppendMenuItem(l.username.length > 0 ? MF_STRING : MF_GRAYED, 1901, "Last.fm password...");
490
			_menu.AppendMenuSeparator();
491
		}
492
		if (this.check_feature("metadb")) {
493
			_metadb.AppendMenuItem(MF_STRING, 900, "Use display preferences");
494
			_metadb.AppendMenuItem(MF_STRING, 901, "Prefer now playing");
495
			_metadb.AppendMenuItem(MF_STRING, 902, "Follow selected track");
496
			_metadb.CheckMenuRadioItem(900, 903, this.selection_mode + 900);
497
			//_metadb.AppendTo(_menu, MF_STRING, "Selection mode");
498
			_menu.AppendMenuSeparator();
499
		}
500
		if (this.check_feature("remap")) {
501
			_menu.AppendMenuItem(MF_STRING, 7, "Artist field remapping...");
502
			//_menu.AppendMenuSeparator();
503
		}
504
		//_menu.AppendMenuItem(MF_STRING, 20, "Update script");
505
		//_menu.AppendMenuSeparator();
506
        
507
        
508
        //_menu.AppendTo(_menu, MF_STRING, "Font size");
509
510
            //_menu.AppendMenuItem(MF_STRING, 30, 12);
511
			//_menu.AppendMenuItem(MF_STRING, 31, 14);
512
			//_menu.AppendMenuItem(MF_STRING, 32, 16);
513
514
_menu.CheckMenuRadioItem(30, 33, this.normal_font_size == 12 ? 30 : this.normal_font_size == 14 ? 31 : 32);
515
        
516
        
517
        
518
		if (utils.IsKeyPressed(0x10)) _menu.AppendMenuItem(MF_STRING, 9, "Properties");
519
		//_menu.AppendMenuItem(MF_STRING, 10, "Configure...");
520
		idx = _menu.TrackPopupMenu(x, y);
521
		switch(idx) {
522
			case 1:
523
				on_item_focus_change();
524
				break;
525
			case 2:
526
			case 3:
527
			case 4:
528
			case 5:
529
			case 6:
530
				a.id = idx - 2;
531
				window.SetProperty("artreader_id", a.id);
532
				on_item_focus_change();
533
				break;
534
			case 7:
535
				this.artist_tf = this.InputBox("The default is %artist%\n\nYou can use the full foobar2000 title formatting syntax here.", "Artist field remapping", this.artist_tf);
536
				if (this.artist_tf == "") this.artist_tf = "%artist%";
537
				window.SetProperty("artist_tf", this.artist_tf);
538
				on_item_focus_change();
539
				break;
540
			case 9:
541
				window.ShowProperties();
542
				break;
543
			case 10:
544
				window.ShowConfigure();
545
				break;
546
			case 11:
547
				c.gloss = !c.gloss;
548
				window.SetProperty("cd_gloss", c.gloss);
549
				window.Repaint();
550
				break;
551
			case 12:
552
				c.shadow = !c.shadow;
553
				window.SetProperty("cd_shadow", c.shadow);
554
				window.Repaint();
555
				break;
556
			case 15:
557
			case 16:
558
			case 17:
559
			case 18:
560
				a.type = idx == 15 ? "crop" : idx == 16 ? "crop top" : idx == 17 ? "centre" : "stretch";
561
				window.SetProperty("artreader_image_type", a.type);
562
				window.RepaintRect(a.x, a.y, a.w, a.h);
563
				break;
564
			case 20:
565
				this.update_script();
566
				break;
567
			case 21:
568
				this.browser("https://www.google.com/search?tbm=isch&q=" + encodeURIComponent(this.eval("%album artist%[ %album%]")));
569
				break;
570
			case 22:
571
				this.aad();
572
				break;
573
			case 23:
574
				fb.ShowPopupMessage("You can get Album Art Downloader here:\n\nhttp://www.hydrogenaudio.org/forums/index.php?showtopic=57392", this.name);
575
				break;
576
			case 25:
577
			case 26:
578
				t.source = idx == 25 ? "last.fm" : "wikipedia";
579
				window.SetProperty("biography_source", t.source);
580
				t.artist = "";
581
				on_item_focus_change();
582
				break;
583
			case 28:
584
				li.show_timestamp = !li.show_timestamp;
585
				window.SetProperty("show_timestamp", li.show_timestamp);
586
				window.RepaintRect(li.x, li.y, li.w, li.h);
587
				break;
588
			case 29:
589
				li.discogs_match = !li.discogs_match;
590
				window.SetProperty("discogs_match", li.discogs_match);
591
				li.artist = "";
592
				on_item_focus_change();
593
				break;
594
			case 30:
595
			case 31:
596
			case 32:
597
				this.normal_font_size = idx == 30 ? 12 : idx == 31 ? 14 : 16;
598
				window.SetProperty("normal_font_size", this.normal_font_size);
599
				this.font_changed();
600
				break;
601
			case 40:
602
				np_buttons = !np_buttons;
603
				window.SetProperty("np_buttons", np_buttons);
604
				on_size();
605
				window.Repaint();
606
				break;
607
			case 41:
608
				np_cd = !np_cd;
609
				window.SetProperty("np_cd", np_cd);
610
				window.Repaint();
611
				break;
612
			case 42:
613
				np_track = !np_track;
614
				window.SetProperty("np_track", np_track);
615
				on_size();
616
				window.Repaint();
617
				break;
618
			case 43:
619
				np_bio = !np_bio;
620
				window.SetProperty("np_bio", np_bio);
621
				window.Repaint();
622
				break;
623
			case 44:
624
			case 45:
625
				np_track_top = idx == 44 ? true : false;
626
				window.SetProperty("np_track_top", np_track_top);
627
				on_size();
628
				window.Repaint();
629
				break;
630
			case 46:
631
				np_rating = !np_rating;
632
				window.SetProperty("np_rating", np_rating);
633
				on_size();
634
				window.Repaint();
635
				break;
636
			case 51:
637
			case 52:
638
			case 53:
639
				l.auto_correct = !l.auto_correct;
640
				window.SetProperty("lastfm_auto_correct", l.auto_correct);
641
				if (this.metadb) {
642
					if (idx == 51) li.get();
643
					if (idx == 53) t.get();
644
				}
645
				break;
646
			case 100:
647
			case 101:
648
			case 102:
649
			case 103:
650
			case 104:
651
				this.background_mode = idx - 100;
652
				window.SetProperty("background_mode", this.background_mode);
653
				window.Repaint();
654
				break;
655
			case 105:
656
				this.background_custom_colour = this.InputBox("Enter a custom colour for the background. Uses RGB. Example usage:\n\n234-211-74", this.name, this.background_custom_colour);
657
				window.SetProperty("background_custom_colour", this.background_custom_colour);
658
				window.Repaint();
659
				break;
660
			case 200:
661
				t.filename_tf = this.InputBox("Use title formatting to specify a path to your text file. eg: $directory_path(%path%)\\info.txt\n\nIf you prefer, you can specify just the path to a folder and the first txt or log file will be used.", this.name, t.filename_tf);
662
				window.SetProperty("text_filename_tf", t.filename_tf);
663
				on_item_focus_change();
664
				break;
665
			case 201:
666
				t.fixed = !t.fixed;
667
				window.SetProperty("text_fixed_font", t.fixed);
668
				t.calc();
669
				window.RepaintRect(t.x, t.y, t.w, t.h);
670
				break;
671
			case 400:
672
				this.run("explorer /select,\"" + im.files[im.index] + "\"");
673
				break;
674
			case 401:
675
				this.run("\"" + im.files[im.index] + "\"");
676
				break;
677
			case 402:
678
				im.delete_image();
679
				break;
680
			case 403:
681
				im.update();
682
				break;
683
			case 404:
684
			case 405:
685
			case 406:
686
			case 407:
687
				im.type = idx == 404 ? "crop" : idx == 405 ? "crop top" : idx == 406 ? "centre" : "stretch";
688
				window.SetProperty("image_type", im.type);
689
				window.Repaint();
690
				break;
691
			case 410:
692
			case 415:
693
			case 420:
694
			case 430:
695
				im.cycle = idx - 410;
696
				window.SetProperty("image_cycle", im.cycle);
697
				break;
698
			case 440:
699
			case 441:
700
				im.mode = idx == 440 ? "last.fm" : "custom";
701
				window.SetProperty("image_mode", im.mode);
702
				im.artist = "";
703
				im.folder = "";
704
				on_item_focus_change();
705
				break;
706
			case 442:
707
				im.custom_folder_tf = this.InputBox("Enter title formatting or an absolute path to a folder. You can specify multiple folders using | as a separator.", this.name, im.custom_folder_tf);
708
				if (im.custom_folder_tf == "") im.custom_folder_tf = "$directory_path(%path%)";
709
				window.SetProperty("image_custom_folder_tf", im.custom_folder_tf);
710
				im.folder = "";
711
				on_item_focus_change();
712
				break;
713
			case 450:
714
				im.download();
715
				break;
716
			case 451:
717
			case 452:
718
			case 453:
719
				im.auto_download = idx == 451 ? "off" : idx == 452 ? "library" : "all";
720
				window.SetProperty("image_auto_download", im.auto_download);
721
				break;
722
			case 461:
723
			case 463:
724
			case 465:
725
			case 470:
726
			case 480:
727
				im.limit = idx - 460;
728
				window.SetProperty("image_limit", im.limit);
729
				break;
730
			case 490:
731
			case 491:
732
			case 492:
733
			case 493:
734
				im.type = idx == 490 ? "crop" : idx == 491 ? "crop top" : idx == 492 ? "centre" : "stretch";
735
				window.SetProperty("image_type", im.type);
736
				window.RepaintRect(im.x, im.y, im.w, im.h);
737
				break;
738
			case 500:
739
			case 501:
740
			case 502:
741
				li.echonest_mode = idx - 500;
742
				window.SetProperty("echonest_mode", li.echonest_mode);
743
				li.artist = "";
744
				on_item_focus_change();
745
				break;
746
			case 600:
747
				li.autoplaylists_title = this.InputBox("Enter a new title", this.name, li.autoplaylists_title);
748
				if (li.autoplaylists_title == "") li.autoplaylists_title = "Autoplaylists";
749
				window.SetProperty("autoplaylists_title", li.autoplaylists_title);
750
				window.Repaint();
751
				break;
752
			case 601:
753
			case 602:
754
			case 603:
755
			case 604:
756
			case 605:
757
			case 606:
758
			case 607:
759
			case 608:
760
				li.autoplaylists_slot = idx - 600;
761
				window.SetProperty("autoplaylists_slot", li.autoplaylists_slot);
762
				li.filename = this.settings_folder + "autoplaylists" + li.autoplaylists_slot;
763
				li.offset = 0;
764
				li.update();
765
				break;
766
			case 610:
767
			case 611:
768
				li.autoplaylists_success = idx == 610 ? true : false;
769
				window.SetProperty("autoplaylists_success", li.autoplaylists_success);
770
				break;
771
			case 620:
772
			case 621:
773
			case 622:
774
			case 623:
775
			case 624:
776
			case 625:
777
			case 626:
778
			case 627:
779
				li.data.push(li.autoplaylists_deleted_items[idx - 620]);
780
				if (li.data.length > li.rows) li.offset = li.data.length - li.rows;
781
				li.autoplaylists_deleted_items.splice(idx - 620, 1);
782
				this.save(li.data.join("\n"), li.filename);
783
				window.NotifyOthers("autoplaylists", "update");
784
				li.update();
785
				break;
786
			case 650:
787
				li.autoplaylists_remove_duplicates = !li.autoplaylists_remove_duplicates
788
				window.SetProperty("autoplaylists_remove_duplicates", li.autoplaylists_remove_duplicates);
789
				break;
790
			case 700:
791
			case 701:
792
				li.musicbrainz_mode = idx == 700 ? "releases" : "URLs";
793
				window.SetProperty("musicbrainz_mode", li.musicbrainz_mode);
794
				li.artist = "";
795
				on_item_focus_change();
796
				break;
797
			case 702:
798
				li.musicbrainz_live = !li.musicbrainz_live;
799
				window.SetProperty("musicbrainz_live", li.musicbrainz_live);
800
				li.artist = "";
801
				on_item_focus_change();
802
				break;
803
			case 703:
804
				li.musicbrainz_album = !li.musicbrainz_album;
805
				window.SetProperty("musicbrainz_album", li.musicbrainz_album);
806
				li.artist = "";
807
				on_item_focus_change();
808
				break;
809
			case 704:
810
				li.musicbrainz_ep = !li.musicbrainz_ep;
811
				window.SetProperty("musicbrainz_ep", li.musicbrainz_ep);
812
				li.artist = "";
813
				on_item_focus_change();
814
				break;
815
			case 705:
816
				li.musicbrainz_single= !li.musicbrainz_single;
817
				window.SetProperty("musicbrainz_single", li.musicbrainz_single);
818
				li.artist = "";
819
				on_item_focus_change();
820
				break;
821
			case 706:
822
				li.musicbrainz_show_release_type = !li.musicbrainz_show_release_type;
823
				window.SetProperty("musicbrainz_show_release_type", li.musicbrainz_show_release_type);
824
				window.RepaintRect(li.x, li.y, li.w, li.h);
825
				break;
826
			case 801:
827
			case 802:
828
			case 803:
829
			case 804:
830
			case 805:
831
				li.lastfm_mode = idx - 801;
832
				window.SetProperty("lastfm_mode", li.lastfm_mode);
833
				li.artist = "";
834
				on_item_focus_change();
835
				break;
836
			case 810:
837
				li.lastfm_pause = !li.lastfm_pause;
838
				window.SetProperty("lastfm_pause", li.lastfm_pause);
839
				break;
840
			case 811:
841
			case 812:
842
				li.lastfm_button = idx == 811 ? "spotify" : "auto";
843
				window.SetProperty("lastfm_button", li.lastfm_button);
844
				window.RepaintRect(li.x, li.y, li.w, li.h);
845
				break;
846
			case 900:
847
			case 901:
848
			case 902:
849
				this.selection_mode = idx - 900;
850
				window.SetProperty("selection_mode", this.selection_mode);
851
				on_item_focus_change();
852
				break;
853
			case 1000:
854
				li.get();
855
				break;
856
			case 1001:
857
			case 1002:
858
			case 1003:
859
				li.lastfm_charts_mode = idx - 1001;
860
				window.SetProperty("lastfm_charts_mode", li.lastfm_charts_mode);
861
				li.update();
862
				break;
863
			case 1004:
864
			case 1005:
865
			case 1006:
866
			case 1007:
867
			case 1008:
868
			case 1009:
869
				li.lastfm_charts_period = idx - 1004;
870
				window.SetProperty("lastfm_charts_period", li.lastfm_charts_period);
871
				li.update();
872
				break;
873
			case 1010:
874
				li.lastfm_charts_bar_colour = this.InputBox("Enter a custom colour for the bars. Uses RGB. Example usage:\n\n234-211-74", this.name, li.lastfm_charts_bar_colour);
875
				window.SetProperty("lastfm_charts_bar_colour", li.lastfm_charts_bar_colour);
876
				window.Repaint();
877
				break;
878
			case 1100:
879
			case 1109:
880
				ps.full_import = idx == 1100 ? true : false;
881
				ps.start_import();
882
				break;
883
			case 1101:
884
				ps.finish_import();
885
				break;
886
			case 1102:
887
				ps.show_console = !ps.show_console;
888
				window.SetProperty("playcount_sync_show_console", ps.show_console);
889
				break;
890
			case 1103:
891
				ps.library = !ps.library;
892
				window.SetProperty("playcount_sync_library", ps.library);
893
				break;
894
			case 1104:
895
			case 1105:
896
				ps.auto_love = idx == 1104 ? false : true;
897
				window.SetProperty("playcount_sync_auto_love", ps.auto_love);
898
				break;
899
			case 1106:
900
				ps.auto_love_tf = this.InputBox("The result of the title formatting set here must equal 1 for a track to be automatically loved.\n\nExample:\n\n$ifequal(%rating%,5,1,0)", this.name, ps.auto_love_tf);
901
				window.SetProperty("playcount_sync_auto_love_tf", ps.auto_love_tf);
902
				break;
903
			case 1107:
904
			case 1108:
905
				ps.icon = idx == 1107 ? "tango" : "silk";
906
				window.SetProperty("playcount_sync_icon", ps.icon);
907
				b.update();
908
				break;
909
			case 1200:
910
			case 1201:
911
			case 1202:
912
			case 1203:
913
			case 1204:
914
			case 1205:
915
				th.mode = idx == 1200 ? "grid" : idx == 1201 ? "left" : idx == 1202 ? "right" : idx == 1203 ? "top" : idx == 1204 ? "bottom" : "off";
916
				window.SetProperty("thumbs_mode", th.mode);
917
				th.nc = true;
918
				on_size();
919
				window.Repaint();
920
				break;
921
			case 1275:
922
			case 1300:
923
			case 1350:
924
			case 1400:
925
			case 1450:
926
			case 1500:
927
				th.px = idx - 1200;
928
				window.SetProperty("thumbs_px", th.px);
929
				th.nc = true;
930
				on_size();
931
				window.Repaint();
932
				break;
933
			case 1700:
934
				t.title = this.InputBox("You can use full title formatting here.", this.name, t.title);
935
				window.SetProperty("text_title", t.title);
936
				window.Repaint();
937
				break;
938
			case 1701:
939
				t.tag = this.InputBox("Enter a custom tag.", this.name, t.tag);
940
				window.SetProperty("text_tag", t.tag);
941
				t.filename = "";
942
				on_item_focus_change();
943
				break;
944
			case 1702:
945
				t.centre = !t.centre;
946
				window.SetProperty("text_centre", t.centre);
947
				window.RepaintRect(t.x, t.y, t.w, t.h);
948
				break;
949
			case 1800:
950
			case 1801:
951
			case 1802:
952
				var period = idx == 1800 ? 0 : idx == 1801 ? ONE_DAY : ONE_WEEK;
953
				s.clear_images(period);
954
				break;
955
			case 1803:
956
				s.library = !s.library;
957
				window.SetProperty("seekbar_library", s.library);
958
				break;
959
			case 1804:
960
				s.sox_params = this.InputBox("All SoX spectrogram options should work here.\n\n-r, -d and -o are already configured so do not use those. Check sox.pdf for everything else.", this.name, s.sox_params);
961
				window.SetProperty("sox_params", s.sox_params);
962
				if (fb.IsPlaying) s.playback_new_track();
963
				break;
964
			case 1805:
965
				s.marker = this.InputBox("Enter a custom colour for the marker. Uses RGB. Example usage:\n\n234-211-74", this.name, s.marker);
966
				window.SetProperty("seekbar_marker", s.marker);
967
				break;
968
			case 1900:
969
				l.update_username();
970
				break;
971
			case 1901:
972
				l.update_password();
973
				break;
974
		}
975
		_menu.Dispose();
976
		_custom_background.Dispose();
977
		_metadb.Dispose();
978
		_cover.Dispose();
979
		_font.Dispose();
980
		_sub1.Dispose();
981
		_sub2.Dispose();
982
		_sub3.Dispose();
983
		_sub4.Dispose();
984
		_sub5.Dispose();
985
	}
986
	
987
	this.features_init = function() {
988
		for (i = 0; i < this.features.length; i++) {
989
			switch(this.features[i]) {
990
				case "custom_background":
991
					this.background_mode = window.GetProperty("background_mode", 1);
992
					this.background_custom_colour = window.GetProperty("background_custom_colour", "0-0-0");
993
					break;
994
				case "metadb":
995
					this.selection_mode = window.GetProperty("selection_mode", 1);
996
					this.metadb = fb.GetFocusItem();
997-
					this.selection_mode = window.GetProperty("selection_mode", 0);
997+
998
				case "themes":
999
					this.themed_seekbar = window.CreateThemeManager("Progress");
1000
					this.themed_header = window.CreateThemeManager("Header");
1001
					this.themed_header.SetPartAndStateId(1, 1);
1002
					this.themed_button = window.CreateThemeManager("Button");
1003
					break;
1004
				case "remap":
1005
					this.artist_tf = window.GetProperty("artist_tf", "%artist%");
1006
					break;
1007
			}
1008
		}
1009
	}
1010
	
1011
	this.eval = function(tf) {
1012
		if (!this.metadb || tf == "") return "";
1013
		if (fb.IsPlaying && this.metadb.RawPath.indexOf("file://") != 0) {
1014
			return fb.TitleFormat(tf).Eval();
1015
		} else {
1016
			return fb.TitleFormat(tf).EvalWithMetadb(this.metadb);
1017
		}
1018
	}
1019
	
1020
	this.open = function(filename) {
1021
		return utils.ReadTextFile(filename);
1022
	}
1023
	
1024
	this.save = function(t, f) {
1025
		try {
1026
			var ts = this.fso.OpenTextFile(f, 2, true, -1);
1027
			ts.WriteLine(t);
1028
			ts.Close();
1029
			return true;
1030
		} catch(e) {
1031
			return false;
1032
		}
1033
	}
1034
	
1035
	this.console = function(message) {
1036
		fb.trace(this.name + ": " + message);
1037
	}
1038
	
1039
	this.strip_tags = function(value) {
1040
		try {
1041
			this.doc.open();
1042
			var div = this.doc.createElement("div");
1043
			div.innerHTML = value.replace(/\n/g, "<br>");
1044
			return div.innerText.trim();
1045
		} catch(e) {
1046
			return "Error reading content.";
1047
		}
1048
	}
1049
	
1050
	this.InputBox = function(prompt, title, value) {
1051
		prompt = prompt.replace(/"/g, '" + Chr(34) + "').replace(/\n/g, '" + Chr(13) + "');
1052
		title = title.replace(/"/g, '" + Chr(34) + "');
1053
		value = value.replace(/"/g, '" + Chr(34) + "');
1054
		var temp_value = this.vb.eval('InputBox' + '("' + prompt + '", "' + title + '", "' + value + '")');
1055
		return typeof temp_value == "undefined" ? value : temp_value.trim();
1056
	}
1057
	
1058
	this.MsgBox = function(prompt, buttons, title) {
1059
		prompt = prompt.replace(/"/g, '" + Chr(34) + "').replace(/\n/g, '" + Chr(13) + "');
1060
		title = title.replace(/"/g, '" + Chr(34) + "');
1061
		return this.vb.eval('MsgBox' + '("' + prompt + '", "' + buttons + '", "' + title + '")');
1062
	}
1063
	
1064
	this.check_feature = function(f) {
1065
		for (i = 0; i < this.features.length; i++) {
1066
			if (this.features[i] == f) return true;
1067
		}
1068
		return false;
1069
	}
1070
	
1071
	this.browser = function(command) {
1072
		if (!this.run(command)) this.MsgBox("Unable to launch your default browser.", 0, this.name);
1073
	}
1074
	
1075
	this.run = function(command) {
1076
		try {
1077
			this.WshShell.Run(command);
1078
			return true;
1079
		} catch(e) {
1080
			return false;
1081
		}
1082
	}
1083
	
1084
	this.aad = function() {
1085
		var aad_path = window.GetProperty("aad_path", "");
1086
		if (!this.fso.FileExists(aad_path)) {
1087
			var folder = this.app.BrowseForFolder(0, "Locate Album Art Downloader", 0x00000200);
1088
			var file = folder ? folder.items().item().path + "\\AlbumArt.exe" : "";
1089
			if (this.fso.FileExists(file)) {
1090
				aad_path = file;
1091
				window.SetProperty("aad_path", file);
1092
			}
1093
		}
1094
		if (this.fso.FileExists(aad_path)) {
1095
			var album_artist = "/ar \"" + this.eval("[%album artist%]") + "\"";
1096
			var album = " /al \"" + this.eval("[%album%]") + "\"";
1097
			this.run("\"" + aad_path + "\"" + album_artist + album);
1098
		}
1099
	}
1100
	
1101
	this.tt = function(t) {
1102
		if (this.tooltip.Text == t) return;
1103
		this.tooltip.Text = t;
1104
		this.tooltip.Activate();
1105
	}
1106
	
1107
	this.ttd = function() {
1108
		this.tooltip.Text = "";
1109
		this.tooltip.Deactivate();
1110
	}
1111
	
1112
	this.splitRGB = function(value) {
1113
		var temp_col = value.split("-");
1114
		return RGB(temp_col[0], temp_col[1], temp_col[2]);
1115
	}
1116
	
1117
	this.draw_background = function(gr) {
1118
		if (this.check_feature("custom_background")) {
1119
			var col;
1120
			switch(this.background_mode) {
1121
				case 0:
1122
					col = null;
1123
					break;
1124
				case 1:
1125
					col = this.backcolour;
1126
					break;
1127
				case 2:
1128
					col = utils.GetSysColor(15);
1129
					break;
1130
				case 3:
1131
					col = this.splitRGB(this.background_custom_colour);
1132
					break;
1133
				case 4:
1134
					col = null;
1135
					var spacing = 3;
1136
					var line_thickness = 1;
1137
					var bg = RGB(65, 65, 65);
1138
					var colour = RGB(92, 92, 92);
1139
					gr.FillSolidRect(0, 0, this.w, this.h, bg);
1140
					for (i = 0; i < Math.ceil(this.w / spacing); i++) {
1141
						gr.DrawLine(i * spacing, 0, i * spacing, this.h, line_thickness, colour);
1142
					}
1143
					for (i = 0; i < Math.ceil(this.h / spacing); i++) {
1144
						gr.DrawLine(0, i * spacing, this.w, i * spacing, line_thickness, colour);
1145
					}
1146
					break;
1147
			}
1148
			if (col) gr.FillSolidRect(0, 0, this.w, this.h, col);
1149
		} else {
1150
			gr.FillSolidRect(0, 0, this.w, this.h, this.backcolour);
1151
		}
1152
	}
1153
	
1154
	this.draw_image = function(gr, img, pos_x, pos_y, width, height, type, border, alpha) {
1155
		if (!img) return;
1156
		gr.SetInterpolationMode(7);
1157
		switch(type) {
1158
			case "crop":
1159
			case "crop top":
1160
				var sr = img.Width / img.Height;
1161
				var dr = width / height;
1162
				if (sr < dr) {
1163
					var r = img.Width / width;
1164
					var ch = height * r;
1165
					var sy = Math.round((img.Height - ch) / (type == "crop top" ? 4 : 2));
1166
					var cw = img.Width;
1167
					var sx = 0;
1168
				} else {
1169
					var r = img.Height / height;
1170
					var cw = width * r;
1171
					var sx = Math.round((img.Width - cw) / 2);
1172
					var ch = img.Height;
1173
					var sy = 0;
1174
				}
1175
				gr.DrawImage(img, pos_x, pos_y, width, height, sx + 5, sy + 5, cw - 10, ch - 10, 0, alpha || 255);
1176
				if (border) gr.DrawRect(pos_x, pos_y, width - 1, height - 1, 1, border);
1177
				break;
1178
			case "stretch":
1179
				gr.DrawImage(img, pos_x, pos_y, width, height, 0, 0, img.Width, img.Height, 0, alpha || 255);
1180
				if (border) gr.DrawRect(pos_x, pos_y, width - 1, height - 1, 1, border);
1181
				break;
1182
			case "centre":
1183
			default:
1184
				var s = Math.min(width / img.Width, height / img.Height);
1185
				var nw = Math.round(img.Width * s);
1186
				var nh = Math.round(img.Height * s);
1187
				pos_x += Math.round((width - nw) / 2);
1188
				pos_y += Math.round((height - nh) / 2);
1189
				gr.DrawImage(img, pos_x, pos_y, nw, nh, 0, 0, img.Width, img.Height, 0, alpha || 255);
1190
				if (border) gr.DrawRect(pos_x, pos_y, nw - 1, nh - 1, 1, border);
1191
				break;
1192
		}
1193
	}
1194
	
1195
	this.header = function(gr, text) {
1196
		this.left_text(gr, text, this.title_font, this.textcolour_hl, 6, 6, this.w - 77, 24);
1197
		gr.DrawLine(6, 29, this.w - 6, 29, 1, this.textcolour_hl);
1198
	}
1199
	
1200
	this.centre_text = function(gr, text, font, colour, x, y, w, h) {
1201
		gr.GdiDrawText(text, font, colour, x, y, w, h, DT_VCENTER | DT_CENTER | DT_WORDBREAK | DT_CALCRECT | DT_NOPREFIX);
1202
	}
1203
	
1204
	this.left_text = function(gr, text, font, colour, x, y, w, h) {
1205
		gr.GdiDrawText(text, font, colour, x, y, w, h, DT_VCENTER | DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
1206
	}
1207
	
1208
	this.right_text = function(gr, text, font, colour, x, y, w, h) {
1209
		gr.GdiDrawText(text, font, colour, x, y, w, h, DT_VCENTER | DT_RIGHT | DT_END_ELLIPSIS | DT_CALCRECT | DT_NOPREFIX);
1210
	}
1211
	
1212
	this.format_time = function(t) {
1213
		var h = Math.floor(t / 3600);
1214
		var m = Math.floor((t -= h * 3600) / 60);
1215
		var s = Math.floor(t -= m * 60);
1216
		return((h > 0 ? h + ":" + (m < 10 ? "0" + m : m) : m) + ":" + (s < 10 ? "0" + s : s));
1217
	}
1218
	
1219
	this.check_version = function() {
1220
		//not used any more but will leave intact so old scripts don't crash.
1221
		return;
1222
	}
1223
	
1224
	this.update_script = function() {
1225
		this.xmlhttp.open("GET", this.common_url, true);
1226
		this.xmlhttp.send();
1227
		this.xmlhttp.onreadystatechange = function() {
1228
			if (p.xmlhttp.readyState == 4) {
1229
				switch(p.xmlhttp.status) {
1230
					case 200:
1231
						var text = p.xmlhttp.responsetext;
1232
						switch (true) {
1233
							case text.indexOf("function panel") == -1:
1234
								p.MsgBox("Unexpected server error. Not updating.", 0, p.name);
1235
								break;
1236
							case text.indexOf('var VERSION = "' + VERSION) == 0:
1237
								p.MsgBox("Already using latest version.", 0, p.name);
1238
								break;
1239
							default:
1240
								if (p.save(text, p.common_filename)) {
1241
									if (p.MsgBox("Download successful. Restart foobar2000 now?", 4, p.name) == 6) fb.RunMainMenuCommand("File/Restart");
1242
								} else {
1243
									p.MsgBox("Unexpected error. Not updating.", 0, p.name);
1244
								}
1245
								break;
1246
						}
1247
						break;
1248
					case 404:
1249
						fb.ShowPopupMessage("The script reports that the update file has not been found. It could be a temporary problem with dropbox but if this error persists then it is most likely due to support stopping for this version. Check the hydrogenaudio thread or the full zip for updates.\n\nhttp://www.hydrogenaudio.org/forums/index.php?showtopic=77883\n" + p.root_url + "samples.zip", p.name);
1250
						break;
1251
					default:
1252
						p.console(p.xmlhttp.responsetext || "HTTP error: " + p.xmlhttp.status);
1253
						break;
1254
				}
1255
			}
1256
		}
1257
	}
1258
	
1259
	this.json = function(text) {
1260
		try {
1261
			var data = JSON.parse(text);
1262
			return data;
1263
		} catch(e) {
1264
			this.console("JSON.parse error.");
1265
			return false;
1266
		}
1267
	}
1268
	
1269
	this.json_reverse_sort = function(data, prop) {
1270
		data = data.sort(function(a, b) {
1271
			if (!a[prop] || b[prop] > a[prop]) return 1;
1272
			if (!b[prop] || b[prop] < a[prop]) return -1;
1273
			return 0;
1274
		});
1275
		return data;
1276
	}
1277
	
1278
	this.name = name;
1279
	this.features = features;
1280
	this.dui = window.InstanceType;
1281
	this.script_path = fb.ProfilePath + "marc2003\\";
1282
	this.images_path = this.script_path + "images\\";
1283
	this.root_url = "https://dl.dropboxusercontent.com/u/22801321/wsh/"
1284
	this.common_url = this.root_url + "marc2003/common7.js";
1285
	this.common_filename = this.script_path + "common7.js";
1286
	this.tooltip = window.CreateTooltip();
1287
	this.w = 0;
1288
	this.h = 0;
1289
	this.mx = 0;
1290
	this.my = 0;
1291
	this.metadb = fb.GetFocusItem();
1292
	this.app = new ActiveXObject("Shell.Application");
1293
	this.WshShell = new ActiveXObject("WScript.Shell");
1294
	this.fso = new ActiveXObject("Scripting.FileSystemObject");
1295
	this.doc = new ActiveXObject("htmlfile");
1296
	this.vb = new ActiveXObject("ScriptControl");
1297
	this.vb.Language = "VBScript";
1298
	this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
1299
	this.settings_folder = fb.ProfilePath + "wsh_settings\\";
1300
	if (!this.fso.FolderExists(this.settings_folder)) this.fso.CreateFolder(this.settings_folder);
1301
	this.data_folder = fb.ProfilePath + "wsh_lastfm\\";
1302
	if (!this.fso.FolderExists(this.data_folder)) this.fso.CreateFolder(this.data_folder);
1303
	this.artist = "";
1304
	this.artist_tf = "%artist%";
1305
	this.normal_font_size = window.GetProperty("normal_font_size", 12);
1306
	this.list_font_size = 11;
1307
	this.up_img = gdi.Image(this.images_path + "up.png");
1308
	this.down_img = gdi.Image(this.images_path + "down.png");
1309
	this.features_init();
1310
	this.colors_changed();
1311
	this.font_changed();
1312
}
1313
1314
function artreader(x, y, w, h) {
1315
	this.draw = function(gr) {
1316
		if (!this.img) return;
1317
		p.draw_image(gr, this.img, this.x, this.y, this.w, this.h, this.type);
1318
	}
1319
	
1320
	this.metadb_changed = function() {
1321
		if (!p.metadb) return;
1322
		this.img && this.img.Dispose();
1323
		this.path = "";
1324
		this.img = utils.GetAlbumArtV2(p.metadb, this.id);
1325
		window.RepaintRect(this.x, this.y, this.w, this.h);
1326
		utils.GetAlbumArtAsync(window.ID, p.metadb, this.id);
1327
	}
1328
	
1329
	this.get_album_art_done = function(ip) {
1330
		this.path = ip;
1331
	}
1332
	
1333
	this.trace = function(x, y) {
1334
		return (x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h);
1335
	}
1336
	
1337
	this.wheel = function(step) {
1338
		if (!this.trace(p.mx, p.my)) return false;
1339
		this.id -= step;
1340
		if (this.id < 0) this.id = 4;
1341
		if (this.id > 4) this.id = 0;
1342
		window.SetProperty("artreader_id", this.id);
1343
		this.metadb_changed();
1344
		return true;
1345
	}
1346
	
1347
	this.move = function(x, y) {
1348
		if (this.trace(x, y)) {
1349
			if (this.img) p.tt("Original size: " + this.img.Width + "x" + this.img.Height + "px");
1350
			return true;
1351
		} else {
1352
			p.ttd();
1353
			return false;
1354
		}
1355
	}
1356
	
1357
	this.lbtn_dblclk = function(x, y) {
1358
		if (!this.trace(x, y)) return false;
1359
		if (this.img) p.run("explorer /select,\"" + this.path + "\"");
1360
		return true;
1361
	}
1362
	
1363
	this.x = x;
1364
	this.y = y;
1365
	this.w = w;
1366
	this.h = h;
1367
	this.id = window.GetProperty("artreader_id", 0);
1368
	this.img = null;
1369
	this.path = null;
1370
	this.type = window.GetProperty("artreader_image_type", "crop");
1371
}
1372
1373
function buttons() {
1374
	this.draw = function(gr) {
1375
		for (i in this.buttons) {
1376
			this.buttons[i].draw(gr);
1377
		}
1378
	}
1379
	
1380
	this.move = function(x, y) {
1381
		this.tmp_btn = null;
1382
		for (i in this.buttons) {
1383
			if (this.buttons[i].trace(x, y)) this.tmp_btn = i;
1384
		}
1385
		if (this.btn == this.tmp_btn) return this.btn;
1386
		if (this.tmp_btn) this.buttons[this.tmp_btn].state("hover");
1387
		if (this.btn) this.buttons[this.btn].state("normal");
1388
		this.btn = this.tmp_btn;
1389
		return this.btn;
1390
	}
1391
	
1392
	this.lbtn_up = function(x, y) {
1393
		if (!this.btn) return false;
1394
		this.buttons[this.btn].lbtn_up(x, y);
1395
		return true;
1396
	}
1397
	
1398
	this.leave = function() {
1399
		if (this.btn) this.buttons[this.btn].state("normal");
1400
		this.btn = null;
1401
	}
1402
	
1403
	this.buttons = [];
1404
	this.btn = null;
1405
}
1406
1407
function button(x, y, w, h, img_src, func, tiptext, themed) {
1408
	this.draw = function (gr) {
1409
		if (this.themed) {
1410
			p.themed_button.SetPartAndStateId(1, this.id);
1411
			p.themed_button.DrawThemeBackground(gr, this.x, this.y, this.w, this.h);
1412
			this.img && p.draw_image(gr, this.img, this.x + Math.round((this.w - this.img.Width) / 2), this.y + Math.round((this.h - this.img.Height) / 2), this.img.Width, this.img.Height, "centre");
1413
		} else {
1414
			this.img && p.draw_image(gr, this.img, this.x, this.y, this.w, this.h, "centre");
1415
		}
1416
	}
1417
	
1418
	this.trace = function(x, y) {
1419
		return (x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h);
1420
	}
1421
	
1422
	this.lbtn_up = function () {
1423
		this.func && this.func(x, y);
1424
	}
1425
	
1426
	this.state = function(s) {
1427
		if (s == "hover") {
1428
			this.img = this.img_hover;
1429
			this.id = 2;
1430
			p.tt(this.tiptext);
1431
		} else {
1432
			this.img = this.img_normal;
1433
			this.id = 0;
1434
			p.ttd();
1435
		}
1436
		window.RepaintRect(this.x, this.y, this.w, this.h);
1437
	}
1438
	
1439
	this.x = x;
1440
	this.y = y;
1441
	this.w = w;
1442
	this.h = h;
1443
	this.func = func;
1444
	this.tiptext = tiptext;
1445
	this.themed = themed;
1446
	this.id = 0;
1447
	this.img_normal = gdi.Image(p.images_path + img_src.normal);
1448
	this.img_hover = img_src.hover ? gdi.Image(p.images_path + img_src.hover) : this.img_normal;
1449
	this.img = this.img_normal;
1450
}
1451
1452
function cd(x, y, w, h) {
1453
	this.draw = function(gr) {
1454
		if (this.shadow) p.draw_image(gr, this.shadow_img, this.x, this.y, this.w, this.h, "centre");
1455
		p.draw_image(gr, this.case_img, this.x, this.y, this.w, this.h, "centre");
1456
		if (this.img) {
1457
			this.ratio = Math.min(this.w / this.case_img.Width, this.h / this.case_img.Height);
1458
			this.nw = 488 * this.ratio;
1459
			this.nh = 476 * this.ratio;
1460
			this.nx = Math.round((this.w - (452 * this.ratio)) / 2);
1461
			this.ny = Math.round((this.h - this.nh) / 2);
1462
			p.draw_image(gr, this.img, this.nx + this.x, this.ny + this.y, this.nw, this.nh, "crop top");
1463
		}
1464
		p.draw_image(gr, this.semi_img, this.x, this.y, this.w, this.h, "centre");
1465
		if (this.gloss) p.draw_image(gr, this.gloss_img, this.x, this.y, this.w, this.h, "centre");
1466
	}
1467
	
1468
	this.metadb_changed = function() {
1469
		if (!p.metadb) return;
1470
		this.img && this.img.Dispose();
1471
		this.path = "";
1472
		this.img = utils.GetAlbumArtV2(p.metadb, 0);
1473
		window.Repaint();
1474
		utils.GetAlbumArtAsync(window.ID, p.metadb, 0);
1475
	}
1476
	
1477
	this.get_album_art_done = function(ip) {;
1478
		this.path = ip;
1479
	}
1480
	
1481
	this.trace = function(x, y) {
1482
		return (x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h);
1483
	}
1484
	
1485
	this.move = function(x, y) {
1486
		if (this.trace(x, y)) {
1487
			if (this.img) p.tt("Original size: " + this.img.Width + "x" + this.img.Height + "px");
1488
			return true;
1489
		} else {
1490
			p.ttd();
1491
			return false;
1492
		}
1493
	}
1494
	
1495
	this.lbtn_dblclk = function(x, y) {
1496
		if (!this.trace(x, y)) return false;
1497
		if (this.img) p.run("explorer /select,\"" + this.path + "\"");
1498
		return true;
1499
	}
1500
	
1501
	this.x = x;
1502
	this.y = y;
1503
	this.w = w;
1504
	this.h = h;
1505
	this.shadow_img = gdi.Image(p.images_path + "shadow.png");
1506
	this.case_img = gdi.Image(p.images_path + "case.png");
1507
	this.semi_img = gdi.Image(p.images_path + "semi.png");
1508
	this.gloss_img = gdi.Image(p.images_path + "gloss.png");
1509
	this.gloss = window.GetProperty("cd_gloss", true);
1510
	this.shadow = window.GetProperty("cd_shadow", true);
1511
	this.img = null;
1512
	this.path = null;
1513
}
1514
1515
function images(x, y, w, h) {
1516
	this.playback_time = function(time) {
1517
		if (this.mode == "last.fm" && time == 1 && this.files.length == 0 && p.metadb.RawPath == fb.GetNowPlaying().RawPath && ((this.auto_download == "library" && fb.IsMetadbInMediaLibrary(p.metadb)) || this.auto_download == "all")) this.download();
1518
		if (this.cycle > 0 && this.images.length > 1 && time % this.cycle == 0) {
1519
			this.index++;
1520
			if (this.index == this.images.length) this.index = 0;
1521
			window.RepaintRect(this.x, this.y, this.w, this.h);
1522
		}
1523
	}
1524
	
1525
	this.draw = function (gr) {
1526
		if (this.images.length == 0) return;
1527
		p.draw_image(gr, this.images[this.index], this.x, this.y, this.w, this.h, this.type);
1528
	}
1529
	
1530
	this.metadb_changed = function() {
1531
		if (!p.metadb) return false;
1532
		switch(this.mode) {
1533
			case "last.fm":
1534
				p.artist = p.eval(p.artist_tf);
1535
				if (this.artist == p.artist) return false;
1536
				this.artist = p.artist;
1537
				this.folder = p.data_folder + p.eval("$crc32(" + p.artist_tf + ")");
1538
				if (!p.fso.FolderExists(this.folder)) p.fso.CreateFolder(this.folder);
1539
				break;
1540
			case "custom":
1541
				this.temp_folder = p.eval(this.custom_folder_tf);
1542
				if (this.temp_folder == this.folder) return false;
1543
				this.folder = this.temp_folder;
1544
				break;
1545
		}
1546
		this.update();
1547
		return true;
1548
	}
1549
	
1550
	this.trace = function(x, y) {
1551
		return (x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h);
1552
	}
1553
	
1554
	this.wheel = function(step) {
1555
		if (!p.metadb || this.images.length < 2 || !this.trace(p.mx, p.my) || (typeof th == "object" && th.mode == "grid" && !th.overlay)) return false;
1556
		this.index -= step;
1557
		if (this.index < 0) this.index = this.images.length - 1;
1558
		if (this.index >= this.images.length) this.index = 0;
1559
		window.RepaintRect(this.x, this.y, this.w, this.h);
1560
		return true;
1561
	}
1562
	
1563
	this.lbtn_dblclk = function(x, y) {
1564
		if (!this.trace(x, y)) return false;
1565
		p.run("\"" + this.files[this.index] + "\"");
1566
		return true;
1567
	}
1568
	
1569
	this.download = function() {
1570
		window.Repaint();
1571
		if (p.artist == "" || p.artist == "?") return;
1572
		p.WshShell.Run('"' + p.script_path + 'art.exe" "' + p.artist + '" "' + this.folder + '" ' + this.limit, 0, true);
1573
		this.update();
1574
		window.NotifyOthers("images", "update");
1575
	}
1576
	
1577
	this.update = function() {
1578
		this.index = 0;
1579
		for (i = 0; i < this.images.length; i++) {
1580
			try { this.images[i].Dispose(); } catch(e) {}
1581
		}
1582
		this.folders = this.folder.split("|");
1583
		this.files = [];
1584
		for (i = 0; i < this.folders.length; i++) {
1585
			if (p.fso.FolderExists(this.folders[i])) this.files = this.files.concat(utils.Glob(this.folders[i] + "\\*.jpg").toArray(), utils.Glob(this.folders[i] + "\\*.jpeg").toArray(), utils.Glob(this.folders[i] + "\\*.png").toArray(), utils.Glob(this.folders[i] + "\\*.gif").toArray());
1586
		}
1587
		this.images = [];
1588
		for (i = 0; i < this.files.length; i++) {
1589
			this.images[i] = gdi.Image(this.files[i]);
1590
		}
1591
		if (typeof th == "object") {
1592
			th.nc = true;
1593
			th.calc();
1594
		}
1595
		window.Repaint();
1596
	}
1597
	
1598
	this.delete_image = function() {
1599
		p.app.Namespace(10).MoveHere(this.files[this.index]);
1600
		this.update();
1601
	}
1602
	
1603
	this.x = x;
1604
	this.y = y;
1605
	this.w = w;
1606
	this.h = h;
1607
	this.files = [];
1608
	this.images = [];
1609
	this.index = 0;
1610
	this.artist = "";
1611
	this.folder = "";
1612
	this.limit = window.GetProperty("image_limit", 5);
1613
	this.cycle = window.GetProperty("image_cycle", 0);
1614
	this.mode = window.GetProperty("image_mode", "last.fm");
1615
	this.type = window.GetProperty("image_type", "crop top");
1616
	this.auto_download = window.GetProperty("image_auto_download", "all");
1617
	this.custom_folder_tf = window.GetProperty("image_custom_folder_tf", "$directory_path(%path%)");
1618
}
1619
1620
function lastfm() {
1621
	this.notify_data = function(name, data) {
1622
		if (name == "lastfm" && data == "update") {
1623
			this.username = p.open(this.username_file).trim();
1624
			this.sk = p.open(this.sk_file).trim();
1625
			if (p.check_feature("lastfm_charts")) li.update();
1626
			if (typeof ps == "object") {
1627
				ps.loved_working = false;
1628
				ps.playcount_working = false;
1629
				b.update();
1630
				on_item_focus_change();
1631
			}
1632
			if (p.check_feature("lastfm")) {
1633
				li.artist = "";
1634
				on_item_focus_change();
1635
			}
1636
			if (p.check_feature("lastfm_album")) {
1637
				t.artist = "";
1638
				t.album = "";
1639
				on_item_focus_change();
1640
			}
1641
			window.Repaint();
1642
		}
1643
	}
1644
	
1645
	this.post = function(method, metadb) {
1646
		switch(method) {
1647
			case "auth.getMobileSession":
1648
				var func = function() {
1649
					var json_data = p.json(l.xmlhttp.responsetext);
1650
					if (!json_data) return;
1651
					if (json_data.error) {
1652
						p.MsgBox(json_data.message, 0, p.name);
1653
					} else if (json_data.session) {
1654
						p.save(json_data.session.key, l.sk_file);
1655
						window.NotifyOthers("lastfm", "update");
1656
						l.notify_data("lastfm", "update");
1657
					}
1658
				}
1659
				
1660
				this.authToken = hex_md5(this.username + hex_md5(this.password));
1661
				this.api_sig = hex_md5("api_key" + this.api_key + "authToken" + this.authToken + "method" + method + "username" + this.username + this.secret);
1662
				this.data = "method=" + method + "&api_key=" + this.api_key + "&api_sig=" + this.api_sig + "&format=json&authToken=" + this.authToken + "&username=" + this.username;
1663
				break;
1664
			case "track.love":
1665
			case "track.unlove":
1666
				var func = function() {
1667
					var json_data = p.json(l.xmlhttp.responsetext);
1668
					if (!json_data) return;
1669
					if (json_data.error) {
1670
						p.console(json_data.message);
1671
					} else if (json_data.status && json_data.status == "ok") {
1672
						p.console("Track " + (method == "track.love" ? "loved successfully." : "unloved successfully."));
1673
						fb.RunContextCommandWithMetadb("Customdb Love " + (method == "track.love" ? 1 : 0), metadb, 8);
1674
					}
1675
				}
1676
				
1677
				if (!metadb || this.username.length == 0 || this.sk.length != 32) return;
1678
				this.artist = fb.TitleFormat(p.artist_tf).EvalWithMetadb(metadb);
1679
				this.track = fb.TitleFormat("%title%").EvalWithMetadb(metadb);
1680
				if (this.artist == "" || this.artist == "?" || this.track == "?") return;
1681
				p.console("Attempting to " + (method == "track.love" ? "love \"" : "unlove \"") + this.track + "\" by \"" + this.artist + "\"");
1682
				p.console("Contacting Last.fm....");
1683
				this.api_sig = hex_md5("api_key" + this.api_key + "artist" + this.artist + "method" + method + "sk" + this.sk + "track" + this.track + this.secret);
1684
				this.data = "method=" + method + "&api_key=" + this.api_key + "&api_sig=" + this.api_sig + "&format=json&sk=" + this.sk + "&artist=" + encodeURIComponent(this.artist) + "&track=" + encodeURIComponent(this.track);
1685
				break;
1686
			default:
1687
				return;
1688
		}
1689
		
1690
		this.xmlhttp.open("POST", "https://ws.audioscrobbler.com/2.0/", true);
1691
		this.xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
1692
		this.xmlhttp.setRequestHeader("User-Agent", this.user_agent);
1693
		this.xmlhttp.send(this.data);
1694
		this.xmlhttp.onreadystatechange = function() {
1695
			if (l.xmlhttp.readyState == 4) {
1696
				if (l.xmlhttp.status == 200) {
1697
					func();
1698
				} else {
1699
					p.console(l.xmlhttp.responsetext || "HTTP error: " + l.xmlhttp.status);
1700
				}
1701
			}
1702
		}
1703
	}
1704
	
1705
	this.update_username = function() {
1706
		var old_username = this.username;
1707
		this.username = p.InputBox("Enter your Last.fm username.", p.name, this.username);
1708
		if (this.username.length > 0 && this.username != old_username) p.save("", this.sk_file);
1709
		p.save(this.username, this.username_file);
1710
		window.NotifyOthers("lastfm", "update");
1711
		this.notify_data("lastfm", "update");
1712
	}
1713
	
1714
	this.update_password = function() {
1715
		this.password = p.InputBox("Enter your Last.fm password\n\nThis is only required to love/unlove tracks.", p.name, "");
1716
		if (this.password.length > 0) {
1717
			this.sk = "";
1718
			p.save(this.sk, this.sk_file);
1719
			window.NotifyOthers("lastfm", "update");
1720
			this.notify_data("lastfm", "update");
1721
			this.post("auth.getMobileSession");
1722
		}
1723
	}
1724
	
1725
	this.get_url = function() {
1726
		return "http://ws.audioscrobbler.com/2.0/?format=json&api_key=" + this.api_key + "&user=" + this.username + "&username=" + this.username + "&s=" + Math.random();
1727
	}
1728
	
1729
	this.username_error = "Use the right click menu to set your Last.fm username.";
1730
	this.password_error = "Use the right click menu to set your Last.fm password.";
1731
	this.url = "http://www.last.fm/";
1732
	this.api_key = "56d9e050cc2d6b36102c8b4a5fe6152d";
1733
	this.secret = "9f1f4346ce3ba206390074ff8cb4c6ce";
1734
	this.user_agent = "foobar2000_wsh_panel_mod_scripts";
1735
	this.username_file = p.settings_folder + "username";
1736
	this.username = p.open(this.username_file).trim();
1737
	this.sk_file = p.settings_folder + "sk";
1738
	this.sk = p.open(this.sk_file).trim();
1739
	this.auto_correct = window.GetProperty("lastfm_auto_correct", true);
1740
	this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
1741
}
1742
1743
function list(x, y, w, h) {
1744
	this.size = function() {
1745
		this.row_height = this.mode == "echonest" ? 90 : 20;
1746
		this.rows = Math.floor((this.h - (this.mode == "echonest" ? 50 : 30)) / this.row_height);
1747
		this.index = 0;
1748
		this.offset = 0;
1749
		switch(true) {
1750
			case this.mode == "lastfm_charts":
1751
				this.text_width = Math.round(this.w / 3);
1752
				this.lastfm_charts_bar_x = this.text_width + 30;
1753
				break;
1754
			case this.mode == "musicbrainz" && this.musicbrainz_mode == "releases":
1755
				this.text_width = this.w - 120;
1756
				break;
1757
			default:
1758
				this.text_width = this.w - 50;
1759
				break;
1760
		}
1761
		this.but_x = this.x + Math.round((this.w - 15) / 2);
1762
		this.up_btn = new sb(this.but_x, this.y, 15, 15, p.up_img, "li.offset > 0", function() { li.wheel(1); });
1763
		this.down_btn = new sb(this.but_x, this.y + this.h - 15, 15, 15, p.down_img, "li.offset < li.items - li.rows", function() { li.wheel(-1); });
1764
	}
1765
	
1766
	this.draw = function(gr) {
1767
		switch(this.mode) {
1768
			case "discogs":
1769
				for (i = 0; i < Math.min(this.items, this.rows); i++) {
1770
					p.left_text(gr, this.names[i + this.offset], p.list_font, p.textcolour, this.x + this.text_x, this.y + 15 + (i * this.row_height), this.text_width, 20);
1771
					p.right_text(gr, this.dates[i + this.offset], p.list_font, p.textcolour_hl, this.x, this.y + 15 + (i * this.row_height), this.w, 20);
1772
				}
1773
				break;
1774
			case "lastfm_charts":
1775
				var max_bar_width = (this.w - this.lastfm_charts_bar_x - 40) / this.playcounts[0];
1776
				for (i = 0; i < Math.min(this.items, this.rows); i++) {
1777
					p.right_text(gr, this.ranks[i + this.offset] + ".", p.list_font, p.textcolour_hl, this.x, this.y + 15 + (i * this.row_height), 17, 20);
1778
					var bar_width = max_bar_width * this.playcounts[i + this.offset];
1779
					var bar_colour = p.splitRGB(this.lastfm_charts_bar_colour);
1780
					gr.FillSolidRect(this.lastfm_charts_bar_x + this.x, this.y + 16 + (i * this.row_height), bar_width, 18, bar_colour);
1781
					p.left_text(gr, this.playcounts[i + this.offset].addCommas(), p.list_font, p.textcolour, this.x + 5 + this.lastfm_charts_bar_x + bar_width, this.y + 15 + (i * this.row_height), 50, 20);
1782
					p.left_text(gr, this.names[i + this.offset], p.list_font, p.textcolour, this.x + this.text_x, this.y + 15 + (i * this.row_height), this.text_width, 20);
1783
				}
1784
				break;
1785
			case "lastfm":
1786
				for (i = 0; i < Math.min(this.items, this.rows); i++) {
1787
					if (this.lastfm_mode == 0) p.draw_image(gr, this.lastfm_button == "spotify" ? this.lastfm_spotify_img : this.autoplaylist_img, this.x, this.y + 16 + (i * this.row_height), 16, 16, "centre");
1788
					if (this.lastfm_mode == 1 || this.lastfm_mode == 3 || this.lastfm_mode == 4) p.draw_image(gr, this.autoplaylist_img, this.x, this.y + 16 + (i * this.row_height), 16, 16, "centre");
1789
					p.left_text(gr, this.names[i + this.offset], p.list_font, p.textcolour, this.x + this.text_x, this.y + 15 + (i * this.row_height), this.text_width, 20);
1790
				}
1791
				break;
1792
			case "musicbrainz":
1793
				for (i = 0; i < Math.min(this.items, this.rows); i++) {
1794
					if (this.musicbrainz_mode == "URLs") p.draw_image(gr, this.musicbrainz_images[this.images[i + this.offset]], this.x, this.y + 16 + (i * this.row_height), 16, 16, "centre");
1795
					if (this.musicbrainz_mode == "releases") {
1796
						p.draw_image(gr, this.autoplaylist_img, this.x, this.y + 16 + (i * this.row_height), 16, 16, "centre");
1797
						if (this.musicbrainz_show_release_type) p.right_text(gr, this.release_types[i + this.offset], p.list_font, p.textcolour, this.x, this.y + 15 + (i * this.row_height), this.w - 30, 20);
1798
						p.right_text(gr, this.dates[i + this.offset], p.list_font, p.textcolour_hl, this.x, this.y + 15 + (i * this.row_height), this.w, 20);
1799
					}
1800
					p.left_text(gr, this.names[i + this.offset], p.list_font, p.textcolour, this.x + this.text_x, this.y + 15 + (i * this.row_height), this.text_width, 20);
1801
				}
1802
				break;
1803
			case "autoplaylists":
1804
				for (i = 0; i < Math.min(this.items, this.rows); i++) {
1805
					p.left_text(gr, this.names[i + this.offset], p.list_font, p.textcolour, this.x, this.y + 15 + (i * this.row_height), this.text_width, 20);
1806
					if (!this.autoplaylists_editing && this.autoplaylists_hover && this.index == i + this.offset) {
1807
						p.draw_image(gr, this.autoplaylists_edit_img, this.x + this.w - 40, this.y + 16 + (i * this.row_height), 16, 16, "centre");
1808
						p.draw_image(gr, this.autoplaylists_del_img, this.x + this.w - 20, this.y + 16 + (i * this.row_height), 16, 16, "centre");
1809
					}
1810
				}
1811
				break;
1812
			case "echonest":
1813
				for (i = 0; i < Math.min(this.items, this.rows); i++) {
1814
					p.left_text(gr, this.names[i + this.offset], p.title_font, p.textcolour_hl, this.x, this.y + 15 + (i * this.row_height), this.w - 110, 24);
1815
					p.right_text(gr, this.dates[i + this.offset], p.title_font, p.textcolour_hl, this.x, this.y + 15 + (i * this.row_height), this.w, 24);
1816
					gr.GdiDrawText(this.summaries[i + this.offset], p.normal_font, p.textcolour, this.x, this.y + 37 + (i * this.row_height), this.w, p.normal_font.Height * Math.floor(64 / p.normal_font.Height), DT_WORDBREAK | DT_CALCRECT | DT_NOPREFIX);
1817
				}
1818
				break;
1819
		}
1820
		this.up_btn.draw(gr);
1821
		this.down_btn.draw(gr);
1822
		if (this.show_timestamp) p.left_text(gr, this.date, p.list_font, p.textcolour_hl, this.x, this.y + this.h - 15, this.w, 15);
1823
	}
1824
	
1825
	this.metadb_changed = function() {
1826
		p.artist = p.eval(p.artist_tf);
1827
		if (this.artist == p.artist) return false;
1828
		this.artist = p.artist;
1829
		this.folder = p.data_folder + p.eval("$crc32(" + p.artist_tf + ")");
1830
		if (!p.fso.FolderExists(this.folder)) p.fso.CreateFolder(this.folder);
1831
		this.update();
1832
		return true;
1833
	}
1834
	
1835
	this.trace = function(x, y) {
1836
		return (x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h);
1837
	}
1838
	
1839
	this.wheel = function(step) {
1840
		if (!this.trace(p.mx, p.my)) return false;
1841
		if (this.items > this.rows) {
1842
			this.offset -= step * (this.mode == "echonest" ? 1 : 3);
1843
			if (this.offset < 0) this.offset = 0;
1844
			if (this.rows + this.offset > this.items) this.offset = this.items - this.rows;
1845
			window.RepaintRect(this.x, this.y, this.w, this.h);
1846
		}
1847
		return true;
1848
	}
1849
	
1850
	this.move = function(x, y) {
1851
		this.index = Math.floor((y - this.y - 15) / this.row_height) + this.offset;
1852
		this.in_range = this.index >= this.offset && this.index < this.offset + Math.min(this.rows, this.items);
1853
		switch(true) {
1854
			case !this.trace(x, y):
1855
				window.SetCursor(IDC_ARROW);
1856
				p.ttd();
1857
				return false;
1858
			case this.mode == "autoplaylists" && this.autoplaylists_editing:
1859
			case this.mode == "lastfm" && l.username.length == 0:
1860
			case !this.in_range:
1861
				window.SetCursor(IDC_ARROW);
1862
				this.leave();
1863
				break;
1864
			case this.up_btn.trace(x, y):
1865
			case this.down_btn.trace(x, y):
1866
				window.SetCursor(IDC_HAND);
1867
				break;
1868
			case this.mode == "musicbrainz" && this.musicbrainz_mode == "releases" && x > this.x && x < this.x + 20:
1869
			case this.mode == "lastfm" && this.text_x == 20 && x > this.x && x < this.x + 20:
1870
				window.SetCursor(IDC_HAND);
1871
				switch(true) {
1872
					case this.lastfm_mode == 0 && this.lastfm_button == "spotify":
1873
						p.tt('spotify:search:"' + this.names[this.index].replace(/\s+/g,"+") + '"');
1874
						break;
1875
					default:
1876
						p.tt("Autoplaylist: " + this.queries[this.index]);
1877
						break;
1878
				}
1879
				break;
1880
			case this.mode == "autoplaylists":
1881
				switch(true) {
1882
					case x > this.x && x < this.x + Math.min(this.names_widths[this.index], this.text_width):
1883
						window.SetCursor(IDC_HAND);
1884
						p.tt('Run "' + this.names[this.index] + '"');
1885
						break;
1886
					case x > this.x + this.w - 40 && x < this.x + this.w - 20:
1887
						window.SetCursor(IDC_HAND);
1888
						p.tt('Edit "' + this.names[this.index] + '"');
1889
						break;
1890
					case x > this.x + this.w - 20 && x < this.x + this.w:
1891
						window.SetCursor(IDC_HAND);
1892
						p.tt('Delete "' + this.names[this.index] + '"');
1893
						break;
1894
					default:
1895
						window.SetCursor(IDC_ARROW);
1896
						this.leave();
1897
						break;
1898
				}
1899
				this.autoplaylists_hover = true;
1900
				window.RepaintRect(this.x + this.w - 40, this.y + 15, 40, Math.min(this.rows, this.items) * this.row_height);
1901
				break;
1902
			case this.mode == "echonest":
1903
			case x > this.x + this.text_x && x < this.x + this.text_x + Math.min(this.names_widths[this.index], this.text_width):
1904
				window.SetCursor(IDC_HAND);
1905
				p.tt(this.urls[this.index]);
1906
				break;
1907
			default:
1908
				window.SetCursor(IDC_ARROW);
1909
				p.ttd();
1910
				break;
1911
		}
1912
		return true;
1913
	}
1914
	
1915
	this.lbtn_up = function(x, y) {
1916
		if (!this.trace(x, y)) return false;
1917
		switch(true) {
1918
			case this.mode == "autoplaylists" && this.autoplaylists_editing:
1919
			case this.mode == "lastfm" && l.username.length == 0:
1920
			case !this.in_range:
1921
			case this.up_btn.lbtn_up(x, y):
1922
			case this.down_btn.lbtn_up(x, y):
1923
				break;
1924
			case this.mode == "musicbrainz" && this.musicbrainz_mode == "releases" && x > this.x && x < this.x + 20:
1925
			case this.mode == "lastfm" && this.text_x == 20 && x > this.x && x < this.x + 20:
1926
				switch(true) {
1927
					case this.lastfm_mode == 0 && this.lastfm_button == "spotify":
1928
						if (p.run('spotify:search:"' + this.names[this.index].replace(/\s+/g,"+") + '"')) {
1929
							if (fb.IsPlaying && !fb.IsPaused && this.lastfm_pause) fb.Pause();
1930
						} else {
1931
							p.MsgBox("Unable to launch Spotify.", 0, p.name);
1932
						}
1933
						break;
1934
					default:
1935
						fb.CreateAutoPlaylist(fb.PlaylistCount, this.names[this.index], this.queries[this.index]);
1936
						fb.ActivePlaylist = fb.PlaylistCount - 1;
1937
						break;
1938
				}
1939
				break;
1940
			case this.mode == "autoplaylists":
1941
				switch(true) {
1942
					case x > this.x && x < this.x + Math.min(this.names_widths[this.index], this.text_width):
1943
						this.autoplaylists_run(this.names[this.index], this.queries[this.index], this.sorts[this.index], this.forced[this.index]);
1944
						break;
1945
					case x > this.x + this.w - 40 && x < this.x + this.w - 20:
1946
						this.autoplaylists_edit(x, y);
1947
						break;
1948
					case x > this.x + this.w - 20 && x < this.x + this.w:
1949
						this.autoplaylists_delete();
1950
						break
1951
				}
1952
				break;
1953
			case this.mode == "echonest":
1954
			case x > this.x + this.text_x && x < this.x + this.text_x + Math.min(this.names_widths[this.index], this.text_width):
1955
				p.browser(this.urls[this.index]);
1956
				break;
1957
		}
1958
		return true;
1959
	}
1960
	
1961
	this.leave = function() {
1962
		p.ttd();
1963
		if (this.mode != "autoplaylists") return;
1964
		this.autoplaylists_hover = false;
1965
		window.RepaintRect(this.x, this.y, this.w, this.h);
1966
	}
1967
	
1968
	this.update = function() {
1969
		this.items = 0;
1970
		this.offset = 0;
1971
		this.index = 0;
1972
		this.names = [];
1973
		this.names_widths = [];
1974
		this.urls = [];
1975
		this.dates = [];
1976
		this.queries = [];
1977
		this.ranks = [];
1978
		this.playcounts = [];
1979
		this.sorts = [];
1980
		this.summaries = [];
1981
		this.release_types = [];
1982
		this.images = [];
1983
		this.forced = [];
1984
		this.data = [];
1985
		this.file = null;
1986
		this.date = "";
1987
		var temp_bmp = gdi.CreateImage(1, 1);
1988
		var temp_gr = temp_bmp.GetGraphics();
1989
		switch(this.mode) {
1990
			case "autoplaylists":
1991
				this.text = p.open(this.filename);
1992
				this.data = this.text.split(/\r?\n/g);
1993
				this.items = this.data.length;
1994
				for (i = 0; i < this.items; i++) {
1995
					var temp = this.data[i].split("¬");
1996
					this.names[i] = temp[0];
1997
					this.queries[i] = temp[1];
1998
					this.sorts[i] = temp[2];
1999
					this.forced[i] = temp[3];
2000
					this.names_widths[i] = temp_gr.CalcTextWidth(this.names[i], p.list_font);
2001
				}
2002
				break;
2003
			case "lastfm":
2004
				this.text_x = 20;
2005
				this.filename = this.folder + "\\" + this.lastfm_methods[this.lastfm_mode] + ".json";
2006
				if (p.fso.fileExists(this.filename)) {
2007
					this.file = p.fso.Getfile(this.filename);
2008
					this.json_text = p.open(this.filename);
2009
					this.json_data = p.json(this.json_text);
2010
					if (this.json_data) {
2011
						switch(true) {
2012
							case this.json_data.error > 0:
2013
								p.console(this.json_data.message);
2014
								break;
2015
							case this.lastfm_mode == 0 && this.json_data.similarartists && typeof this.json_data.similarartists.artist == "object":
2016
								this.data = this.json_data.similarartists.artist;
2017
								break;
2018
							case this.lastfm_mode == 1 && this.json_data.toptags && typeof this.json_data.toptags.tag == "object":
2019
								this.data = this.json_data.toptags.tag;
2020
								break;
2021
							case this.lastfm_mode == 2 && this.json_data.topfans && typeof this.json_data.topfans.user == "object":
2022
								this.text_x = 0;
2023
								this.data = this.json_data.topfans.user;
2024
								break;
2025
							case this.lastfm_mode == 3 && this.json_data.topalbums && typeof this.json_data.topalbums.album == "object":
2026
								this.data = this.json_data.topalbums.album;
2027
								break;
2028
							case this.lastfm_mode == 4 && this.json_data.toptracks && typeof this.json_data.toptracks.track == "object":
2029
								this.data = this.json_data.toptracks.track;
2030
								break;
2031
						}
2032
						this.items = this.data.length;
2033
						for (i = 0; i < this.items; i++) {
2034
							this.names[i] = this.data[i].name;
2035
							this.urls[i] = this.data[i].url;
2036
							this.names_widths[i] = temp_gr.CalcTextWidth(this.names[i], p.list_font);
2037
							if (this.lastfm_mode == 0) this.queries[i] = "artist HAS " + this.names[i];
2038
							if (this.lastfm_mode == 1) this.queries[i] = "genre HAS " + this.names[i];
2039
							if (this.lastfm_mode == 3) this.queries[i] = "album HAS " + this.names[i] + " OR title HAS " + this.names[i];
2040
							if (this.lastfm_mode == 4) this.queries[i] = "title HAS " + this.names[i];
2041
						}
2042
					}
2043
					if (Date.parse(Date()) - Date.parse(this.file.DateLastModified) > ONE_DAY) this.get();
2044
				} else {
2045
					this.get();
2046
				}
2047
				break;
2048
			case "lastfm_charts":
2049
				this.filename = this.folder + "\\" + l.username + "_" + this.lastfm_charts_modes[this.lastfm_charts_mode] + "_" + this.lastfm_charts_periods[this.lastfm_charts_period] + ".json";
2050
				if (p.fso.fileExists(this.filename)) {
2051
					this.file = p.fso.Getfile(this.filename);
2052
					this.json_text = p.open(this.filename);
2053
					this.json_data = p.json(this.json_text);
2054
					if (this.json_data) {
2055
						switch(true) {
2056
							case this.json_data.error > 0:
2057
								p.console(this.json_data.message);
2058
								break;
2059
							case this.lastfm_charts_mode == 0 && this.json_data.topartists && typeof this.json_data.topartists.artist == "object":
2060
								this.data = this.json_data.topartists.artist;
2061
								break;
2062
							case this.lastfm_charts_mode == 1 && this.json_data.topalbums && typeof this.json_data.topalbums.album == "object":
2063
								this.data = this.json_data.topalbums.album;
2064
								break;
2065
							case this.lastfm_charts_mode == 2 && this.json_data.toptracks && typeof this.json_data.toptracks.track == "object":
2066
								this.data = this.json_data.toptracks.track;
2067
								break;
2068
						}
2069
						this.items = this.data.length;
2070
						for (i = 0; i < this.items; i++) {
2071
							this.names[i] = this.lastfm_charts_mode != 0 ? this.data[i].artist.name + " - " + this.data[i].name : this.data[i].name;
2072
							this.ranks[i] = (i > 0 && this.data[i].playcount == this.data[i - 1].playcount) ? this.ranks[i - 1] : this.data[i]["@attr"].rank;
2073
							this.urls[i] = this.data[i].url;
2074
							this.names_widths[i] = temp_gr.CalcTextWidth(this.names[i], p.list_font);
2075
							this.playcounts[i] = this.data[i].playcount;
2076
						}
2077
					}
2078
					if (Date.parse(Date()) - Date.parse(this.file.DateLastModified) > ONE_DAY) this.get();
2079
				} else {
2080
					this.get();
2081
				}
2082
				break;
2083
			case "discogs":
2084
				this.filename = this.folder + "\\discogs.json";
2085
				if (p.fso.fileExists(this.filename)) {
2086
					this.file = p.fso.Getfile(this.filename);
2087
					this.json_text = p.open(this.filename);
2088
					this.json_data = p.json(this.json_text);
2089
					if (this.json_data && this.json_data.results) {
2090
						this.data = p.json_reverse_sort(this.json_data.results, "year");
2091
						var swap_artist = fb.TitleFormat("$swapprefix($lower(" + p.artist + ")) - ").Eval(true);
2092
						for (i = 0; i < this.data.length; i++) {
2093
							var temp_artist = this.data[i].title.replace(/\s{2,}/g,' ');
2094
							if (this.discogs_match && temp_artist.toLowerCase().indexOf(swap_artist) != 0) continue;
2095
							this.dates.push(this.data[i].year || "");
2096
							this.urls.push("http://www.discogs.com" + this.data[i].uri);
2097
							this.names.push(temp_artist);
2098
							this.names_widths.push(temp_gr.CalcTextWidth(temp_artist, p.list_font));
2099
						}
2100
						this.items = this.names.length;
2101
					}
2102
					if (Date.parse(Date()) - Date.parse(this.file.DateLastModified) > ONE_WEEK) this.get();
2103
				} else {
2104
					this.get();
2105
				}
2106
				break;
2107
			case "musicbrainz":
2108
				this.filename = this.folder + "\\musicbrainz_" + this.musicbrainz_mode + ".json";
2109
				if (p.fso.fileExists(this.filename)) {
2110
					this.file = p.fso.Getfile(this.filename);
2111
					this.json_text = p.open(this.filename);
2112
					this.json_data = p.json(this.json_text);
2113
					if (this.json_data) {
2114
						switch(this.musicbrainz_mode) {
2115
							case "releases":
2116
								this.data = this.json_data["release-groups"] || [];
2117
								this.data = p.json_reverse_sort(this.data, "first-release-date");
2118
								for (i = 0; i < this.data.length; i++) {
2119
									switch(true) {
2120
										case !this.musicbrainz_live && this.data[i]["secondary-types"].join("").toLowerCase().indexOf("live") > -1:
2121
											break;
2122
										case this.musicbrainz_single && this.data[i]["primary-type"] == "Single":
2123
										case this.musicbrainz_ep && this.data[i]["primary-type"] == "EP":
2124
										case this.musicbrainz_album && this.data[i]["primary-type"] == "Album":
2125
											var name = this.data[i].title;
2126
											this.dates.push(this.data[i]["first-release-date"].substring(0,4));
2127
											this.names.push(name);
2128
											this.urls.push("http://musicbrainz.org/release-group/" + this.data[i].id);
2129
											this.release_types.push(this.data[i]["primary-type"]);
2130
											this.names_widths.push(temp_gr.CalcTextWidth(name, p.list_font));
2131
											this.queries.push("album HAS " + name + " OR title HAS " + name);
2132
											break;
2133
									}
2134
								}
2135
								this.items = this.names.length;
2136
								break;
2137
							case "URLs":
2138
								this.data = this.json_data.relations || [];
2139
								this.items = this.data.length;
2140
								for (i = 0; i < this.items; i++) {
2141
									this.urls[i] = this.data[i].url.resource;
2142
									this.images[i] = "";
2143
									if (this.data[i].type == "official homepage") {
2144
										this.images[i] = "home";
2145
									} else {
2146
										for (name in this.musicbrainz_images) {
2147
											if (this.urls[i].indexOf(name) > -1) {
2148
												this.images[i] = name;
2149
												break;
2150
											}
2151
										}
2152
										if (this.images[i].length == 0) this.images[i] = "external";
2153
									}
2154
									this.names[i] = decodeURIComponent(this.urls[i]);
2155
									this.names_widths[i] = temp_gr.CalcTextWidth(this.names[i], p.list_font);
2156
								}
2157
								break;
2158
						}
2159
					}
2160
					if (Date.parse(Date()) - Date.parse(this.file.DateLastModified) > ONE_WEEK) this.get();
2161
				} else {
2162
					this.get();
2163
				}
2164
				break;
2165
			case "echonest":
2166
				this.filename = this.folder + "\\echonest_" + this.echonest_modes[this.echonest_mode] + ".json";
2167
				if (p.fso.fileExists(this.filename)) {
2168
					this.file = p.fso.Getfile(this.filename);
2169
					this.json_text = p.open(this.filename);
2170
					this.json_data = p.json(this.json_text);
2171
					if (this.json_data && this.json_data.response && this.json_data.response.status) {
2172
						if (this.json_data.response.status.code == 0) {
2173
							this.data = this.json_data.response[this.echonest_modes[this.echonest_mode]] || [];
2174
							this.items = this.data.length;
2175
							for (i = 0; i < this.items; i++) {
2176
								this.names[i] = p.strip_tags(this.data[i].name);
2177
								this.urls[i] = (this.data[i].url || "").replace(/\\/g,"");
2178
								this.temp_date = (this.data[i].date_posted || this.data[i].date_reviewed || this.data[i].date_found || "").substring(0, 10);
2179
								this.dates[i] = this.temp_date.substring(8,10) + "-" + this.temp_date.substring(5,7) + "-" + this.temp_date.substring(0,4);
2180
								this.summaries[i] = p.strip_tags(this.data[i].summary);
2181
							}
2182
						} else {
2183
							p.console(this.json_data.response.status.message);
2184
						}
2185
					}
2186
					if (Date.parse(Date()) - Date.parse(this.file.DateLastModified) > ONE_DAY) this.get();
2187
				} else {
2188
					this.get();
2189
				}
2190
				break;
2191
		}
2192
		temp_bmp.ReleaseGraphics(temp_gr);
2193
		temp_bmp.Dispose();
2194
		temp_gr = null;
2195
		temp_bmp = null;
2196
		if (this.file) {
2197
			var temp_date = new Date(this.file.DateLastModified);
2198
			this.date = "Updated: " + temp_date.toLocaleTimeString() + ", " + temp_date.toLocaleDateString();
2199
		}
2200
		window.Repaint();
2201
	}
2202
	
2203
	this.get = function() {
2204
		var fn = this.filename;
2205
		var func = function() {
2206
			if (!p.save(li.xmlhttp.responsetext, fn)) return;
2207
			li.update();
2208
		}
2209
		var url = "";
2210
		var user_agent = "";
2211
		switch(this.mode) {
2212
			case "discogs":
2213
				if (this.artist == "" || this.artist == "?") return;
2214
				url = "http://api.discogs.com/database/search?per_page=100&type=master&artist=" + encodeURIComponent(this.artist);
2215
				user_agent = "foobar2000_wsh_panel_mod_discogs +http://www.hydrogenaudio.org/forums/index.php?showuser=19379";
2216
				break;
2217
			case "lastfm_charts":
2218
				if (l.username.length == 0) return(p.console(l.username_error));
2219
				url = l.get_url() + "&method=user." + this.lastfm_charts_methods[this.lastfm_charts_mode] + "&period=" + this.lastfm_charts_periods[this.lastfm_charts_period];
2220
				user_agent = l.user_agent;
2221
				break;
2222
			case "lastfm":
2223
				if (this.artist == "" || this.artist == "?") return;
2224
				if (l.username.length == 0) return(p.console(l.username_error));
2225
				url = l.get_url() + "&method=" + this.lastfm_methods[this.lastfm_mode] + "&artist=" + encodeURIComponent(this.artist) + "&autocorrect=" + (l.auto_correct ? 1 : 0);
2226
				user_agent = l.user_agent;
2227
				break;
2228
			case "musicbrainz":
2229
				user_agent = "foobar2000_wsh_panel_mod_musicbrainz +http://www.hydrogenaudio.org/forums/index.php?showuser=19379";
2230
				var fn1 = this.folder + "\\MUSICBRAINZ_ARTISTID.txt";
2231
				this.musicbrainz_id = p.eval("$if3(%musicbrainz_artistid%,%musicbrainz artist id%,)");
2232
				if (this.musicbrainz_id.length == 0) this.musicbrainz_id = p.open(fn1);
2233
				if (this.musicbrainz_id.length == 0) {
2234
					if (this.artist == "" || this.artist == "?") return;
2235
					url = "http://search.musicbrainz.org/ws/2/artist/?query=artist:" + encodeURIComponent(this.artist.toLowerCase()) + "&fmt=json";
2236
					var func = function() {
2237
						var json_data = p.json(li.xmlhttp.responsetext);
2238
						if (!json_data) return;
2239
						var artists = json_data["artist-list"].artist || [];
2240
						for (i = 0; i < artists.length; i++) {
2241
							if (li.artist.toLowerCase() == artists[i].name.toLowerCase()) {
2242
								if (!p.save(artists[i].id, fn1)) return;
2243
								li.get();
2244
								break;
2245
							}
2246
						}
2247
					}
2248
				} else {
2249
					if (this.musicbrainz_mode == "releases") url = "http://www.musicbrainz.org/ws/2/release-group?artist=" + this.musicbrainz_id + "&limit=100&offset=0&fmt=json";
2250
					else url = "http://www.musicbrainz.org/ws/2/artist/" + this.musicbrainz_id + "?inc=url-rels&fmt=json";
2251
				}
2252
				break;
2253
			case "echonest":
2254
				if (this.artist == "" || this.artist == "?") return;
2255
				url = "http://developer.echonest.com/api/v4/artist/" + this.echonest_modes[this.echonest_mode] + "/?name=" + encodeURIComponent(this.artist) + "&api_key=EKWS4ESQLKN3G2ZWV";
2256
				break;
2257
		}
2258
		if (url.length == 0) return;
2259
		this.xmlhttp.open("GET", url, true);
2260
		if (user_agent.length > 0) this.xmlhttp.setRequestHeader('User-Agent', user_agent);
2261
		this.xmlhttp.send();
2262
		this.xmlhttp.onreadystatechange = function() {
2263
			if (li.xmlhttp.readyState == 4) {
2264
				if (li.xmlhttp.status == 200) {
2265
					func();
2266
				} else {
2267
					p.console(li.xmlhttp.responsetext || "HTTP error: " + li.xmlhttp.status);
2268
				}
2269
			}
2270
		}
2271
	}
2272
	
2273
	this.init = function() {
2274
		switch(true) {
2275
			case p.check_feature("discogs"):
2276
				this.mode = "discogs";
2277
				this.discogs_match = window.GetProperty("discogs_match", false);
2278
				break;
2279
			case p.check_feature("lastfm_charts"):
2280
				this.mode = "lastfm_charts";
2281
				this.text_x = 20;
2282
				this.lastfm_charts_modes = ["artist", "album", "track"];
2283
				this.lastfm_charts_mode = window.GetProperty("lastfm_charts_mode", 0);
2284
				this.lastfm_charts_methods = ["getTopArtists", "getTopAlbums", "getTopTracks"];
2285
				this.lastfm_charts_periods_display = ["overall", "last 7 days", "1 month", "3 month", "6 month", "12 month"];
2286
				this.lastfm_charts_periods = ["overall", "7day", "1month", "3month", "6month", "12month"];
2287
				this.lastfm_charts_period = window.GetProperty("lastfm_charts_period", 0);
2288
				this.lastfm_charts_bar_colour = window.GetProperty("lastfm_charts_bar_colour", "72-127-221");
2289
				this.folder = p.data_folder + "charts";
2290
				if (!p.fso.FolderExists(this.folder)) p.fso.CreateFolder(this.folder);
2291
				break;
2292
			case p.check_feature("lastfm"):
2293
				this.mode = "lastfm";
2294
				this.lastfm_methods = ["artist.getSimilar", "artist.getTopTags", "artist.getTopFans", "artist.getTopAlbums", "artist.getTopTracks"];
2295
				this.lastfm_modes = ["similar artists", "top tags", "top fans", "top albums", "top tracks"];
2296
				this.lastfm_mode = window.GetProperty("lastfm_mode", 0);
2297
				this.lastfm_button = window.GetProperty("lastfm_button", "spotify");
2298
				this.lastfm_pause = window.GetProperty("lastfm_pause", true);
2299
				this.lastfm_spotify_img = gdi.Image(p.images_path + "spotify.png");
2300
				break;
2301
			case p.check_feature("musicbrainz"):
2302
				this.mode = "musicbrainz";
2303
				this.text_x = 20;
2304
				this.musicbrainz_images = {
2305
					"viaf.org": gdi.Image(p.images_path + "viaf_small.png"),
2306
					"allmusic.com": gdi.Image(p.images_path + "allmusic_small.png"),
2307
					"external": gdi.Image(p.images_path + "external_small.png"),
2308
					"bbc.co.uk": gdi.Image(p.images_path + "bbc_small.png"),
2309
					"facebook.com": gdi.Image(p.images_path + "facebook_small.png"),
2310
					"home": gdi.Image(p.images_path + "home_small.png"),
2311
					"last.fm": gdi.Image(p.images_path + "lastfm_small.png"),
2312
					"twitter.com": gdi.Image(p.images_path + "twitter_small.png"),
2313
					"soundcloud.com": gdi.Image(p.images_path + "soundcloud_small.png"),
2314
					"wikipedia.org": gdi.Image(p.images_path + "wikipedia_small.png"),
2315
					"discogs.com": gdi.Image(p.images_path + "discogs_small.png"),
2316
					"myspace.com": gdi.Image(p.images_path + "myspace_small.png"),
2317
					"youtube.com": gdi.Image(p.images_path + "youtube_small.png"),
2318
					"imdb.com": gdi.Image(p.images_path + "imdb_small.png"),
2319
					"plus.google.com": gdi.Image(p.images_path + "google_plus_small.png"),
2320
					"lyrics.wikia.com": gdi.Image(p.images_path + "lyrics_wikia_small.png"),
2321
					"flickr.com": gdi.Image(p.images_path + "flickr_small.png"),
2322
					"vimeo.com": gdi.Image(p.images_path + "vimeo_small.png")
2323
				}
2324
				this.musicbrainz_mode = window.GetProperty("musicbrainz_mode", "releases");
2325
				this.musicbrainz_show_release_type = window.GetProperty("musicbrainz_show_release_type", true);
2326
				this.musicbrainz_live = window.GetProperty("musicbrainz_live", false);
2327
				this.musicbrainz_ep = window.GetProperty("musicbrainz_ep", true);
2328
				this.musicbrainz_single = window.GetProperty("musicbrainz_single", true);
2329
				this.musicbrainz_album = window.GetProperty("musicbrainz_album", true);
2330
				break;
2331
			case p.check_feature("echonest"):
2332
				this.mode = "echonest";
2333
				this.echonest_modes = ["news", "reviews", "blogs"];
2334
				this.echonest_mode = window.GetProperty("echonest_mode", 0);
2335
				this.echonest_url = "http://the.echonest.com/";
2336
				break;
2337
			case p.check_feature("autoplaylists"):
2338
				this.autoplaylists_add = function() {
2339
					if (this.autoplaylists_editing) return;
2340
					this.autoplaylists_editing = true;
2341
					var new_name = p.InputBox("Enter a name for your autoplaylist", p.name, "");
2342
					if (new_name == "") { this.autoplaylists_editing = false; return; }
2343
					var new_query = p.InputBox("Enter your autoplaylist query", p.name, "");
2344
					if (new_query == "") { this.autoplaylists_editing = false; return; }
2345
					var new_sort = p.InputBox("Enter a sort pattern\n\n(optional)", p.name, "");
2346
					var new_force = new_sort.length > 0 ? p.MsgBox("Force sort?", 4, p.name) : 7;
2347
					this.data.push(new_name + "¬" + new_query + "¬" + new_sort + "¬" + new_force);
2348
					if (this.data.length > this.rows) this.offset = this.data.length - this.rows;
2349
					p.save(this.data.join("\n"), this.filename);
2350
					if (this.autoplaylists_success) this.autoplaylists_run(new_name, new_query, new_sort, new_force);
2351
					window.NotifyOthers("autoplaylists", "update");
2352
					this.update();
2353
					this.autoplaylists_editing = false;
2354
				}
2355
				
2356
				this.autoplaylists_run = function(n, q, s, f) {
2357
					if (this.autoplaylists_remove_duplicates) {
2358
						i = 0;
2359
						while(i < fb.PlaylistCount) {
2360
							if (fb.GetPlaylistName(i) == n) fb.RemovePlaylist(i);
2361
							else i++;
2362
						}
2363
					}
2364
					fb.CreateAutoPlaylist(fb.PlaylistCount, n, q, s, f == 6 ? true : false);
2365
					fb.ActivePlaylist = fb.PlaylistCount - 1;
2366
				}
2367
				
2368
				this.autoplaylists_edit = function(x, y) {
2369
					var _menu = window.CreatePopupMenu();
2370
					var i = this.index;
2371
					_menu.AppendMenuItem(MF_STRING, 1, "Rename...");
2372
					_menu.AppendMenuItem(MF_STRING, 2, "Edit query...");
2373
					_menu.AppendMenuItem(MF_STRING, 3, "Edit sort pattern...");
2374
					_menu.AppendMenuItem(MF_STRING, 4, "Force Sort");
2375
					_menu.CheckMenuItem(4, this.forced[i] == 6 ? true : false);
2376
					this.autoplaylists_editing = true;
2377
					this.autoplaylists_hover = false;
2378
					window.RepaintRect(this.x + this.w - 40, this.y + 15, 40, Math.min(this.rows, this.items) * this.row_height);
2379
					idx = _menu.TrackPopupMenu(x, y);
2380
					switch(idx) {
2381
						case 0:
2382
							this.autoplaylists_editing = false;
2383
							_menu.Dispose();
2384
							return;
2385
						case 1:
2386
							var new_name = p.InputBox("Rename your autoplaylist", p.name, this.names[i]);
2387
							if (new_name != "") this.names[i] = new_name;
2388
							break;
2389
						case 2:
2390
							var new_query = p.InputBox("Enter your autoplaylist query", p.name, this.queries[i]);
2391
							if (new_query != "") this.queries[i] = new_query;
2392
							break;
2393
						case 3:
2394
							var new_sort = p.InputBox("Enter a sort pattern\n\n(optional)", p.name, this.sorts[i]);
2395
							this.sorts[i] = new_sort;
2396
							this.forced[i] = new_sort.length > 0 ? p.MsgBox("Force sort?", 4, p.name) : 7;
2397
							break;
2398
						case 4:
2399
							this.forced[i] = this.forced[i] == 6 ? 7 : 6;
2400
							break;
2401
					}
2402
					_menu.Dispose();
2403
					var temp = this.names[i] + "¬" + this.queries[i] + "¬" + this.sorts[i] + "¬" + this.forced[i];
2404
					if (this.data[i] != temp) {
2405
						this.data[i] = temp;
2406
						p.save(this.data.join("\n"), this.filename);
2407
						if (this.autoplaylists_success) this.autoplaylists_run(this.names[i], this.queries[i], this.sorts[i], this.forced[i]);
2408
						window.NotifyOthers("autoplaylists", "update");
2409
						this.update();
2410
					}
2411
					this.autoplaylists_editing = false;
2412
				}
2413
				
2414
				this.autoplaylists_delete = function() {
2415
					if (this.offset > 1) this.offset--;
2416
					this.autoplaylists_deleted_items.unshift(this.data[this.index]);
2417
					this.data.splice(this.index, 1);
2418
					p.save(this.data.join("\n"), this.filename);
2419
					window.NotifyOthers("autoplaylists", "update");
2420
					this.update();
2421
				}
2422
				
2423
				this.mode = "autoplaylists";
2424
				this.autoplaylists_del_img = gdi.Image(p.images_path + "cross.png");
2425
				this.autoplaylists_edit_img = gdi.Image(p.images_path + "edit.png");
2426
				this.autoplaylists_hover = false;
2427
				this.autoplaylists_editing = false;
2428
				this.autoplaylists_remove_duplicates = window.GetProperty("autoplaylists_remove_duplicates", true);
2429
				this.autoplaylists_success = window.GetProperty("autoplaylists_success", true);
2430
				this.autoplaylists_title = window.GetProperty("autoplaylists_title", "Autoplaylists");
2431
				this.autoplaylists_slot = window.GetProperty("autoplaylists_slot", 1);
2432
				this.filename = p.settings_folder + "autoplaylists" + this.autoplaylists_slot;
2433
				this.autoplaylists_deleted_items = [];
2434
				break;
2435
		}
2436
	}
2437
	
2438
	this.x = x;
2439
	this.y = y;
2440
	this.w = w;
2441
	this.h = h;
2442
	this.index = 0;
2443
	this.offset = 0;
2444
	this.items = 0;
2445
	this.text = "";
2446
	this.artist = "";
2447
	this.url = "";
2448
	this.text_x = 0;
2449
	this.show_timestamp = window.GetProperty("show_timestamp", true);
2450
	this.autoplaylist_img = gdi.Image(p.images_path + "search_small.png");
2451
	this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
2452
	this.init();
2453
}
2454
2455
function playcount_sync(x, y) {
2456
	this.playback_time = function() {
2457
		this.time_elapsed++;
2458
		if (this.time_elapsed == 3 && this.auto_love && fb.TitleFormat(this.auto_love_tf).Eval() == 1 && this.old_userloved == 0) {
2459
			p.console("Automatically loving this track....");
2460
			this.love_track();
2461
		}
2462
		if (this.time_elapsed == this.target_time) {
2463
			if (!this.library || fb.IsMetadbInMediaLibrary(p.metadb)) this.sync_track();
2464
			else p.console("Track not in library. Not updating");
2465
		}
2466
	}
2467
	
2468
	this.metadb_changed = function() {
2469
		if (!p.metadb) return false;
2470
		this.artist = fb.TitleFormat(p.artist_tf).EvalWithMetadb(p.metadb);
2471
		this.track = fb.TitleFormat("%title%").EvalWithMetadb(p.metadb);
2472
		this.old_userloved = fb.TitleFormat("%LASTFM_LOVED_DB%").EvalWithMetadb(p.metadb) == 1 ? 1 : 0;
2473
		this.old_userplaycount = fb.TitleFormat("%LASTFM_PLAYCOUNT_DB%").EvalWithMetadb(p.metadb);
2474
		this.method = this.old_userloved == 1 ? "track.unlove" : "track.love";
2475
		this.tooltip = (this.old_userloved == 1 ? "Unlove" : "Love") + " \"" + this.track + "\" by \"" + this.artist + "\".";
2476
		this.crc32 = fb.TitleFormat("$crc32($lower(%artist%%title%))").EvalWithMetadb(p.metadb);
2477
		b.update();
2478
		return true;
2479
	}
2480
	
2481
	this.playback_new_track = function() {
2482
		this.auto_love_count = 0;
2483
		this.time_elapsed = 0;
2484
		switch(true) {
2485
			case fb.PlaybackLength == 0:
2486
				this.target_time = 240;
2487
				break;
2488
			case fb.PlaybackLength >= 30:
2489
				this.target_time = Math.min(Math.floor(fb.PlaybackLength / 2), 240);
2490
				break;
2491
			default:
2492
				this.target_time = 5;
2493
				break;
2494
		}
2495
		on_item_focus_change();
2496
	}
2497
	
2498
	this.playback_edited = function() {
2499
		if (this.auto_love && fb.TitleFormat(this.auto_love_tf).Eval() == 1 && this.old_userloved == 0 && this.auto_love_count == 0) {
2500
			this.auto_love_count = 1;
2501
			p.console("Automatically loving this track....");
2502
			this.love_track();
2503
		}
2504
	}
2505
	
2506
	this.tf = function(value) {
2507
		return(value.replace(/'/g, "''").replace(/,/g, "','").replace(/\//g, "'/'").replace(/\(/g, "'('").replace(/\)/g, "')'").replace(/\[/g, "'['").replace(/\]/g, "']'"));
2508
	}
2509
	
2510
	this.love_track = function() {
2511
		l.post(this.method, p.metadb);
2512
	}
2513
	
2514
	this.sync_track = function() {
2515
		switch(true) {
2516
			case this.loved_working || this.playcount_working:
2517
				return;
2518
			case !utils.CheckComponent("foo_customdb", true):
2519
				p.console("Not contacting Last.fm. foo_customdb is missing.");
2520
				return;
2521
			default:
2522
				p.console("Contacting Last.fm....");
2523
				this.get(l.get_url() + "&method=track.getinfo&artist=" + encodeURIComponent(this.artist) + "&track=" + encodeURIComponent(this.track) + "&autocorrect=" + (l.auto_correct ? 1 : 0), function() {
2524
					ps.update_track();
2525
				});
2526
		}
2527
	}
2528
	
2529
	this.get = function(url, func) {
2530
		if (l.username.length == 0) {
2531
			this.loved_working = false;
2532
			this.playcount_working = false;
2533
			p.console(l.username_error);
2534
			return;
2535
		}
2536
		this.xmlhttp.open("GET", url, true);
2537
		this.xmlhttp.setRequestHeader('User-Agent', l.user_agent);
2538
		this.xmlhttp.send();
2539
		this.xmlhttp.onreadystatechange = function() {
2540
			if (ps.xmlhttp.readyState == 4) {
2541
				if (ps.xmlhttp.status == 200) {
2542
					func();
2543
				} else {
2544
					p.console(ps.xmlhttp.responsetext || "HTTP error: " + ps.xmlhttp.status);
2545
				}
2546
			}
2547
		}
2548
	}
2549
	
2550
	this.update_track = function() {
2551
		this.json_data = p.json(ps.xmlhttp.responsetext);
2552
		if (!this.json_data || this.json_data.error > 0 || !this.json_data.track) {
2553
			p.console(ps.xmlhttp.responsetext);
2554
			return;
2555
		}
2556
		this.userplaycount = this.json_data.track.userplaycount > 0 ? ++this.json_data.track.userplaycount : 1;
2557
		this.userloved = this.json_data.track.userloved == 1 ? 1 : 0;
2558
		if (fb.IsPlaying && this.time_elapsed >= this.target_time && fb.PlaybackLength > 29 && fb.PlaybackLength < 10800) {
2559
			switch(true) {
2560
				case this.userplaycount < this.old_userplaycount:
2561
					p.console("Playcount returned from Last.fm is lower than current value. Not updating.");
2562
					break;
2563
				case this.old_userplaycount == this.userplaycount:
2564
					p.console("No changes found. Not updating.");
2565
					break;
2566
				default:
2567
					p.console("Last.fm responded ok. Attempting to update playcount...");
2568
					fb.RunContextCommandWithMetadb("Customdb Delete Playcount", p.metadb, 8);
2569
					if (this.old_userloved == 1) fb.RunContextCommandWithMetadb("Customdb Love 0", p.metadb, 8);
2570
					var attempt = 0;
2571
					while(fb.TitleFormat("%LASTFM_PLAYCOUNT_DB%").EvalWithMetadb(p.metadb) != this.userplaycount && attempt <= 3) {
2572
						var query1 = '\"INSERT INTO quicktag(url,subsong,fieldname,value) VALUES(\\"' + this.crc32 + '\\",\\"-1\\",\\"LASTFM_PLAYCOUNT_DB\\",\\"' + this.userplaycount + '\\")\";';
2573
						p.WshShell.Run(p.fso.GetFile(p.script_path + "sqlite3.exe").ShortPath + " " + p.fso.GetFile(fb.ProfilePath + "customdb_sqlite.db").ShortPath + " " + query1, 0, true);
2574
						attempt++;
2575
					}
2576
					fb.RunContextCommandWithMetadb("Customdb Refresh", p.metadb, 8);
2577
					if (fb.TitleFormat("%LASTFM_PLAYCOUNT_DB%").EvalWithMetadb(p.metadb) == this.userplaycount) p.console("Playcount updated successfully.");
2578
					else p.console("Database error. Playcount not updated.");
2579
					break;
2580
			}
2581
		}
2582
		if (this.old_userloved != this.userloved) fb.RunContextCommandWithMetadb("Customdb Love " + this.userloved, p.metadb, 8);
2583
	}
2584
	
2585
	this.start_import = function() {
2586
		if (this.show_console) fb.ShowConsole();
2587
		this.loved_page_errors = 0;
2588
		this.playcount_page_errors = 0;
2589
		ps.pages = 0;
2590
		this.r = 1;
2591
		this.sql = "BEGIN TRANSACTION;\n";
2592
		this.loved_working = true;
2593
		this.sync_loved(1);
2594
	}
2595
	
2596
	this.sync_loved = function(page) {
2597
		if (!this.loved_working) return(p.console("Import aborted."));
2598
		this.page = page;
2599
		this.get(l.get_url() + "&method=user.getlovedtracks&limit=200&page=" + this.page, function() {
2600
			ps.update_loved();
2601
		});
2602
	}
2603
	
2604
	this.update_loved = function() {
2605
		this.json_data = p.json(ps.xmlhttp.responsetext);
2606
		if (this.json_data && this.json_data.error > 0) {
2607
			this.loved_working = false;
2608
			p.MsgBox("There is a problem with the Last.fm web services. Please try again later.\n\n" + ps.xmlhttp.responsetext, 0, p.name);
2609
			return;
2610
		}
2611
		if (this.json_data && this.json_data.lovedtracks && this.json_data.lovedtracks.track) {
2612
			if (this.page == 1) this.pages = this.json_data.lovedtracks["@attr"].totalPages;
2613
			for (i = 0; i < this.json_data.lovedtracks.track.length; i++) {
2614
				var data = [];
2615
				data[0] = this.json_data.lovedtracks.track[i].artist.name;
2616
				data[1] = this.json_data.lovedtracks.track[i].name;
2617
				data[2] = 1;
2618
				if (data.length == 3) {
2619
					p.console(this.r + ": " + data[0] + " - " + data[1]);
2620
					var url = fb.TitleFormat("$crc32($lower(" + this.tf(data[0]) + this.tf(data[1]) + "))").Eval(true);
2621
					this.sql += 'INSERT OR REPLACE INTO quicktag(url,subsong,fieldname,value) VALUES("' + url + '","-1","LASTFM_LOVED_DB","' + data[2] + '");' + "\n";
2622
					this.r++;
2623
				}
2624
			}
2625
			p.console("Loved tracks: completed page " + this.page + " of " + this.pages);
2626
		} else if (this.pages > 0) {
2627
			this.loved_page_errors++;
2628
		}
2629
		if (this.page < this.pages) {
2630
			this.page++;
2631
			this.sync_loved(this.page);
2632
		} else {
2633
			this.loved_working = false;
2634
			if (this.full_import) {
2635
				this.playcount_working = true;
2636
				this.pages = 0;
2637
				this.r = 1;
2638
				this.sync_playcount(1);
2639
			} else {
2640
				try {
2641
					this.sql += "COMMIT;"
2642
					var ts = p.fso.OpenTextFile(this.sql_file, 2, true, 0);
2643
					ts.WriteLine(this.sql);
2644
					ts.Close();
2645
					this.finish_import();
2646
				} catch(e) {
2647
				}
2648
				if (this.loved_page_errors > 0) {
2649
					p.console("Loved track page errors: " + this.loved_page_errors + " (200 records are lost for every page that fails.)");
2650
				} else {
2651
					p.console("There were no errors reported.");
2652
				}
2653
			}
2654
		}
2655
	}
2656
	
2657
	this.sync_playcount = function(page) {
2658
		if (this.playcount_working == false) return(p.console("Import aborted."));
2659
		this.page = page;
2660
		this.get(l.get_url() + "&method=library.gettracks&limit=100&page=" + this.page, function() {
2661
			ps.update_playcount();
2662
		});
2663
	}
2664
	
2665
	this.update_playcount = function() {
2666
		this.json_data = p.json(ps.xmlhttp.responsetext);
2667
		if (this.json_data && this.json_data.error > 0) {
2668
			this.playcount_working = false;
2669
			p.MsgBox("There is a problem with the Last.fm web services. Please try again later.\n\n" + ps.xmlhttp.responsetext, 0, p.name);
2670
			return;
2671
		}
2672
		if (this.json_data && this.json_data.tracks && this.json_data.tracks.track) {
2673
			if (this.page == 1) this.pages = this.json_data.tracks["@attr"].totalPages
2674
			for (i = 0; i < this.json_data.tracks.track.length; i++) {
2675
				var data = [];
2676
				data[0] = this.json_data.tracks.track[i].artist.name;
2677
				data[1] = this.json_data.tracks.track[i].name;
2678
				data[2] = this.json_data.tracks.track[i].playcount;
2679
				if (data[2] == 0) {
2680
					this.page = this.pages;
2681
					break;
2682
				}
2683
				if (data.length == 3) {
2684
					p.console(this.r + ": " + data[0] + " - " + data[1] + " " + data[2]);
2685
					var url = fb.TitleFormat("$crc32($lower(" + this.tf(data[0]) + this.tf(data[1]) + "))").Eval(true);
2686
					this.sql += 'INSERT OR REPLACE INTO quicktag(url,subsong,fieldname,value) VALUES("' + url + '","-1","LASTFM_PLAYCOUNT_DB","' + data[2] + '");' + "\n";
2687
					this.r++;
2688
				}
2689
			}
2690
			p.console("Playcount: completed page " + this.page + " of " + this.pages);
2691
		} else if (this.pages > 0) {
2692
			this.playcount_page_errors++;
2693
		}
2694
		if (this.page < this.pages) {
2695
			this.page++;
2696
			this.sync_playcount(this.page);
2697
		} else {
2698
			try {
2699
				this.sql += "COMMIT;"
2700
				var ts = p.fso.OpenTextFile(this.sql_file, 2, true, 0);
2701
				ts.WriteLine(this.sql);
2702
				ts.Close();
2703
				this.finish_import();
2704
			} catch(e) {
2705
			}
2706
			this.playcount_working = false;
2707
			if (this.loved_page_errors + this.playcount_page_errors > 0) {
2708
				p.console("Loved track page errors: " + this.loved_page_errors + " (200 records are lost for every page that fails.)");
2709
				p.console("Playcount page errors: " + this.playcount_page_errors + " (100 records are lost for every page that fails.)");
2710
			} else {
2711
				p.console("There were no errors reported.");
2712
			}
2713
		}
2714
	}
2715
	
2716
	this.finish_import = function() {
2717
		try {
2718
			var cmd_file = p.fso.GetFile(p.script_path + "lastfm_sql.cmd").ShortPath;
2719
			var db_file = p.fso.GetFile(fb.ProfilePath + "customdb_sqlite.db").ShortPath;
2720
			p.run(cmd_file + " " + p.fso.GetFile(p.script_path + "sqlite3.exe").ShortPath + " " + db_file + " " + p.fso.GetFile(this.sql_file).ShortPath);
2721
		} catch(e) {
2722
		}
2723
	}
2724
	
2725
	this.update_button = function() {
2726
		var exclamation = this.icon == "tango" ? "exclamation.png" : "exclamation_small.png";
2727
		var love = this.icon == "tango" ? "love.png" : "love_small.png";
2728
		var love_h = this.icon == "tango" ? "love_h.png" : "love_h_small.png";
2729
		this.size = this.icon == "tango" ? 32 : 20;
2730
		switch(true) {
2731
			case l.username.length == 0:
2732
				this.n = exclamation;
2733
				this.h = exclamation;
2734
				this.tooltip = l.username_error;
2735
				this.func = null;
2736
				break;
2737
			case l.sk.length != 32:
2738
				this.n = exclamation;
2739
				this.h = exclamation;
2740
				this.tooltip = l.password_error;
2741
				this.func = null;
2742
				break;
2743
			case !p.metadb:
2744
				this.n = exclamation;
2745
				this.h = exclamation;
2746
				this.tooltip = "No selection";
2747
				this.func = null;
2748
				break;
2749
			case !utils.CheckComponent("foo_customdb", true):
2750
				this.n = exclamation;
2751
				this.h = exclamation;
2752
				this.tooltip = "The foo_customdb component is not installed. Please refer to the readme.";
2753
				this.func = null;
2754
				break;
2755
			default:
2756
				this.n = this.old_userloved == 1 ? love_h : love;
2757
				this.h = this.old_userloved == 1 ? love : love_h;
2758
				this.func = function() { ps.love_track(); }
2759
				break;
2760
		}
2761
	}
2762
	
2763
	this.x = x;
2764
	this.y = y;
2765
	this.loved_working = false;
2766
	this.playcount_working = false;
2767
	this.sql_file = p.data_folder + "lastfm.sql";
2768
	this.page = 0;
2769
	this.last_page = 0;
2770
	this.auto_love = window.GetProperty("playcount_sync_auto_love", false);
2771
	this.auto_love_tf = window.GetProperty("playcount_sync_auto_love_tf", "");
2772
	this.icon = window.GetProperty("playcount_sync_icon", "tango");
2773
	this.show_console = window.GetProperty("playcount_sync_show_console", true);
2774
	this.library = window.GetProperty("playcount_sync_library", false);
2775
	this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
2776
	window.SetInterval(function() {
2777
		var temp = ps.page > 1 ? ps.page - 1 : 1;
2778
		if (ps.loved_working && ps.page == ps.last_page) {
2779
			ps.xmlhttp.abort();
2780
			ps.sync_loved(temp);
2781
		} else if (ps.playcount_working && ps.page == ps.last_page) {
2782
			ps.xmlhttp.abort();
2783
			ps.sync_playcount(temp);
2784
		} else {
2785
			ps.last_page = ps.page;
2786
		}
2787
	}, 15000);
2788
}
2789
2790
function rating(x, y, bs) {
2791
	this.draw = function(gr) {
2792
		if (!p.metadb) return;
2793
		for (i = 1; i < 6; i++) {
2794
			this.img = i > (this.hover ? this.lrating : this.rating) ? this.off_img : this.on_img;
2795
			p.draw_image(gr, this.img, this.x + this.bs * (i - 1), this.y, this.bs, this.bs, "centre");
2796
		}
2797
	}
2798
	
2799
	this.metadb_changed = function() {
2800
		if (!p.metadb) return false;
2801
		this.hover = false;
2802
		this.rating = p.eval("$if2(%rating%,0)");
2803
		this.tiptext = p.eval(this.tiptext_tf);
2804
		this.lrating = this.rating;
2805
		window.RepaintRect(this.x, this.y, this.w, this.bs);
2806
		return true;
2807
	}
2808
	
2809
	this.trace = function(x, y) {
2810
		return x > this.x - this.bs && x < this.x + (this.bs * 5) && y > this.y && y < this.y + this.bs;
2811
	}
2812
	
2813
	this.move = function(x, y) {
2814
		if (!this.trace(x, y)) {
2815
			if (this.hover) this.leave();
2816
			return false;
2817
		}
2818
		if (p.metadb) {
2819
			if (x > this.x) p.tt(this.tiptext);
2820
			this.hover = true;
2821
			this.lrating = Math.ceil((x - this.x) / this.bs);
2822
			window.RepaintRect(this.x, this.y, this.w, this.bs);
2823
		}
2824
		return true;
2825
	}
2826
	
2827
	this.lbtn_up = function(x, y) {
2828
		if (!this.trace(x, y)) return false;
2829
		if (this.check) {
2830
			if (this.lrating != this.rating && this.hover == 1) fb.RunContextCommandWithMetadb("Rating/" + (this.lrating == 0 ? "<not set>" : this.lrating), p.metadb, 8);
2831
		} else {
2832
			p.MsgBox(this.error_text, 0, p.name);
2833
			p.browser("http://www.foobar2000.org/components/view/foo_playcount");
2834
		}
2835
		return true;
2836
	}
2837
	
2838
	this.leave = function() {
2839
		p.ttd();
2840
		this.hover = false;
2841
		window.RepaintRect(this.x, this.y, this.w, this.bs);
2842
	}
2843
	
2844
	this.x = x;
2845
	this.y = y;
2846
	this.bs = bs;
2847
	this.w = this.bs * 5;
2848
	this.hover = false;
2849
	this.rating = null;
2850
	this.lrating = null;
2851
	this.img = null;
2852
	this.off_img = gdi.Image(p.images_path + "off.png");
2853
	this.on_img = gdi.Image(p.images_path + "on.png");
2854
	this.check = utils.CheckComponent("foo_playcount", true);
2855
	this.error_text = "This script requires foo_playcount.";
2856
	this.tiptext_tf = this.check ? 'Rate "%title%" by "%artist%"' : this.error_text;
2857
}
2858
2859
function sb(x, y, w, h, img, isVisible, func) {
2860
	this.x = x;
2861
	this.y = y;
2862
	this.w = w;
2863
	this.h = h;
2864
	this.img = img;
2865
	this.isVisible = new Function("return (" + isVisible + ");");
2866
	this.func = func;
2867
	
2868
	this.draw = function(gr) {
2869
		if (this.isVisible()) p.draw_image(gr, this.img, this.x, this.y, this.w, this.h, "centre");
2870
	}
2871
	
2872
	this.trace = function(x, y) {
2873
		return(x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h && this.isVisible());
2874
	}
2875
	
2876
	this.lbtn_up = function(x, y) {
2877
		if (!this.trace(x, y)) return false;
2878
		this.func && this.func(x, y);
2879
		return true;
2880
	}
2881
}
2882
2883
function seekbar(x, y, w, h) {
2884
	this.trace = function(x, y) {
2885
		return (x > this.x && x < this.x + this.w && y > this.y - (this.drag ? 200 : 0) && y < this.y + this.h + (this.drag ? 200 : 0));
2886
	}
2887
	
2888
	this.draw = function(gr) {
2889
		p.draw_background(gr);
2890
		switch(this.mode) {
2891
			case "spectrogram":
2892
				if (this.working) {
2893
					p.draw_image(gr, this.hourglass_img, this.x, this.y + Math.round((this.h - 32) / 2), this.w, 32, "centre");
2894
				} else {
2895
					p.draw_image(gr, this.img, this.x, this.y, this.w, this.h, "stretch");
2896
				}
2897
				if (fb.IsPlaying && fb.PlaybackLength > 0) {
2898
					this.calc_pos();
2899
					gr.FillSolidRect(this.x + this.pos - 2, this.y, 2, this.h, p.splitRGB(this.marker));
2900
				}
2901
				break;
2902
			case "nyan_cat":
2903
				if (fb.IsPlaying && fb.PlaybackLength > 0) {
2904
					this.calc_pos();
2905
					for (i = 0; i < this.offsets.length; i++) {
2906
						gr.FillSolidRect(this.x - 48, this.y + this.offsets[i], this.pos + 2, this.heights[i], this.colours[i]);
2907
					}
2908
					p.draw_image(gr, this.cat_img[this.z], this.pos, this.y, 48, 30);
2909
				} else {
2910
					p.draw_image(gr, this.cat_img[0], 0, this.y, 48, 30);
2911
				}
2912
				break;
2913
		}
2914
	}
2915
	
2916
	this.playback_new_track = function() {
2917
		if (this.mode != "spectrogram") return;
2918
		var metadb = fb.GetNowPlaying();
2919
		this.img && this.img.Dispose();
2920
		this.img = null;
2921
		try {
2922
			this.png_filename = spectrogram_cache + "\\" + fb.TitleFormat("$crc32(%path%)").EvalWithMetadb(metadb) + this.sox_params + ".png";
2923
			switch(true) {
2924
				case !metadb:
2925
				case metadb.RawPath.indexOf("file") != 0:
2926
					p.console("Skipping... Not a valid file type.");
2927
					break;
2928
				case fb.PlaybackLength == 0:
2929
					p.console("Skipping... Unknown length.");
2930
					break;
2931
				case fb.TitleFormat("$if($or($strcmp(%__cue_embedded%,yes),$strcmp($right(%path%,3),cue)),cue,)").EvalWithMetadb(metadb) == "cue":
2932
					p.console("Skipping... Cannot support cuesheets.");
2933
					break;
2934
				case fb.TitleFormat("%subsong%").EvalWithMetadb(metadb) > 0:
2935
					p.console("Skipping... Cannot support tracks with chapters.");
2936
					break;
2937
				case this.library && !fb.IsMetadbInMediaLibrary(metadb):
2938
					p.console("Skipping... Track not in library.");
2939
					break;
2940
				case p.fso.FileExists(this.png_filename):
2941
					this.img = gdi.Image(this.png_filename);
2942
					break;
2943
				default:
2944
					this.working = true
2945
					window.Repaint();
2946
					var filename = metadb.Path;
2947
					var length = fb.TitleFormat("%length%").EvalWithMetadb(metadb);
2948
					var cmd = "cmd /c \"\"" + ffmpeg_exe + "\" -i \"" + filename + "\" -t " + length + " -f sox - | \"" + sox_exe + "\" -p -n " + this.sox_params + " -d " + length + " -r -o \"" + this.png_filename + "\"\"";
2949
					//p.console(cmd);
2950
					try { p.WshShell.Run(cmd, 0, true); } catch(e) {}
2951
					this.working = false;
2952
					this.img = gdi.Image(this.png_filename);
2953
					break;
2954
			}
2955
		} catch(e) {
2956
			//p.console(e);
2957
		}
2958
		window.Repaint();
2959
	}
2960
	
2961
	this.playback_stop = function() {
2962
		window.Repaint();
2963
	}
2964
	
2965
	this.playback_seek = function() {
2966
		this.update();
2967
	}
2968
	
2969
	this.move = function(x, y) {
2970
		if (this.drag) this.update();
2971
		if (this.trace(x, y)) {
2972
			if (fb.IsPlaying && fb.PlaybackLength > 0) {
2973
				x -= this.x;
2974
				this.drag_seek = x < 0 ? 0 : x > this.w ? 1 : x / this.w;
2975
				if (x != this.old_x || y != this.old_y) {
2976
					p.tt(p.format_time(fb.PlaybackLength * this.drag_seek));
2977
					this.old_x = x;
2978
					this.old_y = y;
2979
				}
2980
			}
2981
			return true;
2982
		} else {
2983
			this.leave();
2984
			return false;
2985
		}
2986
	}
2987
	
2988
	this.lbtn_down = function(x, y) {
2989
		if (!this.trace(x, y)) return false;
2990
		if (fb.IsPlaying && fb.PlaybackLength > 0) this.drag = true;
2991
		return true;
2992
	}
2993
	
2994
	this.lbtn_up = function(x, y) {
2995
		if (!this.trace(x, y)) return false;
2996
		if (this.drag) {
2997
			this.drag = false;
2998
			fb.PlaybackTime = fb.PlaybackLength * this.drag_seek;
2999
		}
3000
		return true;
3001
	}
3002
	
3003
	this.leave = function() {
3004
		this.drag = false;
3005
		p.ttd();
3006
	}
3007
	
3008
	this.update = function() {
3009
		window.RepaintRect(0, this.y, p.w, this.h);
3010
	}
3011
	
3012
	this.calc_pos = function() {
3013
		this.pos = this.drag ? this.w * this.drag_seek : this.w * (fb.PlaybackTime / fb.PlaybackLength);
3014
	}
3015
	
3016
	this.init = function() {
3017
		switch(true) {
3018
			case p.check_feature("spectrogram"):
3019
				this.clear_images = function(period) {
3020
					var now = Date.parse(Date());
3021
					images = utils.Glob(spectrogram_cache + "\\*.*").toArray();
3022
					for (i = 0; i < images.length; i++) {
3023
						if (this.png_filename == images[i]) continue;
3024
						var file = p.fso.Getfile(images[i]);
3025
						var file_date = Date.parse(file.DateLastModified);
3026
						if (now - file_date > period) {
3027
							try {
3028
								p.fso.DeleteFile(images[i]);
3029
							} catch(e) {
3030
								p.console("Could not delete " + images[i]);
3031
							}
3032
						}
3033
					}
3034
				}
3035
				
3036
				this.mode = "spectrogram";
3037
				this.library = window.GetProperty("seekbar_library", false);
3038
				this.marker = window.GetProperty("seekbar_marker", "240-240-240");
3039
				this.sox_params = window.GetProperty("sox_params", "channels 1 spectrogram -Y 130").trim();
3040
				this.working = false;
3041
				this.img = false;
3042
				this.hourglass_img = gdi.Image(p.images_path + "hourglass.png");
3043
				if (!p.fso.FolderExists(spectrogram_cache)) p.fso.CreateFolder(spectrogram_cache);
3044
				if (fb.IsPlaying) {
3045
					window.SetTimeout(function() {
3046
						s.playback_new_track();
3047
					}, 100);
3048
				}
3049
				break;
3050
			case p.check_feature("nyan_cat"):
3051
				this.mode = "nyan_cat";
3052
				this.cat_img = [gdi.Image(p.images_path + "\\seekbar cat.png"), gdi.Image(p.images_path + "\\seekbar cat 2.png")];
3053
				this.offsets = [2, 6, 10, 15, 20, 24];
3054
				this.heights = [4, 4, 5, 5, 4, 4];
3055
				this.colours = [RGB(255, 0, 0), RGB(255, 153, 0), RGB(255, 255, 0), RGB(51, 255, 0), RGB(0, 153, 255), RGB(102, 51, 255)];
3056
				break;
3057
		}
3058
	}
3059
	
3060
	this.x = x;
3061
	this.y = y;
3062
	this.w = w;
3063
	this.h = h;
3064
	this.z = 0;
3065
	this.pos = 0;
3066
	this.drag = false;
3067
	this.drag_seek = 0;
3068
	this.old_x = 0;
3069
	this.old_y = 0;
3070
	this.init();
3071
	this.seekbar_timer = window.SetInterval(function() {
3072
		s.z = s.z == 0 ? 1 : 0;
3073
		if (!fb.IsPlaying || fb.IsPaused || fb.PlaybackLength == 0) return;
3074
		s.update();
3075
	}, 150);
3076
}
3077
3078
function text(x, y, w, h) {
3079
	this.size = function() {
3080
		this.rows = Math.floor((this.h - 36) / p.row_height);
3081
		this.but_x = this.x + Math.round((this.w - 15) / 2);
3082
		this.up_btn = new sb(this.but_x, this.y, 15, 15, p.up_img, "t.offset > 0", function() { t.wheel(1); });
3083
		this.down_btn = new sb(this.but_x, this.y + this.h - 15, 15, 15, p.down_img, "t.offset < t.text_rows - t.rows", function() { t.wheel(-1); });
3084
		this.calc();
3085
	}
3086
	
3087
	this.draw = function(gr, colour) {
3088
		for (i = 0; i < Math.min(this.rows, this.text_rows); i++) {
3089
			if (this.centre) p.centre_text(gr, this.text_array[i + this.offset], this.fixed ? p.fixed_font : p.normal_font, colour || p.textcolour, this.x, 18 + this.y + (i * p.row_height), this.w, p.row_height);
3090
			else p.left_text(gr, this.text_array[i + this.offset], this.fixed ? p.fixed_font : p.normal_font, colour || p.textcolour, this.x, 18 + this.y + (i * p.row_height), this.w, p.row_height);
3091
		}
3092
		this.up_btn.draw(gr);
3093
		this.down_btn.draw(gr);
3094
	}
3095
	
3096
	this.metadb_changed = function() {
3097
		if (!p.metadb) return false;
3098
		switch(this.mode) {
3099
			case "simple_tag":
3100
				this.temp_filename = p.metadb.Path;
3101
				if (this.filename == this.temp_filename) return false;
3102
				this.filename = this.temp_filename;
3103
				this.text = p.eval(this.tag);
3104
				break;
3105
			case "simple_text":
3106
				this.temp_filename = p.eval(this.filename_tf);
3107
				if (this.filename == this.temp_filename) return false;
3108
				this.text = "";
3109
				this.filename = this.temp_filename;
3110
				if (p.fso.FolderExists(this.filename)) {
3111
					this.files = [];
3112
					this.files = this.files.concat(utils.Glob(this.filename + "\\*.txt").toArray(), utils.Glob(this.filename + "\\*.log").toArray());
3113
					this.files.sort();
3114
					this.text = p.open(this.files[0]);
3115
				} else {
3116
					this.filenames = this.filename.split("|");
3117
					for (i = 0; i < this.filenames.length; i++) {
3118
						if (p.fso.fileExists(this.filenames[i])) {
3119
							this.text = p.open(this.filenames[i]);
3120
							break;
3121
						}
3122
					}
3123
				}
3124
				this.text = this.text.replace(/\t/g, "    ");
3125
				break;
3126
			case "lastfm_wiki":
3127
				p.artist = p.eval(p.artist_tf);
3128
				if (this.artist == p.artist) return false;
3129
				this.artist = p.artist;
3130
				this.folder = p.data_folder + p.eval("$crc32(" + p.artist_tf + ")");
3131
				if (!p.fso.FolderExists(this.folder)) p.fso.CreateFolder(this.folder);
3132
				this.filename = this.folder + "\\artist.getInfo_mod.json";
3133
				this.text = "Nothing found.";
3134
				this.url = "http://www.last.fm/";
3135
				if (p.fso.fileExists(this.filename)) {
3136
					this.json_text = p.open(this.filename);
3137
					this.json_data = p.json(this.json_text);
3138
					if (this.json_data && this.json_data.response && this.json_data.response.biographies) {
3139
						this.items = this.json_data.response.biographies.length;
3140
						for (i = 0; i < this.items; i++) {
3141
							if (this.json_data.response.biographies[i].site == this.source) {
3142
								this.text = this.json_data.response.biographies[i].text;
3143
								this.url = this.json_data.response.biographies[i].license["attribution-url"];
3144
								break;
3145
							}
3146
						}
3147
						if (this.source == "last.fm") {
3148
							this.text = this.text.replace(/\.  /g, ".\n\n")
3149
						} else {
3150
							this.text = this.text.replace(/\. \n/g, ".\n\n").replace(/\n\n\n/g, "\n\n").replace(/edit:\n/g , ":\n");
3151
						}
3152
					}
3153
				} else {
3154
					this.get();
3155
				}
3156
				break;
3157
			case "lastfm_album":
3158
				var temp_album = p.eval("[%album%]");
3159
				var temp_artist = p.eval("[%album artist%]");
3160
				if (this.album == temp_album && this.artist == temp_artist) return false;
3161
				this.artist = temp_artist;
3162
				this.album = temp_album;
3163
				this.folder = p.data_folder + p.eval("$crc32(" + p.artist_tf + ")");
3164
				if (!p.fso.FolderExists(this.folder)) p.fso.CreateFolder(this.folder);
3165
				this.filename = this.folder + "\\album.getInfo_" + p.eval("$crc32(%album%)") + ".json";
3166
				this.text = "Nothing found.";
3167
				this.url = l.url;
3168
				if (p.fso.fileExists(this.filename)) {
3169
					this.file = p.fso.GetFile(this.filename);
3170
					this.json_text = p.open(this.filename);
3171
					this.json_data = p.json(this.json_text);
3172
					if (this.json_data && this.json_data.album) {
3173
						if (this.json_data.album.url) this.url = this.json_data.album.url;
3174
						if (this.json_data.album.wiki && this.json_data.album.wiki.content) {
3175
							this.text = p.strip_tags(this.json_data.album.wiki.content);
3176
							this.text = this.text.replace("User-contributed text is available under the Creative Commons By-SA License and may also be available under the GNU FDL.", "").trim();
3177
						}
3178
					}
3179
					if (Date.parse(Date()) - Date.parse(this.file.DateLastModified) > ONE_WEEK) this.get();
3180
				} else {
3181
					this.get();
3182
				}
3183
				break;
3184
		}
3185
		this.calc();
3186
		window.Repaint();
3187
		return true;
3188
	}
3189
	
3190
	this.trace = function(x, y) {
3191
		return (x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h);
3192
	}
3193
	
3194
	this.wheel = function(step) {
3195
		if (!this.trace(p.mx, p.my)) return false;
3196
		if (this.text_rows > this.rows) {
3197
			this.offset -= step * 3;
3198
			if (this.offset < 0) this.offset = 0;
3199
			if (this.offset + this.rows > this.text_rows) this.offset = this.text_rows - this.rows;
3200
			window.RepaintRect(this.x, this.y, this.w, this.h);
3201
		}
3202
		return true;
3203
	}
3204
	
3205
	this.move = function(x, y) {
3206
		switch(true) {
3207
			case !this.trace(x, y):
3208
				window.SetCursor(IDC_ARROW);
3209
				return false;
3210
			case this.up_btn.trace(x, y):
3211
			case this.down_btn.trace(x, y):
3212
				window.SetCursor(IDC_HAND);
3213
				return true;
3214
			default:
3215
				window.SetCursor(IDC_ARROW);
3216
				return false;
3217
		}
3218
	}
3219
	
3220
	this.lbtn_up = function(x, y) {
3221
		if (!this.trace(x, y)) return false;
3222
		this.up_btn.lbtn_up(x, y);
3223
		this.down_btn.lbtn_up(x, y);
3224
		return true;
3225
	}
3226
	
3227
	this.get = function() {
3228
		var fn = this.filename;
3229
		var func = function() {
3230
			if (!p.save(t.xmlhttp.responsetext, fn)) return;
3231
			t.artist = "";
3232
			t.album = "";
3233
			on_item_focus_change();
3234
		}
3235
		var url = "";
3236
		var user_agent = "";
3237
		switch(this.mode) {
3238
			case "lastfm_album":
3239
				if (this.artist == "" || this.album == "") return;
3240
				url = l.get_url() + "&method=album.getInfo&artist=" + encodeURIComponent(this.artist) + "&album=" + encodeURIComponent(this.album);
3241
				user_agent = l.user_agent;
3242
				break;
3243
			case "lastfm_wiki":
3244
				if (this.artist == "" || this.artist == "?") return;
3245
				url = "http://developer.echonest.com/api/v4/artist/biographies?api_key=EKWS4ESQLKN3G2ZWV&format=json&name=" + encodeURIComponent(this.artist);
3246
				break;
3247
		}
3248
		if (url.length == 0) return;
3249
		this.xmlhttp.open("GET", url, true);
3250
		if (user_agent.length > 0) this.xmlhttp.setRequestHeader('User-Agent', user_agent);
3251
		this.xmlhttp.send();
3252
		this.xmlhttp.onreadystatechange = function() {
3253
			if (t.xmlhttp.readyState == 4) {
3254
				if (t.xmlhttp.status == 200) {
3255
					func();
3256
				} else {
3257
					p.console(t.xmlhttp.responsetext || "HTTP error: " + t.xmlhttp.status);
3258
				}
3259
			}
3260
		}
3261
	}
3262
	
3263
	this.calc = function() {
3264
		this.offset = 0;
3265
		this.text_rows = 0;
3266
		if (this.w < 100 || this.text.length == 0) return;
3267
		var temp_bmp = gdi.CreateImage(1, 1);
3268
		var temp_gr = temp_bmp.GetGraphics();
3269
		var paragraphs = this.text.split("\n");
3270
		this.text_array = [];
3271
		for (i = 0; i < paragraphs.length; i++) {
3272
			if (this.fixed) {
3273
				this.text_array.push(paragraphs[i]);
3274
			} else {
3275
				var lines = temp_gr.EstimateLineWrap(paragraphs[i], p.normal_font, this.w).toArray();
3276
				for (j = 0; j < lines.length; j += 2) {
3277
					this.text_array.push(lines[j].trim());
3278
				}
3279
			}
3280
		}
3281
		this.text_rows = this.text_array.length;
3282
		temp_bmp.ReleaseGraphics(temp_gr);
3283
		temp_bmp.Dispose();
3284
		temp_gr = null;
3285
		temp_bmp = null;
3286
	}
3287
	
3288
	this.init = function() {
3289
		switch(true) {
3290
			case p.check_feature("lastfm_album"):
3291
				this.mode = "lastfm_album";
3292
				break;
3293
			case p.check_feature("lastfm_wiki"):
3294
				this.mode = "lastfm_wiki";
3295
				this.source = window.GetProperty("biography_source", "last.fm");
3296
				break;
3297
			case p.check_feature("simple_text"):
3298
				this.mode = "simple_text";
3299
				this.title = window.GetProperty("text_title", "$directory_path(%path%)");
3300
				this.filename_tf = window.GetProperty("text_filename_tf", "$directory_path(%path%)");
3301
				this.filename = "";
3302
				this.fixed = window.GetProperty("text_fixed_font", true);
3303
				break;
3304
			case p.check_feature("simple_tag"):
3305
				this.mode = "simple_tag";
3306
				this.title = window.GetProperty("text_title", "%title%");
3307
				this.filename = "";
3308
				this.centre = window.GetProperty("text_centre", false);
3309
				this.tag = window.GetProperty("text_tag", "[%lyrics%]");
3310
				break;
3311
		}
3312
	}
3313
	
3314
	this.x = x;
3315
	this.y = y;
3316
	this.w = w;
3317
	this.h = h;
3318
	this.text = "";
3319
	this.artist = "";
3320
	this.album = "";
3321
	this.url = "";
3322
	this.fixed = false;
3323
	this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
3324
	this.init();
3325
	this.size();
3326
}
3327
3328
function thumbs() {
3329
	this.trace = function(x, y) {
3330
		return (x > this.x && x < this.x + this.w && y > this.y && y < this.y + this.h);
3331
	}
3332
	
3333
	this.draw = function(gr) {
3334
		switch(true) {
3335
			case im.images.length == 0:
3336
				break;
3337
			case this.mode == "off":
3338
				p.draw_image(gr, im.images[im.index], im.x, im.y, im.w, im.h, im.type);
3339
				break;
3340
			case !this.img:
3341
				break;
3342
			case this.mode == "grid":
3343
				gr.DrawImage(this.img, this.x, this.y, this.w, this.h, 0, this.offset * this.px, this.w, this.h);
3344
				if (this.overlay) {
3345
					gr.FillSolidRect(this.x, this.y, this.w, this.h, RGBA(0, 0, 0, 126));
3346
					p.draw_image(gr, im.images[im.index], im.x, im.y, im.w, im.h, "centre");
3347
				}
3348
				break;
3349
			case this.mode == "left":
3350
			case this.mode == "right":
3351
				gr.DrawImage(this.img, this.x, this.y, this.w, this.h, 0, this.offset * this.px, this.w, this.h);
3352
				p.draw_image(gr, im.images[im.index], im.x, im.y, im.w, im.h, im.type);
3353
				break;
3354
			case this.mode == "bottom":
3355
			case this.mode == "top":
3356
				gr.DrawImage(this.img, this.x, this.y, this.w, this.h, this.offset * this.px, 0, this.w, this.h);
3357
				p.draw_image(gr, im.images[im.index], im.x, im.y, im.w, im.h, im.type);
3358
				break;
3359
		}
3360
	}
3361
	
3362
	this.calc = function() {
3363
		this.offset = 0;
3364
		switch(true) {
3365
			case p.w < this.px || p.h < this.px || this.mode == "off":
3366
				this.nc = true;
3367
				this.img && this.img.Dispose();
3368
				this.img = null;
3369
				im.x = 0;
3370
				im.y = 0;
3371
				im.w = p.w;
3372
				im.h = p.h;
3373
				break;
3374
			case this.mode == "grid":
3375
				this.x = 0;
3376
				this.y = 0;
3377
				this.w = p.w;
3378
				this.h = p.h;
3379
				im.x = 40;
3380
				im.y = 40;
3381
				im.w = this.w - 80;
3382
				im.h = this.h - 80;
3383
				if (!this.nc && this.columns != Math.floor(this.w / this.px)) this.nc = true;
3384
				this.overlay = false;
3385
				this.rows = Math.ceil(this.h / this.px);
3386
				this.columns = Math.floor(this.w / this.px);
3387
				this.img_rows = Math.ceil(im.images.length / this.columns);
3388
				if (this.nc && im.images.length > 0) {
3389
					this.nc = false;
3390
					this.img && this.img.Dispose();
3391
					this.img = null;
3392
					this.img = gdi.CreateImage(Math.min(this.columns, im.images.length) * this.px, this.img_rows * this.px);
3393
					var temp_gr = this.img.GetGraphics();
3394
					temp_gr.SetInterpolationMode(7);
3395
					var ci = 0;
3396
					var row, col;
3397
					for (row = 0; row < this.img_rows; row++) {
3398
						for (col = 0; col < this.columns; col++) {
3399
							if (ci == im.images.length) continue;
3400
							p.draw_image(temp_gr, im.images[ci], col * this.px, row * this.px, this.px, this.px, "crop top");
3401
							ci++;
3402
						}
3403
					}
3404
					this.img.ReleaseGraphics(temp_gr);
3405
					temp_gr = null;
3406
				}
3407
				break;
3408
			case this.mode == "left":
3409
			case this.mode == "right":
3410
				this.x = this.mode == "left" ? 0 : p.w - this.px;
3411
				this.y = 0;
3412
				this.w = this.px;
3413
				this.h = p.h;
3414
				im.x = this.mode == "right" ? 0 : this.px;
3415
				im.y = 0;
3416
				im.w = p.w - this.px;
3417
				im.h = p.h;
3418
				this.rows = Math.ceil(this.h / this.px);
3419
				if (this.nc && im.images.length > 0) {
3420
					this.nc = false;
3421
					this.img && this.img.Dispose();
3422
					this.img = null;
3423
					this.img = gdi.CreateImage(this.px, this.px * im.images.length);
3424
					var temp_gr = this.img.GetGraphics();
3425
					temp_gr.SetInterpolationMode(7);
3426
					for (i = 0; i < im.images.length; i++) {
3427
						p.draw_image(temp_gr, im.images[i], 0, i * this.px, this.px, this.px, "crop top");
3428
					}
3429
					this.img.ReleaseGraphics(temp_gr);
3430
					temp_gr = null;
3431
				}
3432
				break;
3433
			case this.mode == "bottom":
3434
			case this.mode == "top":
3435
				this.x = 0;
3436
				this.y = this.mode == "top" ? 0 : p.h - this.px;
3437
				this.w = p.w;
3438
				this.h = this.px;
3439
				im.x = 0;
3440
				im.y = this.mode == "bottom" ? 0 : this.px;
3441
				im.w = p.w;
3442
				im.h = p.h - this.px;
3443
				this.columns = Math.ceil(this.w / this.px);
3444
				if (this.nc && im.images.length > 0) {
3445
					this.nc = false;
3446
					this.img && this.img.Dispose();
3447
					this.img = null;
3448
					this.img = gdi.CreateImage(this.px * im.images.length, this.px);
3449
					var temp_gr = this.img.GetGraphics();
3450
					temp_gr.SetInterpolationMode(7);
3451
					for (i = 0; i < im.images.length; i++) {
3452
						p.draw_image(temp_gr, im.images[i], i * this.px, 0, this.px, this.px, "crop top");
3453
					}
3454
					this.img.ReleaseGraphics(temp_gr);
3455
					temp_gr = null;
3456
				}
3457
				break;
3458
		}
3459
	}
3460
	
3461
	this.wheel = function(step) {
3462
		switch(true) {
3463
			case !this.trace(p.mx, p.my):
3464
			case this.overlay:
3465
				return false;
3466
			case this.mode == "grid":
3467
				if (this.img_rows < this.rows) return true;
3468
				this.offset -= step;
3469
				if (this.offset < 0) this.offset = 0;
3470
				if (this.offset > this.img_rows - this.rows) this.offset = this.img_rows - this.rows + 1;
3471
				break;
3472
			case this.mode == "left":
3473
			case this.mode == "right":
3474
				if (im.images.length < this.rows) return true;
3475
				this.offset -= step;
3476
				if (this.offset < 0) this.offset = 0;
3477
				if (this.rows + this.offset > im.images.length) this.offset = im.images.length - this.rows + 1;
3478
				break;
3479
			case this.mode == "bottom":
3480
			case this.mode == "top":
3481
				if (im.images.length < this.columns) return true;
3482
				this.offset -= step;
3483
				if (this.offset < 0) this.offset = 0;
3484
				if (this.columns + this.offset > im.images.length) this.offset = im.images.length - this.columns + 1;
3485
				break;
3486
		}
3487
		window.RepaintRect(this.x, this.y, this.w, this.h);
3488
		return true;
3489
	}
3490
	
3491
	this.move = function(x, y) {
3492
		switch(true) {
3493
			case !this.trace(x, y):
3494
				window.SetCursor(IDC_ARROW);
3495
				return false;
3496
			case this.overlay:
3497
				window.SetCursor(IDC_ARROW);
3498
				return true;
3499
			case this.mode == "grid":
3500
				this.index = Math.floor((x - this.x) / this.px) + (Math.floor((y - this.y) / this.px) * this.columns) + (this.offset * this.columns);
3501
				break;
3502
			case this.mode == "left":
3503
			case this.mode == "right":
3504
				this.index = Math.floor((y - this.y) / this.px) + this.offset;
3505
				break;
3506
			case this.mode == "bottom":
3507
			case this.mode == "top":
3508
				this.index = Math.floor((x - this.x) / this.px) + this.offset;
3509
				break;
3510
		}
3511
		window.SetCursor(this.index < im.images.length ? IDC_HAND : IDC_ARROW);
3512
		return true;
3513
	}
3514
	
3515
	this.lbtn_up = function(x, y) {
3516
		switch(true) {
3517
			case !this.trace(x, y):
3518
				return false;
3519
			case this.mode == "grid" && this.overlay && im.trace(x, y):
3520
				this.overlay = false;
3521
				window.Repaint();
3522
				break;
3523
			case this.mode == "grid" && this.index < im.images.length && !this.overlay:
3524
				this.overlay = true;
3525
				im.index = this.index;
3526
				window.Repaint();
3527
				break;
3528
			case this.overlay:
3529
				break;
3530
			case this.index < im.images.length:
3531
				im.index = this.index;
3532
				window.Repaint();
3533
		}
3534
		return true;
3535
	}
3536
	
3537
	this.lbtn_dblclk = function(x, y) {
3538
		if (!im.trace(x, y) || th.mode == "grid") return false;
3539
		p.run("\"" + im.files[im.index] + "\"");
3540
		return true;
3541
	}
3542
	
3543
	this.nc = false;
3544
	this.mode = window.GetProperty("thumbs_mode", p.check_feature("now_playing") ? "off" : "bottom");
3545
	this.px = window.GetProperty("thumbs_px", 100);
3546
	this.img = null;
3547
}
3548
3549
//json2.js
3550
if(typeof JSON!=='object'){JSON={};}
3551
(function(){'use strict';function f(n){return n<10?'0'+n:n;}
3552
if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+
3553
f(this.getUTCMonth()+1)+'-'+
3554
f(this.getUTCDate())+'T'+
3555
f(this.getUTCHours())+':'+
3556
f(this.getUTCMinutes())+':'+
3557
f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
3558
var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
3559
function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
3560
if(typeof rep==='function'){value=rep.call(holder,key,value);}
3561
switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
3562
gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
3563
v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}
3564
if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){if(typeof rep[i]==='string'){k=rep[i];v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
3565
v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
3566
if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
3567
rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
3568
return str('',{'':value});};}
3569
if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
3570
return reviver.call(holder,key,value);}
3571
text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
3572
('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
3573
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
3574
throw new SyntaxError('JSON.parse');};}}());
3575
3576
/*
3577
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
3578
 * Digest Algorithm, as defined in RFC 1321.
3579
 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
3580
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
3581
 * Distributed under the BSD License
3582
 * See http://pajhome.org.uk/crypt/md5 for more info.
3583
 */
3584
var hexcase=0;function hex_md5(a){return rstr2hex(rstr_md5(str2rstr_utf8(a)))}
3585
function hex_hmac_md5(a,b){return rstr2hex(rstr_hmac_md5(str2rstr_utf8(a),str2rstr_utf8(b)))}
3586
function md5_vm_test(){return hex_md5("abc").toLowerCase()=="900150983cd24fb0d6963f7d28e17f72"}
3587
function rstr_md5(a){return binl2rstr(binl_md5(rstr2binl(a),a.length*8))}
3588
function rstr_hmac_md5(c,f){var e=rstr2binl(c);if(e.length>16){e=binl_md5(e,c.length*8)}
3589
var a=Array(16),d=Array(16);for(var b=0;b<16;b++){a[b]=e[b]^909522486;d[b]=e[b]^1549556828}
3590
var g=binl_md5(a.concat(rstr2binl(f)),512+f.length*8);return binl2rstr(binl_md5(d.concat(g),512+128))}
3591
function rstr2hex(c){try{hexcase}catch(g){hexcase=0}
3592
var f=hexcase?"0123456789ABCDEF":"0123456789abcdef";var b="";var a;for(var d=0;d<c.length;d++){a=c.charCodeAt(d);b+=f.charAt((a>>>4)&15)+f.charAt(a&15)}
3593
return b}
3594
function str2rstr_utf8(c){var b="";var d=-1;var a,e;while(++d<c.length){a=c.charCodeAt(d);e=d+1<c.length?c.charCodeAt(d+1):0;if(55296<=a&&a<=56319&&56320<=e&&e<=57343){a=65536+((a&1023)<<10)+(e&1023);d++}
3595
if(a<=127){b+=String.fromCharCode(a)}else{if(a<=2047){b+=String.fromCharCode(192|((a>>>6)&31),128|(a&63))}else{if(a<=65535){b+=String.fromCharCode(224|((a>>>12)&15),128|((a>>>6)&63),128|(a&63))}else{if(a<=2097151){b+=String.fromCharCode(240|((a>>>18)&7),128|((a>>>12)&63),128|((a>>>6)&63),128|(a&63))}}}}}
3596
return b}
3597
function rstr2binl(b){var a=Array(b.length>>2);for(var c=0;c<a.length;c++){a[c]=0}
3598
for(var c=0;c<b.length*8;c+=8){a[c>>5]|=(b.charCodeAt(c/8)&255)<<(c%32)}
3599
return a}
3600
function binl2rstr(b){var a="";for(var c=0;c<b.length*32;c+=8){a+=String.fromCharCode((b[c>>5]>>>(c%32))&255)}
3601
return a}
3602
function binl_md5(p,k){p[k>>5]|=128<<((k)%32);p[(((k+64)>>>9)<<4)+14]=k;var o=1732584193;var n=-271733879;var m=-1732584194;var l=271733878;for(var g=0;g<p.length;g+=16){var j=o;var h=n;var f=m;var e=l;o=md5_ff(o,n,m,l,p[g+0],7,-680876936);l=md5_ff(l,o,n,m,p[g+1],12,-389564586);m=md5_ff(m,l,o,n,p[g+2],17,606105819);n=md5_ff(n,m,l,o,p[g+3],22,-1044525330);o=md5_ff(o,n,m,l,p[g+4],7,-176418897);l=md5_ff(l,o,n,m,p[g+5],12,1200080426);m=md5_ff(m,l,o,n,p[g+6],17,-1473231341);n=md5_ff(n,m,l,o,p[g+7],22,-45705983);o=md5_ff(o,n,m,l,p[g+8],7,1770035416);l=md5_ff(l,o,n,m,p[g+9],12,-1958414417);m=md5_ff(m,l,o,n,p[g+10],17,-42063);n=md5_ff(n,m,l,o,p[g+11],22,-1990404162);o=md5_ff(o,n,m,l,p[g+12],7,1804603682);l=md5_ff(l,o,n,m,p[g+13],12,-40341101);m=md5_ff(m,l,o,n,p[g+14],17,-1502002290);n=md5_ff(n,m,l,o,p[g+15],22,1236535329);o=md5_gg(o,n,m,l,p[g+1],5,-165796510);l=md5_gg(l,o,n,m,p[g+6],9,-1069501632);m=md5_gg(m,l,o,n,p[g+11],14,643717713);n=md5_gg(n,m,l,o,p[g+0],20,-373897302);o=md5_gg(o,n,m,l,p[g+5],5,-701558691);l=md5_gg(l,o,n,m,p[g+10],9,38016083);m=md5_gg(m,l,o,n,p[g+15],14,-660478335);n=md5_gg(n,m,l,o,p[g+4],20,-405537848);o=md5_gg(o,n,m,l,p[g+9],5,568446438);l=md5_gg(l,o,n,m,p[g+14],9,-1019803690);m=md5_gg(m,l,o,n,p[g+3],14,-187363961);n=md5_gg(n,m,l,o,p[g+8],20,1163531501);o=md5_gg(o,n,m,l,p[g+13],5,-1444681467);l=md5_gg(l,o,n,m,p[g+2],9,-51403784);m=md5_gg(m,l,o,n,p[g+7],14,1735328473);n=md5_gg(n,m,l,o,p[g+12],20,-1926607734);o=md5_hh(o,n,m,l,p[g+5],4,-378558);l=md5_hh(l,o,n,m,p[g+8],11,-2022574463);m=md5_hh(m,l,o,n,p[g+11],16,1839030562);n=md5_hh(n,m,l,o,p[g+14],23,-35309556);o=md5_hh(o,n,m,l,p[g+1],4,-1530992060);l=md5_hh(l,o,n,m,p[g+4],11,1272893353);m=md5_hh(m,l,o,n,p[g+7],16,-155497632);n=md5_hh(n,m,l,o,p[g+10],23,-1094730640);o=md5_hh(o,n,m,l,p[g+13],4,681279174);l=md5_hh(l,o,n,m,p[g+0],11,-358537222);m=md5_hh(m,l,o,n,p[g+3],16,-722521979);n=md5_hh(n,m,l,o,p[g+6],23,76029189);o=md5_hh(o,n,m,l,p[g+9],4,-640364487);l=md5_hh(l,o,n,m,p[g+12],11,-421815835);m=md5_hh(m,l,o,n,p[g+15],16,530742520);n=md5_hh(n,m,l,o,p[g+2],23,-995338651);o=md5_ii(o,n,m,l,p[g+0],6,-198630844);l=md5_ii(l,o,n,m,p[g+7],10,1126891415);m=md5_ii(m,l,o,n,p[g+14],15,-1416354905);n=md5_ii(n,m,l,o,p[g+5],21,-57434055);o=md5_ii(o,n,m,l,p[g+12],6,1700485571);l=md5_ii(l,o,n,m,p[g+3],10,-1894986606);m=md5_ii(m,l,o,n,p[g+10],15,-1051523);n=md5_ii(n,m,l,o,p[g+1],21,-2054922799);o=md5_ii(o,n,m,l,p[g+8],6,1873313359);l=md5_ii(l,o,n,m,p[g+15],10,-30611744);m=md5_ii(m,l,o,n,p[g+6],15,-1560198380);n=md5_ii(n,m,l,o,p[g+13],21,1309151649);o=md5_ii(o,n,m,l,p[g+4],6,-145523070);l=md5_ii(l,o,n,m,p[g+11],10,-1120210379);m=md5_ii(m,l,o,n,p[g+2],15,718787259);n=md5_ii(n,m,l,o,p[g+9],21,-343485551);o=safe_add(o,j);n=safe_add(n,h);m=safe_add(m,f);l=safe_add(l,e)}
3603
return Array(o,n,m,l)}
3604
function md5_cmn(h,e,d,c,g,f){return safe_add(bit_rol(safe_add(safe_add(e,h),safe_add(c,f)),g),d)}
3605
function md5_ff(g,f,k,j,e,i,h){return md5_cmn((f&k)|((~f)&j),g,f,e,i,h)}
3606
function md5_gg(g,f,k,j,e,i,h){return md5_cmn((f&j)|(k&(~j)),g,f,e,i,h)}
3607
function md5_hh(g,f,k,j,e,i,h){return md5_cmn(f^k^j,g,f,e,i,h)}
3608
function md5_ii(g,f,k,j,e,i,h){return md5_cmn(k^(f|(~j)),g,f,e,i,h)}
3609
function safe_add(a,d){var c=(a&65535)+(d&65535);var b=(a>>16)+(d>>16)+(c>>16);return(b<<16)|(c&65535)}
3610
function bit_rol(a,b){return(a<<b)|(a>>>(32-b))};
3611
var p = new panel("Last.fm & Wikipedia Biography", ["metadb", "lastfm_wiki", "remap"]);
3612
var b = new buttons();
3613
var t = new text(0, -20, p.w - 0);
3614
3615
b.buttons.lastfm = new button(p.w - 56, 8, 50, 18, {normal: ""}, function() { p.browser(""); }, "");
3616
3617
on_item_focus_change();
3618
3619
function on_size() {
3620
	p.size();
3621
	t.w = p.w - 1;
3622
	t.h = p.h + 34;
3623
	t.size();
3624
}
3625
3626
function on_paint(gr) {
3627
	p.draw_background(gr);
3628
	//p.header(gr, p.artist);
3629
	lastfm.x = t.source == "last.fm" ? p.w -56 : -100;
3630
	b.draw(gr);
3631
	if (!p.metadb) p.centre_text(gr, "", p.normal_font, p.textcolour, 0, 0, t.w, t.h);
3632
	else t.draw(gr);
3633
}
3634
3635
function on_metadb_changed() {
3636
	if (t.metadb_changed()) {
3637
		//b.buttons.lastfm.func = function() { p.browser(t.url); };
3638
		//b.buttons.lastfm.tiptext = t.url;
3639
	}
3640
}
3641
3642
function on_mouse_wheel(step) {
3643
	t.wheel(step);
3644
}
3645
3646
function on_mouse_move(x, y) {
3647
	p.move(x, y);
3648
	if (b.move(x, y)) return;
3649
	if (t.move(x, y)) return;
3650
}
3651
3652
function on_mouse_lbtn_up(x, y) {
3653
	if (b.lbtn_up(x, y)) return;
3654
	if (t.lbtn_up(x, y)) return;
3655
}