refactored asunder and added build scripts
[stereo.git] / Asunder / src / webserver / spotlight / daap.js
blob4eb4988dbc5286ffa2cf6cb2306c42cce3585ba0
1 var constants = {
2                 put: function (name, code, type) {
3                         var n = name.split('.');
4                         var c = this;
5                         for (i in n) {
6                                 if (i+1 < n.length) {
7                                         c = c[n[i]];
8                                 }
9                                 else {
10                                         c[n[i]] = new Constant(name, code, type);
11                                 }
12                         }
13                 },
14                 dmap: {
15                                 contentcodesresponse: new Constant("dmap.contentcodesresponse", "mccr", 12),
16                                 dictionary: new Constant("dmap.dictionary", "mdcl", 12),
17                                 contentcodesnumber: new Constant("dmap.contentcodesnumber", "mcnm",  5),
18                                 contentcodesname: new Constant("dmap.contentcodesname", "mcna",  9),
19                                 contentcodestype: new Constant("dmap.contentcodestype", "mcty",  3)
20                 },
21                 daap: new Object(),
22                 dacp: new Object(),
23                 dmcp: new Object(),
24                 com: { apple: { itunes: new Object() } }
27 function Constant(name, code, type) {
28         this.name = name;
29         this.code = code;
30         this.type = type;
33 function DACPNode(text) {
34         
35         var parseNum = function (index, len) {
36                 if (len > 4) return parseBigNum(index, len);
37         
38                 var num = text.charCodeAt(index) & 255;
39                 for (var i = 1; i < len; i++) {
40                         num = num * 256;
41                         num += text.charCodeAt(index + i) & 255;
42                 }
43                 return num;
44         };
45         
46         var parseBigNum = function (index, len) {
47                 var num = new BigNumber(text.charCodeAt(index) & 255);
48                 for (var i = 1; i < len; i++) {
49                         num = num.multiply(256);
50                         num = num.add(text.charCodeAt(index + i) & 255);
51                 }
52                 return num;
53         };
54         
55         this.name = function (index) {
56                 return text.substring(index, index+4);
57         };
58         this.length = function (index) {
59                 return parseNum(index+4, 4);
60         };
61         this.byteVal = function (index) {
62                 return parseNum(index+8, 1);
63         };
64         this.shortVal = function (index) {
65                 return parseNum(index+8, 2);
66         };
67         this.intVal = function (index) {
68                 return parseNum(index+8, 4);
69         };
70         this.longVal = function (index) {
71                 return parseNum(index+8, 8);
72         };
73         this.longlongVal = function (index) {
74                 return {
75                         0:      parseNum(index+8, 4),
76                         1:      parseNum(index+12, 4),
77                         2:      parseNum(index+16, 4),
78                         3:      parseNum(index+20, 4)
79                 };
80         };
81         this.stringVal = function (index) {
82                 var length = this.length(index);
83                 var string = "";
84                 for ( var i = 8; i < 8+length;) {
85                         var c = text.charCodeAt(index + i) & 255;
86                         var c2,c3;
87                         if (c < 128) {
88                                 string += String.fromCharCode(c);
89                                 i++;
90                         } else if (c > 191 && c < 224) {
91                                 c2 = text.charCodeAt(index + i + 1) & 255;
92                                 string += String.fromCharCode(((c & 31)) << 6 | (c2 & 63));
93                                 i += 2;
94                         } else {
95                                 c2 = text.charCodeAt(index + i + 1) & 255;
96                                 c3 = text.charCodeAt(index + i + 2) & 255;
97                                 string += String.fromCharCode(((c & 15)) << 12 | (c2 & 63) << 6
98                                                 | (c3 & 63));
99                                 i += 3;
100                         }
101                 }
102                 return string;
103         };
104         this.dateVal = function (index) {
105                 return parseNum(index+8, 8);
106         };
107         this.versionVal = function (index) {
108                 var version = text.charCodeAt(index+8) & 255;
109                 for (i in 1, 2, 3) {
110                         version += ".";
111                         version += text.charCodeAt(index+8 + i) & 255;
112                 }
113                 return version;
114         };
115         this.children = function (index) {
116                 var kids = new Array();
117                 var len = index + 8 + this.length(index);
118                 var i = index+8;
119                 while (i < len) {
120                         kids.push(i);
121                         i += 8 + this.length(i);
122                 }
123                 return kids;
124         };
125         this.findNode = function (index, tag) {
126                 var kids = this.children(index);
127                 for (child in kids) {
128                         if (this.name(kids[child]) == tag) {
129                                 return kids[child];
130                         }
131                 }
132                 return -1;
133         }
136 function DACPPlayStatusUpdate(tree, index) {
138         var kids = tree.children(index);
140         this.album = "";
141         this.title = "";
142         this.artist = "";
143         this.genre = "";
144         this.id = 0;
145         this.status = 0;
146         this.revision = 0;
147         this.container = 0;
148         
149         for (child in kids) {
150                 var node = kids[child];
151                 switch (tree.name(node)) {
152                 case "caps":
153                         this.status = tree.byteVal(node);
154                         break;
155                 case "cmsr":
156                         this.revision = tree.intVal(node);
157                         break;
158                 case "cann":
159                         this.title = tree.stringVal(node);
160                         break;
161                 case "cana":
162                         this.artist = tree.stringVal(node);
163                         break;
164                 case "canl":
165                         this.album = tree.stringVal(node);
166                         break;
167                 case "cang":
168                         this.genre = tree.stringVal(node);
169                         break;
170                 case "canp":
171                         this.container = tree.longlongVal(node)[1];
172                         this.id = tree.longlongVal(node)[3];
173                         break;
174                 }
175         }
178 function getItems(tree, index, handler) {
179         
180         var items = new Array();
181         
182         var kids = tree.children(index);
183         for (child in kids) {
184                 items.push(handler(tree, kids[child]));
185         }
186         
187         return items;
190 function DACPContainer(tree, index) {
191         
192         this.id = 0;
193         this.persistent = 0;
194         this.name = "";
195         this.base = false;
196         this.parent = 0;
197         this.items = 0;
199         var kids = tree.children(index);
200         for (child in kids) {
201                 var node = kids[child];
202                 switch (tree.name(node)) {
203                 case constants.dmap.itemid.code:
204                         this.id = tree.intVal(node);
205                         break;
206                 case constants.dmap.persistentid.code:
207                         this.persistent = tree.longVal(node);
208                         break;
209                 case constants.dmap.itemname.code:
210                         this.name = tree.stringVal(node);
211                         break;
212                 case constants.daap.baseplaylist.code:
213                         this.base = tree.byteVal(node);
214                         break;
215                 case constants.dmap.parentcontainerid.code:
216                         this.parent = tree.intVal(node);
217                         break;
218                 case constants.dmap.itemcount.code:
219                         this.items = tree.intVal(node);
220                         break;
221                 }
222         };
223         
224         this.init();
225         this.node.className += " -database-playlist";
226         this.appendChild(this.name, "-database-playlist-name");
227         //this.appendChild(this.items, "-database-playlist-items");
228         //this.appendChild(this.id, "-database-playlist-id");
230 DACPContainer.prototype = new Item;
231 DACPContainer.prototype.selected = function () {
232         Item.prototype.selected.call(this);
233         dacp.playlist = this.id;
234         dacp.search();
236 DACPContainer.prototype.deselected = function () {
237         Item.prototype.deselected.call(this);
238         if (dacp.playlist == this.id) {
239                 dacp.playlist = 1;
240         }
243 function DACPPlaylist(tree, index) {
245         this.tracks = new Array();
247         var kids = tree.children(index);
248         var list = 0;
249         for (child in kids) {
250                 if (tree.name(kids[child]) == "mlcl") {
251                         list = kids[child];
252                         break;
253                 }
254         }
256         if (!list) return;
257         
258         this.init();
259         
260         kids = tree.children(list);
261         for (child in kids) {
262                 this.tracks.push(new DACPTrack(tree, kids[child]));
263         }
264         
266 DACPPlaylist.prototype = new View;
268 function DACPAlbum(tree, index) {
270         this.id = 0;
271         this.name = "";
272         this.artist = 0;
273         this.persistantId = 0;
275         var kids = tree.children(index);
276         for (child in kids) {
277                 var node = kids[child];
278                 switch (tree.name(node)) {
279                 case "miid":
280                         this.id = tree.intVal(node);
281                         break;
282                 case "minm":
283                         this.name = tree.stringVal(node);
284                         break;
285                 case "asaa":
286                         this.artist = tree.stringVal(node);
287                         break;
288                 case "mper":
289                         this.persistantId = tree.longVal(node);
290                         break;
291                 }
292         }
293         
294         this.init();
295         this.node.className += " -stereo-album";
296         
297         this.appendChild(this.name, "-stereo-album-title");
298         this.appendChild(this.title, "-stereo-album-artist");
300 DACPAlbum.prototype = new Item;
302 function DACPTrack(tree, index) {
304         this.album = "";
305         this.title = "";
306         this.artist = "";
307         this.genre = "";
308         this.id = 0;
309         this.persistent = 0;
310         
311         var kids = tree.children(index);
312         for (child in kids) {
313                 var node = kids[child];
314                 switch (tree.name(node)) {
315                 case "asal":
316                         this.album = tree.stringVal(node);
317                         break;
318                 case "minm":
319                         this.title = tree.stringVal(node);
320                         break;
321                 case "asar":
322                         this.artist = tree.stringVal(node);
323                         break;
324                 case "asag":
325                         this.genre = tree.stringVal(node);
326                         break;
327                 case "miid":
328                         this.id = tree.intVal(node);
329                         break;
330                 case "mper":
331                         this.persistent = tree.longVal(node);
332                         break;
333                 }
334         }
336         this.init();
337         this.node.className += " -stereo-track";
338         
339         var title = document.createElement("DIV");
340         title.appendChild(document.createTextNode(this.title));
341         
342         this.appendChild(this.artist, "-stereo-track-artist");
343         this.appendChild(title, "-stereo-track-title");
344         this.appendChild(this.album, "-stereo-track-album");
345         this.appendChild(this.genre, "-stereo-track-genre");
346         
347         var arrow = document.createElement("SPAN");
348         //arrow.innerHTML = "&#9654;";
349         arrow.innerHTML = "enqueue";
350         arrow.className = "enqueue hover";
351         title.appendChild(arrow);
352         
353         var play = document.createElement("SPAN");
354         play.innerHTML = "play";
355         play.className = "play hover";
356         title.appendChild(play);
357         
358         this.clicked = function (e) {
359                 if (e.target.className && e.target.className.indexOf("play") != -1) {
360                         dacp.play(constants.dmap.itemid.name + ":" + this.id);
361                 }
362                 else if (e.target.className && e.target.className.indexOf("enqueue") != -1) {
363                         dacp.enqueue(constants.dmap.itemid.name + ":" + this.id);
364                 }
365                 else {
366                         DACPTrack.prototype.clicked.call(this);
367                 }
368         };
370 DACPTrack.prototype = new Item;
372 function BrowseItem(tree, index, type) {
374         this.type = type;
375         this.value = tree.stringVal(index);
376         
377         this.init();
378         this.node.className += " -browse-item";
379         
380         var arrow = document.createElement("DIV");
381         //arrow.innerHTML = "&#9654;";
382         arrow.innerHTML = "enqueue";
383         this.appendChild(arrow, "enqueue hover");
384         
385         var play = document.createElement("DIV");
386         play.innerHTML = "play";
387         this.appendChild(play, "play hover");
388         
389         this.appendChild(this.value);
390         
391         this.clicked = function (e) {
392                 if (e.target.className && e.target.className.indexOf("play") != -1) {
393                         dacp.play(this.type.name + ":" + this.value);
394                 }
395                 else if (e.target.className && e.target.className.indexOf("enqueue") != -1) {
396                         dacp.enqueue(this.type.name + ":" + this.value);
397                 }
398                 else {
399                         BrowseItem.prototype.clicked.call(this);
400                 }
401         };
404 BrowseItem.prototype = new Item;
405 BrowseItem.prototype.selected = function () {
406         dacp.setSearch(this.type.name + ":" + this.value);
409 function sysout(text) {
410         if (window.console) {
411                 window.console.log(text);
412         }
413         else {
414                 var p = document.createElement("PRE");
415                 p.className = "debug";
416                 p.appendChild(document.createTextNode(text));
417                 document.body.appendChild(p);
418         }