44 */
55var CrDlna = function ( ) {
66 this . clients = { } ;
7+
8+ // Navigation path stack
9+ this . navigationPath = [ ] ;
10+
11+ // The device we are currently looking at
12+ this . currentDevice = undefined ;
713} ;
814
915/**
@@ -36,14 +42,13 @@ CrDlna.prototype.updateDeviceList = function () {
3642 }
3743 var container = document . createElement ( 'div' ) ;
3844 var device = document . createElement ( 'div' ) ;
39- var listing = document . querySelector ( '#content' )
4045 device . innerText = v . name ;
4146 device . setAttribute ( 'objectId' , '0' ) ;
4247
4348 // If this client has a content directory service, add a click listener
4449 if ( that . clients [ v . name ] && that . clients [ v . name ] . contentDirectory ) {
4550 device . addEventListener ( 'click' , function ( ) {
46-
51+ that . currentDevice = v ;
4752 var sendBrowseRequest = new Promise ( function ( resolve , reject ) {
4853 var client = that . clients [ v . name ] ;
4954 if ( client ) {
@@ -55,7 +60,7 @@ CrDlna.prototype.updateDeviceList = function () {
5560
5661 sendBrowseRequest . then (
5762 function ( data ) {
58- that . drawChildFolders ( that . clients [ v . name ] . device , listing , data ) ;
63+ that . drawChildFolders ( data ) ;
5964 } , function ( error ) {
6065 console . log ( "error getting child folders" ) ;
6166 } ) ;
@@ -70,18 +75,54 @@ CrDlna.prototype.updateDeviceList = function () {
7075
7176} ;
7277
78+ /**
79+ * Draws the navigation path
80+ */
81+ CrDlna . prototype . drawPath = function ( ) {
82+ var that = this ;
83+
84+ var heading = document . querySelector ( '#header' ) ;
85+ heading . innerHTML = '' ;
86+
87+ var item = document . createElement ( 'div' ) ;
88+ item . innerHTML = "Device root" ;
89+ heading . appendChild ( item ) ;
90+
91+ this . navigationPath . forEach ( function ( folder , i , a ) {
92+
93+ item = document . createElement ( 'div' ) ;
94+ item . innerHTML = " > " ;
95+ heading . appendChild ( item ) ;
96+
97+ item = document . createElement ( 'div' ) ;
98+ item . innerHTML = folder . title ;
99+ item . addEventListener ( 'click' , function ( ) {
100+
101+ while ( folder !== that . navigationPath . pop ( ) ) {
102+ // keep popping off anything else in the list
103+ }
104+ that . browseFolder ( folder ) ;
105+ } ) ;
106+ heading . appendChild ( item ) ;
107+
108+ } ) ;
109+
110+
111+ } ;
112+
73113/**
74114 * Draws any child items
75115 *
76- * @param {Object } elem The element into which to add the child items
77116 * @param {Object } data The object contianing the child folder data
78117 */
79- CrDlna . prototype . drawChildFolders = function ( device , elem , data ) {
80- if ( ! data || ! elem ) return ;
118+ CrDlna . prototype . drawChildFolders = function ( data ) {
81119
82120 var that = this ;
121+ var elem = document . querySelector ( '#content' ) ;
83122 elem . innerHTML = '' ;
84123
124+ if ( ! data || ! elem ) return ;
125+
85126 var playback = document . querySelector ( '#playback' ) ;
86127
87128 data . forEach ( function ( v , i , a ) {
@@ -91,15 +132,16 @@ CrDlna.prototype.drawChildFolders = function (device, elem, data) {
91132 child . setAttribute ( 'type' , v . type ) ;
92133 if ( v . type . indexOf ( 'videoItem' ) >= 0 ) {
93134 // Video Item
94- child . innerHTML = '<p>' + v . title + '</p><video width="300" height="240" controls preload="none"><source src="' + v . url + '"></video>' ;
95-
135+ child . innerHTML = '<p>' + v . title + '</p>' ;
136+ child . addEventListener ( 'click' , function ( ) {
137+ playback . innerHTML = '<video width="300" height="240" controls preload="none"><source src="' + v . url + '"></video>' ;
138+ } ) ;
96139 } else if ( v . type . indexOf ( 'imageItem' ) >= 0 ) {
97140 // Image Item
98141 child . innerHTML = '<p>' + v . title + '</p>' ;
99142 child . addEventListener ( 'click' , function ( ) {
100143 playback . innerHTML = '<webview src="' + v . url + '" width="640" height="480" autosize="on"></webview>' ;
101144 } ) ;
102-
103145 } else if ( v . type . indexOf ( 'object.item' ) >= 0 ) {
104146 // Audio Item
105147 child . innerHTML = '<p>' + v . title + '</p>' ;
@@ -109,32 +151,57 @@ CrDlna.prototype.drawChildFolders = function (device, elem, data) {
109151 } else {
110152 // Container
111153 child . classList . add ( 'contentFolder' ) ;
112- child . innerText = v . title ;
113- child . addEventListener ( 'click' , function ( ) {
114-
115- var sendBrowseRequest = new Promise ( function ( resolve , reject ) {
116- var client = that . clients [ device . name ] ;
117- if ( client ) {
118- client . browseFolder ( v . id , resolve ) ;
119- } else {
120- reject ( new Error ( 'No client was found for the device ' + v . name ) ) ;
121- }
122- } ) ;
123154
124- sendBrowseRequest . then (
125- function ( data ) {
126- that . drawChildFolders ( device , elem , data ) ;
127- } , function ( error ) {
128- console . log ( "error getting child folders" ) ;
129- } ) ;
155+ // Fairly inefficient albumArt retrieval - have to do it this way due to security restrictions
156+ // of chrome apps. Could be improved by caching to file system
157+ if ( v . albumArt !== '' ) {
158+ var xhr = new XMLHttpRequest ( ) ;
159+ xhr . open ( 'GET' , v . albumArt , true ) ;
160+ xhr . responseType = 'blob' ;
161+ xhr . onload = function ( e ) {
162+ child . style . backgroundImage = 'url("' + window . URL . createObjectURL ( this . response ) + '")' ;
163+ child . style . backgroundSize = 'cover' ;
164+ } ;
165+ xhr . send ( ) ;
166+ } else {
167+ // load local backup placeholder when we have one
168+ }
130169
170+ child . innerText = v . title ;
171+ child . title = v . title ;
172+ child . addEventListener ( 'click' , function ( ) {
173+ that . browseFolder ( v ) ;
131174 } ) ;
132175 }
133176 elem . appendChild ( child ) ;
134177 } ) ;
135178
136179} ;
137180
181+ /**
182+ * Browse the folder of this device
183+ */
184+ CrDlna . prototype . browseFolder = function ( folder ) {
185+ var that = this ;
186+ var sendBrowseRequest = new Promise ( function ( resolve , reject ) {
187+ var client = that . clients [ that . currentDevice . name ] ;
188+ if ( client ) {
189+ that . navigationPath . push ( folder ) ;
190+ client . browseFolder ( folder . id , resolve ) ;
191+ } else {
192+ reject ( new Error ( 'No client was found for the device ' + v . name ) ) ;
193+ }
194+ } ) ;
195+
196+ sendBrowseRequest . then (
197+ function ( data ) {
198+ that . drawPath ( ) ;
199+ that . drawChildFolders ( data ) ;
200+ } , function ( error ) {
201+ console . log ( "error getting child folders" ) ;
202+ } ) ;
203+ } ;
204+
138205/**
139206 * Binds the various UI controls to any handlers that are needed
140207 */
@@ -151,6 +218,9 @@ CrDlna.prototype.bindControls = function() {
151218
152219} ;
153220
221+ /**
222+ * Kicks off everything by creating a new CrDlna object as well as SSDP and UPNP handlers.
223+ */
154224document . addEventListener ( "DOMContentLoaded" , function ( ) {
155225 console . log ( "Starting Chrome DLNA..." ) ;
156226 var c = new CrDlna ( ) ;
@@ -166,8 +236,6 @@ document.addEventListener("DOMContentLoaded", function() {
166236 c . ssdp . init ( ) ;
167237 c . upnp . init ( ) ;
168238
169-
170-
171239 // Make sure the SSDP cache resets periodically (doing it here as we have
172240 // access to window.setInterval)
173241 window . setInterval ( function ( ) {
0 commit comments