/*
you can use fb.trace(something) to output variables/strings/properties to the Console
and it will be used in some examples below.
any parameters in [] are optional and may be omitted.

any undocumented references to "handle" mean a single playlist item like
fb.GetFocusItem();
fb.GetNowPlaying();
plman.GetPlaylistItems(plman.ActivePlaylist).Item(0) //first item in active playlist

any undocumented references to "handle_list" means multiple playlist or library items like
plman.GetPlaylistSelectedItems(plman.ActivePlaylist);
fb.GetLibraryItems();
*/

interface IFbUtils {
	Properties:
		fb.AlwaysOnTop; (boolean) (read, write)
		/*
		example:
		fb.AlwaysOnTop = !fb.AlwaysOnTop; // toggles the current value.
		*/
		
		fb.ComponentPath; (string) (read)
		/* 
		example:
		fb.trace(fb.ComponentPath); // C:\Users\User\AppData\Roaming\foobar2000\user-components\foo_jscript_panel\
		*/
		
		fb.CursorFollowPlayback; (boolean) (read, write)

		fb.FoobarPath; (string) (read)

		fb.IsPaused; (boolean) (read)

		fb.IsPlaying; (boolean) (read)

		fb.PlaybackLength; (double) (read)
		/*
		example1:
		fb.trace(fb.PlaybackLength); // 322.843414966166
		example2:
		fb.trace(Math.round(fb.PlaybackLength)); // 323
		*/

		fb.PlaybackFollowCursor; (boolean) (read, write)

		fb.PlaybackTime; (double) (read, write)
		/*
		example:
		fb.PlaybackTime = 60; // jumps to the 1 minute mark.
		*/

		fb.ProfilePath; (string) (read)

		fb.ReplaygainMode; (uint) (read, write)
		/*
		0 None
		1 Track
		2 Album
		3 Track/Album by Playback Order (only available in foobar2000 v1.3.8 and above)
		*/

		fb.StopAfterCurrent; (boolean) (read, write)
		/*
		example:
		fb.StopAfterCurrent = !fb.StopAfterCurrent; // toggles the current value.
		*/

		fb.Volume; (float) (read, write);
		/*
		example:
		fb.Volume = 0; // sets the volume to max. -100 is the minimum.
		*/

	Methods:
		fb.AcquireUiSelectionHolder(); (IFbUiSelectionHolder)

			interface IFbUiSelectionHolder {
				Methods:
					Dispose(); (void)
					SetSelection(handle_list); (void)
					// sets the selected items.

					SetPlaylistSelectionTracking(); (void)
					/*
					sets selected items to playlist selection and enables tracking.
					when the playlist selection changes, the stored selection is automatically
					updated. tracking ends when a set method is called on any ui_selection_holder
					or when the last reference to this ui_selection_holder is released.
					*/

					SetPlaylistTracking(); (void)
					/*
					sets selected items to playlist selection and enables tracking.
					when the playlist selection changes, the stored selection is automatically
					updated. tracking ends when a set method is called on any ui_selection_holder
					or when the last reference to this ui_selection_holder is released.
					*/
			}

			/*
			this is typically used to update the selection used by the default UI artwork panel
			or any other panel that makes use of the preferences under
			File>Preferences>Display>Selection viewers. Use in conjunction with the on_focus
			callback (see callbacks.txt).

			example1: (for playlist viewers)

			var selection_holder = fb.AcquireUiSelectionHolder();
			selection_holder.SetPlaylistSelectionTracking();

			function on_focus(is_focused) {
				if (is_focused) { //updates the selection when panel regains focus
					selection_holder = fb.AcquireUiSelectionHolder();
					selection_holder.SetPlaylistSelectionTracking();
				}
			}

			example2: (for library viewers)

			var selection_holder = fb.AcquireUiSelectionHolder();
			var handle_list = null;

			function on_mouse_lbtn_down(x, y) { //presumably going to select something here...
				handle_list = ...;
				selection_holder.SetSelection(handle_list); //must be a valid handle list
			}

			function on_focus(is_focused) {
				if (is_focused) { //updates the selection when panel regains focus
					selection_holder = fb.AcquireUiSelectionHolder();
					if (handle_list && handle_list.Count)
						selection_holder.SetSelection(handle_list); //must be a valid handle list
				}
			}
			*/

		fb.AddDirectory(); (void)

		fb.AddFiles(); (void)

		fb.ClearPlaylist(); (void)
		/*
		clears active playlist.
		if you wish to clear a specific playlist, use plman.ClearPlaylist(playlistIndex).
		*/

		fb.CreateContextMenuManager(); (IContextMenuManager)

			interface IContextMenuManager {
				// see samples\basic\MainMenuManager All-In-One, samples\basic\Menu Sample.txt

				Methods:
					BuildMenu(IMenuObj, base_id, max_id); (void)

					Dispose(); (void)

					ExecuteByID(id); (boolean)

					InitContext(handle_or_handle_list); (void)

					InitNowPlaying(); (void)
			}

		fb.CreateHandleList(); (IFbMetadbHandleList)
		// returns an empty handle list
		
		fb.CreateMainMenuManager(); (IMainMenuManager)

			interface IMainMenuManager {
				// see samples\basic\MainMenuManager All-In-One, samples\basic\Menu Sample.txt

				Methods:
					BuildMenu(IMenuObj, base_id, count); (void)

					Dispose(); (void)

					ExecuteByID(id); (boolean)

					Init(root_name); (void)
			}

		fb.CreateProfiler([name]); (IFbProfiler)

			interface IFbProfiler {
				Properties:
					Time; (int) // milliseconds

				Methods:
					Reset(); (void)
					Print(); (void)
				}

				/*
				example:
				var test = fb.CreateProfiler();
				// do something very time consuming
				fb.trace(test.Time);
				*/

		fb.Exit(); (void)

		fb.GetFocusItem([force]); (IFbMetadbHandle)
		/*
		force: boolean, default true.
		when true, it will use the first item of the active playlist if it is unable to get the focus item.
		*/

		fb.GetLibraryItems(); (IFbMetadbHandleList)
		// returns all Media Library items as a handle list.

		fb.GetLibraryRelativePath(handle); (string)
		/*
		returns an empty string when used on track not in Media Library

		example:
		the foobar2000 Media Library is configured to watch "D:\Music" and the
		path of the now playing item is "D:\Music\Albums\Artist\Some Album\Some Song.flac"

		var handle = fb.GetNowPlaying();
		fb.trace(fb.GetLibraryRelativePath(handle)); // Albums\Artist\Some Album\Some Song.flac

		NOTE: If you intend to loop through a large handle list and you have no control over what is
		happening in the background, consider using try/catch as errors may occur if the Media Library
		settings/content changes.
		*/

		fb.GetNowPlaying(); (IFbMetadbHandle)
		// get handle of now playing item.

		fb.GetQueryItems(handle_list, query); (IFbMetadbHandleList)
		/*
		example1:
		var a = fb.GetQueryItems(plman.GetPlaylistItems(plman.ActivePlaylist), "rating IS 5");
		example2:
		var b = fb.GetQueryItems(fb.GetLibraryItems(), "rating IS 5");
		results are unsorted

		NOTE: use try/catch to handle invalid queries. An empty handle list will be returned if the query
		is valid but there are no results.
		*/

		fb.GetSelection(); (IFbMetadbHandle)
		/*
		gets now playing or selected item. what you get will depend on
		"File>Preferences>Display>Selection viewers".
		the return value may be null.
		*/

		fb.GetSelections([flags]); (IFbMetadbHandleList)
		/*
		flags: 0 default, 1 no now playing
		works like GetSelection(), but returns a handle list.
		always returns a valid handle list instance instead of null.
		*/

		fb.GetSelectionType(); (uint)
		/*
		retrieve what the selection is
		0 undefined (no item)
		1 active_playlist_selection
		2 caller_active_playlist
		3 playlist_manager
		4 now_playing
		5 keyboard_shortcut_list
		6 media_library_viewer
		*/

		fb.IsLibraryEnabled(); (boolean)

		fb.IsMetadbInMediaLibrary(handle); (boolean)
		/*
		example:
		var np = fb.GetNowplaying();
		fb.trace(fb.IsMetadbInMediaLibrary(np)); // if false, playing track is not in Media Library
		*/

		fb.LoadPlaylist(); (void)

		fb.Next(); (void)

		fb.Pause(); (void)

		fb.Play(); (void)

		fb.PlayOrPause(); (void)

		fb.Stop(); (void)

		fb.Prev(); (void)

		fb.Random(); (void)

		fb.RunContextCommand(command[, flags]); (boolean)
		/*
		now playing file only.
		flags:
		0 default (depends on whether SHIFT key is pressed, flag_view_reduced or flag_view_full is selected)
		4 flag_view_reduced
		8 flag_view_full. this can be useful if you need to run context commands the user may have hidden
		using File>Preferences>Display>Context Menu
		example:
		fb.RunContextCommand("Properties");
		*/

		fb.RunContextCommandWithMetadb(command, handle_or_handle_list[, flags]); (boolean)
		// same flags as fb.RunContextCommand. handle_or_handle_list can be something like fb.GetFocusItem()
		// or plman.GetPlaylistSelectedItems(plman.ActivePlaylist)

		fb.RunMainMenuCommand(command); (boolean)
		/*
		example:
		fb.RunMainMenuCommand("File/Add Location...");
		*/
		
		fb.SavePlaylist(); (void)

		fb.ShowConsole(); (void)

		fb.ShowLibrarySearchUI(query); (void)
		// opens the Library>Search window populated with the query you set.

		fb.ShowPopupMessage(msg[, title][, iconid]); (void)
		/*
		title: default "JScript Panel"
		iconid: default 0 see flags.txt > IconId
		*/

		fb.ShowPreferences(); (void)

		fb.TitleFormat(expression); (IFbTitleFormat)

			interface IFbTitleFormat {
				/*
				this will be used in the examples below.
				var tfo = fb.TitleFormat("%artist%");
				*/

				Methods:
					Dispose();
					// example: tfo.Dispose();

					Eval([force]);
					/*
					force: boolean, default false.
						if true, you can process text that doesn't contain
						title formatting even when foobar2000 isn't playing. when playing, you
						should always get a result.

					always use Eval when you want dynamic info such as %playback_time%, %bitrate% etc.
					EvalWithMetadb(fb.GetNowplaying()) will not give the results you want.
					example:
					fb.trace(tfo.Eval());
					*/

					EvalWithMetadb(handle);
					/*
					example:
					fb.trace(tfo.EvalWithMetadb(fb.GetFocusItem()));
					*/
			}

		fb.Trace(message); (void)

		fb.VolumeDown(); (void)

		fb.VolumeMute(); (void)

		fb.VolumeUp(); (void)
}

interface IGdiUtils {
	Methods:
		gdi.CreateImage(w, h); (IGdiBitmap)

		gdi.Font(name, size_px[, style]); (IGdiFont)
		/*
		size_px: see helpers.txt > Point2Pixel function for conversions.
		style: default 0. see flags.txt > FontStyle
		*/

		gdi.Image(path); (IGdiBitmap)
		/*
		example: var img = "e:\\images folder\\my_image.png";
		note that you must escape backslashes whenever they are in quotes.
		*/

		gdi.LoadImageAsync(window_id, path); (uint)
		/*
		window_id: window.ID
		returns a unqiue id. see samples\basic\LoadImageAsync.txt
		*/
}

interface IFbPlaylistManager {
	Properties:
		plman.ActivePlaylist; (int) (read, write)
		/*
		example1: 
		fb.trace(plman.ActivePlaylist); // returns -1 if there is no active playlist
		example2:
		plman.ActivePlaylist = 1; // switches to 2nd playlist.
		*/

		plman.PlaybackOrder; (uint) (read, write)
		/*
		0 Default
		1 Repeat (Playlist)
		2 Repeat (Track)
		3 Random
		4 Shuffle (tracks)
		5 Shuffle (albums)
		6 Shuffle (folders)
		see samples\basic\PBOButton(Menu).txt.
		*/

		plman.PlayingPlaylist; (int) (read, write)
		/*
		example1: 
		fb.trace(plman.PlayingPlaylist); // returns -1 if there is no playing playlist
		*/

		plman.PlaylistCount; (uint) (read)

		plman.PlaylistItemCount(playlistIndex); (uint) (read)
		/*
		example:
		fb.trace(plman.PlaylistItemCount(plman.PlayingPlaylist)); // 12
		*/

		plman.PlaylistRecyclerManager; (IFbPlaylistRecyclerManager) (read)

			interface PlaylistRecyclerManager {
				Properties:
					Count; (uint) (read)
					Name(index); (string) (read)
					Content(index); (IFbMetadbHandleList) (read)

				Methods:
					Purge(affectedItems); (void)
					// affectedItems: array like [1, 3, 5]

					Restore(index); (void)
			}

	Methods:
		// in all these methods, playlistIndex is the target playlist

		plman.AddLocations(playlistIndex, paths[, select]); (void)
		/*
		paths: an array of files/URLs
		select: boolean, default false
		example:
		plman.AddLocations(plman.ActivePlaylist, ["e:\\1.mp3"]);
		note that this operation is asynchronous and may take some time to complete if it's a large array
		any code directly after this in your script will run immediately without waiting for this to finish
		*/

		plman.ClearPlaylist(playlistIndex); (void)
		/*
		example:
		plman.ClearPlaylist(0);
		*/

		plman.ClearPlaylistSelection(playlistIndex); (void)
		/*
		example:
		plman.ClearPlaylistSelection(plman.ActivePlaylist);
		*/

		plman.CreateAutoPlaylist(playlistIndex, name, query[, sort][, flags]); (int)
		/*
		name: a name for the new Autplaylist
		query: a valid query
		sort: default "". title formatting pattern.
		flags: default 0, use 1 to force sort
		returns -1 on failure
		*/

		plman.CreatePlaylist(playlistIndex, name); (uint)
		/*
		example1:
		plman.CreatePlaylist(0, "");
		creates a new playlist first in the list and it will be named "New playlist"
		numbers will be appended to the end for each new un-named playlist
		example2:
		plman.CreatePlaylist(plman.PlaylistCount, "my favourites");
		this will be added at the end of the current playlists
		*/

		plman.DuplicatePlaylist(playlistIndex, name); (uint)
		/*
		name: a name for the new playlist. if name is "", name of source playlist is used.
		duplicate playlist gets inserted directly after the source playlistIndex.
		this duplicates playlist content, not the properties of the playlist eg. Autoplaylist
		*/

		plman.EnsurePlaylistItemVisible(playlistIndex, playlistItemIndex); (void)

		plman.ExecutePlaylistDefaultAction(playlistIndex, playlistItemIndex); (boolean)
		// starts playback by executing default doubleclick/enter action unless overridden by a lock to do something else.

		plman.GetPlayingItemLocation(); (IFbPlayingItemLocation)
		/*
		retrieves playlist position of currently playing item.
		on failure, the property "IsValid" of IFbPlayingItemLocation interface will be set to false.
		*/

			interface IFbPlayingItemLocation {
				Properties:
					IsValid; (boolean) (read)
					PlaylistIndex; (uint) (read)
					PlaylistItemIndex; (uint) (read)
			}

			/*
			example:
			var playing_item_location = plman.GetPlayingItemLocation();
			if (playing_item_location.IsValid) {
				fb.trace(playing_item_location.PlaylistIndex);
				fb.trace(playing_item_location.PlaylistItemIndex);
			}
			*/

		plman.GetPlaylistFocusItemIndex(playlistIndex); (int)
		/*
		example:
		var focus_item_index = plman.GetPlaylistFocusItemIndex(plman.ActivePlaylist); // 0 first item
		returns -1 if nothing is selected
		*/

		plman.GetPlaylistItems(playlistIndex); (IFbMetadbHandleList)
		/*
		example:
		var handle_list = plman.GetPlaylistItems(plman.PlayingPlaylist);
		*/
		
		plman.GetPlaylistName(playlistIndex); (string)
		/*
		example:
		fb.trace(plman.GetPlaylistName(plman.ActivePlaylist));
		*/

		plman.GetPlaylistSelectedItems(playlistIndex); (IFbMetadbHandleList)
		/*
		example:
		var selected_items = plman.GetPlaylistSelectedItems(plman.ActivePlaylist);
		*/

		plman.InsertPlaylistItems(playlistIndex, base, handle_list[, select]); (void)
		/*
		base: position in playlist
		handle_list: items to insert
		select: boolean, default false.

		example1:
		var ap = plman.ActivePlaylist;
		plman.InsertPlaylistItems(ap, 0, fb.GetLibraryItems());
		adds all library tracks to beginning of playlist.

		example2:
		var ap = plman.ActivePlaylist;
		plman.InsertPlaylistItems(ap, plman.PlaylistItemCount(ap), fb.GetLibraryItems());
		adds all library tracks to end of playlist.
		*/

		plman.InsertPlaylistItemsFilter(playlistIndex, base, handle_list[, select]); (void)
		/*
		same as above except any duplicates contained in handle_list are removed.
		*/

		plman.IsAutoPlaylist(playlistIndex); (boolean)

		plman.IsPlaylistItemSelected(playlistIndex, playlistItemIndex); (boolean)

		plman.IsPlaylistLocked(playlistIndex); (boolean)
		/*
		note: this returns true if the playlist is an autoplaylist.
		to determine if a playlist is not an autoplaylist but locked with something like foo_utils or
		foo_playlist_attributes, do something like...

		if (!plman.IsAutoPlaylist(plman.ActivePlaylist) && plman.IsPlaylistLocked(plman.ActivePlaylist)) {
			blah();
		}
		*/

		plman.MovePlaylist(from, to); (boolean)

		plman.MovePlaylistSelection(playlistIndex, delta); (boolean)
		/*
		example:
		plman.MovePlaylistSelection(plman.ActivePlaylist, plman.PlaylistItemCount(plman.ActivePlaylist));
		moves selected items to end of playlist.
		*/

		plman.RemovePlaylist(playlistIndex); (boolean)

		plman.RemovePlaylistSelection(playlistIndex[, crop]); (void)
		/*
		crop: boolean, default false.
		example1:
		plman.RemovePlaylistSelection(plman.ActivePlaylist);
		removes selected items from playlist.
		example2:
		plman.RemovePlaylistSelection(plman.ActivePlaylist, true);
		removes items that are NOT selected.
		*/

		plman.RenamePlaylist(playlistIndex, name); (boolean)

		plman.SetActivePlaylistContext(); (void)
		// workaround so you can run fb.RunMainMenuCommand() on the Edit menu when the playlist doesn't have focus.

		plman.SetPlaylistFocusItem(playlistIndex, playlistItemIndex); (void)
		/*
		example:
		plman.SetPlaylistFocusItem(plman.ActivePlaylist, 0);
		*/

		plman.SetPlaylistFocusItemByHandle(playlistIndex, handle); (void)
		/*
		example:
		var ap = plman.ActivePlaylist;
		var handle = plman.GetPlaylistItems(ap).Item(1); //2nd item in playlist
		plman.SetPlaylistFocusItemByHandle(ap, handle);
		*/

		plman.SetPlaylistSelection(playlistIndex, affectedItems, state); (void)
		/*
		affectedItems: array of item indexes.
		state: boolean.
		example:
		plman.SetPlaylistSelection(plman.ActivePlaylist, [0, 2, 4], true);
		selects tracks first, third and fifth tracks in playlist.
		note this does not affect other selected items.
		*/

		plman.SetPlaylistSelectionSingle(playlistIndex, playlistItemIndex, state); (void)
		/*
		state: boolean
		example1:
		plman.SetPlaylistSelectionSingle(plman.ActivePlaylist, 0, false);
		deselects first playlist item. obviously only works when it is already selected.
		example2:
		var ap = plman.ActivePlaylist;
		plman.SetPlaylistSelectionSingle(ap, plman.PlaylistItemCount(ap) - 1, true);
		selects last item in playlist.
		note this does not affect other selected items.
		*/

		plman.ShowAutoPlaylistUI(playlistIndex); (boolean)
		/*
		shows popup window letting you edit certain Autoplaylist properties.
		example: fb.ShowAutoPlaylistUI(plman.ActivePlaylist);
		before using, check if your playlist is an Autoplaylist by using plman.IsAutoPlaylist(...);
		*/

		plman.SortByFormat(playlistIndex, pattern[, selected_items_only]); (boolean)
		/*
		playlistIndex: index of playlist to alter.
		pattern: title formatting pattern to sort by. set to "" to randomise the order of items.
		selected_items_only: boolean, default false.
		returns true on success, false on failure (playlist locked etc).
		*/

		plman.SortByFormatV2(playlistIndex, pattern[, direction]); (boolean)
		/*
		pattern: title formatting pattern
		direction: default 1 (ascending), -1 (descending).
		*/

		plman.UndoBackup(playlistIndex); (void)
		// call before using other plman methods that add/remove/reorder playlist items so a history will be usable from the Edit menu.

	Queue Methods:
		plman.AddItemToPlaybackQueue(handle); (void)

		plman.AddPlaylistItemToPlaybackQueue(playlistIndex, playlistItemIndex); (void)

		plman.CreatePlaybackQueueItem(); (IFbPlaybackQueueItem)

			interface IFbPlaybackQueueItem {
				Properties:
					Handle; (IFbMetadbHandle) (read, write)
					PlaylistIndex; (uint) (read, write)
					PlaylistItemIndex; (uint) (read, write)

				Methods:
					Equals(item); (boolean)
					// item: an instance of IFbPlaybackQueueItem.

					Dispose(); (void)
			}

		plman.FindPlaybackQueueItemIndex(handle, playlistIndex, playlistItemIndex); (int)
		// returns -1 on failure

		plman.FlushPlaybackQueue(); (void)

		plman.GetPlaybackQueueContents(); (VBArray)
		// returns a VBArray so you need to use .toArray() on the result.

		plman.GetPlaybackQueueCount(); (uint)

		plman.IsPlaybackQueueActive(); (boolean)

		plman.RemoveItemFromPlaybackQueue(index); (void)

		plman.RemoveItemsFromPlaybackQueue(affectedItems); (void)
		// affectedItems: array like [1, 3, 5]
}

interface IJSUtils {
	Properties:
		utils.Version (uint) (read)
		/* new in v1.2.0. returns a 4 digit number corresponding to the version

		v1.2.0 -> 1200
		v1.2.1 -> 1210

		If you try and access this in older components where it doesn't exist, the script will
		crash so you can do a check like this.

		if (!("Version" in utils)) {
			fb.ShowPopupMessage("Current component version is less than v1.2.0. This script requires vX.X.X");
		} else {
			//check the actual version
		}
		*/

	Methods:
		utils.CheckComponent(name[, is_dll]); (boolean)
		/*
		is_dll: boolean, default false.
		if true, method checks filename as well as the internal name.
		example:
		fb.trace(utils.CheckComponent("foo_playcount", true));
		*/

		utils.CheckFont(name); (boolean)
		// name: can be either in English or the localised name in your OS.

		utils.ColorPicker(window_id, default_colour); (int)
		/*
		uses a windows popup dialog to assign a colour to a variable
		window_id: window.ID
		example:
		var colour = utils.ColorPicker(window.ID, RGB(255, 0, 0)); // see docs\helpers.txt for RGB function
		*/

		utils.FileTest(path, mode); (VARIANT)
		/*
		mode:
		"chardet"
		guess the charset of a file and return the codepage. it may not be accurate and returns 0 if an error occurred.
		"e"
		if path exists, return true
		"s"
		retrieve file size, in bytes
		"d"
		if path is a directory, return true
		"split"
		returns a VBArray so you need to use .toArray() on the result.
		example:
		var arr = utils.FileTest("D:\\Somdir\\Somefile.txt", "split").toArray();
		arr[0] <= "D:\\Somedir\\" (always includes backslash at the end)
		arr[1] <= "Somefile"
		arr[2] <= ".txt"
		*/

		utils.FormatDuration(seconds) (string)
		/*
		example:
		fb.trace(utils.FormatDuration(plman.GetPlaylistItems(plman.ActivePlaylist).CalcTotalDuration())); // 1wk 1d 17:25:30
		*/

		utils.FormatFileSize(bytes) (string)
		/*
		example:
		fb.trace(utils.FormatFileSize(plman.GetPlaylistItems(plman.ActivePlaylist).CalcTotalSize())); // 7.9 GB
		*/

		utils.GetAlbumArtAsync(window_id, handle[, art_id][, need_stub][, only_embed][, no_load]); (uint)
		/*
		window_id: window.ID
		art_id: default 0. see flags.txt > AlbumArtId
		need_stub: boolean, default true
		only_embed: boolean, default false
		no_load: boolean, default false. if true, "image" parameter will be null in on_get_album_art_done callback.
		see samples\basic\GetAlbumArtAsync.txt
		*/

		utils.GetAlbumArtEmbedded(rawpath[, art_id]); (IGdiBitmap)
		/*
		art_id: default 0. see flags.txt > AlbumArtId
		example:
		var img = utils.GetAlbumArtEmbedded(fb.GetNowPlaying().RawPath, 0);
		*/

		utils.GetAlbumArtV2(handle[, art_id][, need_stub]); (IGdiBitmap)
		/*
		art_id: default 0. see flags.txt > AlbumArtId
		need stub: boolean, default true.
		see samples\basic\GetAlbumArtV2.txt
		*/

		utils.GetSysColor(index); (uint)
		/*
		index: http://msdn.microsoft.com/en-us/library/ms724371%28VS.85%29.aspx
		example: var splitter_colour = utils.GetSysColor(15);
		returns 0 if failed.
		*/

		utils.GetSystemMetrics(index); (int)
		/*
		index: http://msdn.microsoft.com/en-us/library/ms724385%28VS.85%29.aspx
		returns 0 if failed.
		*/

		utils.Glob(pattern[, exc_mask][, inc_mask]); (VBArray)
		/*
		exc_mask: default FILE_ATTRIBUTE_DIRECTORY, see flags.txt > Used in utils.Glob()
		inc_mask: default 0xffffffff
		returns a VBArray so you need to use .toArray() on the result.
		example:
		var arr = utils.Glob("C:\\*.*").toArray();
		*/

		utils.IsKeyPressed(vkey); (boolean)
		/*
		vkey: http://msdn.microsoft.com/en-us/library/ms927178.aspx
		some are defined in flags.txt > Used with utils.IsKeyPressed()
		*/

		utils.MapString(text, lcid, flags); (string)

		utils.PathWildcardMatch(pattern, str); (boolean)
		// using Microsoft MS-DOS wildcards match type. eg "*.txt", "abc?.tx?"

		utils.ReadTextFile(filename[,codepage]); (string)
		/*
		codepage: default 0 see codepages.txt
		if codepage is 0, text file can be either UTF-16 with BOM or UTF-8 with BOM and ANSI.
		example:
		var text = utils.ReadTextFile("E:\\some text file.txt");
		*/

		utils.ReadINI(filename, section, key[, defaultval]); (string)
		/*
		an INI file should like this:

		[section]
		key=val

		this only returns up to 255 characters per value.
		example:
		var username = utils.ReadINI("e:\\my_file.ini", "Last.fm", "username");
		*/

		utils.WriteINI(filename, section, key, val); (boolean)
		/*
		example:
		utils.WriteINI("e:\\my_file.ini", "Last.fm", "username", "Bob");
		*/
}

interface IFbWindow {
	Properties:
		window.DlgCode(); (int) (read, write)
		/*
		see flags.txt > With window.DlgCode
		example: 
		window.DlgCode(DLGC_WANTALLKEYS);
		*/

		window.ID; (read) (int)
		// required in utils.ColorPicker, utils.GetAlbumArtAsync, utils.LoadImageAsync

		window.InstanceType; (int)
		/*
		returns 0 if using Columns UI, 1 if using default UI.
		you need this to determine which GetFontXXX and GetColorXXX methods to use, assuming you want to support both interfaces.
		*/
		
		window.IsTransparent; (boolean) (read)
		/*
		depends on setting inside JScript Panel Configuration window. you generally use it to determine
		whether or not to draw a background. only useful within Panel Stack Splitter (Columns UI component)
		*/

		window.IsVisible; (boolean) (read)

		window.Height; (int) (read)

		window.MaxHeight; (int) (read, write)
		window.MaxWidth; (int) (read, write)
		window.MinHeight; (int) (read, write)
		window.MinWidth; (int) (read, write)
		// the previous 4 methods can be used to lock the panel size. do not use if panels are contained within Panel Stack Splitter (Columns UI component).

		window.Width; (int) (read)

	Methods:
		window.ClearTimeout(timerID); (void)
		window.ClearInterval(timerID); (void)
		window.SetInterval(func, delay); (uint)
		window.SetTimeout(func, delay); (uint)
		// for the previous 4 methods, see samples\basic\timers.txt

		window.CreatePopupMenu(); (IMenuObj)

			interface IMenuObj {
				// see samples\basic\MainMenuManager All-In-One, samples\basic\Menu Sample.txt

				Properties:
					ID; (uint) (read)

				Methods:
					AppendMenuItem(flags, item_id, text); (void)
					/*
					flags: see flags.txt > Used in AppendMenuItem()
					item_id: integer greater than 0. each menu item needs a unique id.
					*/

					AppendMenuSeparator(); (void)

					AppendTo(parentMenu, flags, text); (void)

					CheckMenuItem(id_or_pos, check[, bypos]); (void)
					/*
					check: boolean.
					bypos: boolean, default false.
					*/

					CheckMenuRadioItem(first, last, check[, bypos]); (void)
					/*
					check: boolean.
					bypos: boolean, default false.
					*/

					Dispose(); (void)

					EnableMenuItem(id_or_pos, enable[, bypos]); (void)
					/*
					enable: boolean.
					bypos: boolean, default false
					*/

					TrackPopupMenu(x, y[, flags]); (int)
					// flags: default 0, see flags.txt > Used in TrackPopupMenu()
			}

		window.CreateThemeManager(class_list); (IThemeManager)
		/*
		class_list: http://msdn.microsoft.com/en-us/library/bb773210%28VS.85%29.aspx
		see samples\basic\SimpleThemedButton.txt, samples\basic\Themed Seek Bar.txt
		*/

			interface IThemeManager {
				// see samples\basic\SimpleThemedButton, samples\basic\Themed Seekbar.txt

				Methods:
					DrawThemeBackground(IGdiGraphics, x, y, w, h[, clip_x][, clip_y][, clip_w][, clip_h]); (void)
					// clip_x, clip_y, clip_w, clip_h: defaults to 0 if omitted

					IsThemePartDefined(partid); (boolean)
					SetPartAndStateID(partid, stateid); (void)
					// partid, stateid: http://msdn.microsoft.com/en-us/library/bb773210%28VS.85%29.aspx
			}

		window.CreateTooltip([font_name][, font_size_px][, font_style]); (IFbTooltip)
		/*
		font_name: default "Segoe UI"
		font_size_px: default 12
		font_style: default 0, see flags.txt > FontStyle
		*/

			interface IFbTooltip {
				/*
				this will be used in the examples below.
				var tooltip = window.CreateTooltip();
				*/

				Properties:
					Text; (string) (read, write)
					/*
					example:
					tooltip.Text = "Whoop";
					*/

					TrackActivate; (boolean) (write)

				Methods:
					Activate(); (void)
					/*
					only do this when text has changed otherwise it will flicker
					example:
					var text = "...";
					if (tooltip.Text != text) {
						tooltip.Text = text;
						tooltip.Activate();
					}
					*/

					Deactivate(); (void)

					Dispose(); (void)

					GetDelayTime(type); (int)
					SetDelayTime(type, time); (void)
					// type: see flags.txt > Used in IFbTooltip.GetDelayTime() and IFbTooltip.SetDelayTime()

					SetMaxWidth(width); (void)
					/*
					use if you want multi-line tooltips
					example:
					tooltip.SetMaxWidth(800);
					tooltip.Text = "Line1\nLine2";
					use \n as a new line separator
					*/

					TrackPosition(x, y); (void)
					// check x, y positions have changed from last time otherwise it will flicker
			}

		window.GetBackgroundImage(); (IGdiBitmap)
		// retrieve pseudo-transparent background image.

		window.GetColorCUI(type[, client_guid]); (uint)
		window.GetColorDUI(type); (uint)

		window.GetFontCUI(type[, client_guid]); (IGdiFont)
		window.GetFontDUI(type); (IGdiFont)
		/*
		type: see flags.txt > Used in window.GetFontXXX()
		client_guid: default "" see flags.txt > Used in GetFontCUI() as client_guid
		note this returns null if the component was unable to determine your font.
		to avoid errors when trying to use the font or access its properties, you
		should use code something like this...

		var font = window.GetFontDUI(0);
		if (!font) {
			fb.trace("Unable to determine your default font. Using Segoe UI instead.");
			font = gdi.Font("Segoe UI", 12);
		}
		*/

		window.NotifyOthers(name, info); (void)
		/*
		name: string
		info: all variable/array/object types should be supported
		listen for notifications in other panels using on_notify_data(name, info) {}
		*/

		window.Reload(); (void)
		// reload panel

		window.Repaint([force]); (void)
		// force: boolean, default false

		window.RepaintRect(x, y, w, h[, force]); (void)
		/*
		force: boolean, default false

		use this instead of window.Repaint on frequently updated areas
		such as time, bitrate, seekbar, etc. this should reduce CPU usage.
		you can use Windows Task Manager to double check.
		*/

		window.SetCursor(id); (void)
		/*
		this would usually be used inside the on_mouse_move callback

		id: see flags.txt > Used in window.SetCursor()
		use -1 if you want to hide the cursor.
		*/

		window.GetProperty(name[, defaultval]); (VARIANT)
		/*
		name: string
		defaultval: string, number, boolean
		get value of name from properties.
		if no value is present, defaultval will be stored and returned
		*/

		window.SetProperty(name, val); (void)
		/*
		name: string
		val: string, number, boolean
		set property value, if val is invalid/null, it is removed
		property values will be saved per panel instance and are remembered between foobar2000 restarts.
		*/

		window.ShowConfigure(); (void)
		// show configuration window of current panel

		window.ShowProperties(); (void)
		// show properties window of current panel
}

interface IGdiFont {
	/*
	this will be used in the examples below.
	var my_font = window.GetFontDUI(0); // see flags.txt > FontTypeDUI
	*/

	Properties:
		Height (int) (read)
		/*
		example:
		fb.trace(my_font.Height); // 15
		*/

		Name (string) (read)
		/*
		example:
		fb.trace(my_font.Name); // Segoe UI
		*/

		Size (float) (read)
		/*
		example:
		fb.trace(my_font.Size); // 12
		*/

		Style (int) (read)
		/*
		example:
		fb.trace(my_font.Style);
		see flags.txt > FontStyle
		*/

	Methods:
		Dispose(); (void)
		/*
		example:
		my_font.Dispose();
		*/
}

interface IGdiBitmap {
	// see samples\basic\Apply Mask.txt, samples\basic\BoxBlur.txt, samples\basic\StackBlur.txt

	Properties:
		Height (int) (read)
		Width (int) (read)

	Methods:
		ApplyAlpha(alpha); (IGdiBitmap)
		// alpha: 0-255

		ApplyMask(img); (boolean)
		// changes will be saved in the current bitmap

		BoxBlur(radius[, iteration]); (void)
		/*
		radius: valid values 1-20
		iteration: default 1. valid values 1-20
		*/

		Clone(x, y, w, h); (IGdiBitmap)

		CreateRawBitmap(); (IGdiRawBitmap)
		// Create a DDB bitmap from IGdiBitmap, which is used in GdiDrawBitmap()

			interface IGdiRawBitmap {
				Properties:
					Width; (int) (read)
					Height; (int) (read)

				Methods:
					Dispose(); (void)
			}

		Dispose(); (void)

		GetColorScheme(max_count); (VBArray)
		// returns a VBArray so you need to use .toArray() on the result.

		GetGraphics();
		// don't forget to use ReleaseGraphics() after operations on IGdiGraphics interface is done.

		ReleaseGraphics(IGdiGraphics); (IGdiGraphics)

		Resize(w, h[, mode]); (IGdiBitmap)
		// mode: default 0. see flags.txt > InterpolationMode

		RotateFlip(mode); (void)
		// mode: see flags.txt > RotateFlipType

		SaveAs(path[, format]); (boolean)
		/*
		path: full path including file extension. the parent folder must already exist.
		format:
		"image/png" (default if omitted)
		"image/bmp"
		"image/jpeg"
		"image/gif"
		"image/tiff"

		example:
		var img = utils.GetAlbumArtEmbedded(fb.GetFocusItem().RawPath, 0);
		if (img)
			img.SaveAs("D:\\export.jpg", "image/jpeg");
		*/

		StackBlur(radius); (void)
		// radius: valid values 2-254
}

interface IGdiGraphics {
	/*
	typically used inside on_paint(gr)
	there are many different ways to get colours.
	use window.GetColorDUI/window.GetColourCUI.
	use RGB function from helpers.txt.
	use predefined colour from helpers.txt.
	and more...
	*/

	Methods:
		gr.CalcTextHeight(str, IGdiFont); (uint)
		// this will only calulate the text height of one line.

		gr.CalcTextWidth(str, IGdiFont); (uint)

		gr.DrawEllipse(x, y, w, h, line_width, color); (void)

		gr.DrawImage(IGdiBitmap, dstX, dstY, dstW, dstH, srcX, srcY, srcW, srcH[, angle][, alpha]); (void)
		/*
		angle: default 0
		alpha: default 255. valid values 0-255.
		*/

		gr.DrawLine(x1, y1, x2, y2, line_width, colour); (void)

		gr.DrawPolygon(color, line_width, points); (void)
		// points: a JScript array.

		gr.DrawString(str, IGdiFont, color, x, y, w, h[, flags]); (void)
		// flags: default 0. see flags.txt > StringFormatFlags

		gr.DrawRect(x, y, w, h, line_width, colour); (void)

		gr.DrawRoundRect(x, y, w, h, arc_width, arc_height, line_width, color); (void)

		gr.EstimateLineWrap(str, IGdiFont, max_width); (VBArray)
		/*
		returns a VBArray so you need to use .toArray() on the result.
		index | meaning
		[0] text line 1
		[1] width of text line 1 (in pixel)
		[2] text line 2
		[3] width of text line 2 (in pixel)
		...
		[2n + 2] text line n
		[2n + 3] width of text line n (px)
		*/

		gr.FillEllipse(x, y, w, h, colour); (void)

		gr.FillGradRect(x, y, w, h, angle, colour1, colour2[, focus]); (void)
		/*
		focus: default 1.0. valid values are between 0.0 and 1.0
		specify where the centred colour will be at its highest intensity.

		note: this may appear buggy depending on rectangle size. the easiest fix is
		to adjust the "angle" by a degree or two.
		*/

		gr.FillPolygon(colour, fillmode, points); (void)
		// fillmode: 0 alternate, 1 winding.
		// points: a JScript array.

		gr.FillRoundRect(x, y, w, h, arc_width, arc_height, colour); (void)

		gr.FillSolidRect(x, y, w, h, colour); (void)

		gr.GdiDrawBitmap(IGdiRawBitmap, dstX, dstY, dstW, dstH, srcX, srcY, srcW, srcH); (void)
		// always faster than DrawImage, does not support alpha channel.

		gr.GdiDrawText(str, IGdiFont, color, x, y, w, h[, format]); (VBArray)
		/*
		format: default 0. see flags.txt > DT_*

		returns a VBArray so you need to use .toArray() on the result.
		index | meaning
		[0] left   (DT_CALCRECT)
		[1] top    (DT_CALCRECT)
		[2] right  (DT_CALCRECT)
		[3] bottom (DT_CALCRECT)
		[4] characters drawn
		*/

		gr.MeasureString(str, IGdiFont, x, y, w, h[, flags]); (IMeasureStringInfo)
		// flags: default 0. see flags.txt > StringFormatFlags

			interface IMeasureStringInfo {
				Properties:
					chars; (int) (read)
					Height; (float) (read)
					lines; (int) (read)
					x; (float) (read)
					y; (float) (read)
					Width; (float) (read)

					/*
					example:
					// ==PREPROCESSOR==
					// @import "%fb2k_component_path%docs\flags.txt"
					// @import "%fb2k_component_path%docs\helpers.txt"
					// ==/PREPROCESSOR==

					var sf = StringFormat(StringAlignment.Near, StringAlignment.Near);
					var text = utils.ReadTextFile("z:\\info.txt");
					var font = window.GetFontDUI(0);

					function on_paint(gr) {
						gr.DrawString(text, font, RGB(255, 0, 0), 0, 0, window.Width, window.Height, sf);
						var temp = gr.MeasureString(text, font, 0, 0, window.Width, 10000, sf);
						// if we want to calculate height, we must set the height to be far larger than what
						// the text could possibly be.

						fb.trace(temp.Height); // 2761.2421875 // far larger than my panel height!
						fb.trace(temp.Chars); // 7967
					}
					*/
			}

		gr.SetInterpolationMode(mode); (void)
		// mode: default 0. see flags.txt > InterpolationMode

		gr.SetSmoothingMode(mode); (void)
		// mode: default 0. see flags.txt > SmoothingMode

		gr.SetTextRenderingHint(mode); (void)
		// mode: default 0. see flags.txt > TextRenderingHint

		gr.GdiAlphaBlend(IGdiRawBitmap, dstX, dstY, dstW, dstH, srcX, srcY, srcW, srcH[, alpha]); (void)
		// alpha: default 255. valid values 0-255.
}

interface IFbMetadbHandle {
	/*
	this will be used in the examples below.
	var handle = fb.GetFocusItem();
	*/

	Properties:
		Path; (string) (read)
		/*
		example:
		fb.trace(handle.Path); // D:\some song.flac
		*/

		RawPath; (string) (read)
		/*
		example:
		fb.trace(handle.RawPath); // file://D:\some song.flac
		*/

		SubSong; (int) (read)

		FileSize; (LONGLONG) (read)

		Length; (double) (read)

	Methods:
		Compare(handle); (boolean)
		/*
		compare two IFbMetadbHandle instances, pointer only.
		example:
		handle.Compare(handle2);
		if you want to compare them physically, use the "RawPath" property.
		*/

		Dispose(); (void)
		/*
		example:
		handle.Dispose();
		*/

		GetFileInfo(); (IFbFileInfo)

			interface IFbFileInfo {
				/*
				this will be used in the examples below.
				var handle = fb.GetFocusItem();
				var f = handle.GetFileInfo();
				*/

				Properties:
					MetaCount; (uint) (read)
					/*
					example:
					fb.trace(f.MetaCount); // 11
					*/

					InfoCount; (uint) (read)
					/*
					example:
					fb.trace(f.InfoCount); // 9
					*/

				Methods:
					Dispose(); (void)
					/*
					example:
					f.Dispose();
					*/

					InfoFind(name); (int)
					// returns -1 on failure

					InfoName(idx); (string)

					InfoValue(idx); (string)

					MetaFind(name); (int)
					// returns -1 on failure

					MetaName(idx); (string)
					/*
					the case of the tag name returned can be different depending on tag type
					so using toLowerCase() or toUpperCase() on the result is recommended
					
					example:
					for (var i = 0; i < f.MetaCount; i++) {
						fb.trace(f.MetaName(i).toUpperCase());
					}
					*/

					MetaValue(idx, vidx); (string)

					MetaValueCount(idx); (uint)
			}
}

interface IFbMetadbHandleList {
	/*
	this will be used in the examples below.
	var handle_list = plman.GetPlaylistItems(plman.ActivePlaylist);
	if you want an empty handle list, do this...
	var handle_list = fb.CreateHandleList();
	*/

	Properties:
		Count; (uint) (read)
		/*
		example:
		fb.trace(handle_list.Count);
		*/

		Item(idx); (IFbMetadbHandle) (read, write)
		/*
		example:
		fb.trace(fb.TitleFormat("%artist%").EvalWithMetadb(handle_list.Item(0)));
		displays artist of first item in handle list
		*/

	Methods:
		Add(handle); (void)
		/*
		example: 
		handle_list.Add(fb.GetNowPlaying());
		*/

		AddRange(handle_list); (void)
		/*
		example:
		handle_list.Add(fb.GetLibraryItems());
		*/
		
		BSearch(handle); (int)
		/*
		must be sorted, faster than Find()
		returns - 1 on failure
		*/

		CalcTotalDuration(); (double)
		// returns total in seconds. consider using utils.FormatDuration() on the result.

		CalcTotalSize(); (LONGLONG)
		// return total in bytes. consider using utils.FormatFileSize() on the result.

		Clone(); (IFbMetadbHandleList)
		/*
		example:
		var handle_list2 = handle_list.Clone();
		*/

		Dispose(); (void)
		/*
		example:
		handle_list.Dispose();
		*/

		Find(handle); (int)
		/*
		if sorted, use BSearch instead
		returns -1 on failure
		*/

		Insert(index, handle); (void)
		/*
		example1:
		handle_list.Insert(0, fb.GetNowPlaying());
		0 inserts at the start of the handle list.
		example2:
		handle_list.Insert(handle_list.Count, fb.GetNowPlaying());
		this inserts at the end of the handle list.
		*/

		InsertRange(index, handle_list); (void)

		MakeDifference(handle_list); (void)
		/*
		must be sorted
		example:
		var one = plman.GetPlaylistItems(0);
		one.Sort();

		var two = plman.GetPlaylistItems(1);
		two.Sort();

		one.MakeDifference(two);
		// "one" now only contains handles that were unique to "one". anything that also existed in "two" will have been removed.
		*/

		MakeIntersection(handle_list); (void)
		/*
		must be sorted
		example:
		var one = plman.GetPlaylistItems(0);
		one.Sort();

		var two = plman.GetPlaylistItems(1);
		two.Sort();

		one.MakeIntersection(two);
		// "one" now only contains handles that were in BOTH "one" AND "two"
		*/

		MakeUnion(handle_list); (void)
		/*
		must be sorted
		example:
		var one = plman.GetPlaylistItems(0);
		one.Sort();

		var two = plman.GetPlaylistItems(1);
		two.Sort();

		one.MakeUnion(two);
		// "one" now contains all handles from "one" AND "two" with any duplicates removed
		*/

		OrderByFormat(tfo, direction); (void)
		/*
		tfo: an instance of IFbTitleFormat.
		direction: integer, ascending while > 0.
		example:
		var handle_list = fb.GetLibraryItems();
		var tfo = fb.TitleFormat("%album artist%|%date%|%album%|%discnumber%|%tracknumber%");
		handle_list.OrderByFormat(tfo, 1);
		*/

		OrderByPath(); (void)

		OrderByRelativePath(); (void)

		Remove(handle); (void)

		RemoveAll(); (void)

		RemoveById(idx); (void)
		/*
		example:
		handle_list.RemoveById(0);
		*/

		RemoveRange(from, num); (void)
		/*
		example:
		handle_list.RemoveRange(10, 20);
		*/

		Sort(); (void)
		// remove duplicates and optimise for other handle list operations

		UpdateFileInfoFromJSON(str); (void)
		// see https://github.com/19379/foo-jscript-panel/wiki/Breaking-Changes#v130
}

interface IDropTargetAction {
	// see samples\basic\Drag Drop Basic.txt

	Properties:
		Parsable; (boolean) (read, write)

		Playlist; (read, write)
		// default: -1, active playlist

		ToSelect; (boolean) (read, write)

	Methods:
		ToPlaylist(); (void)
}
