/* Demo application using Ox.AudioPlayer. */ 'use strict'; Ox.load('UI', function() { var app = { $ui: {}, data: {}, site: { listColumns: [ { format: function(value, data) { return Ox.Element('') .addClass('symbol') .attr({src: Ox.UI.getImageURL( 'symbol' + Ox.toTitleCase( value || (data.query ? 'find': 'click') ) )}); }, id: 'icon', title: 'icon', visible: true, width: 16 }, { id: 'title', title: 'Title', visible: true, width: 136 }, { align: 'right', id: 'items', title: 'Items', visible: true, width: 40 } ], lists: [ {icon: 'audio', id: 'Music', index: 0, items: 3, title: 'Music'}, {icon: 'playlist', id: 'Playing', index: 1, items: 0, title: 'Playing'} ], sort: [ {key: 'artist', operator: '+'}, {key: 'year', operator: '+'}, {key: 'disc', operator: '+'}, {key: 'track', operator: '+'} ], sums: ['duration', 'size'], trackColumns: [ { format: function(value) { return value ? Ox.Element('') .addClass('symbol') .attr({src: Ox.UI.getImageURL( app.user.ui.paused ? 'symbolUnmute' : 'symbolMute' )}) : Ox.Element(); }, id: 'playing', removable: false, title: 'Playing', titleImage: 'mute', visible: true, width: 16 }, { format: function(value) { return value ? Ox.Element('') .addClass('symbol') .attr({src: Ox.UI.getImageURL( 'symbolCheck' )}) : Ox.Element(); }, id: 'checked', removable: false, title: 'Checked', titleImage: 'check', visible: true, width: 16 }, { format: function(value) { return value ? Ox.Element('') .addClass('image') .attr({src: value}) : Ox.Element() }, id: 'artwork', operator: '+', title: 'Artwork', titleImage: 'icon', visible: true, width: 16 }, { align: 'right', id: 'disc', operator: '+', title: 'Disc', titleImage: 'circle', width: 32 }, { align: 'right', id: 'track', operator: '+', title: 'Track', titleImage: 'square', width: 32 }, { id: 'title', operator: '+', removable: false, title: 'Title', visible: true, width: 192 }, { id: 'artist', operator: '+', removable: false, title: 'Artist', visible: true, width: 192 }, { id: 'album', operator: '+', removable: false, title: 'Album', visible: true, width: 192 }, { align: 'right', id: 'year', operator: '+', title: 'Year', visible: true, width: 64 }, { align: 'right', format: function(value) { return Ox.formatDuration(value).substr(4); }, id: 'duration', operator: '-', title: 'Time', visible: true, width: 64 }, { align: 'right', format: function(value) { return Ox.formatValue(value, 'B'); }, id: 'size', operator: '-', title: 'Size', visible: true, width: 64 }, { align: 'right', format: function(value) { return Math.round(value) + ' kbps' }, id: 'bitrate', operator: '-', title: 'Bitrate', visible: true, width: 64 }, { id: 'genre', operator: '+', title: 'Genre', visible: true, width: 128 } ] }, ui: {}, user: { lists: [ { id: 'favorites', index: 0, items: 0, title: 'Favorites', type: 'static' }, { id: 'hip-hop', index: 1, items: 1, query: { conditions: [ {key: 'genre', operator: '==', value: 'Hip-Hop'} ], operator: '&' }, title: 'Hip-Hop' } ], ui: { artwork: 'selected', coverSize: 64, list: 'Music', paused: true, query: { conditions: [], operator: '&', }, selectedAlbum: [], selectedArtist: [], selectedTrack: [], showSidebar: true, sidebarSize: 192, track: 0, tracklistSize: 256, view: 'tracks' } }, utils: {} }; app.user.ui.sort = Ox.clone(app.site.sort, true); app.load = function() { Ox.getJSON('json/music.json', function(data) { app.data.tracks = data; app.data.artists = app.utils.getArtists(); app.data.albums = app.utils.getAlbums(); app.$ui.appPanel = app.ui.appPanel().appendTo(Ox.$body); Ox.$window.bind({resize: app.utils.resizeWindow}); }); }; app.UI = (function() { var that = {}; that.set = function() { var args = Ox.makeObject(arguments), keys = [], previousUI = Ox.clone(app.user.ui, true); Ox.forEach(args, function(value, key) { if (!Ox.isEqual(value, previousUI[key])) { app.user.ui[key] = value; keys.push(key); } }); keys.forEach(function(key) { var data = { previousValue: previousUI[key], value: app.user.ui[key] } Ox.print('ui_' + key.toLowerCase(), JSON.stringify(app.user.ui[key])) Ox.forEach(app.$ui, function($elements) { Ox.makeArray($elements).forEach(function($element) { $element.triggerEvent('ui_' + key.toLowerCase(), data); }); }); }); }; return that; }()); app.ui.appPanel = function() { return Ox.SplitPanel({ elements: [ {element: app.$ui.mainMenu = app.ui.mainMenu(), size: 20}, {element: app.$ui.mainPanel = app.ui.mainPanel()} ], orientation: 'vertical' }); }; app.ui.mainMenu = function() { return Ox.MainMenu({ menus: [ { id: '0xcd', title: '0xCD', items: [ {id: 'about', title: 'About 0xCD'} ] } ] }) .bindEvent({ click: function(data) { if (data.id == 'about') { app.$ui.aboutDialog = ( app.$ui.aboutDialog || app.ui.aboutDialog() ).open(); } } }); }; app.ui.aboutDialog = function() { var $element = Ox.Dialog({ buttons: [ Ox.Button({ id: 'close', title: 'Close' }) .bindEvent({ click: function() { $element.close(); } }) ], content: Ox.Element() .html('Audio Player Example'), height: 256, keys: {enter: 'close', escape: 'close'}, title: 'About 0xCD', width: 512 }); return $element; }; app.ui.mainPanel = function() { return Ox.SplitPanel({ elements: [ {element: app.ui.$toolbar = app.ui.toolbar(), size: 39}, {element: app.ui.$bothPanel = app.ui.bothPanel()} ], orientation: 'vertical' }); }; app.ui.toolbar = function() { return Ox.Bar({size: 39}) .attr({id: 'toolbar'}) .append(app.$ui.audioPlayer = app.ui.audioPlayer()) .append(app.$ui.viewLabel = app.ui.viewLabel()) .append(app.$ui.viewButtons = app.ui.viewButtons()) .append(app.$ui.findSelect = app.ui.findSelect()) .append(app.$ui.findLabel = app.ui.findLabel()) .append(app.$ui.findInput = app.ui.findInput()); }; app.ui.audioPlayer = function() { return Ox.AudioPlayer({ audio: app.data.tracks, paused: true, width: window.innerWidth - 190 }) .css({left: '4px', top: '4px'}) .bindEvent({ paused: function(data) { var id = app.data.tracks[app.user.ui.track].file app.user.ui.paused = data.paused; app.$ui.trackList.value(id, 'playing', false); app.$ui.trackList.value(id, 'playing', true); }, track: function(data) { var id = app.data.tracks[app.user.ui.track].file app.$ui.trackList.value(id, 'playing', false); app.user.ui.track = data.track; $app.$ui.trackList.value(id, 'playing', true); } }); }; app.ui.viewLabel = function() { return Ox.Label({ textAlign: 'center', title: 'Tracks', width: 50 }) .attr({id: 'viewLabel'}); }; app.ui.viewButtons = function() { return Ox.ButtonGroup({ buttons: [ {id: 'tracks', title: 'list', tooltip: 'View Tracks'}, {id: 'albums', title: 'grid', tooltip: 'View Albums'}, {id: 'artists', title: 'columns', tooltip: 'View Artists'} ], max: 1, min: 1, selectable: true, type: 'image' }) .attr({id: 'viewButtons'}) .css({ position: 'absolute', right: '136px', top: '19px' }) .bindEvent({ change: function(data) { app.user.ui.view = data.value; app.$ui.viewLabel.options({title: Ox.toTitleCase(app.user.ui.view)}); app.$ui.rightPanel.replaceElement(0, app.$ui.musicPanel = app.ui.musicPanel()); } }); }; app.ui.findSelect = function() { return Ox.Select({ items: [ {id: 'all', title: 'Find: All'}, {id: 'tracks', title: 'Find: Tracks'}, {id: 'artists', title: 'Find: Artists'}, {id: 'albums', title: 'Find: Albums'} ], overlap: 'left', type: 'image' }) .attr({id: 'findSelect'}) .bindEvent({ change: function(data) { app.$ui.findLabel.options({title: data.title}); } }); }; app.ui.findLabel = function() { return Ox.Label({ title: 'Find: All', width: 112 }) .attr({id: 'findLabel'}); }; app.ui.findInput = function() { return Ox.Input({ clear: true, width: 128 }) .attr({id: 'findInput'}); }; app.ui.bothPanel = function() { return Ox.SplitPanel({ elements: [ { collapsible: true, element: app.$ui.leftPanel = app.ui.leftPanel(), resizable: true, resize: [128, 192, 256, 320, 384], size: app.user.ui.sidebarSize }, { element: app.$ui.rightPanel = app.ui.rightPanel() } ], orientation: 'horizontal' }); } app.ui.leftPanel = function() { return Ox.SplitPanel({ elements: [ { element: app.$ui.listsPanel = app.ui.listsPanel() }, { collapsible: true, element: app.$ui.artworkPanel = app.ui.artworkPanel(), size: app.user.ui.sidebarSize + 16 } ], orientation: 'vertical' }) .bindEvent({ resize: function(data) { app.user.ui.sidebarSize = data.size; [app.$ui.siteLists, app.$ui.userLists].forEach(function($list) { $list.setColumnWidth('title', app.user.ui.sidebarSize - 56); }); app.$ui.listsList app.$ui.leftPanel.size(1, app.user.ui.sidebarSize + 16); app.$ui.artwork.css({ width: app.user.ui.sidebarSize + 'px', height: app.user.ui.sidebarSize + 'px' }); //$musicList.size(); } }) }; app.ui.listsPanel = function() { return Ox.SplitPanel({ elements: [ { element: app.$ui.siteLists = app.ui.siteLists(), size: app.site.lists.length * 16 }, { element: app.$ui.userLists = app.ui.userLists() } ], orientation: 'vertical' }); }; app.ui.siteLists = function() { var $element = Ox.TableList({ columns: app.site.listColumns, items: app.site.lists, max: 1, selected: [app.user.ui.list], sort: [{key: 'index', operator: '+'}], unique: 'id' }) .bindEvent({ select: function(data) { app.UI.set({list: data.ids[0] || app.site.lists[0].id}); }, selectafter: function() { app.UI.set({list: app.user.lists[0].id}); app.$ui.userLists.gainFocus(); }, ui_list: function(data) { $element.options({ selected: data.value[0] == data.value[0].toUpperCase() ? [data.value] : [] }); } }); $element.update = function() { return $element.setColumnWidth('title', app.user.ui.sidebarSize - 56); }; return $element.update(); }; app.ui.userLists = function() { var $element = Ox.TableList({ columns: app.site.listColumns, items: app.user.lists, keys: ['query'], max: 1, selected: [app.user.ui.list], sort: [{key: 'index', operator: '+'}], sortable: true, unique: 'id' }) .bindEvent({ move: function(data) { // ... }, select: function(data) { app.UI.set({list: data.ids[0] || app.site.lists[0].id}); }, selectbefore: function() { app.UI.set({list: Ox.last(app.site.lists).id}); app.$ui.siteLists.gainFocus(); }, ui_list: function(data) { $element.options({ selected: data.value[0] == data.value[0].toLowerCase() ? [data.value] : [] }); } }); $element.update = function() { return $element.setColumnWidth('title', app.user.ui.sidebarSize - 56); }; return $element.update(); }; app.ui.artworkPanel = function() { return Ox.SplitPanel({ elements: [ {element: app.$ui.artworkbar = app.ui.artworkbar(), size: 16}, {element: app.$ui.artwork = app.ui.artwork()} ], orientation: 'vertical' }); }; app.ui.artworkbar = function() { return Ox.Bar({size: 16}) .append(app.$ui.artworkText = app.ui.artworkText()) .bindEvent({ anyclick: function() { app.UI.set({ artwork: app.user.ui.artwork == 'playing' ? 'selected' : 'playing' }); } }); }; app.ui.artworkText = function() { var $element = Ox.Element() .attr({id: 'artworkText'}) .bindEvent({ ui_artwork: function() { $element.update(); } }); $element.update = function() { $element.html( app.user.ui.artwork == 'playing' ? 'Now Playing' : 'Selected Item' ); return $element; }; return $element.update(); }; app.ui.artwork = function() { var $element = Ox.Element('') .attr({src: app.data.tracks[0].artwork}) .bindEvent({ ui_selectedalbum: function(data) { if (app.user.ui.artwork == 'selected') { $element.attr({ src: data.value.length ? Ox.getObjectById(app.data.albums, data.value[0]).artwork : '' }); } }, ui_selectedartist: function(data) { if (app.user.ui.artwork == 'selected') { $element.attr({ src: data.value.length ? Ox.getObjectById(app.data.artists, data.value[0]).artwork : '' }); } }, ui_selectedtrack: function(data) { if (app.user.ui.artwork == 'selected') { $element.attr({ src: data.value.length ? Ox.getObjectById(app.data.tracks, data.value[0]).artwork : '' }); } } }); $element.update = function() { $element.css({ width: app.user.ui.sidebarSize + 'px', height: app.user.ui.sidebarSize + 'px' }); return $element; }; return $element.update(); }; app.ui.rightPanel = function() { return Ox.SplitPanel({ elements: [ {element: app.$ui.musicPanel = app.ui.musicPanel()}, {element: app.$ui.statusbar = app.ui.statusbar(), size: 16} ], orientation: 'vertical' }); }; app.ui.musicPanel = function() { return app.ui[app.user.ui.view + 'Panel'](); }; app.ui.tracksPanel = function() { return Ox.SplitPanel({ elements: [ { collapsible: true, element: app.$ui.albumBrowser = app.ui.albumBrowser(), size: 112 + Ox.UI.SCROLLBAR_SIZE }, { element: app.$ui.trackList = app.ui.trackList() } ], orientation: 'vertical' }); }; app.ui.albumBrowser = function() { var $element = Ox.IconList({ fixedRatio: 1, item: function(data, sort, size) { return { height: size, id: data.id, info: data.year, title: [data.artist, data.title].join(' — '), url: data.artwork, width: size }; }, items: app.data.albums, max: 1, orientation: 'horizontal', pageLength: 100, query: {conditions: [], operator: '&'}, selected: [], size: 64, sort: [{key: 'index', operator: '+'}], unique: 'id' }) .bindEvent({ ui_sort: function() { $element.update(); } }); $element.update = function() { app.utils.getTrackAlbums(function(albums) { $element.options({items: albums}); }); return $element; } return $element; }; app.ui.trackList = function() { return Ox.TableList({ columns: app.site.trackColumns, columnsMovable: true, columnsRemovable: true, columnsResizable: true, columnsVisible: true, items: app.data.tracks, scrollbarVisible: true, sort: Ox.clone(app.user.ui.sort, true), sums: app.site.sums, unique: 'id' }) .bindEvent({ init: function(data) { app.$ui.statusText.update(data); app.utils.getTrackAlbums(function(data) { // ... }); }, open: function(data) { var file = data.ids[0], id = app.data.tracks[app.user.ui.track].id, index = Ox.getIndexById(app.data.tracks, id); if (index == app.user.ui.track) { app.$ui.trackList.value(id, 'playing', true); app.$ui.audioPlayer.options({paused: false, position: 0}); } else { app.$ui.trackList.value(id, 'playing', false); app.user.ui.track = index; app.$ui.audioPlayer.options({paused: false, track: app.user.ui.track}); app.$ui.trackList.value(id, 'playing', true); } }, openpreview: function(data) { app.user.ui.paused = !app.user.ui.paused; app.$ui.audioPlayer.options({paused: app.user.ui.paused}); app.$ui.trackList.closePreview(); }, select: function(data) { app.UI.set({selectedTrack: data.ids}); }, sort: function(data) { app.UI.set({sort: [data]}); } }); }; app.ui.albumsPanel = function() { return Ox.SplitPanel({ elements: [ { element: app.$ui.albumList = app.ui.albumList() }, { element: app.$ui.trackBrowser = app.ui.trackBrowser(), resizable: true, resize: [192, 256, 320], size: app.user.ui.tracklistSize } ], orientation: 'horizontal' }); }; app.ui.albumList = function() { return Ox.IconList({ fixedRatio: 1, item: function(data, sort, size) { var key = Ox.contains(['artist', 'title'], sort[0].key) ? 'year' : sort[0].key, column = Ox.getObjectById(app.site.trackColumns, key), info = (column.format || Ox.identity)(data[key]); return { height: size, id: data.id, info: info, title: [data.artist, data.title].join(' — '), url: data.artwork, width: size }; }, items: app.data.albums, pageLength: 120, query: {conditions: [], operator: '&'}, selected: [], size: 128, sort: Ox.clone(app.user.ui.sort, true), unique: 'id' }) .bindEvent({ select: function(data) { app.UI.set({selectedAlbum: data.ids}); }, openpreview: function() { } }); }; app.ui.trackBrowser = function() { return Ox.TableList({ columns: [ Ox.getObjectById(app.site.trackColumns, 'playing'), Ox.getObjectById(app.site.trackColumns, 'checked'), Ox.getObjectById(app.site.trackColumns, 'artwork'), Ox.extend( Ox.getObjectById(app.site.trackColumns, 'track'), {visible: true} ), Ox.extend( Ox.getObjectById(app.site.trackColumns, 'title'), {width: app.user.ui.tracklistSize - 144 - Ox.UI.SCROLLBAR_SIZE} ), Ox.getObjectById(app.site.trackColumns, 'duration') ], items: app.data.tracks, query: {conditions: [], operator: '&'}, scrollbarVisible: true, selected: [], sort: Ox.clone(app.site.sort, true), unique: 'id' }) .bindEvent({ resize: function(data) { app.user.ui.tracklistSize = data.size; app.$ui.trackBrowser.setColumnWidth( 'title', app.user.ui.tracklistSize - 144 - Ox.UI.SCROLLBAR_SIZE ); }, ui_selectedalbum: function(data) { app.$ui.trackBrowser.options({ items: data.value.length ? Ox.flatten( data.value.map(function(id) { return Ox.getObjectById(app.data.albums, id).items; }) ) : [] }); } }); }; app.ui.artistsPanel = function() { return Ox.ColumnList({ columns: [ { id: 'artists', item: function(data, width) { data.artwork = data.artwork || ''; data.years = data.years || []; var $item = $('
') .addClass('item') .css({width: width + 'px'}); $('') .addClass('itemIcon') .attr({src: data.artwork}) .appendTo($item); $('
') .addClass('itemTitle') .css({width: width - 36 + 'px'}) .html(data.name ? data.name : '') .appendTo($item); $('
') .addClass('itemInfo OxLight') .css({width: width - 36 + 'px'}) .html( [ data.years.join('-'), Ox.formatCount(data.albums, 'album'), Ox.formatCount(data.tracks, 'track'), Ox.formatDuration(data.duration), Ox.formatValue(data.size, 'B') ].join(' — ') ) .appendTo($item); return $item; }, itemHeight: 32, keys: ['albums', 'artwork', 'duration', 'name', 'size', 'tracks', 'years'] }, { id: 'albums', item: function(data, width) { data.artwork = data.artwork || ''; data.tracks = data.tracks || [{duration: 0}]; data.year = data.year || '????'; var $item = $('
') .addClass('item') .css({width: width + 'px'}); $('') .addClass('itemIcon') .attr({src: data.artwork}) .appendTo($item); $('
') .addClass('itemTitle') .css({width: width - 36 + 'px'}) .html(data.title ? data.title : '') .appendTo($item); $('
') .addClass('itemInfo OxLight') .css({width: width - 36 + 'px'}) .html( [ data.year, Ox.formatCount(data.tracks, 'track'), Ox.formatDuration(data.duration), Ox.formatValue(data.size, 'B') ].join(' — ') ) .appendTo($item); return $item; }, itemHeight: 32, keys: ['artwork', 'duration', 'size', 'title', 'tracks', 'year'] }, { id: 'tracks', item: function(data, width) { var $item = $('
') .addClass('item') .css({width: width + 'px'}); $('') .addClass('itemIcon') .attr({src: data.artwork}) .appendTo($item); $('
') .addClass('itemTitle') .css({width: width - 36 + 'px'}) .html(data.title ? data.title : '') .appendTo($item); $('
') .addClass('itemInfo OxLight') .css({width: width - 36 + 'px'}) .html( [ Ox.formatDuration(data.duration), Ox.formatValue(data.size, 'B'), Math.round(data.bitrate) + ' kbps' ].join(' — ') ) .appendTo($item); return $item; }, itemHeight: 32, keys: ['artwork', 'bitrate', 'checked', 'duration', 'playing', 'size', 'title'] } ], items: app.data.artists, list: 'custom', width: window.innerWidth - app.user.ui.sidebarSize - 1 }) .bindEvent({ resize: function(data) { $panel.options({width: data.size}) }, select: function(data) { app.UI.set( 'selected' + Ox.toTitleCase(data.id.slice(0, -1)), data.ids ); } }); }; app.ui.statusbar = function() { return Ox.Bar({size: 16}) .append(app.$ui.statusText = app.ui.statusText()); }; app.ui.statusText = function() { var $element = Ox.Element().attr({id: 'statusText'}); $element.update = function(data) { return $element.html( data ? [ Ox.formatCount(data.items, 'track'), Ox.formatDuration(data.duration), Ox.formatValue(data.size, 'B') ].join(' — ') : 'Loading...' ); }; return $element.update(); }; app.utils.getAlbums = function() { var albums = [] app.data.artists.forEach(function(artist) { artist.items.forEach(function(album) { albums.push(Ox.extend(album, {artist: artist.name})); }); }); return albums; }; app.utils.getArtists = function() { var artists = [], tree = {}; app.data.tracks.forEach(function(track, index) { var artist = track.artist, album = track.year + ' ' + track.album, title = Ox.pad(track.disc, 2) + '\n' + Ox.pad(track.track, 2) + '\n' + track.title; if (!tree[artist]) { tree[artist] = {}; } if (!tree[artist][album]) { tree[artist][album] = {}; } if (!tree[artist][album][title]) { tree[artist][album][title] = track; } }); Ox.forEach(tree, function(albums, artist) { artist = { albums: Ox.len(albums), artwork: 'http://0x2620.org/music/' + artist + '/artwork.png', id: artist, items: [], name: artist }; Ox.forEach(albums, function(tracks, id) { var title = id.substr(5), year = id.substr(0, 4); artist.items.push({ artwork: tracks[Object.keys(tracks)[0]].artwork, duration: Ox.values(tracks).reduce(function(r, v, i) { return r + v.duration; }, 0), id: [artist.id, year + ' ' + title].join('\n'), items: [], size: Ox.values(tracks).reduce(function(r, v, i) { return r + v.size; }, 0), title: title, tracks: Ox.len(tracks), year: year }); Ox.forEach(tracks, function(track, id) { artist.items[artist.items.length - 1].items.push({ artist: track.artist, artwork: track.artwork, bitrate: track.bitrate, checked: track.checked, disc: track.disc, duration: track.duration, id: id, genre: track.genre, playing: track.playing, size: track.size, title: track.title, track: track.track }); }); }); ['duration', 'size', 'tracks'].forEach(function(key) { artist[key] = artist.items.reduce(function(r, v, i) { return r + v[key]; }, 0); }) artist.years = Ox.unique(artist.items.map(function(item) { return item.year; })).sort(); artist.years = [artist.years[0]].concat( artist.years.length > 1 ? [artist.years[artist.years.length - 1]] : [] ); artists.push(artist); }); return artists; }; app.utils.getTrackAlbums = function(callback) { app.$ui.trackList.api({ keys: ['id'], query: app.user.ui.query, sort: app.user.ui.sort }, function(result) { callback(result.data.items.reduce(function(r, v, i) { var albumId = v.id.split('\n').slice(0, 2).join('\n'), last = Ox.last(r); return last && last.albumId == albumId ? r : r.concat( Ox.extend( Ox.clone(Ox.getObjectById(app.data.albums, albumId)), {albumId: albumId, id: v.id, index: r.length} ) ); }, [])); }); }; app.utils.resizeWindow = function() { }; app.load(); window.app = app; });