API Docs for:
Show:

File: generic-list.js

/*******************************************************************************
 * Copyright (c) 2017 Genialist Software Ltd.
 * All rights reserved.
 ******************************************************************************/

var m_pattern_various_albums = /Various Albums/i;
var m_pattern_featured_in = /Featured in/i;
var m_pattern_filename_windows = /([a-z]:){0,1}([/\\][^/\\]*){2}/i;

//console.log("TEST: m_pattern_filename_windows.test(\"c:/doo/bar\") = " + m_pattern_filename_windows.test("c:/doo/bar") + " " + "c:/doo/bar".match(m_pattern_filename_windows));
//console.log("TEST: m_pattern_filename_windows.test(\"doo/bar\") = " + m_pattern_filename_windows.test("doo/bar") + " " + "doo/bar".match(m_pattern_filename_windows));
//console.log("TEST: m_pattern_filename_windows.test(\"Fate/Grand Order: First Order\") = " + m_pattern_filename_windows.test("Fate/Grand Order: First Order") + " "  + "Fate/Grand Order: First Order".match(m_pattern_filename_windows));
//console.log("TEST: m_pattern_filename_windows.test(\"Image/1/2/3\") = " + m_pattern_filename_windows.test("Image/1/2/3") + " "  + "Image/1/2/3".match(m_pattern_filename_windows));

const pPlaylists = new (function() {
	this.format = function(t) {
		if (t.indexOf('\\')>0)
			return pString.basename(t);
		return m_pattern_filename_windows.test(t)? pString.basename(t) : t;
	};
});

const icon_book = "/resources/html/images/32x32/book.png";
const icon_books = "/resources/html/images/32x32/books_stack.png";
const icon_movie = "/resources/html/images/32x32/movies.png";
const icon_video = "/resources/html/images/32x32/video.png";
const icon_music = "/resources/html/images/32x32/music.png";
const icon_photos = "/resources/html/images/32x32/photos.png";
const icon_tv = "/resources/html/images/32x32/television.png";
const icon_radio = "/resources/html/images/32x32/radio_modern.png";

const pGenericList = (new function() {
	var that = this;
	var vdefault = { name: "Objects", thumb: "TBC" };

	this.m_lists = [
		{ lid: "books.medias", 		name: "Books", 			thumb: icon_book, gthumb: icon_books },
		{ lid: "books.groups", 		name: "Books",		 	thumb: icon_book, gthumb: icon_books },
		{ lid: "books.genres", 		name: "Book Genres", 	thumb: icon_book, gthumb: icon_books },
		{ lid: "books.tags", 		name: "Book Tags", 		thumb: icon_book, gthumb: icon_books },
		{ lid: "books.authors", 	name: "Book Authors", 	thumb: icon_book, gthumb: icon_books },

		{ lid: "movies.medias", 	name: "Movie Files", 	thumb: icon_movie },
		{ lid: "movies.movies", 	name: "Movies", 		thumb: icon_movie },
		{ lid: "movies.genres", 	name: "Movie Genres", 	thumb: icon_movie },
		{ lid: "movies.groups", 	name: "Movie Groups", 	thumb: icon_movie },
		{ lid: "movies.tags", 		name: "Movie Tags", 	thumb: icon_movie },
		{ lid: "movies.actors", 	name: "Movie Actors", 	thumb: icon_movie },

		{ lid: "homevideos.medias", name: "Movie Files", 		thumb: icon_video },
		{ lid: "homevideos.movies", name: "Home Movies", 		thumb: icon_video },
		{ lid: "homevideos.tags", 	name: "Home Movie Tags", 	thumb: icon_video },

		{ lid: "music.medias", 		name: "Songs", 			thumb: icon_music },
		{ lid: "music.albums", 		name: "Albums", 		thumb: icon_music },
		{ lid: "music.artists", 	name: "Music Artists", 	thumb: icon_music },
		{ lid: "music.genres", 		name: "Music Genres", 	thumb: icon_music },

		{ lid: "pictures.authors", 	name: "Picture Authors",	thumb: icon_photos },
		{ lid: "pictures.genres", 	name: "Picture Genres", 	thumb: icon_photos },
		{ lid: "pictures.groups", 	name: "Pictures",		 	thumb: icon_photos },
		{ lid: "pictures.medias", 	name: "Pictures",		 	thumb: icon_photos },
		{ lid: "pictures.tags", 	name: "Picture Tags", 		thumb: icon_photos },
		{ lid: "pictures.models", 	name: "Picture Models", 	thumb: icon_photos },

		{ lid: "playlists.default", name: "Playlists", 			thumb: "/resources/html/images/32x32/script_gear.png" },
		{ lid: "playlists.medias", 	name: "Playlists", 			thumb: "/resources/html/images/32x32/script.png",
		  isprivate: { thumb: "/resources/html/images/32x32/script_key.png" }
		},
		{ lid: "playlists.groups", 	name: "Playlist Groups", 	thumb: "/resources/html/images/32x32/script.png" },

		{ lid: "group.playlists.medias", name: "Playlists", thumb: "/resources/html/images/32x32/script.png" },
		
		{ lid: "podcasts.medias", 	name: "Podcast", 			thumb: "TODO" },
		{ lid: "podcasts.albums", 	name: "Podcast Albums", 	thumb: "TODO" },
		{ lid: "podcasts.genres", 	name: "Podcast Genres", 	thumb: "TODO" },

		{ lid: "tvshows.genres", 	name: "TV Show Genres", 	thumb: icon_tv },
		{ lid: "tvshows.medias", 	name: "TV Show Episodes", 	thumb: icon_tv },
		{ lid: "tvshows.seasons", 	name: "TV Shows", 			thumb: icon_tv },
		
		{ lid: 'radios.medias',     name: "Radios",             thumb: icon_radio },
		{ lid: 'radios.radios',     name: "Radios",             thumb: icon_radio }
	];

	var lists = new pMap();
	this.m_lists.forEach(function(ll) { lists.put(ll.lid, ll); });
	
	this.getListObjectNames = function(lid) {
		return lists.getValue(lid, vdefault).name;
	};
	this.getListThumbnail = function(lid, i) {
		var ll = lists.getValue(lid || pMediaLibrary.getLIDfromURL(), vdefault);

		var n = pPageData.getValue('metadata_name');
		if (n) {
			if (n.startsWith("Deny"))
				return "/resources/html/images/32x32/script_red.png"
			if (n.startsWith("Allow"))
				return "/resources/html/images/32x32/script_green.png"
		}
		return (i && i.vprivate == "true" && ll.isprivate)? ll.isprivate.thumb : ll.thumb;
	};
	this.getListGroupThumbnail = function(lid) {
		var ll = lists.getValue(lid, vdefault);
		
		var n = pPageData.getValue('metadata_name');
		if (n) {
			if (n.startsWith("Deny"))
				return "/resources/html/images/32x32/script_red.png"
			if (n.startsWith("Allow"))
				return "/resources/html/images/32x32/script_green.png"
		}
		return ll.gthumb || ll.thumb;
	};
	this.getItemTitle = function(i) {
		var tt = i.title;
		
		if (i.lid && i.lid.endsWith('.tags'))
			return pTitle.format(i.Title || tt || i.name);
		if (i.lid && i.lid.endsWith('.genres'))
			return pTitle.format(tt || i.name);
		
		tt = tt || i.name;
		
		if (i.lid && i.lid.indexOf('playlists.')==0)
			tt = pPlaylists.format(tt);
		else if (i.lid && i.lid.indexOf('.models')>0)
			tt = m_pattern_filename_windows.test(tt)? pString.basename(tt) : tt;
		else if (!i.lid || (i.lid.indexOf('.genres')<0 && i.lid.indexOf('.actors')<0 && i.lid.indexOf('.authors')<0 && i.lid.indexOf('.models')<0))
			tt = pString.basename(tt);//(m_pattern_filename_windows.test(tt))? pString.basename(tt) : tt;
			
		return pTitle.format(tt);
	};
	this.getSortingTitle = function(x) {
		if (x.provider=="|iTunes")
			return x.lib_title_sort || pTextIndexing.toSorting(x.title);
		return x.title_sort? pTextIndexing.toSorting(x.title_sort) : (x.lib_title_sort || pTextIndexing.toSorting(x.title));
	};
	this.getSortingName = function(x) {
		return x.name_sort? pTextIndexing.toSorting(x.name_sort) : (x.lib_name_sort || pTextIndexing.toSorting(x.name));
	};
	this.getAuthorTooltip = function(p_author, p_sep, p_first_sep) {
		var a = pString.split(p_author, '|'), t = '';
		a.forEach(function(a) {
			if (a != '(Unknown)')
				t += ((t.length>0 || p_first_sep != false)? ((p_sep)? p_sep : '&#x000A;') : '')+pString.basename(a); 
		});
		return t;
	};
	this.getItemTooltip_PC = function(i) {
		var r = '';
		if (pString.v(i.tv_rating_description))
			r += '&#x000A;'+i.tv_rating_description;
		if ((''+i.adult) == "true")
			r += '&#x000A;ADULT';
		if (i.advisory === "explicit")
			r += '&#x000A;EXPLICIT';
		if (i.advisory === "clean")
			r += '&#x000A;CLEAN';
		return r;
	};
	this.getItemTooltip = function(i, p_title) {
		var i_lid = i.lid, i_clid = that.getContainerLID(i), r = p_title || that.getItemTitle(i);

		if (r!=i.title)
			if (m_pattern_filename_windows.test(i.title)) {
				r += '&#x000A;'+i.title;
			}
		
		if (i_lid == 'music.albums') {
			r += '&#x000A;'+i.artist;
			r += that.getItemTooltip_PC(i);
		}
		else if ((i_lid == 'books.medias' || i_lid == 'books.groups') && (i_clid!='books.authors' && i_clid!='pictures.authors') && i.author) {
			r += that.getAuthorTooltip(i.author);
			r += that.getItemTooltip_PC(i);
		}
		else if ((i_lid == 'pictures.medias' || i_lid == 'pictures.groups') && (i_clid!='books.authors' && i_clid!='pictures.authors') && i.author) {
			if (pResources.get('library.list.(pictures.authors).enabled') == 'true')
				r += that.getAuthorTooltip(i.author);
			
			r += that.getItemTooltip_PC(i);
		}
		else if (i_lid == 'tvshows.seasons') {
			var i_season_name = i.tv_season_name, i_season = i.season;

			if (pString.v(i_season_name))
				r += '&#x000A;'+i_season_name;
			else if (pString.v(i_season) && i_season.toLowerCase()!='season 0')
				r += '&#x000A;'+i_season;

			r += that.getItemTooltip_PC(i);
		}
		else if (i_lid == 'movies.movies') {
			if (pString.v(i.year))
				r += '&#x000A;'+i.year;

			r += that.getItemTooltip_PC(i);
			r += '&#x000A;Runtime: '+(i.duration || '0');
		}
		else if (i_lid == 'homevideos.movies') {
			var i_date = pDate.toLocaleDateString(pDate.create(i.release_date), null, options_full_date, i.release_date);
			if (i_date != 'Invalid Date') r += '&#x000A;'+i_date;

			r += '&#x000A;Runtime: '+(i.duration || '0');
		}
		else if (i_lid == 'radios.radios') {
			if (i.countries) {
				var c = pString.split(i.countries, '|');
				if (c.length>0)
					r += '&#x000A;Countries: ' + c.join(', ');
			}
			if (i.lang) {
				var c = pString.split(i.lang, '|');
				if (c.length>0)
					r += '&#x000A;Languages: ' + c.join(', ');
			}
		}
		return r;
	};
	this.getItemSubtext = function(i) {

		if (i.sub_text != undefined)
			return i.sub_text;

		var i_lid = i.lid, i_clid = that.getContainerLID(i), i_artist = i.artist;

		if (i_lid == 'music.albums' && that.getContainerLID(i)!='music.artists')
			return i_artist;
		else if ((i_lid == 'books.medias' || i_lid == 'books.groups') && (i_clid!='books.authors' && i_clid!='pictures.authors') && i.author)
			return that.getAuthorTooltip(i.author, '<br>', false);
		else if ((i_lid == 'pictures.medias' || i_lid == 'pictures.groups') && (i_clid!='books.authors' && i_clid!='pictures.authors') && i.author) {
			if (pResources.get('library.list.(pictures.authors).enabled') == 'true')
				return that.getAuthorTooltip(i.author, '<br>', false);
			return '';
		}
		else if (i_lid == 'tvshows.seasons') {
			var i_season_name = i.tv_season_name, i_season = i.season;
			
			if (i_season_name != undefined && i_season_name != '')
				return i_season_name;
			else if (i_season != undefined && i_season != '' && i_season.toLowerCase()!='season 0')
				return i_season;
		}
		else if (i_lid == 'homevideos.movies') {
			var d = pDate.toLocaleDateString(pDate.create(i.release_date+'T00:00:00'), null, null, i.release_date);
			if (d != 'Invalid Date')
				return d;
		}
		else if (i_lid.indexOf('playlists.')==0) {
			if (i.owner_fullname === "false") return "";
			return i.owner_fullname || "";
		}
		return "";
	};
	
	this.getItemName = function(i) {
		return pString.v(i.title)? i.title : i.name;
	};
	
	this.getOnDrop = function(i, tu) {
		if (!pApplicationUI.OPTION_DRAG_AND_DROP) return null;
		if (pResources.get('admin') != "true") return null;
		
		var n = that.getItemName(i);
		if (!pString.v(n)) return null;
		if (n == '(Unknown)') return null;
		if (n == '(Featured In)') return null;
		
		if (i.lid.endsWith('.actors'))
			return function(f, u, e) { pMediaDrop.dropOnActor(f, u, e, n, false, tu ); };
		if (i.lid.endsWith('.artists'))
			return function(f, u, e) { pMediaDrop.dropOnArtist(f, u, e, n, false, tu ); };
		if (i.lid.endsWith('.authors'))
			return function(f, u, e) { pMediaDrop.dropOnAuthor(f, u, e, n, false, tu ); };
		if (i.lid.endsWith('.genres'))
			return function(f, u, e) { pMediaDrop.dropOnGenre(f, u, e, n, false, tu ); };
		if (i.lid.endsWith('.models'))
			return function(f, u, e) { pMediaDrop.dropOnModel(f, u, e, n, false, tu ); };
		if (i.lid.endsWith('.tags'))
			return function(f, u, e) { pMediaDrop.dropOnTag(f, u, e, n, false, tu ); };

		if (i.lid == 'music.albums')
			return function(f, u, e) { pMediaDrop.dropOnMusicAlbum(f, u, e, n, i.artist, false, tu); };
		
		if (i.lid == 'playlists.medias')
			return function(f, u, e) { pMediaDrop.dropOnPlaylist(f, u, e, n/*this.getItemTitle(i)*/, i.id, false, tu); };
			
		if (i.lid == 'tvshows.seasons')
			if (pString.v(i.season))
				return function(f, u, e) { pMediaDrop.dropOnTVShowSeason(f, u, e, n, i.season, false, tu); };
		
		if (pApplicationUI.OPTION_PICTURES_DRAG_AND_DROP && (pResources.get('admin') === "true")/* && (pResources.get("canAddPictures") === "true")*/) {
			if (i.lid == 'books.medias' && (''+i.pictures) === "true")
				return function(f, u, e) { pMediaDrop.dropOnPictureFolder(f, u, e, n, '', false, tu ); };
			if (i.lid == 'pictures.groups')
				return function(f, u, e) { pPictureFolder.drop(f, u, e, tu); };
			if (i.lid == 'pictures.medias')
				return function(f, u, e) { pPictureFolder.drop(f, u, e, tu); };
				//return function(f, u, e) { pMediaDrop.movePictures(f, u, e, tu);/*pMediaDrop.dropOnPictureFolder(f, u, e, n, '', false, tu );*/ };
		}
				
		//return null;
	};
	
	this.getOnDropFilter = function(i, tu) {
		if (i.lid == 'books.medias' && (''+i.pictures) === "true")
			return function(e) { return pMediaDrop.filterPicture(e, tu); };
		if (i.lid == 'pictures.medias')
			return null;//TODO: ... function(e) { return pMediaDrop.filterPicture(e, tu); };
			
		return function(e) { return pMediaDrop.filter(e, tu); };
	};
	
	this.getContainerLID = function(i) {
		var c = i.container_lid;
		return pString.v(c)? c : '';
		//if (i_clid != undefined && i_clid != '')
		//	return i_clid;
		//return '';
	};
	
	var m_searches = [];
	this.int_getURLReady = function(r) {
		for(var i=0 ; i<m_searches.length ; i++)
			if (m_searches[i].request === r)
				return pSearch.m_searches[i];
		return null;
	};
	
	this.acceptItem = function(i) {
		if (that.getContainerLID(i) != 'music.artists') {
			var tt = that.getItemTitle(i);
			if (m_pattern_various_albums.test(tt)) return false;
			if (m_pattern_featured_in.test(tt)) return false;
		}
		return pDevice.canPlay(i);
	};
});

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

const pContentRating = (new function(){
	this.score = function(r) {
		if (r) {
			var p = r.split('|');
			if (p.length>=3)
				return parseInt(p[2]);
		}
		return 0;
	};
});

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

const letters = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "#", "ALL" ] ;

pListInstance = function(p_library_id, p_id, p_child) {
	var that = this, m_id = p_id, lv, tagModeAnd = true, m_index = true, child = p_child, max_single_page = pApplicationUI.OPTION_PAGE_INDEX_THRESHOLD, m_pageOffset = 0;
	const tableCookieName = 'table-'+pLocation.getPath().substring(url_prefix.length)/*+'?page_min='+pLocation.getQueryParameter('page_min', '1')*/;
	
	this.tracePrefix = 'List';
	this.library_id = p_library_id;
	
	function f_letterCookieName(tableid) {
		return 'l-'+pLocation.getPath().substring(url_prefix.length)+'-'+tableid;
	}
	
	function getListView() {
		if (!lv){
			lv = new pListViewInstance(that.library_id, m_id);
			lv.show_list_name_values = this.show_list_name_values;
			lv.pageOffset = this.pageOffset;
		}
		else
			lv.clear();
		return lv;
	}
	
	function f_updateSortMenu() {
		if (lv.items.length>1)
			pDocument.show(m_id+'-sort-menu', 'inline-block');
		else
			pDocument.hide(m_id+'-sort-menu');
	}
	
	function f_sort(lv, letter, is) {
		var s = m_sort = m_sort || pLocation.getQueryParameter('sort');
		if (s) {
			s = sorting.getValue(s);
			if (s && s.f) {
				if (letter!="ALL")
					lv.items = pArray.clone(is).sort(s.f);
				else
					lv.items = s.v || (s.v = pArray.clone(is).sort(s.f));
			}
		}
	} 

	this.pLatinAlphabetIndex = {
		m_current: null,
		m_display: "inline-block",
		m_table: (pCookieManager.getCookie(tableCookieName) =='1')? true : pLocation.getQueryParameter('mode', 'list') == 'table'
	};
	//alert(pLatinAlphabetIndex.m_table);

	this.clear = function() {
		that.m_cache = [];
		that.m_acache = [];
		that.m_tags = [];
		that.m_selected_tags = new pMap();
		that.m_filtered_list = null;
	
		that.m_items = [];
		that.fl = "fltitle";
		that.m_count = 0;
		that.m_letters_count = 0;
	};
	this.clear();
	
	this.update = function() {
		pApplicationUI.update(that.clear);//function(){ /*renderSearchListBusy('${show-id}');*/ this.clear(); })
	};
	
	this.m_showall = false;

	this.show_list_name_values = false;

	this.showAll = function(p_event) {
		if (p_event == 'resize' && that.pLatinAlphabetIndex.m_table === true)
			return;

		var lv = getListView();
		lv.busy = true;
		
		//*** SETUP LIST
		lv.items = that.m_filtered_list? that.m_filtered_list.m_items : that.m_items;
		f_sort(lv, 'ALL', that.m_items);
		
		//*** RENDER
		if (that.pLatinAlphabetIndex.m_table === true)
			lv.renderTable('ALL');
		else
			lv.renderImpl('ALL');

		//*** UPDATE SORT MENU
		f_updateSortMenu();
	};

	this.addToCache = function(i, letter, j) {
		
		function f_reset_sort_value(s) { 
			s.v = null 
		}
		
		function f_sort_tags(t) {
			t = t.toLowerCase();
			if (!that.m_tags.includes(t)) {
				that.m_tags[that.m_tags.length] = t;
				that.m_tags.sort();
			}
		}
		
		//TODO: HACK
		if (letter!="ALL")
			if ((''+(i.name || i_title)).toLowerCase()=='(unknown)')
				letter = '#';
		
		var i_first = false, i_cache = that.m_cache[letter] || (this.m_cache[letter] = []);
		
		//*** CLEAR SORTING VALUES
		sorting.values().forEach(f_reset_sort_value);

		if (i_cache.length==0)
			i_first = true;

		if (i_cache.length==0 || i_cache[i_cache.length-1] !== i) {
			i_cache[i_cache.length] = i;
			if (j==0)
				(that.m_acache[letter] || (that.m_acache[letter] = [])).push(i);

 			if (i.release_date && i.release_date!="") {
				var i_pos = i.release_date.indexOf('T');
				if (i_pos>0)
					i.release_date = i.release_date.substring(0, i_pos);
			}
			if (i.release_date && i.release_date=="")
				i.release_date = "(Unknown)";

			if (!this.parent && i.metadata_tags)
				i.metadata_tags.forEach(f_sort_tags);
		}

		return i_first;
	};

	this.isCached = function(letter) {
		return that.m_cache[letter] != null;
	};
	
	var sorting = new pMap(), m_sort = pDocument.getHistoryState('sort.'+m_id, 'default');
	pDocument.addHistoryStateHandler(function() {
		that.sort(pDocument.getHistoryState('sort.'+m_id));
	});
	
	this.addSorting = function(s) {
		s.value = s.id;
		s.fcall = function(did, v) { //did as this can be called from dialog... 
			m_sort = v; 
			pDocument.setHistoryState('sort.'+m_id, v);
			
			if (that.pLatinAlphabetIndex.m_table === true) {
				if (m_sort != 'default') {
					var c = pMetadata.columns.getValue(v);
					if (c)
						pTable.sortString('table-'+m_id, pTable.columnIndex('table-'+m_id, c.th, 0, 0), 0, true);
					else
						that.show();
				}
				else
					that.show();
			}
			else {
				if (that.m_items.length>0) {
					var s = s = sorting.getValue(v);
					if (s) {
						if (s.sort)
							s.sort();
						else
							f_default_sorting_sort();
					}
				}
				that.show();
			}
		};
		sorting.put(s.id, s); 
		
		//*** DECORATE
		that.decorate(s);
		
	};

	this.decorate = function(s, id) {
		id = id || s.id;
		
		s.text = pDocument.getStyleValue(".sort-"+id+"-name") || s.text;
		s.title = pDocument.getStyleValue(".sort-"+id+"-title");
		s.icon = { src: pDocument.getStyleValue(".sort-"+id+"-icon") }; if (!s.icon.src) s.icon = null;
		s.description = pDocument.getStyleValue(".sort-"+id+"-description");
	};
	
	function f_default_sorting_sort(fl) {
		fl = fl ||  "fltitle";
		
		if (that.fl != fl) {
			var i = pArray.clone(that.m_items);
			that.m_items = [];
			that.m_cache = [];
			that.m_acache = [];
			
			i.forEach(function(i) { that.add(i, fl); });
			that.finish();
		}
	}
	
	this.addSorting({ id: "default", text: "By Default", enabled: true });
	this.addSorting({ id: "added", text: "By Addition Date", enabled: true, f: function(x,y) {
		x = parseInt(x.added);
		y = parseInt(y.added);
		return x < y ? 1 : (x > y? -1 : 0);
	}});
	this.addSorting({ id: "title", text: "By Title/Name", enabled: true, f: function(x,y) {
		x = pGenericList.getItemTitle(x);
		y = pGenericList.getItemTitle(y);
		//console.log('sort: ' + x + ' ' + y);
		return x < y ? -1 : (x > y? 1 : 0);
	}});
	this.addSorting({ id: "artist-title", text: "By Artist, Album", enabled: false, 
		f: function(x,y) {
			var xa = pTextIndexing.toSorting(x.album_artist_sort || x.artist), ya = pTextIndexing.toSorting(y.album_artist_sort || y.artist);
			if (xa != ya)
				return xa < ya ? -1 : 1;
			x = pGenericList.getSortingName(x);
			y = pGenericList.getSortingName(y);
			//console.log('sort: ' + x + ' ' + y);
			return x < y ? -1 : (x > y? 1 : 0);
		}, 
		sort: function() { f_default_sorting_sort("flartist"); }
	});
	this.addSorting({ id: "author-title", text: "By Author, Title", enabled: false, 
		f: function(x,y) {
			var xa = pTextIndexing.toSorting(x.author_sort || x.author), ya = pTextIndexing.toSorting(y.author_sort || y.author);
			if (xa != ya)
				return xa < ya ? -1 : 1;
			x = pGenericList.getSortingTitle(x);
			y = pGenericList.getSortingTitle(y);
			//console.log('sort: ' + x + ' ' + y);
			return x < y ? -1 : (x > y? 1 : 0);
		}, 
		sort: function() { f_default_sorting_sort("flauthor"); }
	});
		
	this.getSorting = function(n, d) {
		return sorting.getValue(n, d);
	};
	
	this.sort = function(n) {
		if (n)
			sorting.getValue(n, sorting.getValue('default')).fcall(null, n);
	};
	
	this.sortMenu = function() {
		var m = sorting.values().filter(pObject.isEnabled);
		m.forEach(function(s) { s.selected = s.id == m_sort });
		pApplicationMenu.menu('sort-list', pArray.sortByText(m));
	};
	
	this.createLetters = function(sid) {
		var h = '';
		letters.forEach(function(l) {
			h += '<span class="listview-item-letter" style="display: none;" id="'+sid+'-'+l+'" name="'+sid+'-'+l+'">';
			h += '<a class="link" id="'+sid+'-link-'+l+'" name="'+sid+'-link-'+l+'" href="javascript:pListManager.getValue(\''+sid+'\').show(\''+sid+'\', \''+l+'\')">'+l+'</a></span>';
		});
		pElement.setInnerHTML(sid+'-listview-index', h);
	};
	
	this.show = function(tid, letter) {
		tid = tid || m_id;
		letter = letter || that.pLatinAlphabetIndex.m_current || "ALL";
		
		if (that.m_filtered_list) {
			that.m_filtered_list.show.call(that.m_filtered_list, tid, letter);
			return;
		}

		renderSearchListBusy(m_id);

		that.pLatinAlphabetIndex.m_current = letter;
		pCookieManager.set(f_letterCookieName(tid), letter);

		var is = that.m_items;
		if (letter!="ALL")
			is = that.m_cache[letter] = that.m_cache[letter] || that.m_items.filter(function(i) { return pGenericList.acceptItem(i) && i.fltitle.index(letter)>=0 });
		//0.9.18
		else {
			is = [];
			letters.forEach(function(l) { if (l!="ALL" && that.m_acache[l]) is = is.concat(that.m_acache[l]); });
			//console.log(is[0]);
			//console.log("aaa")
			//is = that.m_items;
		}
		
		var lv = getListView();

		//*** SETUP LIST
		lv.items = is;
		f_sort(lv, letter, is);
		
		//*** RENDER
		if (that.pLatinAlphabetIndex.m_table === true)
			lv.renderTable(letter);
		else
			lv.renderImpl(letter);

		//*** UPDATE SORT MENU
		f_updateSortMenu();
		
		//*** UPDATE LETTER BUTTONS
		letters.forEach(function(l) {
			pElement.n(tid+'-link-'+l).forEach(function(x) {
				pElement.addAndRemoveClassName(x, l==letter? "selected" : "link", l==letter? "link" : "selected");
			});
		});

	};

	this.showFirst = function(p_event, tid) {
		if (null!=p_event && p_event == 'resize' && pLatinAlphabetIndex.m_table === true)
			return;

		var i_table = pElement.x(tid), f = this.pLatinAlphabetIndex.m_current;
		if (null == f)
			f = pCookieManager.getCookie(f_letterCookieName(tid));

		if (pString.v(f))//null!=f && f!="")
			//for(var i=0 ; i<letters.length ;i++)
				if (pElement.x(tid+"-"+f).style.display == that.pLatinAlphabetIndex.m_display) {
					that.show(tid, f);
					return;
				}

		var l = letters.find(function(l) {
			return pElement.x(tid+"-"+l).style.display == that.pLatinAlphabetIndex.m_display;
		});
		if (l)
			that.show(tid, l);
		/*for(var i=0 ; i<letters.length ;i++)
			if (pElement.x(tid+"-"+letters[i]).style.display == that.pLatinAlphabetIndex.m_display) {
				that.show(tid, letters[i]);
				break;
			}*/
	};

	this.showTable = function() {
		if (that.pLatinAlphabetIndex.m_table === true)
			return;

		that.pLatinAlphabetIndex.m_table = true;
		pCookieManager.set(tableCookieName, '1');

		pElement.x(m_id+'-link-showTable').style.display = "none";
		pElement.x(m_id+'-selected-showTable').style.display = "inline";
		pElement.x(m_id+'-link-showList').style.display = "inline";
		pElement.x(m_id+'-selected-showList').style.display = "none";
		
		if (that.m_showall)
			that.showAll();
		else
			that.showFirst(null, m_id);
		
		//*** DECORATE
		pElement.decorateAll();
	};

	this.showList = function() {
		if (that.pLatinAlphabetIndex.m_table === false)
			return;

		that.pLatinAlphabetIndex.m_table = false;
		pCookieManager.remove(tableCookieName);

		pElement.x(m_id+'-link-showTable').style.display = "inline";
		pElement.x(m_id+'-selected-showTable').style.display = "none";
		pElement.x(m_id+'-link-showList').style.display = "none";
		pElement.x(m_id+'-selected-showList').style.display = "inline";
		
		if (that.m_showall)
			that.showAll();
		else
			that.showFirst(null, m_id);
		
		//*** DECORATE
		pElement.decorateAll();
	};
	
	this.add = function(i, fl) {
		fl = that.fl = fl || "fltitle";
		
		that.m_items[that.m_items.length] = i;

		for(var j=0 ; j<i[fl].length ; j++)
			that.addToCache(i, i[fl].charAt(j), j);
		that.addToCache(i, "ALL");

		//*** SHOW IF POSSIBLE
		/*if (that.m_items.length > max_single_page) {
			var i_first = pCookieManager.getCookie(window.location.pathname+"-"+m_id+"-filter-letter");

			if (null!=i_first && i_first!="" && i_first == i_letter) {
				that.show(m_id, i_first);
				console.log("preview: " + i_first);
			}
		}*/
	};

	this.finish = function() {
		
		function f_hideIndex() {
			pElement.setStyleDisplay(m_id+"-listview-index", "none");
			letters.forEach(function(l) {
				pElement.setStyleDisplay(m_id+'-'+l, "none");
			});
			pElement.setStyleDisplay(m_id+'-'+letters[letters.length-1], that.pLatinAlphabetIndex.m_display);
		}
		
		pConsole.info(that, 'Item count: ' + that.m_items.length);
		var j = JSON.stringify(that.m_items);
		/*pConsole.info(that, 'to JSON...');
		pJSON.parse(j);
		pConsole.info(that, 'to Javascript...'); */
		
		var id = m_id;
		
		if (that.m_filtered_list) {
			that.m_filtered_list.finish.call(that.m_filtered_list);
			return;
		}

		if (m_index && that.m_items.length > max_single_page) {
			var n = that.m_letters_count;
			if (n==1) { // only ALL and one letter... let's show everything
				f_hideIndex();
				that.m_showall = true;
				that.showAll();
			}
			else {
				pElement.x(id+"-listview-index").style.display = that.pLatinAlphabetIndex.m_display;
				letters.forEach(function(l) {
					if (that.isCached(l))
						pElement.x(id+'-'+l).style.display = that.pLatinAlphabetIndex.m_display;
					else
						pElement.x(id+'-'+l).style.display = "none";
				});
				pElement.x(id+'-'+letters[letters.length-1]).style.display = that.pLatinAlphabetIndex.m_display;
				that.showFirst(null, id);
			}
		}
		/*else if (that.m_items.length==0) {
			x = pElement.x(m_id);
			//if (x!=null)
			//	x.style.display = "none";
		}*/
		else {
			f_hideIndex();
			that.m_showall = true;
			that.showAll();
		}
		/*pDocument.addOnResize(function() {
			if (that.m_showall)
				that.showAll('resize');
			else
				that.showFirst('resize', m_id);
		});*/

		if (that.pLatinAlphabetIndex.m_table === true) {
			pElement.x(id+'-link-showTable').style.display = "none";
			pElement.x(id+'-selected-showTable').style.display = "inline";
			pElement.x(id+'-link-showList').style.display = "inline";
			pElement.x(id+'-selected-showList').style.display = "none";
		}

		if (that.parent)
			that.parent.showTags();
		else
			that.showTags();
	};

	const resources_tags_and_title = 'Show all media(s) that have ALL the selected tags...';
	const resources_tags_or_title = 'Show all media(s) that have at least one of the selected tags...';
	this.showTags = function() {
		var x = pElement.x('filter-tags');
		if (x) {
			if (that.m_tags.length<2) {
				pDocument.hide(x);
				return;
			}
			
			var h = '<div class="filter-tag">Filter by tags ';
			if (tagModeAnd) {
				h += '(<span class="selected" title="'+resources_tags_and_title+'">AND</span>|';
				h += '<span class="link" onclick="i_list.setModeAnd(false)"  title="'+resources_tags_or_title+'">OR</span>):&nbsp;';
			}
			else {
				h += '(<span class="link" onclick="i_list.setModeAnd(true)" title="'+resources_tags_and_title+'">AND</span>|';
				h += '<span class="selected" title="'+resources_tags_or_title+'">OR</span>):&nbsp;';
			}
			h += '</div>';
			
			that.m_tags.forEach(function(t) {
				var n = pString.encodeHTML(pTag.format(t));
	
				if (that.tag && that.tag===t)
					h += '<div class="filter-tag"><span class="selected">'+n+ '</span></div>';
				else if (that.isTagSelected(t)===true)
					h += '<div class="filter-tag"><a class="link" href=\"javascript:void(i_list.removeTagFilter(\''+t+'\'))\" style="text-decoration: underline;">'+n+ '</a></div>';
				else
					h += '<div class="filter-tag"><a class="link" href=\"javascript:void(i_list.addTagFilter(\''+t+'\'))\">'+n + '</a></div>';
			});
			pElement.setInnerHTML(x, h);
			pDocument.show(x);
		}
	};

	this.refreshFilteredList = function() {

		if (that.m_selected_tags.length>0) {
			that.m_filtered_list = new pListInstance(that.library_id, m_id, true);
			that.m_filtered_list.parent = that;
			
			that.m_items.forEach(function(i) {
				if (tagModeAnd) {
					var c = 0;
					for(var j=0 ; j<that.m_selected_tags.length ; j++) {
						var t = that.m_selected_tags.keyAt(j);
						if ((i.metadata_tags || []).includes(t)) c++;
					}
					if (c == that.m_selected_tags.length)
						that.m_filtered_list.add(i);
				}
				else
					for(var j=0 ; j<that.m_selected_tags.length ; j++) {
						var t = that.m_selected_tags.keyAt(j);
	
						if ((i.metadata_tags || []).includes(t)) {//}&& i.metadata_tags.indexOf(t)>=0) {
							// all tags of item are currently selected...
							that.m_filtered_list.add(i);
							break;
						}
					}
			});
		}
		else
			that.m_filtered_list = null;

		that.finish();
	}

	this.addTagFilter = function(t, p_refresh) {
		if (!that.isTagSelected(t)) {
			that.m_selected_tags.put(t, true);
			pDocument.setHistoryState("list_tags", pString.concat(that.m_selected_tags.keys(), '|'));
			if (p_refresh != false)
				that.refreshFilteredList();
		}
	};

	this.removeTagFilter = function(t) {
		if (that.isTagSelected(t)) {
			that.m_selected_tags.put(t, null);
			pDocument.setHistoryState("list_tags", pString.concat(that.m_selected_tags.keys(), '|'));
			that.refreshFilteredList();
		}
	};
	
	this.setModeAnd = function(s) {
		if (s && tagModeAnd)
			return;
		
		tagModeAnd = s;
		pDocument.setHistoryState("list_tags_mode_and", ''+s);
		that.refreshFilteredList();
	};

	this.isTagSelected = function(t) {
		t = t.toLowerCase();
		if (that.tag === t)
			return true;
		return that.m_selected_tags.getValue(t, null) != null;
	};

	// "tag" property
	var m_tag;
	Object.defineProperty(this, "tag", { get: function() { return m_tag; }, set: function(v) { if (typeof v == "string") m_tag = v.toLowerCase(); } });

	// "index" property
	Object.defineProperty(this, "index", { get: function() { return m_index; }, set: function(v) { if (typeof v == "boolean") m_index = v; } });

	// "pageOffset" property
	Object.defineProperty(this, "pageOffset", { get: function() { return m_pageOffset; }, set: function(v) { if (typeof v == "int") m_pageOffset = v; } });
	
	if (!child)
		pDocument.addHistoryStateHandler(function(p_state) {
			if (p_state.list_tags) {
				const i_tags = pString.split(p_state.list_tags, '|');
				i_tags.forEach(function(t, i) { that.addTagFilter(t, i==i_tags.length-1) });
			}
			tagModeAnd = p_state.list_tags_mode_and;
		});
};

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

const pListManager = new (function() {
	var that = this;
	var m_lists = new pMap();
	this.tracePrefix = 'List';
	
	this.EVENT_CREATED_FOLDER = 'list.created.folder';
	this.EVENT_CREATED_PLAYLIST = 'list.created.playlist';
	this.EVENT_REMOVED_ITEM = 'list.removed.item';
	this.EVENT_RENAMED_ITEM = 'list.renamed.item';
	
	this.create = function(p_library_id, id) {
		id = id || 'generic-list-default';
		
		var ll = new pListInstance(p_library_id, id);
		m_lists.put(id, ll);
		
		if (id == 'generic-list-default') {
			for(var i in that)
				if (i.startsWith('EVENT_'))
					pLocalStorage.addListener(that[i], pListManager.update);
			/*pLocalStorage.addListener(this.EVENT_REMOVED_ITEM, pListManager.update);
			pLocalStorage.addListener(this.EVENT_CREATED_FOLDER, pListManager.update);
			pLocalStorage.addListener(this.EVENT_CREATED_PLAYLIST, pListManager.update);
			pLocalStorage.addListener(this.EVENT_RENAMED_ITEM, pListManager.update);*/
		}
		
		pConsole.info(this, "Starting creating items...");

		return ll;
	};

	this.getValue = function(id) {
		return m_lists.getValue(id || 'generic-list-default', null);
	};
	
	this.update = function(l) {
		if (l = that.getValue((l && l.toLowerCase)? l : null)) 
			l.update();
	};
	
	this.notify = function(e) {
		if (pApplicationUI.OPTION_LIST_NOTIFY) {
			that.update();
			pLocalStorage.notify(e);
		}
	};
});

/******************************************************************************/
/***  END OF FILE  ************************************************************/
/******************************************************************************/