mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 03:10:07 +00:00 
			
		
		
		
	reworking file API...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									851cbb8cc2
								
							
						
					
					
						commit
						6cce53277b
					
				| @ -161,6 +161,7 @@ Roadmap | |||||||
| 		| 	ribbon length: ~42 | 		| 	ribbon length: ~42 | ||||||
| 		| 	screen width: 4 | 		| 	screen width: 4 | ||||||
| 		| 	jumping to end from start of ribbon | 		| 	jumping to end from start of ribbon | ||||||
|  | 		[_] side-by side view... | ||||||
| 		[_] ASAP: test on Android... | 		[_] ASAP: test on Android... | ||||||
| 		[_] single image mode transition (alpha-blend/fade/none) | 		[_] single image mode transition (alpha-blend/fade/none) | ||||||
| 		[_] 75% image sorting (date/name/...) | 		[_] 75% image sorting (date/name/...) | ||||||
|  | |||||||
							
								
								
									
										263
									
								
								ui/data.js
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								ui/data.js
									
									
									
									
									
								
							| @ -1188,18 +1188,18 @@ function saveLocalStorage(attr){ | |||||||
| * XXX need to cleanup this section... | * XXX need to cleanup this section... | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
| // XXX needs testing...
 | function loadLatestFile(path, dfl, pattern, diff_pattern){ | ||||||
| // XXX use a config object instead of positional arguments...
 |  | ||||||
| function loadLatestFile(path, dfl, pattern, diff_pattern, report){ |  | ||||||
| 	dfl = dfl == null ? path.split(/[\/\\]/).pop() : dfl | 	dfl = dfl == null ? path.split(/[\/\\]/).pop() : dfl | ||||||
| 	path = path == dfl ? '.' : path | 	path = path == dfl ? '.' : path | ||||||
| 
 | 
 | ||||||
|  | 	var error | ||||||
|  | 
 | ||||||
| 	var res = $.Deferred() | 	var res = $.Deferred() | ||||||
| 	 | 	 | ||||||
| 	// can't find diffs if can't list dirs...
 | 	// can't find diffs if can't list dirs...
 | ||||||
| 	if(window.listDir == null && (pattern != null || diff_pattern != null)){ | 	if(window.listDir == null && (pattern != null || diff_pattern != null)){ | ||||||
| 		report && showErrorStatus(report, 'no directory listing support.') | 		res.notify('unsupported', 'directory listing.') | ||||||
| 		return res.reject() | 		return res.reject('listDir unsupported.') | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// find the latest...
 | 	// find the latest...
 | ||||||
| @ -1209,17 +1209,12 @@ function loadLatestFile(path, dfl, pattern, diff_pattern, report){ | |||||||
| 			return pattern.test(e) ? e : null | 			return pattern.test(e) ? e : null | ||||||
| 		}).sort().reverse()[0] | 		}).sort().reverse()[0] | ||||||
| 	} | 	} | ||||||
| 	file = file == null ? dfl : file | 	var file = file == null ? dfl : file | ||||||
| 
 |  | ||||||
| 	report && showStatus(report, 'Loading:', file) |  | ||||||
| 
 |  | ||||||
| 	file = path +'/'+ file |  | ||||||
| 	 | 	 | ||||||
| 	var diff_data = {} | 	var diff_data = {} | ||||||
| 	var diff = true | 	var diff = true | ||||||
| 
 | 
 | ||||||
| 	// collect and merge diffs...
 | 	// collect and merge diffs...
 | ||||||
| 	// XXX no error handling if one of the diff loads fail...
 |  | ||||||
| 	if(diff_pattern != null){ | 	if(diff_pattern != null){ | ||||||
| 		diff_pattern = RegExp(diff_pattern) | 		diff_pattern = RegExp(diff_pattern) | ||||||
| 		var diff_data = [diff_data] | 		var diff_data = [diff_data] | ||||||
| @ -1228,14 +1223,16 @@ function loadLatestFile(path, dfl, pattern, diff_pattern, report){ | |||||||
| 		}).sort() | 		}).sort() | ||||||
| 		diff = $.when.apply(null, $.map(diffs_names, function(e, i){ | 		diff = $.when.apply(null, $.map(diffs_names, function(e, i){ | ||||||
| 					return $.getJSON(path +'/'+ e) | 					return $.getJSON(path +'/'+ e) | ||||||
| 						// XXX this is ugly, had to do it this way as .then(...)
 |  | ||||||
| 						// 		handlers get different argument sets depending on 
 |  | ||||||
| 						// 		whether we have one or more deffereds here...
 |  | ||||||
| 						.done(function(data){ | 						.done(function(data){ | ||||||
| 							diff_data[i+1] = data | 							diff_data[i+1] = data | ||||||
| 							report && showStatus(report, 'Loaded:', e) | 							res.notify('loaded', e) | ||||||
|  | 						}) | ||||||
|  | 						.fail(function(){ | ||||||
|  | 							res.notify('load_error', e) | ||||||
| 						}) | 						}) | ||||||
| 				})) | 				})) | ||||||
|  | 			// NOTE: .then(...) handlers get different signature args 
 | ||||||
|  | 			// 		depending on the number of arguments to .when(...)...
 | ||||||
| 			.then(function(){ | 			.then(function(){ | ||||||
| 				$.extend.apply(null, diff_data) | 				$.extend.apply(null, diff_data) | ||||||
| 				diff_data = diff_data[0] | 				diff_data = diff_data[0] | ||||||
| @ -1243,28 +1240,57 @@ function loadLatestFile(path, dfl, pattern, diff_pattern, report){ | |||||||
| 	}  | 	}  | ||||||
| 
 | 
 | ||||||
| 	// load the main file and merge the diff with it...
 | 	// load the main file and merge the diff with it...
 | ||||||
| 	$.when(diff, $.getJSON(file)) | 	$.when(diff, $.getJSON(path +'/'+ file)) | ||||||
| 		.done(function(_, json){ | 		.done(function(_, json){ | ||||||
| 			json = json[0] | 			json = json[0] | ||||||
| 
 | 
 | ||||||
|  | 			res.notify('loaded', file) | ||||||
|  | 
 | ||||||
| 			// merge diffs...
 | 			// merge diffs...
 | ||||||
| 			if(Object.keys(diff_data).length != 0){ | 			if(Object.keys(diff_data).length != 0){ | ||||||
| 				$.extend(json, diff_data) | 				$.extend(json, diff_data) | ||||||
| 				report && showStatus(report, 'Merged diffs...') | 				res.notify('merged') | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			res.resolve(json) | 			res.resolve(json) | ||||||
| 
 |  | ||||||
| 			report && showStatus(report, 'Done.') |  | ||||||
| 		}) | 		}) | ||||||
| 		.fail(function(){ | 		.fail(function(){ | ||||||
| 			report && showErrorStatus(report, 'Loading: ' + file) | 			res.notify('load_error', file) | ||||||
| 			return res.reject() | 
 | ||||||
|  | 			return res.reject(file) | ||||||
| 		}) | 		}) | ||||||
|  | 
 | ||||||
| 	return res | 	return res | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | function statusNotify(prefix, loader){ | ||||||
|  | 	return loader | ||||||
|  | 		.progress(function(action, data){ | ||||||
|  | 			({ | ||||||
|  | 				load: function(data){  | ||||||
|  | 					showStatus(prefix, 'Loading:', data)  | ||||||
|  | 				}, | ||||||
|  | 				loaded: function(data){  | ||||||
|  | 					showStatus(prefix, 'Loaded:', data)  | ||||||
|  | 				}, | ||||||
|  | 				merged: function(data){  | ||||||
|  | 					showStatus(prefix, 'Merging:', 'Done.')  | ||||||
|  | 				}, | ||||||
|  | 				load_error: function(data){  | ||||||
|  | 					showErrorStatus(prefix, 'Loading:', data)  | ||||||
|  | 				}, | ||||||
|  | 				unsupported: function(data){  | ||||||
|  | 					showErrorStatus(prefix, 'Unsupported:', data)  | ||||||
|  | 				}, | ||||||
|  | 			})[action](data) | ||||||
|  | 		}) | ||||||
|  | 		.done(function(){ | ||||||
|  | 			showStatus(prefix, 'Done.') | ||||||
|  | 		}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| // load the target-specific handlers...
 | // load the target-specific handlers...
 | ||||||
| // CEF
 | // CEF
 | ||||||
| if(window.CEF_dumpJSON != null){ | if(window.CEF_dumpJSON != null){ | ||||||
| @ -1284,7 +1310,11 @@ function loadFileImages(path, no_load_diffs, callback){ | |||||||
| 	// default locations...
 | 	// default locations...
 | ||||||
| 	if(path == null){ | 	if(path == null){ | ||||||
| 		var base = normalizePath(CACHE_DIR) | 		var base = normalizePath(CACHE_DIR) | ||||||
| 		var res = loadLatestFile(base, IMAGES_FILE_DEFAULT, IMAGES_FILE_PATTERN, IMAGES_DIFF_FILE_PATTERN, 'Images:') | 		var res = statusNotify('Images:',  | ||||||
|  | 			loadLatestFile(base,  | ||||||
|  | 				IMAGES_FILE_DEFAULT,  | ||||||
|  | 				IMAGES_FILE_PATTERN,  | ||||||
|  | 				IMAGES_DIFF_FILE_PATTERN)) | ||||||
| 	 | 	 | ||||||
| 	// explicit path...
 | 	// explicit path...
 | ||||||
| 	// XXX need to account for paths without a CACHE_DIR
 | 	// XXX need to account for paths without a CACHE_DIR
 | ||||||
| @ -1294,7 +1324,10 @@ function loadFileImages(path, no_load_diffs, callback){ | |||||||
| 		base += '/'+ CACHE_DIR | 		base += '/'+ CACHE_DIR | ||||||
| 
 | 
 | ||||||
| 		// XXX is this correct???
 | 		// XXX is this correct???
 | ||||||
| 		var res = loadLatestFile(base, path.split(base)[0], RegExp(path.split(base)[0]), null, 'Images:') | 		var res = statusNotify('Images:',  | ||||||
|  | 			loadLatestFile(base,  | ||||||
|  | 				path.split(base)[0],  | ||||||
|  | 				RegExp(path.split(base)[0]))) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	res.done(function(images){ | 	res.done(function(images){ | ||||||
| @ -1304,8 +1337,6 @@ function loadFileImages(path, no_load_diffs, callback){ | |||||||
| 
 | 
 | ||||||
| 	return res | 	return res | ||||||
| } | } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| // Save current images list...
 | // Save current images list...
 | ||||||
| //
 | //
 | ||||||
| // NOTE: this will save the merged images and remove the diff files...
 | // NOTE: this will save the merged images and remove the diff files...
 | ||||||
| @ -1342,7 +1373,10 @@ function loadFileMarks(path, callback){ | |||||||
| 	// default locations...
 | 	// default locations...
 | ||||||
| 	if(path == null){ | 	if(path == null){ | ||||||
| 		var base = normalizePath(CACHE_DIR) | 		var base = normalizePath(CACHE_DIR) | ||||||
| 		var res = loadLatestFile(base, MARKED_FILE_DEFAULT, MARKED_FILE_PATTERN, null, 'Marks:') | 		var res = statusNotify('Marks:', | ||||||
|  | 			loadLatestFile(base,  | ||||||
|  | 				MARKED_FILE_DEFAULT,  | ||||||
|  | 				MARKED_FILE_PATTERN)) | ||||||
| 	 | 	 | ||||||
| 	// explicit path...
 | 	// explicit path...
 | ||||||
| 	// XXX need to account for paths without a CACHE_DIR
 | 	// XXX need to account for paths without a CACHE_DIR
 | ||||||
| @ -1352,7 +1386,10 @@ function loadFileMarks(path, callback){ | |||||||
| 		base += '/'+ CACHE_DIR | 		base += '/'+ CACHE_DIR | ||||||
| 
 | 
 | ||||||
| 		// XXX is this correct???
 | 		// XXX is this correct???
 | ||||||
| 		var res = loadLatestFile(base, path.split(base)[0], RegExp(path.split(base)[0]), 'Marks:') | 		var res = statusNotify('Marks:', | ||||||
|  | 			loadLatestFile(base,  | ||||||
|  | 				path.split(base)[0],  | ||||||
|  | 				RegExp(path.split(base)[0]))) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	res.done(function(images){ | 	res.done(function(images){ | ||||||
| @ -1362,15 +1399,31 @@ function loadFileMarks(path, callback){ | |||||||
| 
 | 
 | ||||||
| 	return res | 	return res | ||||||
| } | } | ||||||
| // XXX save marks...
 | function saveFileMarks(name){ | ||||||
|  | 	name = name == null ? normalizePath(CACHE_DIR +'/'+ Date.timeStamp()) : name | ||||||
|  | 
 | ||||||
|  | 	dumpJSON(name + '-marked.json', MARKED) | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| function loadFileState(data_path, callback){ | // XXX add support for explicit filenames...
 | ||||||
| 	var base = data_path.split(CACHE_DIR)[0] | function loadFileState(path){ | ||||||
| 	base = base == data_path ? '.' : base | 	// XXX explicit data file path...
 | ||||||
|  | 	if(/\.json$/i.test(path)){ | ||||||
|  | 		// XXX at this 
 | ||||||
|  | 		var base = path.split(CACHE_DIR)[0] | ||||||
|  | 		base = base == path ? '.' : base | ||||||
|  | 	} else { | ||||||
|  | 		var base = path.split(CACHE_DIR)[0] | ||||||
|  | 		base = base == path ? '.' : base | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	var res = $.Deferred() | 	var res = $.Deferred() | ||||||
| 
 | 
 | ||||||
| 	$.getJSON(data_path) | 	statusNotify('Data:',  | ||||||
|  | 			loadLatestFile(path,  | ||||||
|  | 				DATA_FILE_DEFAULT,  | ||||||
|  | 				DATA_FILE_PATTERN)) | ||||||
| 		.done(function(json){ | 		.done(function(json){ | ||||||
| 			BASE_URL = base | 			BASE_URL = base | ||||||
| 
 | 
 | ||||||
| @ -1381,38 +1434,36 @@ function loadFileState(data_path, callback){ | |||||||
| 				IMAGES = json.images | 				IMAGES = json.images | ||||||
| 				MARKED = [] | 				MARKED = [] | ||||||
| 				reloadViewer() | 				reloadViewer() | ||||||
|  | 				res.resolve() | ||||||
| 
 | 
 | ||||||
| 			// version 2.0
 | 			// version 2.0
 | ||||||
| 			} else if(json.version == '2.0') { | 			} else if(json.version == '2.0') { | ||||||
| 				DATA = json | 				DATA = json | ||||||
| 				$.when( | 				$.when( | ||||||
| 					// load images...
 | 						// XXX load config...
 | ||||||
| 					loadFileImages(DATA.image_file == null ? | 						// load images...
 | ||||||
| 							normalizePath(DATA.image_file, base)  | 						loadFileImages(DATA.image_file == null ? | ||||||
| 							: null), | 								normalizePath(DATA.image_file, base)  | ||||||
| 					// load marks if available...
 | 								: null), | ||||||
| 					// XXX do we need to do this???
 | 						// load marks if available...
 | ||||||
| 					loadFileMarks()) | 						loadFileMarks()) | ||||||
| 						.done(function(){ | 					.done(function(){ | ||||||
| 							reloadViewer() | 						reloadViewer() | ||||||
| 							callback != null && callback() | 						res.resolve() | ||||||
| 							res.resolve() | 					}) | ||||||
| 						}) |  | ||||||
| 
 | 
 | ||||||
| 			// unknown format...
 | 			// unknown format...
 | ||||||
| 			} else { | 			} else { | ||||||
| 				showStatus('Unknown format.') | 				res.reject('unknown format.') | ||||||
| 				return |  | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
| 		}) | 		}) | ||||||
| 		.fail(function(){ | 		.fail(function(){ | ||||||
| 			showErrorStatus('Loading:', data_path) | 			res.reject('load_error', path) | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 	return res | 	return res | ||||||
| } | } | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| function saveFileState(name, no_normalize_path){ | function saveFileState(name, no_normalize_path){ | ||||||
| 	name = name == null ? Date.timeStamp() : name | 	name = name == null ? Date.timeStamp() : name | ||||||
| 
 | 
 | ||||||
| @ -1429,7 +1480,7 @@ function saveFileState(name, no_normalize_path){ | |||||||
| 
 | 
 | ||||||
| 	dumpJSON(name + '-data.json', DATA) | 	dumpJSON(name + '-data.json', DATA) | ||||||
| 	// XXX do we need to do this???
 | 	// XXX do we need to do this???
 | ||||||
| 	dumpJSON(name + '-marked.json', MARKED) | 	saveFileMarks(name) | ||||||
| 
 | 
 | ||||||
| 	// save the updated images...
 | 	// save the updated images...
 | ||||||
| 	if(IMAGES_UPDATED.length > 0){ | 	if(IMAGES_UPDATED.length > 0){ | ||||||
| @ -1456,78 +1507,84 @@ function saveFileState(name, no_normalize_path){ | |||||||
| //
 | //
 | ||||||
| // XXX this will not load the marks file...
 | // XXX this will not load the marks file...
 | ||||||
| // XXX make sure that save works...
 | // XXX make sure that save works...
 | ||||||
| // XXX might be good to split this into loadFileData and loadDir...
 | function loadDir(path){ | ||||||
| // XXX use loadLatestFile(...)...
 |  | ||||||
| function loadDir(path, raw_load){ |  | ||||||
| 	path = normalizePath(path) | 	path = normalizePath(path) | ||||||
| 	var orig_path = path | 	var orig_path = path | ||||||
| 	var data | 	var data | ||||||
| 
 | 
 | ||||||
|  | 	var res = $.Deferred() | ||||||
|  | 
 | ||||||
| 	showStatus('Loading:', path) | 	showStatus('Loading:', path) | ||||||
|  | 	res.notify('load', path) | ||||||
| 
 | 
 | ||||||
| 	var files = listDir(path) | 	var files = listDir(path) | ||||||
| 
 | 
 | ||||||
| 	if(files == null){ | 	if(files == null){ | ||||||
| 		showErrorStatus('No files in path: ' + path) | 		//showErrorStatus('No files in path: ' + path)
 | ||||||
| 		return | 		res.notify('load_error', path) | ||||||
|  | 		return res.reject() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if(!raw_load){ | 	if(files.indexOf(CACHE_DIR) >= 0){ | ||||||
| 		data = $.map(files, function(e){  | 		path = path +'/'+ CACHE_DIR | ||||||
| 			return DATA_FILE_PATTERN.test(e) ? e : null |  | ||||||
| 		}).sort().reverse()[0] |  | ||||||
| 		data = (data == null && files.indexOf(DATA_FILE_DEFAULT) >= 0) ? DATA_FILE_DEFAULT : data |  | ||||||
| 
 |  | ||||||
| 		// look in the cache dir...
 |  | ||||||
| 		if(data == null){ |  | ||||||
| 			path += '/' + CACHE_DIR |  | ||||||
| 
 |  | ||||||
| 			files = listDir(path) |  | ||||||
| 			if(files != null){ |  | ||||||
| 				data = $.map(listDir(path), function(e){  |  | ||||||
| 					return DATA_FILE_PATTERN.test(e) ? e : null |  | ||||||
| 				}).sort().reverse()[0] |  | ||||||
| 				data = (data == null && files.indexOf(DATA_FILE_DEFAULT) >= 0) ? DATA_FILE_DEFAULT : data |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// load the found data file...
 | 	statusNotify('Dir:', loadFileState(path)) | ||||||
| 	if(data != null){ | 		//.progress(function(action, data){
 | ||||||
| 		showStatus('Loading:', data) | 		//	res.notify(action, data)
 | ||||||
|  | 		//})
 | ||||||
|  | 		.done(function(){ | ||||||
|  | 			res.resolve() | ||||||
|  | 		}) | ||||||
|  | 		.fail(function(){ | ||||||
|  | 			statusNotify('Raw dir:', loadRawDir(orig_path)) | ||||||
|  | 				.done(function(){ | ||||||
|  | 					res.resolve() | ||||||
|  | 				}) | ||||||
|  | 				.fail(function(){ | ||||||
|  | 					res.reject() | ||||||
|  | 				}) | ||||||
| 				 | 				 | ||||||
| 		data = path + '/' + data |  | ||||||
| 
 |  | ||||||
| 		// marks...
 |  | ||||||
| 		// XXX see if there's a marks file...
 |  | ||||||
| 		MARKED = [] |  | ||||||
| 
 |  | ||||||
| 		return loadFileState(data) |  | ||||||
| 			.always(function(){ |  | ||||||
| 				showStatus('Done.') |  | ||||||
| 			}) |  | ||||||
| 
 |  | ||||||
| 	// load the dir as-is...
 |  | ||||||
| 	} else { |  | ||||||
| 		files = listDir(orig_path) |  | ||||||
| 		var image_paths = $.map(files, function(e){ |  | ||||||
| 			return IMAGE_PATTERN.test(e) ? e : null |  | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		if(image_paths.length == 0){ | 	return res | ||||||
| 			showErrorStatus('No images in:', orig_path) | } | ||||||
| 			return  |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		IMAGES = imagesFromUrls(image_paths) |  | ||||||
| 		DATA = dataFromImages(IMAGES) |  | ||||||
| 		BASE_URL = orig_path |  | ||||||
| 		DATA.ribbons = ribbonsFromFavDirs() |  | ||||||
| 		MARKED = [] |  | ||||||
| 
 | 
 | ||||||
| 		reloadViewer() | // XXX check if we need to pass down sorting settings to the generators...
 | ||||||
| 		showStatus('Done.') | function loadRawDir(path){ | ||||||
|  | 	var files = listDir(path) | ||||||
|  | 
 | ||||||
|  | 	var res = $.Deferred() | ||||||
|  | 
 | ||||||
|  | 	var image_paths = $.map(files, function(e){ | ||||||
|  | 		return IMAGE_PATTERN.test(e) ? e : null | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	if(image_paths.length == 0){ | ||||||
|  | 		//showErrorStatus('No images in:', path)
 | ||||||
|  | 		res.notify('load_error', path) | ||||||
|  | 		return res.reject() | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	BASE_URL = path | ||||||
|  | 
 | ||||||
|  | 	IMAGES = imagesFromUrls(image_paths) | ||||||
|  | 	res.notify('loaded', 'images.') | ||||||
|  | 
 | ||||||
|  | 	DATA = dataFromImages(IMAGES) | ||||||
|  | 	res.notify('loaded', 'data.') | ||||||
|  | 
 | ||||||
|  | 	DATA.ribbons = ribbonsFromFavDirs() | ||||||
|  | 	res.notify('loaded', 'fav dirs.') | ||||||
|  | 
 | ||||||
|  | 	MARKED = [] | ||||||
|  | 
 | ||||||
|  | 	sortImagesByDate() | ||||||
|  | 
 | ||||||
|  | 	reloadViewer() | ||||||
|  | 
 | ||||||
|  | 	return res.resolve() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user