Files
Iris/src/js/helpers.js
2017-10-30 16:59:19 +13:00

665 lines
15 KiB
JavaScript
Executable File

export let isTouchDevice = function(){
return 'ontouchstart' in document.documentElement
}
export let sizedImages = function(images){
var sizes = {
small: false,
medium: false,
large: false,
huge: false
}
if (images.length <= 0) return sizes;
for (var i = 0; i < images.length; i++){
let image = images[i]
// Mopidy image object
if (image.__model__ && image.__model__ == 'Image'){
if (image.width < 400){
sizes.small = image.url;
}else if (image.width < 800){
sizes.medium = image.url;
}else if (image.width < 1000){
sizes.large = image.url;
} else {
sizes.huge = image.url;
}
// Mopidy image string
} else if (typeof(image) == 'string'){
sizes.small = image
// spotify-styled images
} else if (image.width !== undefined){
if (image.width < 400){
sizes.small = image.url;
}else if (image.width < 800){
sizes.medium = image.url;
}else if (image.width < 1000){
sizes.large = image.url;
} else {
sizes.huge = image.url;
}
// lastfm-styled images
} else if (image.size !== undefined){
switch(image.size){
case 'mega':
case 'extralarge':
sizes.huge = image['#text']
break
case 'large':
sizes.large = image['#text']
break
case 'medium':
sizes.medium = image['#text']
break
case 'small':
sizes.small = image['#text']
break
}
}
}
if (!sizes.small){
if (sizes.medium) sizes.small = sizes.medium
else if (sizes.large) sizes.small = sizes.large
else if (sizes.huge) sizes.small = sizes.huge
else sizes.small = null
}
if (!sizes.medium){
if (sizes.large) sizes.medium = sizes.large
else if (sizes.huge) sizes.medium = sizes.huge
else sizes.medium = sizes.small
}
if (!sizes.large) sizes.large = sizes.medium;
if (!sizes.huge) sizes.huge = sizes.large;
return sizes;
}
/**
* Digest an array of Mopidy image objects into a universal format. We also re-write
* image URLs to be absolute to the mopidy server (required for proxy setups).
*
* @param mopidy = obj (mopidy store object)
* @param images = array
* @return array
**/
export let digestMopidyImages = function(mopidy, images){
var digested = [];
for (var i = 0; i < images.length; i++){
// Image object (ie from images.get)
if (typeof images[i] === 'object'){
// Accommodate backends that provide URIs vs URLs
var url = images[i].url
if (!url && images[i].uri){
url = images[i].uri
}
// Amend our URL
images[i].url = url
// Replace local images to point directly to our Mopidy server
if (url.startsWith('/images/')){
url = '//'+mopidy.host+':'+mopidy.port+url
}
// String-based image
} else if (typeof images[i] === 'string'){
// Replace local images to point directly to our Mopidy server
if (images[i].startsWith('/images/')){
images[i] = '//'+mopidy.host+':'+mopidy.port+images[i]
}
}
digested.push(images[i])
}
return digested
}
export let generateGuid = function(format = 'xxxxxxxxxxxx'){
return format.replace(/[xy]/g, function(c){
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}
export let getCurrentPusherConnection = function(connections, connectionid){
function isCurrentConnection(connection){
return connection.connectionid == newProps.pusher.connectionid;
}
var currentConnection = newProps.pusher.connections.find(isCurrentConnection);
if (!currentConnection ) return false;
return currentConnection;
}
/**
* Get a track's icon
* @param track object
* @return string
**/
export let getTrackIcon = function(current_track = false, core = false){
if (!core) return false
if (!current_track) return false
if (typeof(current_track.uri) == 'undefined') return false
if (typeof(core.tracks[current_track.uri]) === 'undefined') return false
var track = core.tracks[current_track.uri]
if (!track.images) return false
return sizedImages(track.images).small
}
/**
* Format tracks into our universal format
*
* @param tracks = array
* @return array
**/
export let formatTracks = function(tracks){
if (!tracks || tracks === undefined){
return null;
}
// Handle single recoreds
var singular = false;
if (tracks.constructor !== Array){
tracks = [tracks];
singular = true;
}
var formatted = [];
for (var i = 0; i < tracks.length; i++){
// Nested track object (eg in spotify playlist)
if (tracks[i].track){
var track = Object.assign({}, tracks[i].track);
// Copy supporting values
if (tracks[i].added_by){
track.added_by = tracks[i].added_by;
}
if (tracks[i].added_at){
track.added_at = tracks[i].added_at;
}
if (tracks[i].tlid){
track.tlid = tracks[i].tlid;
}
} else {
var track = Object.assign({}, tracks[i]);
}
if (track.duration_ms){
track.duration = track.duration_ms;
} else if (track.length){
track.duration = track.length;
}
if (track.track_no){
track.track_number = track.track_no;
}
if (track.disc_no){
track.disc_number = track.disc_no;
}
if (track.release_date){
track.date = track.release_date;
}
// Copy images from albums (if applicable)
if (track.album && track.album.images){
if (!track.images || track.images.length > 0){
track.images = track.album.images;
}
}
formatted.push(track);
}
if (singular){
return formatted[0];
} else {
return formatted;
}
}
/**
* Figure out a URI's source namespace
* @param uri = string
**/
export let uriSource = function(uri){
var exploded = uri.split(':');
return exploded[0]
}
export let sourceIcon = function(uri,source = null){
if (uri) source = uriSource(uri)
switch(source){
case 'local':
case 'm3u':
return 'folder'
case 'gmusic':
return 'google'
case 'podcast':
case 'podcast+file':
case 'podcast+http':
case 'podcast+https':
case 'podcast+itunes':
return 'podcast'
case 'tunein':
case 'somafm':
case 'dirble':
return 'microphone'
default:
return source
}
}
/**
* Get an element from a URI
* @param element = string, the element we wish to extract
* @param uri = string
**/
export let getFromUri = function(element,uri){
var exploded = uri.split(':');
var namespace = exploded[0]
switch (element){
case 'mbid':
var index = exploded.indexOf('mbid')
if (index > -1 ) return exploded[index+1]
break
case 'artistid':
if (exploded[1] == 'artist'){
return exploded[2]
}
break
case 'albumid':
if (exploded[1] == 'album'){
return exploded[2]
}
break
case 'playlistid':
if (exploded[1] == 'playlist'){
return exploded[2]
} else if (exploded[1] == 'user' && exploded[3] == 'playlist'){
return exploded[4]
}
break
case 'playlistowner':
if (exploded[1] == 'user' && exploded[3] == 'playlist'){
return exploded[2]
}
break
case 'trackid':
if (exploded[1] == 'track'){
return exploded[2]
}
break
case 'userid':
if (exploded[1] == 'user'){
return exploded[2]
}
break
case 'genreid':
if (exploded[1] == 'genre'){
return exploded[2]
}
break
case 'seeds':
if (exploded[1] == 'discover'){
return exploded[2]
}
break
}
return null
}
/**
* Identify what kind of asset a URI is (playlist, album, etc)
* @param uri = string
* @return string
**/
export let uriType = function(uri){
var exploded = uri.split(':')
if (exploded[0] == 'm3u'){
return 'playlist'
}
switch (exploded[1]){
case 'track':
case 'artist':
case 'album':
case 'playlist':
case 'search':
case 'genre':
case 'discover':
return exploded[1]
break
case 'user':
if (exploded.length > 3 && exploded[3] == 'playlist'){
return 'playlist'
}
return exploded[1]
break
}
return null;
}
/**
* Convert a raw URI into a object index-friendly format. Primarily used for loading local playlists
* @param $uri = string
* @return string
**/
export let indexFriendlyUri = function (uri){
var output = encodeURI(uri)
output = output.replace("'",'%27')
return output
}
/**
* Digest an array of objects and pull into simple array of one property
*
* @param property = string
* @param items = Array
* @return Array
**/
export let arrayOf = function(property, items){
let array = []
for (let i = 0; i < items.length; i++){
array.push(items[i][property])
}
return array
}
/**
* Merge duplicated items in an array
*
* @param list Array the unclean array
* @param key string = the unique key (id, uri, tlid, etc)
**/
export let mergeDuplicates = function(list, key){
var clean_list = [];
var keyed_list = {};
for(var i in list){
var item = list[i]
if (item[key] in keyed_list){
item = Object.assign({}, keyed_list[item[key]], item)
}
keyed_list[item[key]] = item;
}
for(i in keyed_list){
clean_list.push(keyed_list[i]);
}
return clean_list;
}
/**
* Remove duplicate items in a simple array
*
* @param list Array the unclean array
**/
export let removeDuplicates = function(array){
var unique = [];
for (var i in array){
if (unique.indexOf(array[i]) <= -1){
unique.push(array[i])
}
}
return unique;
}
/**
* Apply a partial text search on an array of objects
*
* @param field = string (the field we're to search)
* @param value = string (the value to find)
* @param array = array of objects to search
* @return array
**/
export let applyFilter = function(field, value, array){
var results = []
for (var i = 0; i < array.length; i++){
if (array[i][field] && array[i][field].toLowerCase().includes(value.toLowerCase())){
results.push(array[i])
}
}
return results
}
/**
* Convert a list of indexes to a useable range
* We ignore stragglers, and only attend to the first 'bunch' of consecutive indexes
*
* @param indexes array of int
**/
export let createRange = function (indexes){
// sort our indexes smallest to largest
function sortAsc(a,b){
return a - b
}
indexes.sort(sortAsc);
// iterate indexes to build the first 'bunch'
var first_bunch = []
var previous_index = false
for(var i = 0; i < indexes.length; i++){
if (!previous_index || previous_index == indexes[i]-1){
first_bunch.push(indexes[i])
previous_index = indexes[i]
}
// TODO: break when we find an integer step for better performance
}
return {
start: first_bunch[0],
length: first_bunch.length
}
}
/**
* Sort an array of objects
* @param array = array to sort
* @param property = string to sort by
* @param reverse = boolean
* @return array
**/
export let sortItems = function (array, property, reverse = false){
function compare(a,b){
var a_value = a
var a_property_split = property.split('.')
for (var i = 0; i < a_property_split.length; i++){
if (typeof(a_value[a_property_split[i]]) === 'undefined'){
a_value = false
break
} else {
a_value = a_value[a_property_split[i]]
}
}
var b_value = b
var b_property_split = property.split('.')
for(var i = 0; i < b_property_split.length; i++){
if (typeof(b_value[b_property_split[i]]) === 'undefined'){
b_value = false
break
} else {
b_value = b_value[b_property_split[i]]
}
}
if (typeof(a_value) === 'boolean'){
if (a_value && !b_value) return -1
if (!a_value && b_value) return 1
return 0
}else if (typeof(a_value) === 'string'){
if (!a_value || !b_value ) return 0
if (a_value.toLowerCase() > b_value.toLowerCase()) return 1
if (a_value.toLowerCase() < b_value.toLowerCase()) return -1
return 0
} else {
if (parseInt(a_value) > parseInt(b_value)) return 1
if (parseInt(a_value) < parseInt(b_value)) return -1
return 0
}
}
var sorted = Object.assign([], array.sort(compare))
if (reverse ) sorted.reverse()
return sorted
}
/**
* Figure out if a value is a number
* @param data = mixed
* @return boolean
**/
export let isNumeric = function (data){
return !isNaN(parseFloat(data)) && isFinite(data)
}
/**
* Set window title
* @param track
**/
export let setWindowTitle = function (track = false, play_state = false){
var title = 'No track playing'
if (track){
var icon = '\u25A0 ';
var artist_string = '';
if (track.artists){
for (var i = 0; i < track.artists.length; i++){
if (artist_string != ''){
artist_string += ', ';
}
artist_string += track.artists[i].name
}
}
if (play_state && play_state == 'playing'){
icon = '\u25B6 ';
}
title = icon +' '+ track.name +' - '+ artist_string;
}
document.title = title
}
/**
* Detect if an item is in the loading queue. We simply loop all load items to
* see if any items contain our searched key.
*
* TODO: Explore performance of this
* TODO: Allow wildcards
*
* @param load_queue = obj (passed from store)
* @param key = string (the string to lookup)
* @return boolean
**/
export let isLoading = function(load_queue = [], keys = []){
// Loop all of our load queue items
for (var load_queue_key in load_queue){
// Make sure it's not a root object method
if (load_queue.hasOwnProperty(load_queue_key)){
// Loop all the keys we're looking for
for (var i = 0; i < keys.length; i++){
if (load_queue[load_queue_key].includes(keys[i])){
return true
}
}
}
}
return false
}
/**
* Get indexed record(s) by URI from our asset index
*
* @param store = obj
* @param uris = mixed (array or string)
* @return array
**/
export let getIndexedRecords = function(index, uris){
var records = []
// Wrap in array, if we've only got one URI
if (!uris instanceof Array){
uris = [uris]
}
for (var i = 0; i < uris.length; i++){
if (index.hasOwnProperty(uris[i])){
records.push(index[uris[i]])
}
}
return records
}