1
0
Fork 0

Hmmm yes a commit message...

master
Matthieu Lalonde 12 years ago committed by xSmurf
parent e575830303
commit 859758ced1

@ -0,0 +1,51 @@
server {
listen localhost;
server_name daapr.localhost;
root /home/www-data/daapr;
index index.html;
location ~ ^/favicon.ico$ {
root /home/www-data/roundcube/web/skins/default/images;
log_not_found off;
access_log off;
expires max;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
location ~ /(\.|temp|logs) {
deny all;
access_log off;
log_not_found off;
}
}
server {
listen 80;
server_name daap.localhost;
add_header Access-Control-Allow-Origin "*";
location / {
proxy_pass http://localhost:3689;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_redirect off;
#proxy_buffering off;
proxy_buffering on;
#proxy_buffers 128k;
proxy_cache daap;
proxy_cache_valid 6d;
proxy_cache_valid 404 1m;
proxy_ignore_headers Cache-Control Expires DAAP-Server;
#proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

@ -1,12 +1,293 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Backbone.js/Underscore.js via Require.js Learning Page</title> <title>DAAPr</title>
<script src="resources/vendors/require/require.min.js"></script>
<script src="resources/js/main.js"></script> <meta charset="utf-8">
</head> <meta http-equiv="Content-type" content="text/html; charset=utf-8">
<body> <!--meta href="/"-->
<div>Backbone.js/Underscore.js via Require.js Learning Page</div>
<div class="testhook"></div> <style type="text/css">
</body> @import "/resources/css/normalize.css";
@import "/resources/vendors/bootstrap/2.1.1/css/bootstrap.min.css";
@import "/resources/css/datatables.css";
@import "/resources/css/app.css";
@import "/resources/css/player.css";
</style>
<script data-main="resources/js/main" src="resources/vendors/require/require.min.js">/**/</script>
</head>
<body>
<div class="ui-layout-north" id="wrapperPlayer">
<div class="daaprControlsWrap">
<a id="buttonPrev" class="disabled"></a>
<a id="buttonPlay" class="disabled"></a>
<a id="buttonNext" class="disabled"></a>
<div id="volumeWrapper">
<a id="buttonVolDown" class="disabled"></a>
<input type="range" id="buttonVolume" minx="0" max="1" step="0.05" value="0" disabled />
<a id="buttonVolUp" class="disabled"></a>
</div>
<div class="clearfix"></div>
</div>
<form action="#" class="form-search controls" id="wrapperSearchWidget">
<div class="input-prepend">
<button type="submit" tabindex="-1" class="btn"><i class="icon-search"></i></button>
<input type="text" tabindex="1" class="span3 search-query">
</div>
</form>
<div id="daaprPlayerViewport">
<h1>DAAPr<sup>3</sup></h1>
</div>
</div>
<div class="ui-layout-west">
<div id="wrapperSideBar">
<ul>
<li>
<span>
<i class="icon-home"></i>&nbsp;<span>Servers</span>
<a href="javascript:void(0);" class="sidebar-list-action"><i class="icon-plus-sign"></i></a>
</span>
<ul>
<li>
<span>
<i class="icon-folder-close"></i>&nbsp;<span>Bigthug</span>
<a href="javascript:void(0);" class="sidebar-list-action"><i class="icon-minus-sign"></i></a>
</span>
</li>
<li>
<span>
<i class="icon-folder-close"></i>&nbsp;<span>WebDevTest</span>
<a href="javascript:void(0);" class="sidebar-list-action"><i class="icon-minus-sign"></i></a>
</span>
</li>
<li>
<span>
<i class="icon-folder-close"></i>&nbsp;<span>ReallyLongNameWithNoBreaks</span>
<a href="javascript:void(0);" class="sidebar-list-action"><i class="icon-minus-sign"></i></a>
</span>
</li>
<li>
<span>
<i class="icon-folder-close"></i>&nbsp;<span>Really Long Name Breaks</span>
<a href="javascript:void(0);" class="sidebar-list-action"><i class="icon-minus-sign"></i></a>
</span>
</li>
</ul>
</li>
<li class="droppable"><span><i class="icon-list"></i>&nbsp;Playlist</span></li>
</ul>
</div>
</div>
<div class="ui-layout-center" class="loading">
<div class="ui-layout-north row-fluid span12" id="wrapperBrowser">
<div class="third">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="tableGenres"></table>
</div>
<div class="third">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="tableArtists"></table>
</div>
<div class="third">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="tableAlbums"></table>
</div>
</div>
<div class="ui-layout-center" id="wrapperList">
<table cellpadding="0" cellspacing="0" border="0" class="display" id="tableList"></table>
</div>
</div>
<div class="ui-layout-south row-fluid" id="wrapperFooter">
<div id="wrapperFooterLeft" class="span4">
<button class="btn btn-mini btn-inverse" type="button" data-toggle="side-bar"><i class="icon-chevron-left icon-white"></i></button>
<button class="btn btn-mini btn-inverse" type="button" data-toggle="button"><i class="icon-random icon-white"></i></button>
<button class="btn btn-mini btn-inverse" type="button" data-toggle="button"><i class="icon-retweet icon-white"></i></button>
</div>
<div id="wrapperFooterCenter" class="span4">
<span class="list-count">0</span>&nbsp;items
</div>
<div id="wrapperFooterRight" class="span4">
<!-- <div class="btn-group">
<button class="btn btn-inverse btn-mini"><i class="icon-download-alt icon-white"></i>Download</button>
<button class="btn btn-inverse btn-mini dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-up" role="menu" aria-labelledby="dLabel">
<li><a tabindex="-1" href="#">Action</a></li>
<li><a tabindex="-1" href="#">Another action</a></li>
<li><a tabindex="-1" href="#">Something else here</a></li>
<li class="divider"></li>
<li><a tabindex="-1" href="#">Separated link</a></li>
</ul>
</div> -->
<button class="btn btn-mini btn btn-inverse" type="button" data-target="modalAboutDaapr"><i class="icon-question-sign icon-white"></i></button>
</div>
</div>
<ul id="listItemContextMenu" class="context-menu dropdown-menu">
<li><a href="javascript:void(0);" data-target="listItemDetails"><i class="icon-info-sign"></i>&nbsp;Details...</a></li>
<li><a href="javascript:void(0);" data-target="permalink"><i class="icon-bookmark"></i>&nbsp;Direct link</a></li>
<li><a href="javascript:void(0);" data-target="download"><i class="icon-download-alt"></i>&nbsp;Download</a></li>
</ul>
<ul id="listColumnsContextMenu" class="context-menu dropdown-menu">
<li><a href="javascript:void(0);">Title</a></li>
<li><a href="javascript:void(0);">Artist</a></li>
<li><a href="javascript:void(0);">Album</a></li>
<li><a href="javascript:void(0);">Track</a></li>
<li><a href="javascript:void(0);">Disc</a></li>
<li><a href="javascript:void(0);">Year</a></li>
<li><a href="javascript:void(0);">Time</a></li>
<li><a href="javascript:void(0);">Genre</a></li>
</ul>
<script type="text/html" id="_tmplModalNewServer">
<div id="listItemDetails" data-closable="true">
<div class="overlay"></div>
<div class="modal" style="width: auto;margin-left: auto;margin-right: auto;" data-show="true" tabindex="-1" role="dialog" aria-labelledby="listItemDetailsLabel" aria-hidden="true">
<div class="modal-body" style="padding: 10px 10px 0 10px;">
<form>
<fieldset>
<legend>Add Server</legend>
<label>Address</label>
<input type="text" placeholder="daap://domain.tld">
</fieldset>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-mini" data-dismiss="modal" aria-hidden="true"><i class="icon-remove"></i>&nbsp;Cancel</button>
<button class="btn btn-mini btn-primary" data-dismiss="modal" aria-hidden="true"><i class="icon-ok"></i>&nbsp;Add&nbsp;&amp;&nbsp;Connect</button>
</div>
</div>
</div>
</script>
<script type="text/html" id="_tmplModalServerLogin">
<div id="modalServerLogin">
<div class="overlay"></div>
<div class="modal" data-show="true" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-body" style="font-size: 1.2em">
<form>
<fieldset>
<legend>{{dmap_itemname}} login</legend>
<p>
<input type="text" class="input-large" placeholder="Login">
</p>
<p>
<input type="password" class="input-large" placeholder="Password">
</p>
<!--label class="checkbox">
<input type="checkbox"> Remember me
</label-->
</fieldset>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-mini" data-dismiss="modal" aria-hidden="true"><i class="icon-remove"></i>&nbsp;Cancel</button>
<button class="btn btn-mini btn-primary" data-dismiss="modal" aria-hidden="true"><i class="icon-ok"></i>&nbsp;Login</button>
</div>
</div>
</div>
</script>
<script type="text/html" id="_tmplModalServerLoading">
<div id="modalServerLoading">
<div class="overlay"></div>
<div class="modal">
</div>
</div>
</script>
<!-- Underscore Templates -->
<script type="text/html" id="_tmplPlayerView">
<div class="daaprPlayerTitleIndicator">{{dmap_itemname}}</div>
<div class="daaprPlayerInfoIndicator">{{daap_songartist}}</div>
<div class="daaprProgressWrap">
<span id="daaprProgressTimeElapsed">0:00</span>
<progress min="0" max="1" value="0" id="daaprPlayerProgress"></progress>
<span id="daaprProgressTimeRemain">-0:00</span>
</div>
</script>
<script type="text/html" id="_tmplModalAboutDaapr">
<div id="modalAboutDaapr" data-closable="true">
<div class="overlay"></div>
<div class="modal" data-show="true" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-header">
<h3 id="myModalLabel">About DAAPr<sup>3</sup></h3>
</div>
<div class="modal-body" style="font-size: 1.2em">
<p>
<b>DAAPr</b>, in its cubic iteration, is a web client for the
<a href="http://en.wikipedia.org/wiki/Digital_Audio_Access_Protocol" title="Digital Audio Access Protocol">DAAP</a> media sharing protocol.
It is 100% client side javascript built using <a href="http://backbonejs.org" title="Backbone.js">Backbone.js</a>,
<a href="http://jquery.com">jQuery</a> and <a href="http://twitter.github.com/bootstrap/" title="Twitter Bootstrap">Bootstrap</a>.
</p>
<p>
<a href="https://matth.lalonde.me/contact/">Feedback</a> is appreciated.
</p>
<p>
This software is <a href="http://en.wikipedia.org/wiki/Copyleft">Copyleft</a> 2012 <a href="https://matth.lalonde.me">Matthieu Lalonde</a> under <a href="http://www.gnu.org/copyleft/lesser.html">LGPL</a>, no guarantees whatsoever.
</p>
</div>
<div class="modal-footer">
<button class="btn btn-mini btn-primary" data-dismiss="modal" aria-hidden="true"><i class="icon-remove"></i>&nbsp;Close</button>
</div>
</div>
</div>
</script>
<script type="text/html" id="_tmplModalFatal">
<div id="modalFatal">
<div class="overlay"></div>
<div class="modal" data-show="true" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-header alert alert-error">
<h3 id="myModalLabel">A fatal error has occured</h3>
</div>
<div class="modal-body" style="font-size: 1.2em">
</div>
<div class="modal-footer">
<button class="btn btn-mini btn-primary" data-dismiss="modal" aria-hidden="true"><i class="icon-refresh"></i>&nbsp;Reload</button>
</div>
</div>
</div>
</script>
<script type="text/html" id="_tmplListItemDetails">
<div id="listItemDetails" data-closable="true">
<div class="overlay"></div>
<div class="modal hide fade" data-show="true" tabindex="-1" role="dialog" aria-labelledby="listItemDetailsLabel" aria-hidden="true">
<div class="modal-header">
<!--button type="button" class="close" data-dismiss="modal" aria-hidden="true">Ă—</button-->
<h4 id="myModalLabel">Song Details</h4>
</div>
<div class="modal-body">
<table class="table table-striped table-hover table-condensed">
<%
_.each(items, function(item) {
print("<tr>\n");
print(" <td>\n");
print(" " + item.title + ":\n");
print(" </td>\n");
print(" <td>\n");
print(" " + item.data + "\n");
print(" </td>\n");
print("</tr>\n");
});
%>
</table>
</div>
<div class="modal-footer">
<button class="btn btn-mini" data-target="download" aria-hidden="true" data-loading-text="Downloading..."><i class="icon-download-alt"></i>&nbsp;Download</button>
<button class="btn btn-mini btn-primary" data-dismiss="modal" aria-hidden="true"><i class="icon-remove"></i>&nbsp;Close</button>
</div>
</div>
</div>
</script>
</body>
</html> </html>

File diff suppressed because one or more lines are too long

@ -0,0 +1,46 @@
/**
* DataTables
*/
table.dataTable {
margin: 0 auto;
clear: both;
width: 100%;
}
table.dataTable thead th,
table.dataTable tbody td {
height: ;
padding: 0px 2px;
}
table.dataTable thead th {
cursor: pointer;
*cursor: hand;
}
table.dataTable td.center,
table.dataTable td.dataTables_empty {
text-align: center;
}
table.dataTable tr td.sorting_1 { background-color: #EBEBEB; }
table.dataTable tr td.sorting_2 { background-color: #EBEBEB; }
table.dataTable tr td.sorting_3 { background-color: #EBEBEB; }
/*
table.dataTable tr.odd { background-color: #E2E4FF; }
table.dataTable tr.even { background-color: white; }
table.dataTable tr.odd td.sorting_1 { background-color: #D3D6FF; }
table.dataTable tr.odd td.sorting_2 { background-color: #DADCFF; }
table.dataTable tr.odd td.sorting_3 { background-color: #E0E2FF; }
table.dataTable tr.even td.sorting_1 { background-color: #EAEBFF; }
table.dataTable tr.even td.sorting_2 { background-color: #F2F3FF; }
table.dataTable tr.even td.sorting_3 { background-color: #F9F9FF; }
*/
/*.sorting { background: url('../vendors/DataTables-1.9.4/media/images/sort_both.png') no-repeat center right; }*/
.sorting_asc { background: url('../vendors/DataTables-1.9.4/media/images/sort_asc.png') no-repeat center right; }
.sorting_desc { background: url('../vendors/DataTables-1.9.4/media/images/sort_desc.png') no-repeat center right; }
.sorting_asc_disabled { background: url('../vendors/DataTables-1.9.4/media/images/sort_asc_disabled.png') no-repeat center right; }
.sorting_desc_disabled { background: url('../vendors/DataTables-1.9.4/media/images/sort_desc_disabled.png') no-repeat center right; }

@ -0,0 +1,375 @@
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/*
* Prevents modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for `hidden` attribute not present in IE 8/9.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/*
* 1. Sets default font family to sans-serif.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Removes default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/*
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
* Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
}
/*
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/*
* Addresses styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/*
* Corrects font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* Sets consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/*
* Addresses inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/*
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/*
* Removes border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/*
* Corrects overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/*
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Corrects font family not being inherited in all browsers.
* 2. Corrects font size not being inherited in all browsers.
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/*
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/*
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/*
* Re-set default cursor for disabled elements.
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to `content-box` in IE 8/9.
* 2. Removes excess padding in IE 8/9.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Removes inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE 8/9.
* 2. Improves readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

@ -0,0 +1,247 @@
div#wrapperPlayer #daaprPlayerViewport {
margin: 6px auto 0 auto;
background: transparent url('../img/player_background.png') repeat-x;
height: 40px;
width: 450px;
border: 1px solid #6B6D5E;
border-bottom-color: #DCDDDE;
border-left-color: #939885;
border-right-color: #939885;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
z-index: 10;
}
div#wrapperPlayer #daaprPlayerViewport h1 {
color: #5F5F5F;
font-size: 2em;
font-style: oblique;
font-variant: small-caps;
font-weight: bold;
line-height: 40px;
text-align: center;
text-shadow: #ACACAC 2px 2px 2px;
margin: 0;
-webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.3, rgb(0,0,0)),
color-stop(0.27, rgb(46,46,46)),
color-stop(0.83, rgb(0,0,0))
);
-moz-linear-gradient(
center top,
rgb(0,0,0) 30%,
rgb(46,46,46) 27%,
rgb(0,0,0) 83%
);
}
.daaprPlayerTitleIndicator,
.daaprPlayerInfoIndicator {
height: 12px;
line-height: 12px;
font-size: 0.7em;
text-align: center;
margin-bottom: 2px;
}
.daaprPlayerTitleIndicator {
font-size: 0.7em;
font-weight: bold;
}
#daaprProgressTimeRemain,
#daaprProgressTimeElapsed {
font-size: 0.7em;
}
#daaprProgressTimeRemain {
vertical-align: top;
height: 7px;
line-height: 7px;
padding-right: 5px;
}
#daaprProgressTimeElapsed {
vertical-align: top;
height: 7px;
line-height: 7px;
padding-left: 5px;
}
.daaprProgressWrap {
width: auto;
text-align: center;
margin: 0 auto;
height: 9px;
}
.daaprControlsWrap {
float: left;
clear: left;
margin-top: 8px;
margin-left: 25px;
}
.daaprControlsWrap #buttonPlay {
float: left;
clear: none;
display: block;
width: 37px;
height: 38px;
margin-left: 4px;
margin-right: 4px;
background: transparent url('../img/button_play.png') no-repeat left top;
}
.daaprControlsWrap #buttonPlay.playing {
background-image: url('../img/button_pause.png');
}
.daaprControlsWrap #buttonPlay.stopped {
background-image: url('../img/button_stop.png');
}
.daaprControlsWrap #buttonVolume,
.daaprControlsWrap #buttonPrev,
.daaprControlsWrap #buttonNext {
float: left;
clear: none;
display: block;
width: 31px;
height: 32px;
margin-top: 3px;
}
.daaprControlsWrap #buttonPrev {
background: url('../img/button_prev.png') no-repeat left top;
}
.daaprControlsWrap #buttonNext {
background: url('../img/button_next.png') no-repeat left top;
}
.daaprControlsWrap #buttonNext:active,
.daaprControlsWrap #buttonPrev:active,
.daaprControlsWrap #buttonPlay:active,
.daaprControlsWrap #buttonNext.disabled:active,
.daaprControlsWrap #buttonPrev.disabled:active,
.daaprControlsWrap #buttonPlay.disabled:active {
background-position: left bottom;
}
progress,
progress::-webkit-progress-bar-value,
progress::-webkit-progress-value,
progress::-moz-progress-bar {
-webkit-appearance: progress-bar;
/*background-color: #BFBFBF;*/
}
progress#daaprPlayerProgress {
vertical-align: top;
/*-moz-border-radius: 7px;
-webkit-border-radius: 7px;*/
display: inline-block;
/*height: 7px;*/
margin-top: -3px;
padding: 0;
width: 350px;
/*border: 1px solid #393939;*/
}
/*
progress#daaprPlayerProgress, progress#daaprPlayerProgress span {
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
}
progress#daaprPlayerProgress span {
background: url('../img/progressbar_fg.png') repeat-x;
display: block;
height: 7px;
margin: 0;
padding: 0;
}
*/
#volumeWrapper {
float: left;
clear: none;
margin-left: 25px;
margin-top: 16px;
}
#volumeWrapper input#buttonVolume {
float: left;
clear: none;
vertical-align: bottom;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
/*background: url('../img/volume_bg.png') repeat-x;*/
display: block;
height: 6px;
line-height: 6px;
padding: 0;
width: 74px;
border: 1px solid #393939;
border-top: none;
margin-left: 3px;
margin-right: 3px;
margin-top: 2px;
}
#volumeWrapper input#buttonVolume,
#volumeWrapper input#buttonVolume span {
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
}
/*
#volumeWrapper progress#buttonVolume span {
background: url('../img/volume_fg.png') repeat-x;
display: block;
height: 6px;
width: 0%;
margin: 0;
padding: 0;
}
#volumeWrapper progress#buttonVolume span a {
display: none;
}
*/
#volumeWrapper a#buttonVolDown {
vertical-align: top;
display:block;
float: left;
clear: left;
height: 10px;
width: 9px;
background: transparent url('../img/volume_minus.png') no-repeat;
}
#volumeWrapper a#buttonVolUp {
vertical-align: top;
display:block;
float: left;
clear: right;
height: 10px;
width: 13px;
background: transparent url('../img/volume_plus.png') no-repeat;
}
table tbody tr.itemplaying td:first-child span {
display: inline-block;
background-color: transparent;
background-image: url('../img/playicon2.png');
background-repeat: no-repeat;
background-position: left bottom;
width: 12px;
height: 12px;
line-height: 14px;
}
table tbody tr.itemplaying.itempaused td:first-child span {
background-image: url('../img/playicon.png');
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

@ -7,17 +7,37 @@
// Filename: app.js // Filename: app.js
define([ define([
"jquery" "views/app"
, "underscore" , "views/footer"
, "backbone" , "views/sidebar"
, "views/player"
, "views/main"
, "models/client" , "models/client"
, "models/dmap"
], ],
function( $, _, Backbone, ClientModel ) { function(
"use strict"; AppView
, FooterView
, SideBarView
, PlayerView
, MainView
, ClientModel
, DMAPModel
) {
"use strict";
var init = function(){ var init = function() {
console.log("app.js::init()");
console.log( "app.js > init()" ); this.Views = {};
this.Views.Footer = new FooterView();
this.Views.SideBar = new SideBarView();
this.Views.Player = new PlayerView();
this.Views.Main = new MainView();
this.Views.App = new AppView();
var client = new ClientModel({ var client = new ClientModel({
hostname: "daap.localhost" hostname: "daap.localhost"
@ -25,19 +45,21 @@ function( $, _, Backbone, ClientModel ) {
, port: 80 , port: 80
, password: "lawl" , password: "lawl"
}); });
console.log(client.attributes);
client.on("unauthorized", function __client_unauthorized() { client.on("unauthorized", function __client_unauthorized() {
console.log(arguments); console.log(arguments);
}); });
client.on("inited", function () { client.on("inited", function (collections) {
console.log(client.url(), arguments); console.log(client.url(), arguments);
var item = collections.databases[collections.databasesInfo.get("dmap_listing").at(0).id].get("dmap_listing").get(107);
console.log(item, item.toJSON(), collections.databases[collections.databasesInfo.get("dmap_listing").at(0).id].get("dmap_listing").toJSON());
}); });
console.log(client); //client.init();
client.init(); return this;
}; };
return { init: init }; return { init: init };

@ -1,26 +1,51 @@
/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
define([ define([
"backbone" "underscore"
, "backbone"
, "models/dmap" , "models/dmap"
] ]
, function (Backbone, DMAPModel) { , function (_, Backbone, DMAPModel) {
"use strict";
var that var that
, Collection = Backbone.Collection.extend({ , Collection = Backbone.Collection.extend({
model: DMAPModel attributes: {}
, model: DMAPModel
, attributes: {}
, initialize: function (attributes, options) { , initialize: function (attributes, options) {
that = this; that = this
;
options.fetch && that.fetch = options.fetch && delete options.fetch; if (!_.isObject(options)) {
options = {};
}
_.extend({}, that.attributes, attributes); if (_.isFunction(options.fetch)) {
that.fetch = options.fetch;
delete options.fetch;
}
Model.__super__.set(that, [undefined, options]); if (!_.isUndefined(options.model)) {
that.model = options.model;
delete options.model;
}
Collection.__super__.initialize.call(that, [attributes, options]);
return that; return that;
} }
/*
, toJSON: function(options) {
return this.map(function(model){ return model.toJSON(options); });
}
*/
, parse: function () {console.error("PARSE", arguments);}
, fetch: function (options) { , fetch: function (options) {
that.trigger("dmap.collection.fetch", this, arguments); that.trigger("dmap.collection.fetch", this, arguments);

@ -0,0 +1,22 @@
/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
define([
"underscore"
, "backbone"
, "backbone-localstorage"
]
, function (_, Backbone) {
"use strict";
return Backbone.Collection.extend({
localStorage: new Backbone.LocalStorage("daap-servers")
, initialize: function () {
return this.__super__.initialize.call(this, arguments);
}
});
});

@ -12,7 +12,7 @@ require.config({
, "bootstrap-contextmenu": ["bootstrap"] , "bootstrap-contextmenu": ["bootstrap"]
, "jquery": { , "jquery": {
exports: "jQuery.fn" exports: "$"
} }
, "backbone": { , "backbone": {
@ -26,6 +26,10 @@ require.config({
, exports: "Backbone" , exports: "Backbone"
} }
, "backbone-localstorage": {
deps: ["backbone"]
}
, "underscore": { , "underscore": {
exports: "_" exports: "_"
} }
@ -47,12 +51,13 @@ require.config({
, "jquery-layout": "../vendors/jquery/layout/1.2.0/jquery.layout.min" , "jquery-layout": "../vendors/jquery/layout/1.2.0/jquery.layout.min"
, "datatables": "../vendors/jquery/datatables/1.9.4/media/js/jquery.dataTables.min" , "datatables": "../vendors/jquery/datatables/1.9.4/media/js/jquery.dataTables.min"
, "bootstrap": "../vendors/backbone/2.1.1/js/bootstrap.min" , "bootstrap": "../vendors/bootstrap/2.1.1/js/bootstrap.min"
, "bootstrap-contextmenu": "../vendors/backbone/contextmenu/59986df48f/js/bootstrap-contextmenu" , "bootstrap-contextmenu": "../vendors/bootstrap/contextmenu/59986df48f/js/bootstrap-contextmenu"
, "underscore": "../vendors/lodash/0.9.0/lodash.underscore.min" , "underscore": "../vendors/lodash/0.9.0/lodash.underscore.min"
, "backbone": "../vendors/backbone/0.9.2/backbone-min" , "backbone": "../vendors/backbone/0.9.2/backbone-min"
, "backbone-localstorage": "../vendors/backbone/localstorage/8651291560/backbone.localStorage-min"
, "toolbox": "../vendors/backbone/toolbox/e0cac9f/toolbox" , "toolbox": "../vendors/backbone/toolbox/e0cac9f/toolbox"
, "toolbox-extras": "../vendors/backbone/toolbox/e0cac9f/toolbox.extra" , "toolbox-extras": "../vendors/backbone/toolbox/e0cac9f/toolbox.extra"
} }
@ -70,6 +75,9 @@ require(
, function ( App, $ ) { , function ( App, $ ) {
"use strict"; "use strict";
// JQuery Backwords compatibility fix
$.curCSS = $.css;
$("body").ready(function(){ $("body").ready(function(){
App.init(); App.init();
}); });

@ -30,26 +30,20 @@ define([
xhr.open("GET", url, true); xhr.open("GET", url, true);
xhr.responseType = responseType; xhr.responseType = responseType;
xhr.timeout = 2000;
xhr.setRequestHeader("Content-type", contentType); xhr.setRequestHeader("Content-type", contentType);
xhr.overrideMimeType("text/plain; charset=x-user-defined"); // Technically not need with v2 and responseType but for good measures
// TODO: Add this: if (!_.isEmpty(that.attributes.basicauth)) {
// xhr.timeout = 4000;
// xhr.ontimeout = function () { alert("Timed out!!!"); }
console.log(that.attributes.password, that.attributes.username, window.btoa((that.attributes.username || "") + ":" + (that.attributes.password || "")));
if (!_.isEmpty(that.attributes.password) || !_.isEmpty(that.attributes.username)) {
var basicAuth = window.btoa((that.attributes.username || "") + ":" + (that.attributes.password || ""));
xhr.withCredentials = true; xhr.withCredentials = true;
xhr.setRequestHeader("Authorization", "Basic " + basicAuth); xhr.setRequestHeader("Authorization", "Basic " + that.attributes.basicauth);
} }
//xhr.overrideMimeType('text/plain; charset=x-user-defined');
try { try {
xhr.onerror = function() {console.log(arguments, this);}; xhr.onerror = function () {console.log("Error", arguments, this);};
xhr.onabort = function() {console.log(arguments, this);}; xhr.onabort = function () {console.log("Abort", arguments, this);};
xhr.onload = function(e) { xhr.ontimeout = function () {console.log("Timeout", arguments, this);};
xhr.onload = function (e) {
if (this.status === 200) { if (this.status === 200) {
callback(null, this.response); callback(null, this.response);
} else if (this.status === 401) { } else if (this.status === 401) {
@ -66,53 +60,172 @@ define([
} }
} }
, fetchContentTypes = function (success, error) { , fetchXHR = function (path, success, error) {
xhr(that.url("/content-codes?"), function (err, content) { xhr(that.url(path), function __fetchXHRCallback(err, content) {
if (err) { if (err) {
error || (error = function (err) {throw [err, content]}()); if (!_.isFunction(error)) {
error = function (err, cont) {
throw [err, cont];
};
}
error(err, content); error(err, content);
} else { } else {
that.collections.contentCodes = new DMAPModel([], { success(content);
buffer: content
});
success && success(content);
} }
}); });
} }
, fetchContentTypes = function (success, error) {
fetchXHR("/content-codes", function __fetchContentTypesCallback(content) {
that.collections.contentCodes = new DMAPModel(content);
if (_.isFunction(success)) {
success(content);
}
}, error);
}
, fetchServerInfo = function (success, error) { , fetchServerInfo = function (success, error) {
xhr(that.url("/server-info?"), function (err, content) { fetchXHR("/server-info", function __fetchServerInfoCallback(content) {
if (err) { that.collections.serverInfo = new DMAPModel(content, {
error || error = function (err) {throw [err, content]}(); contentCodes: that.collections.contentCodes
});
error(err, content); if (_.isFunction(success)) {
success(content);
}
}, error);
}
} else { , fetchLogin = function (success, error) {
that.collections.serverInfo = new DMAPModel([], { fetchXHR("/login", success, error);
buffer: content }
, contentCodes: that.collections.contentCodes
, fetchDatabases = function (success, error) {
fetchXHR("/databases", function __fetchDatabasesCallback(content) {
that.collections.databasesInfo = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
});
if (_.isFunction(success)) {
success(content);
}
}, error);
}
, fetchDatabase = function (success, error, dbId) {
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
fetchXHR("/databases/" + dbId + "/items", function __fetchDatabaseCallback(content) {
that.collections.databases[dbId] = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
}); });
success && success(content);
if (_.isFunction(success)) {
success(content);
}
}, error);
}
, fetchPlaylists = function (success, error, dbId) {
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
fetchXHR("/databases/" + dbId + "/containers", function __fetchPlaylistsCallback(content) {
that.collections.playlistsInfo = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
});
if (_.isFunction(success)) {
success(content);
}
}, error);
}
, fetchPlaylist = function (success, error, playlistId, dbId) {
if (_.isUndefined(playlistId) || !_.isNumeric(playlistId)) {
playlistId = 1;
} }
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
var requestUri = "/databases/"
;
requestUri += dbId;
requestUri += "/containers/";
requestUri += playlistId;
requestUri += "/items";
requestUri += "?meta=dmap.itemid,dmap.persistentid,dmap.containeritemid";
fetchXHR(requestUri, function __fetchPlaylistCallback(content) {
if (!_.isArray(that.collections.playlists[dbId])) {
that.collections.playlists[dbId] = [];
}
that.collections.playlists[dbId][playlistId] = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
}); });
if (_.isFunction(success)) {
success(content);
}
}, error);
} }
, fetchLogin = function () { , fetchBrowse = function (success, error, dbId, type) {
// TODO: We need to detect if the server supports browsing or not an implement our own otherwise.
if (_.isEmpty(type) || !_.isString(type) || !_.isArray(that.collections.browser[type])) {
throw new Error("INVALID_BROWSE_TYPE");
}
var requestUri = "/databases/"
;
requestUri += dbId;
requestUri += "/browse/";
requestUri += type;
requestUri += "?meta=dmap.itemid,dmap.persistentid,dmap.containeritemid";
fetchXHR(requestUri, function __fetchBrowseCallback(content) {
that.collections.browser[type][dbId] = new DMAPModel(content, {
contentCodes: that.collections.contentCodes
});
if (_.isFunction(success)) {
success(content);
}
}, error);
} }
, fetchDatabases = function () { , fetchGenres = function (success, error, dbId) {
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
return fetchBrowse(success, error, dbId, "genres");
} }
, fetchDatabase = function () { , fetchArtists = function (success, error, dbId) {
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
return fetchBrowse(success, error, dbId, "artists");
} }
, fetchPlaylists = function () { , fetchAlbums = function (success, error, dbId) {
if (_.isUndefined(dbId) || !_.isNumeric(dbId)) {
dbId = 1;
}
return fetchBrowse(success, error, dbId, "albums");
} }
, Client = Toolbox.Base.extend({ , Client = Toolbox.Base.extend({
@ -123,13 +236,60 @@ define([
_.extend(that, Client.defaults); _.extend(that, Client.defaults);
_.extend(that.attributes, attributes); _.extend(that.attributes, attributes);
that.setAuth();
return this; return this;
} }
, setAuth: function(username, password) {
if (!_.isUndefined(username)) {
that.attributes.username = username;
}
if (!_.isUndefined(password)) {
that.attributes.password = password;
}
if (!_.isEmpty(that.attributes.password) || !_.isEmpty(that.attributes.username)) {
that.attributes.basicauth = window.btoa((that.attributes.username || "") + ":" + (that.attributes.password || ""));
}
}
, init: function () { , init: function () {
var that = this
;
that.console.debug("Fetching content types");
fetchContentTypes(function () { fetchContentTypes(function () {
that.console.debug("Fetching server info");
fetchServerInfo(function () { fetchServerInfo(function () {
that.console.debug("Fetching login");
fetchLogin(function () {
// TODO: Here we need to request /update for the database version
that.console.debug("Fetching databases info");
fetchDatabases(function () {
//that.console.debug("Fetching database 1");
//fetchDatabase(function () {
that.console.debug("Fetching playlists info");
fetchPlaylists(function () {
//that.console.debug("Fetching playlist 1 of db 1");
//fetchPlaylist(function () {
//that.console.debug("Fetching genres of db 1");
//fetchGenres(function () {
//that.console.debug("Fetching artists of db 1");
//fetchArtists(function () {
//that.console.debug("Fetching albums of db 1");
//fetchAlbums(function () {
that.trigger("inited", that.collections); that.trigger("inited", that.collections);
//});
//});
//});
//});
});
//});
});
});
}); });
}); });
@ -138,6 +298,22 @@ define([
,logout: function () { ,logout: function () {
}
, buildRequestUid: function(path, fields, query) {
if (_.isEmpty(path) || !_.isString(path)) {
throw new Error("INVALID_TYPE_ERROR");
}
if (!_.isArray(fields)) {
fields = [];
}
if (!_.isArray(query)) {
query = [];
}
} }
,url: function (request) { ,url: function (request) {
@ -148,8 +324,39 @@ define([
uri = request; // Add leading slash here! uri = request; // Add leading slash here!
} }
if (_.isNumber(this.collections.session.id)) {
var prefix = "?";
if (uri.indexOf("?") >= 0) {
prefix = "&";
}
uri += prefix + "session-id=" + this.collections.session.id;
}
return this.attributes.protocol + "://" + this.attributes.hostname + ":" + this.attributes.port + uri; return this.attributes.protocol + "://" + this.attributes.hostname + ":" + this.attributes.port + uri;
} }
, console: {
log: function() {
var args = arguments;
if (_.isString(args[0])) {
args[0] = that.url() + " " + args[0];
}
console.log(args);
}
, debug: function() {
var args = arguments;
if (_.isString(args[0])) {
args[0] = that.url() + " " + args[0];
}
console.debug(args);
}
}
}, { }, {
defaults: { defaults: {
attributes: { attributes: {
@ -161,6 +368,16 @@ define([
, password: "" , password: ""
} }
, status: 0
, statusCodes: {
disconnected: 0
, connecting: 1
, timedout: 2
, unauthorized: 3
, connected: 4
}
, collections: { , collections: {
contentCodes: null contentCodes: null
@ -172,20 +389,20 @@ define([
, databasesInfo:null , databasesInfo:null
, databases: null , databases: []
, playlistsInfo:null , playlistsInfo:null
, genres: null , playlists: []
, browser: {
genres: []
, artists: null , artists: []
, albums: null , albums: []
} }
} }
, lawl: function() {
console.info(arguments);
} }
}); });

@ -11,154 +11,59 @@ define([
, function (_, Toolbox) { , function (_, Toolbox) {
"use strict"; "use strict";
var that var
Types = function(code) {
, Types = Toolbox.Base.extend({ this.code = code;
constructor: function (code) { this.length = null;
this.code = code this.unpack = null;
, this.length = null this.typeMap = typeMap;
;
this.getCode = function() {
if (_.isNumber(code)) {
if (!_.isUndefined(Types.typeMap[code])) {
_.extend(this, Types.typeMap[code]);
} else {
}
}
return this;
}
, code: null
, length: null
, unpack: null
, getCode: function() {
return this.code; return this.code;
} };
, getByteLength: function() { this.getByteLength = function() {
return this.length; return this.length;
} };
}
, {
typeMap: { if (_.isNumber(this.code)) {
1: { if (!_.isUndefined(typeMap[this.code])) {
length: 1 _.extend(this, typeMap[this.code]);
, unpack: function (chunk) { } else {
Types.getInt8(chunk);
}
}
, 2: {
length: 1
, unpack: function (chunk) {
Types.getUint8(chunk);
}
}
, 3: {
length: 2
, unpack: function (chunk) {
Types.getInt16(chunk);
}
}
, 4: {
length: 2
, unpack: function (chunk) {
Types.getUint16(chunk);
}
}
, 5: {
length: 4
, unpack: function (chunk) {
Types.getInt32(chunk);
}
}
, 6: {
length: 4
, unpack: function (chunk) {
Types.getUint32(chunk);
}
}
, 7: {
length: 8
, unpack: function (chunk) {
Types.getFloat64(chunk);
}
}
// There is no unsigned 64bit integer in JS.
, 8: {
length: 8
, unpack: function (chunk) {
Types.getFloat64(chunk);
}
}
, 9: {
length: null
, unpack: function (chunk, length) {
Types.getUTFString(chunk, length);
}
}
, 10: {
length: 4
, unpack: function (chunk) {
Types.getDate(chunk);
}
}
, 11: {
length: 2
, unpack: function (chunk) {
Types.getVersion(chunk);
} }
} }
, 12: { return this;
length: null
, unpack: function (chunk, length) {
Types.getModel(chunk, length);
}
}
} }
, getInt8: function (chunk) { , TypesPrototype = {
return new DataView(chunk.slice(0,Types.typeMap[1].length)).getInt8(0, false); getInt8: function (chunk) {
return new DataView(chunk.slice(0,typeMap[1].length)).getInt8(0, false);
} }
, getUint8: function (chunk) { , getUint8: function (chunk) {
return new DataView(chunk.slice(0,Types.typeMap[2].length)).getUint8(0, false); return new DataView(chunk.slice(0,typeMap[2].length)).getUint8(0, false);
} }
, getInt16: function (chunk) { , getInt16: function (chunk) {
return new DataView(chunk.slice(0,Types.typeMap[3].length)).getInt16(0, false); return new DataView(chunk.slice(0,typeMap[3].length)).getInt16(0, false);
} }
, getUint16: function (chunk) { , getUint16: function (chunk) {
return new DataView(chunk.slice(0,Types.typeMap[4].length)).getUint16(0, false); return new DataView(chunk.slice(0,typeMap[4].length)).getUint16(0, false);
} }
, getInt32: function (chunk) { , getInt32: function (chunk) {
return new DataView(chunk.slice(0,Types.typeMap[5].length)).getInt32(0, false); return new DataView(chunk.slice(0,typeMap[5].length)).getInt32(0, false);
} }
, getUint32: function (chunk) { , getUint32: function (chunk) {
return new DataView(chunk.slice(0,Types.typeMap[6].length)).getUint32(0, false); return new DataView(chunk.slice(0,typeMap[6].length)).getUint32(0, false);
} }
, getFloat64: function (chunk) { , getFloat64: function (chunk) {
return new DataView(chunk.slice(0,Types.typeMap[7].length)).getFloat64(0, false); return new DataView(chunk.slice(0,typeMap[7].length)).getFloat64(0, false);
} }
, getString: function(chunk, length) { , getString: function(chunk, length) {
@ -190,11 +95,11 @@ define([
} }
, getDate: function (chunk) { , getDate: function (chunk) {
return new Date(Types.getUint32(chunk)); return new Date(TypesPrototype.getUint32(chunk) * 1000);
} }
, getVersion: function (chunk) { , getVersion: function (chunk) {
return Types.getInt8(chunk.slice(0,1)) + "." + Types.getInt8(chunk.slice(1,2)); return TypesPrototype.getInt8(chunk.slice(0,1)) + "." + TypesPrototype.getInt8(chunk.slice(1,2));
} }
, getModel: function (chunk) { , getModel: function (chunk) {
@ -204,21 +109,85 @@ define([
, getUnknown: function (chunk) { , getUnknown: function (chunk) {
// Unimplemented // Unimplemented
throw "ERROR_UNIMPLEMENTED"; throw "ERROR_UNIMPLEMENTED";
/*
switch (chunk.byteLength) {
// We can tring a string
default:
break; // switch (chunk.byteLength) {
} // // We can tring a string
*/ // default:
//
// break;
// }
} }
, getType: function (chunk) { , getType: function (chunk) {
return Types.getString(chunk.slice(0, 4)).toLowerCase(); return TypesPrototype.getString(chunk.slice(0, 4)).toLowerCase();
}
}
, typeMap = {
1: {
length: 1
, unpack: TypesPrototype.getInt8
}
, 2: {
length: 1
, unpack: TypesPrototype.getUint8
}
, 3: {
length: 2
, unpack: TypesPrototype.getInt16
}
, 4: {
length: 2
, unpack: TypesPrototype.getUint16
}
, 5: {
length: 4
, unpack: TypesPrototype.getInt32
}
, 6: {
length: 4
, unpack: TypesPrototype.getUint32
}
, 7: {
length: 8
, unpack: TypesPrototype.getFloat64
}
// There is no unsigned 64bit integer in JS.
, 8: {
length: 8
, unpack: TypesPrototype.getFloat64
}
, 9: {
length: null
, unpack: TypesPrototype.getUTFString
}
, 10: {
length: 4
, unpack: TypesPrototype.getDate
}
, 11: {
length: 2
, unpack: TypesPrototype.getVersion
}
, 12: {
length: null
, unpack: TypesPrototype.getModel
} }
};
}); Types.prototype = TypesPrototype;
return Types; return Types;
}); });

@ -7,43 +7,61 @@
define([ define([
"underscore" "underscore"
, "backbone" , "backbone"
, "toolbox"
, "models/dmap-type" , "models/dmap-type"
, "collections/dmap" , "collections/dmap"
] ]
, function (_, Backbone, DMAPType, DMAPCollection) { , function (_, Backbone, Toolbox, DMAPType, DMAPCollection) {
"use strict"; "use strict";
var that var that
, browserTags = {
"abal": "abal"
, "abar": "abar"
, "abcp": "abcp"
, "abgn": "abgn"
, "abro": "abro"
}
, Model = Backbone.Model.extend({ , Model = Backbone.Model.extend({
idAttribute: "_id" idAttribute: "_id"
, initialize: function (attributes, options) { , initialize: function (attributes, options) {
that = this; that = this;
this.contentCodes = options && options.contentCodes || null; var contentCodes = options && options.contentCodes || null;
console.log("Initial", options, this.contentCodes);
if (options.buffer instanceof ArrayBuffer) { if (attributes instanceof ArrayBuffer) {//(options.buffer instanceof ArrayBuffer) {
attributes = parseWrapper(options.buffer); attributes = parseWrapper(attributes, contentCodes);
delete options.buffer; }
this.set(attributes); if (attributes && attributes.byteLength) {
delete attributes.byteLength;
} }
attributes._id = that.getAttributesId(attributes);
this.set(attributes);
return this; return this;
} }
/*
, set: function (attributes, options) {
console.log(attributes, options);
this._id = attributes["dmap.persistentid"] || attributes["dmap.itemid"] || attributes["dmap.containeritemid"]; , getAttributesId: function(attributes) {
return /*attributes["dmap_persistentid"] || */attributes["dmap_itemid"] || attributes["dmap_containeritemid"] || null;
}
Model.__super__.set(this, arguments); , toJSON: function(options) {
var attrs = _.clone(this.attributes);
return this; if (!_.isUndefined(attrs._id)) {
attrs.id = attrs._id;
delete attrs._id;
}
return attrs;
} }
*/
, update: function (attributes, options) { , update: function (attributes, options) {
that.trigger("dmap.model.update", this, arguments); that.trigger("dmap.model.update", this, arguments);
@ -85,27 +103,26 @@ define([
} }
}) })
, parseCodeDictionnary = function(dictChunk) { , parseContentCode = function(dictChunk) {
var codeElement = {}; var codeElement = {};
while (dictChunk.byteLength > 8) { while (dictChunk.byteLength > 8) {
var contentCode = DMAPType.getType(dictChunk); var contentCode = DMAPType.prototype.getType(dictChunk);
var contentLength = DMAPType.getInt32(dictChunk.slice(4,8)); var contentLength = DMAPType.prototype.getInt32(dictChunk.slice(4,8));
//console.log(contentLength); //console.log(contentLength);
switch (contentCode) { switch (contentCode) {
case 'mcnm': case 'mcnm':
codeElement.code = DMAPType.getString(dictChunk.slice(8, 8+contentLength)); codeElement.code = DMAPType.prototype.getString(dictChunk.slice(8, 8+contentLength));
break; break;
case 'mcna': case 'mcna':
var codeName = DMAPType.getString(dictChunk.slice(8, 8+contentLength)); var codeName = DMAPType.prototype.getString(dictChunk.slice(8, 8+contentLength));
codeElement.name = codeName.replace(/\./g, '_'); codeElement.name = codeName.replace(/\./g, '_');
//console.log(codeElement.name);
break; break;
case 'mcty': case 'mcty':
var typeData = DMAPType.getInt16(dictChunk.slice(8, 8+contentLength)); var typeData = DMAPType.prototype.getInt16(dictChunk.slice(8, 8+contentLength));
codeElement.type = new DMAPType(typeData); codeElement.type = new DMAPType(typeData);
break; break;
} }
@ -116,45 +133,68 @@ define([
return codeElement; return codeElement;
} }
, parseBinaryChunk = function (binaryChunk) { , parseBinaryChunk = function (binaryChunk, contentCodes) {
var entry = {}; var entry = {};
while (binaryChunk.byteLength > 8) { while (binaryChunk.byteLength > 8) {
var tagName = DMAPType.getType(binaryChunk); var tagName = DMAPType.prototype.getType(binaryChunk);
var tagLength = DMAPType.getInt32(binaryChunk.slice(4,8)); var tagLength = DMAPType.prototype.getInt32(binaryChunk.slice(4,8));
//console.debug(codeInfo, tagName, tagLength);
if (tagName !== "mstt") { // Skip the status code, we really don't care for it.
if (that.contentCodes === null) { if (tagName === "mdcl") { // Our contentCodes is probably empty as we found the content codes
switch (tagName) { var contentCode = parseContentCode(binaryChunk.slice(8, 8+tagLength));
case 'mdcl':
var contentCode = parseCodeDictionnary(binaryChunk.slice(8, 8+tagLength));
var codeId = contentCode.code.toLowerCase(); var codeId = contentCode.code.toLowerCase();
entry[codeId] = _.extend({}, contentCode, contentCode.type); entry[codeId] = _.extend({}, contentCode, contentCode.type);
break; } else {
var codeInfo = contentCodes.get(tagName);
case 'mstt': if (!_.isObject(codeInfo)) {
// Nothing to do with the status, skip it console.info("Unknow content code: " + tagName, tagLength);
break;
}
} else { } else {
var codeInfo = that.contentCodes.get(tagName); if (codeInfo.getCode() !== 12 ) {
entry[codeInfo.name] = codeInfo.unpack(binaryChunk.slice(8), tagLength);
} else if ((tagName in browserTags) === true) {
tagLength = binaryChunk.byteLength; // This is how fucked DAAP is, we overwrite the length because
// in this specific case 'mlit' which is reported as a container is in fact a string
var browserList = binaryChunk.slice(8);
entry[codeInfo.name] = [];
while (browserList.byteLength > 8) {
var itemTag = DMAPType.prototype.getType(browserList);
var itemLength = DMAPType.prototype.getInt32(browserList.slice(4,8));
entry[codeInfo.name].push(DMAPType.prototype.getUTFString(browserList.slice(8), itemLength));
if (codeInfo) { browserList = browserList.slice(8 + itemLength);
if (codeInfo.type === 12) { }
if (typeof entry[codeInfo.name] === "undefined") { } else if (codeInfo.getCode() === 12) {
// Here we might want to select different collection types depending on the tag var result = parseBinaryChunk(binaryChunk.slice(8, tagLength), contentCodes);
if (result) {
var keys = null;
if (_.isString(result)) {
if ( !(entry[codeInfo.name] instanceof DMAPCollection) ) {
entry[codeInfo.name] = [];
}
entry[codeInfo.name].push(result);
} else if (getItemId(result) !== null) {
if ( !(entry[codeInfo.name] instanceof DMAPCollection) ) {
entry[codeInfo.name] = new DMAPCollection(); entry[codeInfo.name] = new DMAPCollection();
} }
entry[codeInfo.name].add(binaryChunk.slice(8, tagLength), {silent: true}); entry[codeInfo.name].add(new Model(result));
} else { // Here we found a listing item and we are going to squash the single item array
//console.log(codeInfo); } else if (_.isObject(result) && Object.keys(result).length === 1) {
entry[codeInfo.name] = codeInfo.unpack(binaryChunk.slice(8), tagLength); entry[codeInfo.name] = result[Object.keys(result)[0]];
} }
} else { } else {
// TODO: we should try to guess. // We could alternatively autodetect the type here
console.info("Unknown type: " + tagName + "(" + tagLength + ")"); }
}
}
} }
} }
@ -164,26 +204,29 @@ define([
return entry; return entry;
} }
, parseWrapper = function(binaryData) { , parseWrapper = function(binaryData, contentCodes) {
var containerType = DMAPType.getType(binaryData); if (binaryData.byteLength <= 8) {
//var binaryLength = DMAPType.getUint32(binaryData.slice(4,8)); return parseBinaryChunk(binaryData, contentCodes);
} else {
var containerType = DMAPType.prototype.getType(binaryData);
//var binaryLength = DMAPType.prototype.getUint32(binaryData.slice(4,8));
var contentData = binaryData.slice(8); var contentData = binaryData.slice(8);
switch (containerType) { switch (containerType) {
// Update // Update
case "mupd": //case "mupd":
// Unsupported // // Unsupported
return; // return;
break; //break;
// Browsing list
default: case "abro":
console.info("Unknown type: " + containerType/*, binaryLength*/, contentData);
// Content codes // Content codes
case "mccr": case "mccr":
// Server Info // Server Info
case "msrv": case "msrv":
// Login // Login
case "mlog": case "mlog":
// Item listing
case "mlit":
// Database // Database
case "adbs": case "adbs":
// Database info // Database info
@ -192,11 +235,18 @@ define([
case "aply": case "aply":
// List of songs // List of songs
case "apso": case "apso":
return parseBinaryChunk(contentData); return parseBinaryChunk(contentData, contentCodes);
break; default:
console.info("Unknown type: " + containerType, contentData);
return parseBinaryChunk(binaryData, contentCodes);
}
} }
} }
;
, getItemId = function (item) {
return item["dmap_persistentid"] || item["dmap_itemid"] || item["dmap_containeritemid"] || null;
}
;
return Model; return Model;
}); });

@ -0,0 +1,71 @@
/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
define([
"underscore"
, "backbone"
, "toolbox"
, "models/dmap-type"
]
, function (_, Backbone, Toolbox, DMAPType) {
"use strict";
var that
, set = function (attributes) {
}
, DMAP = Toolbox.Base.extend({
id: null
, attributes: false
, collection: false
, parentid: null
, index: {}
, items: []
, constructor: function (buffer, parent, options) {
}
, set: function () {
}
, get: function () {
}
, getItem: function () {
}
, getItemByIndex: function () {
}
, parseBinary: function () {
}
, parseContentCode: function () {
}
, addItem: function (item) {
}
, remove: function (items) {
}
});
_.extend(DMAP.prototype, Backbone.Events, {});
return DMAP;
});

@ -0,0 +1,59 @@
/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
define([
"jquery"
, "backbone"
, "jquery-layout"
]
, function ($, Backbone) {
"use strict";
return Backbone.View.extend({
el: $("body")
, layout: null
, layoutItems: null
, events: {
}
, initialize: function() {
//_.bindAll(this);
var that = this;
that.layout = that.$el.layout({
//applyDefaultStyles: true
defaults: {
closable: false
, resizable: false
, slidable: false
, spacing_open: 0
, spacing_closed: 0
}
, north: {
size: 52
}
, west: {
size: 150
, closable: true
}
, center: {
resizable: false
}
, south: {
size: 30
}
});
}
});
});

@ -0,0 +1,32 @@
/*
that.layoutItems = $(that.layout.panes.center).layout({
defaults: {
closable: false
, resizable: true
, slidable: true
, spacing_open: 5
, spacing_closed: 5
}
, north: {
minSize: 100
, size: 200
, closable: true
//, onresize: function (pane, $pane, state, options) {
// var viewportHeight = $pane.innerHeight() - App.BrowserView.$el.find(".dataTables_scrollHead").height();
// App.BrowserView.$el.find(".dataTables_scrollBody").height(viewportHeight);
// //App.BrowserView.reDraw();
// }
}
, center: {
minSize: 200
//, onresize: function (pane, $pane, state, options) {
// var viewportHeight = $pane.innerHeight() - App.ListView.$el.find(".dataTables_scrollHead").height();
// App.ListView.$el.find(".dataTables_scrollBody").height(viewportHeight);
// //App.ListView.render();
// }
}
});
*/

@ -0,0 +1,14 @@
/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
define([
"bootstrap"
]
, function (Bootstrap) {
"use strict";
return function() {};
});

@ -0,0 +1,13 @@
/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
define([
]
, function () {
"use strict";
return function() {};
});

@ -0,0 +1,13 @@
/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
define([
]
, function () {
"use strict";
return function() {};
});

@ -0,0 +1,13 @@
/*jslint laxbreak:true */
/*jslint laxcomma:true */
/*jslint loopfunc:true */
/*jslint strict:true */
/*jslint browser:true */
/*jslint devel:true */
define([
]
, function () {
"use strict";
return function() {};
});

@ -0,0 +1 @@
Subproject commit 8651291560d37403bf347466213bd8b900b0a501

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save