current track is highlighted and other small changes

This commit is contained in:
Wouter van Wijk 2012-12-29 00:07:12 +01:00
parent 5079b4d2b2
commit 4630c026aa
23 changed files with 98 additions and 7431 deletions

View File

@ -4,9 +4,10 @@ Mopidy Browser Client
Mopidy is a music server which can play music from `Spotify
<http://www.spotify.com/>`_ or from your local hard drive.
This is an html/js/css client for Mopidy, which has tighter integration with the server than traditional mpd-clients.
This is a responsive html/js/css client for Mopidy. Responsive, so it works on desktop and mobile browsers.
To install Mopidy, check out
`the installation docs <http://docs.mopidy.com/en/latest/installation/>`_.
Drop the files from this client in a folder on your Mopidy-system and change the settings.py (of Mopidy) to make it work. See http://docs.mopidy.com/en/latest/settings/ for instructions on the settings.
Drop the web folder from this client in the mopidy/frontends/ws folder and change the settings.py (of Mopidy) to make it work. work. See `Settings <http://docs.mopidy.com/en/latest/settings/>`_ for instructions on the settings.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,214 +0,0 @@
/********************************
* FORM
* jquery.form.css
********************************/
/* normalize */
form{
margin:0;
padding:0;
}
/********************************
* Normal elements
********************************/
form label.required:after{
content:" *";
}
/*********************************
* error message
*********************************/
input.invalid{
outline:1px solid gold;
}
div.errormsg{
padding:5px;
position:absolute;
display:inline-block;
color:black;
font-size:14px;
margin:0;
margin-left:10px;
font-weight:normal;
background-color:white;
border:1px solid black;
border-radius:5px;
box-shadow:5px 5px 5px rgba(0,0,0,0.5);
z-index:1;
}
div.errormsg:before,div.errormsg:after{
position:absolute;
left:-10px;
bottom:5px;
content:" ";
width:0;
height:0;
padding:0;
border:10px solid transparent;
border-width:8px 10px;
border-right-color:inherit;
border-left-width:0;
}
div.errormsg:after{
border-right-color:white;
left:-8px;
}
input.invalid + div.errormsg{
}
/*************************
* Placeholder
*************************/
input.placeholder,
textarea.placeholder{
color:#bbb;
}
/*************************
* type.range
*************************/
input.range{
padding-bottom:-20px;
border:0;
cursor:pointer;
color:transparent;
background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA+kAAAAeCAYAAABQQl9DAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADscAAA7HATiSL3YAAAO9SURBVHhe7d0xbttAEAVQKmlTBCl8AQcQYMG5i3MTnkQQoKvEN1GhQpFvEANJI5MUQ6pJxXUZzOwTwMrN/DcLGB+iyKbxIUCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBP4JrGAQIECAAAEC8QT2+33fdd3Hdyb/1bbtl3jpTEyAAAECBOoVUNLr3b3kBAgQIBBYYLvdjt+fnhYTXC6X1x/Pz91U0u8CxzQ6AQIECBCoTuBDdYkFJkCAAAECSQSGYWiWrn76mw8BAgQIECAQT0BJj7czExMgQIAAgZvAXMSXrrm8+xAgQIAAAQLxBJT0eDszMQECBAgQuAkMfb98KelOCQECBAgQCCmgpIdcm6EJECBAgMBU0gu3u1+VdEeEAAECBAiEFFDSQ67N0AQIECBAoFzSh+sVEQECBAgQIBBQQEkPuDQjEyBAgACBWaCfbndfuoaug0SAAAECBAgEFFDSAy7NyAQIECBAYBYoPt3dN+kOCQECBAgQCCmgpIdcm6EJECBAgIDfpDsDBAgQIEAgo4CSnnGrMhEgQIBAFQKlV7B5T3oVR0BIAgQIEEgooKQnXKpIBAgQIFCHQPHp7m53r+MQSEmAAAEC6QRWu91uvPpHnm6xAhEgQIBAfoFvj4+LIS9vb83xeMyPICEBAgQIEEgmsNput+Nms0kWSxwCBAgQIJBb4HA4NJuHh8WQ8+3up9OpWa/XuSGkI0CAAAECyQTmkv57yvQpWS5xCBAgQIBAeoFSAe+mb9J/ns/pDQQkQIAAAQLZBFbZAslDgAABAgRqEJjvhPt6f78Ydfq9+uv55aVr2/auBg8ZCRAgQIBAFgEPjsuySTkIECBAoDqB0oPj5r/5ECBAgAABAvEElPR4OzMxAQIECBC4CZRewTZ4KKxTQoAAAQIEQgoo6SHXZmgCBAgQINA0pW/S+75HRIAAAQIECAQUUNIDLs3IBAgQIEBgFvCedOeAAAECBAjkE1DS8+1UIgIECBCoRGCYvi0vXZUwiEmAAAECBFIJKOmp1ikMAQIECNQk4DfpNW1bVgIECBCoRUBJr2XTchIgQIBAOgFPd0+3UoEIECBAgECjpDsEBAgQIEAgqMD8cLilyyvYgi7V2AQIECBQvYCSXv0RAECAAAECUQU83T3q5sxNgAABAgSWBZR0p4MAAQIECAQVcLt70MUZmwABAgQIFASUdMeDAAECBAjEFPjzzivYPo/jOMSMZmoCBAgQIECAAAECBAgQIECAAAECBAgQIPCfBf4CXCs3jGOIZn4AAAAASUVORK5CYII=) no-repeat center center;
}
/*************************
* input.number
*************************/
input.number{
margin-right:0;
padding-right: 22px;
}
span.number{
margin-right: 32px;
margin-left: -20px;
display:inline-block;
vertical-align:middle;
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
}
span.number span{
border:10px solid transparent;
border-width:10px 5px;
display:block;
width:0;
height:0;
margin:2px;
border-bottom-width:0;
border-bottom-color:#555555;
border-top-color:#555555;
}
span.number span:hover{
border-bottom-color:black;
border-top-color:black;
}
span.number :first-child{
border-bottom-width:10px;
border-top-width:0;
}
/*************************
* Calendar
*************************/
div.calendar{
display:inline-block;
color:white;
}
div.calendar div{
background:blue;
position:absolute;
width:auto;
box-shadow:0 0 10px black;
border-radius:5px;
}
div.calendar table{
border-collapse: collapse;
border-spacing: 0;
margin-bottom: 20px;
width:100%;
width:auto;
margin:10px;
}
div.calendar table caption{
background:inherit;
width:inherit;
text-align:center;
margin-bottom:10px;
}
div.calendar table caption .current{
margin:0;
text-align:center;
display:inline-block;
box-sizing:border-box;
-moz-box-sizing:border-box;
font-weight:bold;
}
div.calendar table caption a{
float:left;
text-align:left;
}
div.calendar table caption span + a{
float:right;
text-align:right;
}
div.calendar table th,
div.calendar table td{
text-align:center;
border: 1px solid #99CCFF;
border: 1px solid rgba(255,255,255,0.3);
padding:0;
}
div.calendar table td{
padding:0 6px;
}
div.calendar td.selected{
background:#99CCFF;
}
div.calendar a{
color:inherit;
text-decoration:none;
}
/*************************
* Datalist
*************************/
div.datalist{
display:none;
position:absolute;
background-color:white;
border:1px solid gray;
}
input.datalist + div.datalist{
display:block;
}
div.datalist ul{
margin:5px;
padding:0;
}
div.datalist ul li{
list-style:none;
padding:5px;
}
div.datalist ul li:hover,
div.datalist ul li.hover{
text-decoration:underline;
background-color:#efefef;
}
datalist select{
display:none;
}

View File

@ -1,35 +1,36 @@
/*@navbarBackground: green;
@navbarBackgroundHighlight: green;
@navbarLinkColor: white;
@navbarLinkColorHover: white;
@navbarBackgroundHighlight: green;
@navbarLinkColor: white;
@navbarLinkColorHover: white;
.navbar .nav {
height: @navbarHeight;
}
.navbar .nav > li > a {
padding: (@navbarHeight - 20)/2 10px ((@navbarHeight - 20)/2 + 1);
}
.navbar .brand {
padding: (((@navbarHeight - 20) / 2) * 0.8) 20px (((@navbarHeight - 20) / 2) * 1.2);
}
*/
.navbar .nav {
height: @navbarHeight;
}
.navbar .nav > li > a {
padding: (@navbarHeight - 20)/2 10px ((@navbarHeight - 20)/2 + 1);
}
.navbar .brand {
padding: (((@navbarHeight - 20) / 2) * 0.8) 20px (((@navbarHeight - 20) / 2) * 1.2);
}
*/
.content {
display: none;
}
.menutext {
vertical-align:bottom;
vertical-align: bottom;
}
#trackslider {
width:90%;
width: 90%;
}
#slidercontainer {
margin-left: 2%; width:95%;
margin-top: 5px;
margin-bottom: 5px;
margin-left: 2%;
width: 95%;
margin-top: 5px;
margin-bottom: 5px;
}
#mainmenu a {
@ -63,41 +64,40 @@ body {
}
@media (max-width: 979px) {
body {
padding-top: 0px;
}
.btsquare {
display: block;
}
body {
padding-top: 0px;
}
.btsquare {
display: block;
}
}
@media (min-width: 979px) {
.btsquare {
display: inline;
}
.btsquare {
display: inline;
}
}
.nobreak, .btsquare {
display: inline;
}
#allresultloader {
#allresultloader, .loader {
display: none;
}
}
#playlistslist li {
padding: 5px;
list-style-type:none;
list-style-type: none;
}
#playlistslist li:nth-of-type(odd) {
background-color:#444;
}
background-color: #444;
}
#playlistslist li:nth-of-type(even) {
background-color:#333;
}
background-color: #333;
}
#volumecontainer {
margin-top: 3px;
@ -110,14 +110,12 @@ body {
margin-top: 20px;
}
#searchresults {
display: none;
}
.resultrow:hover {
.resultrow:hover {
background-color: #555 !important;
}
.brand {
margin-right: 20px;
@ -132,7 +130,7 @@ body {
}
div {
/* border: 1px solid #444; */
/* border: 1px solid #444; */
}
.breakafter {
@ -144,26 +142,33 @@ div {
/*display:none;*/
}
.currenttrack {
font-weight: bold;
background-image: url('../img/icons/play_alt_16x16.png');
background-repeat:no-repeat;
padding-left: 20px;
}
#songinfo {
overflow: hidden;
margin-left: auto ;
margin-right: auto ;
margin-top: 5px;
overflow: hidden;
margin-left: auto;
margin-right: auto;
margin-top: 5px;
}
#infoname {
font: bold;
color: #ddd;
font-size:14px;
text-shadow:1px 1px #555;
font-size: 14px;
text-shadow: 1px 1px #555;
}
#infoartist {
color: #aaa;
font-size:11px;
text-shadow:1px 1px #333;
font-size: 11px;
text-shadow: 1px 1px #333;
}
#playlistpane {
visibility:hidden;
visibility: hidden;
}

View File

@ -1,92 +0,0 @@
Mopidiy Webservices Protocol
- events
- nested playlists
- coverart
- playlist edit
Optional
- people
- 'artistpedia'
- toplists artist
----
Protocol
Base: socketio
Events to mopidy (client generated):
------------------------------------
SetPlay
Toggles play status. Arguments: none, true or false (true=play, none toggles)
Previoustrack/nexttreck
Argument: none
Loadtracks:
Loads tracks into the current playlist. Arguments: an uri of a playlist, album, search, artist. Or a custom list of track-uris
Playuri
Plays track in playlist, continues to next track if there is one. Argument: track-uri
Seek
Goto a position in the current track. Argument: time (in seconds)
SetVolume
Argument: 0-100
SetMute
Toggles mute status. Arguments: none, true or false (true=mute, none toggles)
Getartist/getplaylist/getalbum
Returns a list of tracks, including names, etc. Argument: uri
Getplaylists
Returns a nested list of playlists of the user, with names and uris, playlist-folders
Getservicedata
Returns data of the current service (spotify, rdio, whatever). Returns: serviceid, service name, logo, username
Getcurrentplaylist
Returns a list of the currently loaded playlist (complete)
Getcurrentplaylisturi
Returns an uri of the currently loaded playlist, or none if its a custom list
Getcurrenttrack
Returns trackdata of current track
Getstatus
Returns array of statusses of play, mute, currenttrack, position, volume, shuffle, repeat
Getfavoritesongs
Returns a list of starred songs
Getfavoritealbums
Returns a list of starred albums (needs workaround for spotify)
Getfavoriteartists
Returns a list of starred artists (needs workaround for spotify)
Getnewalbums
Returns a list of what's news albums
Setshuffle/setrepeat
Toggles shuffle/repeat status. Arguments: none, true or false (true=on, none toggles)
Search
Returns a list of tracks. Arguments: keywords, method (none=all, album, artist, track)
Events from Mopidy to client:
-----------------------------
Currenttrackupdated
Volumeupdated
Playstatusupdated
PlaylistUpdated
MessageUser
SearchResults
AlbumResults
ArtistResults
PLaylistResults
UserPlaylistsResults

View File

@ -5,11 +5,11 @@
<meta content="true" name="MSSmartTagsPreventParsing" />
<meta http-equiv="X-UA-Compatible" content="IE=9" />
<!-- proble <meta name = "viewport" content = "width=device-width, initial-scale = 1.0, user-scalable = no"> -->
<meta name = "viewport" content = "width=device-width, initial-scale = 1.0, user-scalable = no" />
<!-- <meta name="viewport" content="width=device-width, user-scalable=no"> -->
<!-- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no"> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
<link rel="apple-touch-icon" href="img/icons/headphones_32x28.png" />
<script src="js/html5slider.js"></script>
@ -265,3 +265,4 @@
</body>
</html>
html>

View File

@ -1,112 +0,0 @@
## 2.0 BOOTSTRAP JS PHILOSOPHY
These are the high-level design rules which guide the development of Bootstrap's plugin apis.
---
### DATA-ATTRIBUTE API
We believe you should be able to use all plugins provided by Bootstrap purely through the markup API without writing a single line of JavaScript. This is Bootstrap's first class API.
We acknowledge that this isn't always the most performant and it may sometimes be desirable to turn this functionality off altogether. Therefore, as of 2.0 we provide the ability to disable the data attribute API by unbinding all events on the body namespaced with `'data-api'`. This looks like this:
$('body').off('.data-api')
To target a specific plugin, just include the plugins name as a namespace along with the data-api namespace like this:
$('body').off('.alert.data-api')
---
### PROGRAMATIC API
We also believe you should be able to use all plugins provided by Bootstrap purely through the JavaScript API.
All public APIs should be single, chainable methods, and return the collection acted upon.
$(".btn.danger").button("toggle").addClass("fat")
All methods should accept an optional options object, a string which targets a particular method, or null which initiates the default behavior:
$("#myModal").modal() // initialized with defaults
$("#myModal").modal({ keyboard: false }) // initialized with no keyboard
$("#myModal").modal('show') // initializes and invokes show immediately
---
### OPTIONS
Options should be sparse and add universal value. We should pick the right defaults.
All plugins should have a default object which can be modified to affect all instances' default options. The defaults object should be available via `$.fn.plugin.defaults`.
$.fn.modal.defaults = { … }
An options definition should take the following form:
*noun*: *adjective* - describes or modifies a quality of an instance
Examples:
backdrop: true
keyboard: false
placement: 'top'
---
### EVENTS
All events should have an infinitive and past participle form. The infinitive is fired just before an action takes place, the past participle on completion of the action.
show | shown
hide | hidden
All infinitive events should provide preventDefault functionality. This provides the abililty to stop the execution of an action.
$('#myModal').on('show', function (e) {
if (!data) return e.preventDefault() // stops modal from being shown
})
---
### CONSTRUCTORS
Each plugin should expose its raw constructor on a `Constructor` property -- accessed in the following way:
$.fn.popover.Constructor
---
### DATA ACCESSOR
Each plugin stores a copy of the invoked class on an object. This class instance can be accessed directly through jQuery's data API like this:
$('[rel=popover]').data('popover') instanceof $.fn.popover.Constructor
---
### DATA ATTRIBUTES
Data attributes should take the following form:
- data-{{verb}}={{plugin}} - defines main interaction
- data-target || href^=# - defined on "control" element (if element controls an element other than self)
- data-{{noun}} - defines class instance options
Examples:
// control other targets
data-toggle="modal" data-target="#foo"
data-toggle="collapse" data-target="#foo" data-parent="#bar"
// defined on element they control
data-spy="scroll"
data-dismiss="modal"
data-dismiss="alert"
data-toggle="dropdown"
data-toggle="button"
data-toggle="buttons-checkbox"
data-toggle="buttons-radio"

View File

@ -1,5 +0,0 @@
// http://localhost:6680/mpd/list/artist/album/Tool
var baseurl = '/mpd/';
var host = 'http://localhost';
var port = 6680;
var mpdstatusurl = host + ':' + port + baseurl + 'status';

File diff suppressed because it is too large Load Diff

View File

@ -55,9 +55,18 @@ function setSongInfo(data) {
}
}
$("#trackslider").attr("max", data["length"]);
console.log('max: ' + data["length"]);
$("#infoartist").html(artists);
$("#songlength").html(timeFromSeconds(data["length"] / 1000));
$('#currenttable tr .name').each(
function() {
console.log(this.className);
this.className = "name";
if(this.id == data["uri"]) {
this.className += ' currenttrack';
// this.parentNode.parentNode.style.marginLeft="20px";
}
} );
}
function setPlayState(nwplay) {
@ -187,7 +196,7 @@ function setShuffle(nwshuffle) {
function doPrevious() {
// if position > one second -> go to begin, else go to previous track
if (currentposition > 1000) {
if (currentposition > 5000) {
doSeekPos(0);
} else {
mopidy.playback.previous();
@ -255,6 +264,16 @@ function setPosition(pos) {
$("#songelapsed").html(timeFromSeconds(currentposition / 1000));
}
function resetSong() {
pauseTimer();
setPlayState(false);
setPosition(0);
$("#infoname").html('');
$("#trackslider").attr("max", '');
$("#infoartist").html('');
$("#songlength").html('0:00');
}
//update everything as if reloaded
function updateStatusOfAll() {
mopidy.playback.getCurrentTrack().then(currentTrackResults, console.error);
@ -290,11 +309,24 @@ function initSocketevents() {
getPlaylists();
});
mopidy.on("event:playbackStateChanged", function (data) {
switch (data["new_state"]){
case "stopped":
resetSong();
break;
case "playing":
mopidy.playback.getTimePosition().then(currentPositionResults, console.error);
resumeTimer();
setPlayState(true);
break;
}
});
mopidy.on("event:tracklistChanged", function (data) {
getCurrentPlaylist();
});
mopidy.on("event:trackPlaybackStopped", function (data) {
/* mopidy.on("event:trackPlaybackStopped", function (data) {
pauseTimer();
setPlayState(false);
});
@ -309,7 +341,7 @@ function initSocketevents() {
resumeTimer();
setPlayState(true);
});
*/
mopidy.on("event:seeked", function (data) {
setPosition(parseInt(data["time_position"]));
});
@ -368,6 +400,7 @@ $(document).ready(function() {
switchContent(divid, uri);
});
resetSong();
//TODO
//setVolume(50);
@ -415,10 +448,10 @@ $(document).ready(function() {
if (location.hash.length < 2) {
switchContent("playlists");
}
initgui = false;
//update gui every x seconds from mopdidy
setInterval(updateStatusOfAll, 5000);
// setInterval(updateStatusOfAll, 5000);
});
function backbt() {

View File

@ -1,854 +0,0 @@
/**
* @author Andrew Dodson
* @since 25th may 2007
* @since Oct 2011 (that's refreshing!)
*/
(function($){
"use strict";
// test for support
$.support.datalist = (function(){
// El.list is available
var el = document.createElement('input');
return typeof el.list !== 'undefined';
})();
$.support.placeholder = test('input[placeholder=wicked]');
$.support.range = test('input[type=range]');
$.support.number = test('input[type=number]');
$.support.date = test('input[type=date]');
// inputSupport
function test(s){
var m = s.match(/^([a-z]+)(\[([a-z]+)(\=([a-z]+))?\])?$/i);
try{
var test = document.createElement(m[1]);
if(m[3]&&m[5]){
test[m[3]] = m[5];
//console.log(test[m[3]] +':'+ m[5]);
return test[m[3]] === m[5];
}
else if(m[3]){
return m[3] in test;
}
}
catch(e){
return false;
}
return true;
}
/**
* Basic Custom events for user interactions
*/
$(":input").live('keydown', function(e){
if(e.which===40){
$(this).trigger('down');
}
if(e.which===38){
$(this).trigger('up');
}
});
// the interval would be better if it was per input
var interval;
/**
* CheckValidity
* Our shim for which recreates the CheckValidity of HTML5 input's upon form submission
*/
function checkValidity(elem){
if (elem && 'checkValidity' in elem){
return elem.checkValidity();
}
var $el = $(elem),
m = {
url : /^https?\:\/\/[a-z0-9]+/i,
date : /^[0-9]{2,4}[\/\:\.\-][0-9]{2}[\/\:\.\-][0-9]{2,4}$/,
time : /^[0-9]{2}\:[0-9]{2}(\:[0-9]{2})?$/,
tel : /^\+?[0-9\s\.]{10,14}/,
email : /^[a-z0-9\.\'\-]+@[a-z0-9\.\-]+$/i,
number : /^-?[0-9]+(\.[0-9]+)?$/i,
text : /.+/
};
// REQUIRED ATTRIBUTES
var type = $el.attr('type'),
min = $el.attr('min'),
max = $el.attr('max'),
step = $el.attr('step'),
example = {
url : "http://www.domain.com",
time : "12:30",
email : "name@domain.com",
date : "2012-12-31"
}[type],
required= (!!$el.attr('required')),
pattern = $el.attr('pattern'),
value = (type === "checkbox" && !$el.prop('checked')) ? '' : (elem.value || elem.innerHTML),
errorMsgs = {
valueMissing : (type === "checkbox" ? "Please tick this box if you want to proceed" : "Value missing"),
tooLong : "Too Long",
typeMismatch : "Not a valid " + type + ( example ? " (e.g. " +example+ ")" : ''),
patternMismatch : "Invalid pattern",
rangeOverflow : "Value must be less than or equal to "+max,
rangeUnderflow : "Value must be greater than or equal to "+min,
stepMismatch : "Invalid value"
};
//
// Remove placeholder
if($el.filter(".placeholder").attr('placeholder') === value){
value = "";
}
elem.validity = {
valueMissing : required&&value.length===0,
tooLong : false,
typeMismatch : (value.length>0)&&(type in m)&&!value.match( m[type] ),
patternMismatch : pattern&&(value.length>0)&&!value.match( new RegExp('^'+pattern+'$') ),
rangeOverflow : max && value.length && value > parseFloat(max),
rangeUnderflow : min && value.length && value < parseFloat(min),
stepMismatch : step && value.length && value%parseFloat(step),
customError : false,
valid : false // default
};
// if this is a color?
if(type==='color'&&value.length>0){
// does it work?
var div = document.createElement("color");
try{
div.style.backgroundColor = value;
}
catch(e){}
if( !div.style.backgroundColor ){
elem.validity.typeMismatch = true;
}
}
// remove any previous error messages
if($el.hasClass('invalid')){
$el
.removeClass('invalid')
.nextUntil(':input')
.filter("div.errormsg")
.remove();
}
// Test each message
for(var x in elem.validity){
if(elem.validity[x]){
$el
.trigger('invalid')
.addClass('invalid') // ADD CLASS
.after('<div class="errormsg">'+errorMsgs[x]+'</div>');
setTimeout(function(){
$el
.removeClass('invalid')
.nextUntil(':input')
.filter("div.errormsg")
.fadeOut();
},10e3);
return false;
}
}
return (elem.validity.valid = true);
};
// Check a form, or an individual value
$.fn.checkValidity = function(){
var b = true;
// AN HTML FORM WOULDN'T POST IF THERE ARE ERRORS. HOWEVER
($(this).is(':input') ? $(this) : $(":input", this)).each(function(){
if(b){
b = checkValidity(this);
}
});
return b;
},
// Add form submit
$('form').live('submit', function(e){
var b = $(this).checkValidity();
if(b){
// if this has passed lets remove placeholders
$(":input.placeholder[placeholder]", this).val("");
}
else{
// find the item in question and focus on it
var $first = $('.invalid',this);
if($first.length){
$first.get(0).focus();
}
// prevent any further executions.. of course anything else could have been called.
e.preventDefault();
e.stopPropagation();
}
return b;
});
// Add the oninput check
$('input,textarea', 'form').live('input blur', function(e){
$(this)
.removeClass('error')
.next('div.error')
.remove(); // clear away error markup because `it ugly`!
if(interval){
clearTimeout(interval);
}
if(e.type==='blur'||e.type==='focusout'){
// check validity and provide information to user.
$(this).checkValidity();
} else {
var el = this;
interval = setTimeout(function(){$(el).checkValidity();},3000);
}
});
// Placeholder support
if(!$.support.placeholder){
/**
* Hide/show the placeholder
*/
$(':input.placeholder[placeholder]', 'form').live('focus', function(e){
var method = (this.tagName==='INPUT'?'val':'text');
if($(this)[method]()===$(this).attr('placeholder')){
$(this)[method]("").removeClass('placeholder');
}
});
$(':input[placeholder]', 'form').live('focusout', function(){
var method = (this.tagName==='INPUT'?'val':'text');
if($(this)[method]()===''){
$(this)[method]($(this).attr('placeholder')).addClass("placeholder");
}
});
}
/**
* input[list=id]
* Datalist provides a mechanism for suggesting values in an input field
*/
if(!$.support.datalist){
// Add keyup event to build the list based on user suggestions
$('input[list]').live("keyup",function(e){
// Show
$(this).addClass("datalist");
// $list
var $list = $(this).nextUntil(":input").filter("div.datalist").eq(0);
if($list.length===0){
$list = $("<div class='datalist'></div>").css({
position: 'absolute'
}).insertAfter(this);
}
// dont change the list?
if((e.which===38||e.which===40)&&$list.find("ul").length>0){
return;
}
// get the datalist
var list = [],
value = $(this).val().toLowerCase();
log("#" + $(this).attr("list"));
$( "option", "#" + $(this).attr("list") ).each(function(){
var v = $(this).attr("value").toLowerCase();
log(v);
if(v.indexOf(value)>-1){
list.push(v);
}
});
$list.empty();
$list.width($(this).width());
// AppendTo DOM
$("<ul><li>"+list.join("<\/li><li>")+"<\/li><\/ul>").appendTo($list);
});
// add events
$("input[list] + div.datalist li").live('click',function(){
$(this).parents("div.datalist").prevAll("input[list]").eq(0).val( $(this).text() );
});
$('input[list]').live("up down",function(e){
var $list = $(this).nextUntil(":input").filter("div.datalist").eq(0),
$sel = $list.find("li.hover");
if($sel.length){
$sel = $sel[e.type==='up'?'prev':'next']().addClass('hover');
$sel[e.type==='down'?'prev':'next']().removeClass('hover');
}
else{
$sel = $list.find("li:first").addClass('hover');
}
$(this).val($sel.text());
});
// hide the datalist on blur
$('input[list]').live("blur",function(e){
// self
var self = this;
// hide on timeut, because it might have been the datalist which was selected
setTimeout(function(){
$(self).removeClass("datalist");
},100);
});
}
/**
* input[type=color]
* @todo: Create a color wheel popup
*/
$("form input[type=color]").live("change", function(){
$(this).css({backgroundColor:this.value});
var rgb = $(this).css("backgroundColor"),
m;
if(rgb){
m = rgb.match(/([0-9]+).*?([0-9]+).*?([0-9]+)/);
// @todo a fix for IE8 to show colors in rgb format
if(!m && ("currentStyle" in this)){
//m = rgb.match(/([0-9]+).*?([0-9]+).*?([0-9]+)/);
}
if(m){
// change the text color to contrast with the backgorund color
this.style.color = ( parseInt(m[1]) + parseInt(m[2]) + parseInt(m[3]) ) < 500 ? 'white' : 'black';
}
}
});
/**
* Calendar
*/
if(!$.support.date){
$("form input[type=date]").live("focus select", function(){
var $calendar = $("+ div.calendar div", this).fadeIn('fast')
,days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']
,month= ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
,$input = $(this);
if(!$calendar.length){
$calendar = $('<div class="calendar"><div></div></div>').insertAfter(this).find('div');
}
// trigger close calendar when clicked outside
$(this).blur(function(){$calendar.fadeOut('fast');});
var d = new Date();
if($(this).val().match(/^[0-9]{4}\-[0-1]?[0-9]\-[0-3]?[0-9]$/)){
var a = $(this).val().split("-").slice(0,3);
d.setYear(a[0]);
d.setDate(a[2]);
d.setMonth(--a[1]);
}
// print the headline
(function createcalendar(d){
var s='<table><caption></caption><thead><tr>';
for(var x in days){
s += "<th>"+days[x]+"<\/th>";
}
s+='</tr></thead><tbody>';
var dom = d.getDate();// user selected dom(day of month)?
d.setDate(1);
// pad the table
var dow = d.getDay();// first dom falls on a...
if(dow>0){
s += "<tr><td colspan='"+dow+"'>&nbsp;<\/td>";
}
// get the last day of the month
d.setMonth(d.getMonth()+1);
d.setDate(0);
for(var j=1;j<=d.getDate();j++){
s += ((dow+j-1)%7===0?'<tr>':'')
+ "<td"+(dom===j?' class="selected"':'')
+ "><a href='javascript:void(0);'>"+j+"<\/a><\/td>"
+ ((dow+j)%7===0?'</tr>':'');
}
// pad the table
if(d.getDay()<6){
s += "<td colspan='"+(6-d.getDay())+"'>&nbsp;<\/td><\/tr>";
}
s+='<\/tbody><\/table>';
// create the calendar and add events
$calendar.empty().append('<a href="javascript:void(0);" class="close">close</a>').find('a').click(function(){
$calendar.fadeOut('fast');
});
$(s)
.prependTo($calendar)
.find('td')
.click(function(){
var s = $(this).text();
$input.val(d.getUTCFullYear()+'-'+( (d.getMonth()+1) < 10 ? '0' : '' ) + (d.getMonth()+1)+'-'+( s < 10 ? '0' : '' ) + s );
$input.trigger('blur');
$calendar.fadeOut('fast');
})
.end()
.find('caption')
.append('<a href="javascript:void(0);" class="prev">'+ month[d.getMonth()==0?11:(d.getMonth()-1)] +'</a> <span class="current">'+ month[d.getUTCMonth()] + ' ' + d.getUTCFullYear() +'</span> <a href="javascript:void(0);" class="next">'+ month[(d.getMonth()+1)%12] +'</a>')
.find('a')
.click(function(){
if(this.className==='current'){
//$input.val(d.getUTCFullYear()+'-'+(d.getMonth()+1));
return false;
}
$calendar.fadeIn('fast');
d.setDate(1);
d.setMonth((d.getMonth()+{'next':1,'prev':-1}[this.className]));
log(d);
createcalendar(d);
return false;
});
})(d);
});
}
/**
* Expands textarea as one types
*/
$('textarea', 'form').live('keyup focus', function(){
var el = this;
if(el.tagName!=="TEXTAREA"){return;}
// has the scroll height changed?, we do this because we can successfully change the height
var prvLen = el.preValueLength;
el.preValueLength = el.value.length;
if(el.scrollHeight===el.prvScrollHeight&&el.prvOffsetHeight===el.offsetHeight&&el.value.length>=prvLen){
return;
}
while(el.rows>1 && el.scrollHeight<el.offsetHeight){
el.rows--;
}
var h=0;
while(el.scrollHeight > el.offsetHeight && h!==el.offsetHeight && (h=el.offsetHeight) ){
el.rows++;
}
el.rows++;
el.prvScrollHeight = el.scrollHeight;
el.prvOffsetHeight = el.offsetHeight;
});
/**
* log
*/
function log(){
if (typeof(console) === 'undefined'||typeof(console.log) === 'undefined') return;
if (typeof console.log === 'function') {
console.log.apply(console, arguments); // FF, CHROME, Webkit
}
else{
console.log(Array.prototype.slice.call(arguments)); // IE
}
}
/**
* HTML5 Placeholder shim
* 1.
*/
$.fn.placeholder = function(){
if($.support.placeholder){
return false;
}
// check for support for the placeholder attribute
return ( $(this).is("[placeholder]") ? $(this) : $(":input", this) ).filter("[placeholder]").each(function(){
// insert the text as the value
this.value = $(this).attr('placeholder');
}).addClass("placeholder")
.watch("value", function(e){
$(this).removeClass('placeholder');
})
.watch("placeholder", function(e){
if($(this).hasClass('placeholder')){
this.value = $(this).attr('placeholder');
//log("placeholder"+this.value);
}
});
//*/
};
var leftButtonDown = false;
$(":input").live('mousedown',function(e){
// Left mouse button was pressed, set flag
if(e.which === 1) leftButtonDown = true;
});
$(document).mouseup(function(e){
// Left mouse button was released, clear flag
if(e.which === 1) leftButtonDown = false;
});
/**
* Range
*/
$.fn.range = function(){
if($.support.range){
return false;
}
// check for support for the placeholder attribute
return ( $(this).is("[type=range]") ? $(this) : $("input", this) ).filter("[type=range]").each(function(){
/**
// hide the input box
$(this).hide();
// Add a range slider
$("<div class='range'><div class='line'></div><div class='pointer'></div></div>").insertAfter(this).touch(function(e){
log(e.offsetX);
$("div.pointer", this).css({marginLeft:e.offsetX+"px"});
});
*/
var step = parseFloat($(this).attr('step')) || 1,
max = parseFloat($(this).attr('max')) || 100,
min = parseFloat($(this).attr('min')) || 0,
w = $(this).width();
$(this).addClass("range").bind("click mousemove",function(e){
if(!leftButtonDown&&e.type === "mousemove"){
return;
}
// FF bug
if(!e.offsetX){
e.offsetX = e.clientX - $(this).offset().left;
}
var w = $(this).width(),
v = ((e.offsetX/w)*(max-min))+min,
m = v%step;
v -= m;
if((2*m)>step){
v += step;
}
// boundaries
v = Math.max(v,min);
v = Math.min(v,max);
// value
$(this).val(v);
});
}).watch('value', function(){
var step = parseFloat($(this).attr('step')) || 1,
max = parseFloat($(this).attr('max')) || 100,
min = parseFloat($(this).attr('min')) || 0,
w = $(this).width(),
v = $(this).val();
// style
var x = (v-min)/(max-min);
$(this).css({backgroundPosition: ((x*w)-500)+"px center" });
});
};
/**
* input[type=number]
* Adds up and down controls to increment/decrement a number field
* Controls: can be clicked once or held down
*/
$.fn.number = function(){
if($.support.number){
return false;
}
// kill iterations to increase the value
var interval = null;
$(document.body).mouseup(function(){
if(interval){
clearTimeout(interval);
}
});
// check for support for the input[type=number] attribute
return ( $(this).is("[type=number]") ? $(this) : $("input", this) ).filter("[type=number]").each(function(){
// Found
log("input[type=number]", this);
var el = this,
w = $(this).outerWidth(),
min = $(this).attr('min'),
max = $(this).attr('max'),
step = parseFloat($(this).attr('step'))||1;
// Listen for up down events on the element
$(this).bind('up down', function(e){
var n = (parseInt($(this).val())||0)+(e.type==='up'?step:-step);
if(n>max){
n=max;
}
if(n<min){
n=min;
}
$(this).val(n);
}).bind('blur', function(){
if( $(this).val() !== $(this).filter('.placeholder').attr('placeholder') ){
$(this).val( $(this).val().replace(/[^\d\.\-]/ig,'') );
}
});
var $span = $(this)
// add the controls
.after('<span class="number" unselectable="on"><span unselectable="on"></span><span unselectable="on"></span></span>')
.addClass("number")
.find("+ span.number")
.attr("unselectable", true)
.find("span")
.mousedown(function (e){
var i = $(this).parent().children().index(this);
(function change(){
// trigger up down events
$(el).trigger(i?"down":"up");
// press'n'hold can be cancelled by keyup (above)
interval = setTimeout(change,100);
})();
})
.parents('span.number');
// add dimensions
setTimeout(function(){
var pR = 0,// parseInt($(el).css("paddingRight")),
mR = parseInt($(el).css("marginRight"));
$(el).css({
paddingRight: ( pR + 22 )+"px",
marginRight: 0,
width : ($(el).width() - ($(el).outerWidth() - w)) + "px"
});
$span.css({
marginRight:mR+"px",
marginTop: ( $(el).offset().top - $span.offset().top ) + 'px'
});
},1);
});
};
/**
* Touch
* @param callback function - Every touch event fired
* @param complete function- Once all touch event ends
*/
$.fn.touch = function(callback, complete){
// Store pointer action
var mousedown = {};
$("body").bind('mousedown MSPointerDown', function(e){
mousedown[e.originalEvent.pointerId||0] = e.originalEvent;
});
$("body").bind('mouseup MSPointerUp', function(e){
mousedown[e.originalEvent.pointerId||0] = null;
});
// loop through and add events
return $(this).each(function(){
// bind events
$(this)
.bind("selectstart",function(e){return false;})
.bind("touchstart touchmove",function(e){
var touches = e.originalEvent.touches || e.originalEvent.changedTouches || [e];
for(var i=0; i<touches.length; i++){
touches[i].pointerId = touches[i].identifier;
touches[i].offsetX = touches[i].clientX - $(this).offset().left;
touches[i].offsetY = touches[i].clientY - $(this).offset().top;
// do not paint on the touchstart
if(e.type==='touchmove'){
callback.call(this, touches[i],mousedown[touches[i].identifier]);
}
// save last event in a object literal
// to overcome event overwriting which means we can't just store the last event.
mousedown[touches[i].identifier] = {
offsetX : touches[i].offsetX,
offsetY : touches[i].offsetY,
pointerId : touches[i].pointerId
};
}
e.stopPropagation();
e.preventDefault();
return false;
})
.bind("mousemove MSPointerMove",function(e){
if(e.type==='mousemove'&&"msPointerEnabled" in window.navigator){
return;
}
// default pointer ID
e.originalEvent.pointerId = e.originalEvent.pointerId || 0;
// only trigger if we have mousedown/pointerdown
if(( e.originalEvent.pointerId in mousedown ) && ( mousedown[e.originalEvent.pointerId] !== null )){
callback.call(this,e.originalEvent, mousedown[e.originalEvent.pointerId]);
mousedown[e.originalEvent.pointerId] = e.originalEvent;
}
e.preventDefault();
e.stopPropagation();
});
});
}
/**
* $("form").form()
* Converts forms with...
* 1. "textarea[type=html]" to a WYSIWYG editor
* 2. "table.multiple" to add buttons for adding/removing additional rows
* 3. "input[placeholder] will add html5 placeholder
*/
$.fn.form = function(){
return ( $(this).is('form') ? $(this) : $('form',this)).each(function(){
// Add the placeholder support
$(":input[placeholder]", this).placeholder();
// Add the number support
$(":input[type=number]", this).number();
// Add range
$("input[type=range]", this).range();
// prevent propagation of the form if it fails.
// this has to be bound to the form element directly, before additional events are added, otherwise it may not be executed.
}).submit(function(e){
var b = $(this).checkValidity();
if(b){
// if this has passed lets remove placeholders
$(":input.placeholder[placeholder]", this).val("");
}
else{
// find the item in question and focus on it
var $first = $('.invalid',this);
if($first.length){
$first.get(0).focus();
}
// prevent any further executions.. of course anything else could have been called.
e.preventDefault();
e.stopPropagation();
}
return b;
});
};
/**
* Watch
* Trigger callbacks when attributes of an element change
*/
$.fn.watch = function(props, callback, timeout){
if(!timeout)
timeout = 10;
return this.each(function(){
var $el = $(this),
el = this,
func = function(){ __check.call(el, $(el)) },
data = { props: props.split(","),
func: callback,
vals: [] };
$.each(data.props, function(i) { data.vals[i] = $el.prop(data.props[i]) || $el.css(data.props[i]) || $el.attr(data.props[i]); });
if (typeof (this.onpropertychange) == "object" && "attachEvent" in this){
this.attachEvent("onpropertychange", func );
/**
never seems to work
} else if ($.browser.mozilla){
$el.bind("DOMAttrModified", callback);
*/
} else {
setInterval( func, timeout);
}
function __check($el) {
var changed = false,
temp = "";
for(var i=0;i < data.props.length; i++) {
temp = $el.prop(data.props[i]) || $el.css(data.props[i]) || $el.attr(data.props[i]);
if(data.vals[i] != temp){
data.vals[i] = temp;
changed = true;
break;
}
}
if(changed && data.func) {
data.func.call($el, data);
}
}
});
}
})(jQuery);

File diff suppressed because one or more lines are too long

View File

@ -1,11 +0,0 @@
/*
* jQuery UI Touch Punch 0.2.2
*
* Copyright 2011, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
(function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return;}var c=b.ui.mouse.prototype,e=c._mouseInit,a;function d(g,h){if(g.originalEvent.touches.length>1){return;}g.preventDefault();var i=g.originalEvent.changedTouches[0],f=document.createEvent("MouseEvents");f.initMouseEvent(h,true,true,window,1,i.screenX,i.screenY,i.clientX,i.clientY,false,false,false,false,0,null);g.target.dispatchEvent(f);}c._touchStart=function(g){var f=this;if(a||!f._mouseCapture(g.originalEvent.changedTouches[0])){return;}a=true;f._touchMoved=false;d(g,"mouseover");d(g,"mousemove");d(g,"mousedown");};c._touchMove=function(f){if(!a){return;}this._touchMoved=true;d(f,"mousemove");};c._touchEnd=function(f){if(!a){return;}d(f,"mouseup");d(f,"mouseout");if(!this._touchMoved){d(f,"click");}a=false;};c._mouseInit=function(){var f=this;f.element.bind("touchstart",b.proxy(f,"_touchStart")).bind("touchmove",b.proxy(f,"_touchMove")).bind("touchend",b.proxy(f,"_touchEnd"));e.call(f);};})(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,391 +0,0 @@
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
// License: New BSD License
// Reference: http://dev.w3.org/html5/websockets/
// Reference: http://tools.ietf.org/html/rfc6455
(function() {
if (window.WEB_SOCKET_FORCE_FLASH) {
// Keeps going.
} else if (window.WebSocket) {
return;
} else if (window.MozWebSocket) {
// Firefox.
window.WebSocket = MozWebSocket;
return;
}
var logger;
if (window.WEB_SOCKET_LOGGER) {
logger = WEB_SOCKET_LOGGER;
} else if (window.console && window.console.log && window.console.error) {
// In some environment, console is defined but console.log or console.error is missing.
logger = window.console;
} else {
logger = {log: function(){ }, error: function(){ }};
}
// swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
if (swfobject.getFlashPlayerVersion().major < 10) {
logger.error("Flash Player >= 10.0.0 is required.");
return;
}
if (location.protocol == "file:") {
logger.error(
"WARNING: web-socket-js doesn't work in file:///... URL " +
"unless you set Flash Security Settings properly. " +
"Open the page via Web server i.e. http://...");
}
/**
* Our own implementation of WebSocket class using Flash.
* @param {string} url
* @param {array or string} protocols
* @param {string} proxyHost
* @param {int} proxyPort
* @param {string} headers
*/
window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
var self = this;
self.__id = WebSocket.__nextId++;
WebSocket.__instances[self.__id] = self;
self.readyState = WebSocket.CONNECTING;
self.bufferedAmount = 0;
self.__events = {};
if (!protocols) {
protocols = [];
} else if (typeof protocols == "string") {
protocols = [protocols];
}
// Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
// Otherwise, when onopen fires immediately, onopen is called before it is set.
self.__createTask = setTimeout(function() {
WebSocket.__addTask(function() {
self.__createTask = null;
WebSocket.__flash.create(
self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
});
}, 0);
};
/**
* Send data to the web socket.
* @param {string} data The data to send to the socket.
* @return {boolean} True for success, false for failure.
*/
WebSocket.prototype.send = function(data) {
if (this.readyState == WebSocket.CONNECTING) {
throw "INVALID_STATE_ERR: Web Socket connection has not been established";
}
// We use encodeURIComponent() here, because FABridge doesn't work if
// the argument includes some characters. We don't use escape() here
// because of this:
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
// But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
// preserve all Unicode characters either e.g. "\uffff" in Firefox.
// Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
// additional testing.
var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
if (result < 0) { // success
return true;
} else {
this.bufferedAmount += result;
return false;
}
};
/**
* Close this web socket gracefully.
*/
WebSocket.prototype.close = function() {
if (this.__createTask) {
clearTimeout(this.__createTask);
this.__createTask = null;
this.readyState = WebSocket.CLOSED;
return;
}
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
return;
}
this.readyState = WebSocket.CLOSING;
WebSocket.__flash.close(this.__id);
};
/**
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
*
* @param {string} type
* @param {function} listener
* @param {boolean} useCapture
* @return void
*/
WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
if (!(type in this.__events)) {
this.__events[type] = [];
}
this.__events[type].push(listener);
};
/**
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
*
* @param {string} type
* @param {function} listener
* @param {boolean} useCapture
* @return void
*/
WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
if (!(type in this.__events)) return;
var events = this.__events[type];
for (var i = events.length - 1; i >= 0; --i) {
if (events[i] === listener) {
events.splice(i, 1);
break;
}
}
};
/**
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
*
* @param {Event} event
* @return void
*/
WebSocket.prototype.dispatchEvent = function(event) {
var events = this.__events[event.type] || [];
for (var i = 0; i < events.length; ++i) {
events[i](event);
}
var handler = this["on" + event.type];
if (handler) handler.apply(this, [event]);
};
/**
* Handles an event from Flash.
* @param {Object} flashEvent
*/
WebSocket.prototype.__handleEvent = function(flashEvent) {
if ("readyState" in flashEvent) {
this.readyState = flashEvent.readyState;
}
if ("protocol" in flashEvent) {
this.protocol = flashEvent.protocol;
}
var jsEvent;
if (flashEvent.type == "open" || flashEvent.type == "error") {
jsEvent = this.__createSimpleEvent(flashEvent.type);
} else if (flashEvent.type == "close") {
jsEvent = this.__createSimpleEvent("close");
jsEvent.wasClean = flashEvent.wasClean ? true : false;
jsEvent.code = flashEvent.code;
jsEvent.reason = flashEvent.reason;
} else if (flashEvent.type == "message") {
var data = decodeURIComponent(flashEvent.message);
jsEvent = this.__createMessageEvent("message", data);
} else {
throw "unknown event type: " + flashEvent.type;
}
this.dispatchEvent(jsEvent);
};
WebSocket.prototype.__createSimpleEvent = function(type) {
if (document.createEvent && window.Event) {
var event = document.createEvent("Event");
event.initEvent(type, false, false);
return event;
} else {
return {type: type, bubbles: false, cancelable: false};
}
};
WebSocket.prototype.__createMessageEvent = function(type, data) {
if (document.createEvent && window.MessageEvent && !window.opera) {
var event = document.createEvent("MessageEvent");
event.initMessageEvent("message", false, false, data, null, null, window, null);
return event;
} else {
// IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
return {type: type, data: data, bubbles: false, cancelable: false};
}
};
/**
* Define the WebSocket readyState enumeration.
*/
WebSocket.CONNECTING = 0;
WebSocket.OPEN = 1;
WebSocket.CLOSING = 2;
WebSocket.CLOSED = 3;
// Field to check implementation of WebSocket.
WebSocket.__isFlashImplementation = true;
WebSocket.__initialized = false;
WebSocket.__flash = null;
WebSocket.__instances = {};
WebSocket.__tasks = [];
WebSocket.__nextId = 0;
/**
* Load a new flash security policy file.
* @param {string} url
*/
WebSocket.loadFlashPolicyFile = function(url){
WebSocket.__addTask(function() {
WebSocket.__flash.loadManualPolicyFile(url);
});
};
/**
* Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
*/
WebSocket.__initialize = function() {
if (WebSocket.__initialized) return;
WebSocket.__initialized = true;
if (WebSocket.__swfLocation) {
// For backword compatibility.
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
}
if (!window.WEB_SOCKET_SWF_LOCATION) {
logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
return;
}
if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
!WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
var swfHost = RegExp.$1;
if (location.host != swfHost) {
logger.error(
"[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
"('" + location.host + "' != '" + swfHost + "'). " +
"See also 'How to host HTML file and SWF file in different domains' section " +
"in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
"by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
}
}
var container = document.createElement("div");
container.id = "webSocketContainer";
// Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
// Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
// But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
// Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
// the best we can do as far as we know now.
container.style.position = "absolute";
if (WebSocket.__isFlashLite()) {
container.style.left = "0px";
container.style.top = "0px";
} else {
container.style.left = "-100px";
container.style.top = "-100px";
}
var holder = document.createElement("div");
holder.id = "webSocketFlash";
container.appendChild(holder);
document.body.appendChild(container);
// See this article for hasPriority:
// http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
swfobject.embedSWF(
WEB_SOCKET_SWF_LOCATION,
"webSocketFlash",
"1" /* width */,
"1" /* height */,
"10.0.0" /* SWF version */,
null,
null,
{hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
null,
function(e) {
if (!e.success) {
logger.error("[WebSocket] swfobject.embedSWF failed");
}
}
);
};
/**
* Called by Flash to notify JS that it's fully loaded and ready
* for communication.
*/
WebSocket.__onFlashInitialized = function() {
// We need to set a timeout here to avoid round-trip calls
// to flash during the initialization process.
setTimeout(function() {
WebSocket.__flash = document.getElementById("webSocketFlash");
WebSocket.__flash.setCallerUrl(location.href);
WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
for (var i = 0; i < WebSocket.__tasks.length; ++i) {
WebSocket.__tasks[i]();
}
WebSocket.__tasks = [];
}, 0);
};
/**
* Called by Flash to notify WebSockets events are fired.
*/
WebSocket.__onFlashEvent = function() {
setTimeout(function() {
try {
// Gets events using receiveEvents() instead of getting it from event object
// of Flash event. This is to make sure to keep message order.
// It seems sometimes Flash events don't arrive in the same order as they are sent.
var events = WebSocket.__flash.receiveEvents();
for (var i = 0; i < events.length; ++i) {
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
}
} catch (e) {
logger.error(e);
}
}, 0);
return true;
};
// Called by Flash.
WebSocket.__log = function(message) {
logger.log(decodeURIComponent(message));
};
// Called by Flash.
WebSocket.__error = function(message) {
logger.error(decodeURIComponent(message));
};
WebSocket.__addTask = function(task) {
if (WebSocket.__flash) {
task();
} else {
WebSocket.__tasks.push(task);
}
};
/**
* Test if the browser is running flash lite.
* @return {boolean} True if flash lite is running, false otherwise.
*/
WebSocket.__isFlashLite = function() {
if (!window.navigator || !window.navigator.mimeTypes) {
return false;
}
var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
return false;
}
return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
};
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
// NOTE:
// This fires immediately if web_socket.js is dynamically loaded after
// the document is loaded.
swfobject.addDomLoadEvent(function() {
WebSocket.__initialize();
});
}
})();

View File

@ -1,35 +0,0 @@
/*
Content-Type: multipart/related; boundary="_ANY_STRING_WILL_DO_AS_A_SEPARATOR"
--_ANY_STRING_WILL_DO_AS_A_SEPARATOR
Content-Location:handlenormal
Content-Transfer-Encoding:base64
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKeSURBVHjaYvz//z8DNQFAADExUBkABBDVDQQIIKobCBBALNgEGYGAYek5OQYhcW8Gdk5bBjZ2DbDEr583GH5+P8zw7uVWhmijR/+xRABAADGiiwHNYuZZdclaQkKiUEeY19aIm4FdkZ2RhYOJkfHHf4Z/J778/7ni4Yc9b1++mMgQZXAcqP8vsn6AAGLBcFnbCmUhIaESa1EeWyue/5wCLIzAYAGa9O8/AxsDA5MdDyOzoBK/+4w/v1jftK0oA2q5jexSgABC9zIbg4xSkKQQv5UQ01+2d78ZGN7+YvgHUg3CjFDMxcjE6iEnbL/vk3rkMwaGDqDQT5gBAAGEbiAXAzefA+Pv36wPvzEwPGFk+PfnPwNGOLEwMjD+ZWBiURYTcgMaOAnZQIAAQjeQB2ig6oPvvxkefWP4izc6GYEW/eeQButhYHgPEwYIIEwv//vH9OIHmE04C/0DhwYbshBAAGEmm1dPXgjIKAjzsTAysgKj4x8WY5mAAfn7HwPD1+fPXr9FkwMIIHQDfzHcuXyVQVZRi5uZkZEXaChI859/iEhhgVry+S/Dv18Pb9wA60ECAAGEbuAXhr1r9v2UUzL6p6mrKsDCxCjACjKUkYEZKAkK1H/AFPLh9///3x/devT7wMa9YD1IACCA0LPeN4bjO89937J04+PLF+4//PH337e////zMjMySAFTNogG8Z/duvb48Yal69/uWH0SrAcJAAQQSk4BJ2wGBl4gVmHQs7RjsPNzZtAwUmGQUhAGK3j24C3DjXN3GA5t2stw6fghoMgdIP6MnLABAghr1gNS3EAsBcQKUJoXKv0ZZCwQP4DSX9GzHkAAMWIrYKEuZQMndEg6Y4NHGiTMQN78ha1wAAggRmqX2AABRPXyECDAAK8q7HNsZWwXAAAAAElFTkSuQmCC==
--_ANY_STRING_WILL_DO_AS_A_SEPARATOR
Content-Location:handleglow
Content-Transfer-Encoding:base64
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAPcSURBVHjaYvz//z8DGmBEohmR+DDwHwkzINFgABBALFgMYoKyWaBsZiQ5kOa/QPwPiP9A+f+QDQYIIBY0w5ihmBWI2aCYBSrGADUMZNAvKP4NFfsLNeM/QAAhu5AZqpkdiDmhmAvKh6kDGfYTiL8B8Xco/olkGQNAALFATYZ5DaSZ+9evX/zLn/9QW/2B0enuH2bDNwwsMiDFIgx/niiz/D0fKvB/X6Qkxy02NjYmtHD9BxBAjMBIYYQaxgHEPO/evRPufvbP8dwf9hghbg5tAy5GVnl2BiYORkbG7/8Z/p/+8v/3ibffLliz/VxaKc18UEhI6C1Q3xcg/gFyJUAAsSBFANvPnz+5Vj79onXyJ2+cqiC7jgUPI6sgCwMj0Gog/s/Aycjw35aXgU2QmdNo+/M/zEC1r5O4uc+ys7P/ggbHP4AAYkLyMtuLFy/4N3xgcGfk4NIUZv7P8vHPP8YHP/4zAPH/+xAMZP9j5Gb6z6ItxKO7/yuTD0gPNPLAZgEEEMyF4Ah5/vw5/4N/CkZy//8xP/vJyPDyN8P/fwygIEGkmn/AMAIq/v/7PwMTIzun2fPnt/nl5eVfw5IXQAAhJxvmN2/e8L4T0JBi/PGX8cVvxv/MjEwMwKD7j5x2QRnh7/9////+A5r4j1UYpAc5rQIEEHKyYfj69SsrA/8/ple//jEwMzMBDQQ5g4EROVmDXPgXhP8C1fz7A9GDBAACiAU5B/z+/fuPwNf3734JiPIKsTEysgIdCPQwI8xEUMSAc+p/Joaff/8zsH/8+AGkB5oGwd4ACCAWpOz0B+i9rxLvntx/KCAqxw4U5mMBeRsoCfU0JPYY//8Buu4jMHDFPzx/CNIDjWGwoQABxIKUH39xc3O/1bx/49gHXhG1P1JyUjyMDMyCrIwgbzOADf7HCDT8P+N7oHq2ty9eKb24eYxbWvAtNBuCzPgPEEBMUAPB+VNWVvad6p9PZxVvnzzw8+mDlw+///n75c/f/5zAsBQFpkcQ/eXPv//fXzx5w3vtxH7Zb29OgvRADQQXFgABhByGv5WVlT/du3fv7qdLl7Zyn37x6Z6MtsUZMUWZj3yifCBF/J9efxJ5df+J0pOrJ1QYvh9W0NO7C9IDLSTAYQgQQMhe/isgIPBDW1v7NTCgLzNcv/5J6NKuG8DcIwWMVB5w2mJk/ALMFc+A2e2upqbmQ6DatyA9SEXaf4AAYoQWsCjFFzBtsZ09e5bn6dOnfKDE/ufPHw6w7SwsPyQlJT9KS0t/MjY2/iIiIvILqfgC+xQggBiRSmyqFLAAAcSCVrQzICn4R04VABBgAIVgtDIhSVTgAAAAAElFTkSuQmCC==
--_ANY_STRING_WILL_DO_AS_A_SEPARATOR
Content-Location:handledisabled
Content-Transfer-Encoding:base64
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJSSURBVHjaYvz//z8DNQFAADExUBkABBDVDQQIIKobCBBALNgEGYHA1dVVjp2d3ZuZmdkWiDVA4n///r0BxId//vy5dffu3Y/+Y4kAgABiRBcDmsXs4eFhJSQklCcqKmoLpNm4ubmZWVhYQAb+f/Pmza87d+7s+/DhwxSgoceB+v8i6wcIIBZ0l1lYWCjz8fEVSUpK2gAN5GBjY2MCamIEGgaS/w8UYwaKuV6+fJkVqPYVUOw2sksBAgjdyyDXBAENtAZqYgd6jen79+8gcWRvMAENYVNQULD7/ft3GJDfDcQ/YZIAAYRuIBcrK6sD0ELWz58/M3758uUftnQKNBBEsQoLCzsD6anIBgIEELqBPEADVYGGMQENI5TiQaYqgPQA8XuYIEAAYXgZ6CJs3sQKoK5nQxYDCCCMZAM07AU/P78Q0KWMUK/hNAzok1fo4gABhG7gr48fP14FGqgJTHuMoKQCMhQ5HGH8P3/+/AcaeA2kB9kAgABCN/DLkydP9vHy8hoBk40KMKYZgRjFILCtv34xANPhvUePHu0F6UE2ACCA0A389uLFi3OcnJwbgexgoKEKTExMjBwcHAygIAAmk/8/fvxgAKp5CEzcGx8+fHgGpAfZAIAAYkTzDijQeIFYBZgk7KSkpJwFBQVVuLi4hMG2ffv29v3793eePXu29+3bt4eAQneA+DNywgYIIKxZD0hxA7EUNFlIQS0Bgc9A/AyIH0Dpr+hZDyCAGHEkXEZocuCCpjNY0vgFDTOQN39hKxwAAoiR2iU2QABRvTwECDAAxcXwW8Zw7FoAAAAASUVORK5CYII==
--_ANY_STRING_WILL_DO_AS_A_SEPARATOR
Content-Location:blank
Content-Transfer-Encoding:base64
R0lGODlhFAAUAIAAAP///wAAACH5BAEAAAAALAAAAAAUABQAAAIRhI+py+0Po5y02ouz3rz7rxUAOw==
--_ANY_STRING_WILL_DO_AS_A_SEPARATOR
Content-Location:fullsprite
Content-Transfer-Encoding:base64
iVBORw0KGgoAAAANSUhEUgAAABQAAABQCAYAAAAZQFV3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjFFRDg4NEVDNENDODExRTFCMTZDREIyQTZDMjlDNTQ2IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjFFRDg4NEVENENDODExRTFCMTZDREIyQTZDMjlDNTQ2Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUVEODg0RUE0Q0M4MTFFMUIxNkNEQjJBNkMyOUM1NDYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MUVEODg0RUI0Q0M4MTFFMUIxNkNEQjJBNkMyOUM1NDYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz68iMNZAAAIQElEQVR42uxYW28b1xGes7tckiIp3iRboi6mbollya5DF4VsVWpaw42ABCmaogWSFH3oDyjQoijQP2AUyFOLFshbn4SqsYFabowmdaW4NhDJiKXaUuwotm6UKJK6kKIkipfl7p7OWR3aa5kiVdQoEjQLDM7u2TPfnss3MztDKKXwPC8BnvP1xQeUSnUSvGBwshl8R18Fq70PZOtx44WSn4F89hYkV6/B26ElWuIAyP4+xBKd70311tXV/bzb7+oLOcDaYiWSTSAkR0EfT9P8UDj1j8Rq/Lfw1ukx1NcOnKExs4tDbT6f75e9tc6+c05q90gEtwWRdAoyblG/k4jeVvcr76qKZePi0K9Q5ZF5pvuXLENj6xv1Pvc5n6DJyQJAQgGdjWZCuFQRwTLQ7P/W6PaLb0YBfoNd+YMAq8BR/TIpFCzhDECEgK5SeGafJAJEA0FqO+L7LgL+rhygEwE7FrMFWMqAVvY4CX6I2hoMHYDNg5es60I8Z9xXNiHd2A25PG3WInFPY9BfLRFiwePQS8AKuJEFHWA3Fl1PVOChArPT96Gp5YRDJMSFoExZ1Z8cisQ/sqOBroRnZgydMoBpGLk8mm9uDemdJzs8kkA8FgZKQMSXbFN1ZEiqQGl26eFS4cbwiKFTxvQyMPbhZPb9weHl6bsL4ZymZzRKXSKBADKbtew5+vDB8vKVwb8kPrh029A5yFIMYgO4UNrh1Nl+6H/9PBwPtUMg6DcGRBcTMDM5CzevjsDU2E3smUXZMRO7pOlh40AJoAR56+KvdxgsyiJvd/ebHinlYPlMZYPoezyTHx/a3p6xZSqHcg5fCn9ITC0xPRcvapJnLEoqASQUOczvRdM7WqQjisqfdTOwtA9M5GLhByHzMSJ/r3EghUuB92kcg5pnKHJlK4qdSxV/Lo5Tuatip5zlkjd9zBhITEtjyg5FUdx/iuVeuJQi35lTxZc2QGpkg2tAjbRJ2r9+6KGjb9bbHsqyLOzbV53RhnAwG+NcMpn0vxPVvz2pWn/sc9i6TlcRyzErCDbkZhY94CdpWhhPZO72yvnBXzeI/8RwkeDcZE5Pk0wHIOfz+ao/r6RP3M67ftLhtXb3OInFKyFXGV9xAnZ0qn0ukL2iPfS3mCri2PWfOhwTVqtV4duhC6Yly/F43H0lBa8QW1WnX6TSlqqTRQx1KHRhT/BeJw6BSl0+58mPdoXXmA4/PAOrOEPjQGKxmHtRD4aaqS5G8wRWC0B1YFvyhDXovigOpgWKftZq/0Ys9sh97Nix9SK9zLQRNzY2XEnP8QDJaSReIFTECIpbR83cZaaqUZ1q6GUF3eJnOmauPmUpu7u7FnDrwpqigygKCMimAcRMazZDjYmGY3R1T6eEpRgWUCgUVM/uZlLx1Lp8shFTCC6YFBHZwRi+hAqQ1yhYt7ZSTIdz8LGlFM1JxeXt1iUjC2FPbbMVuzFQ4SzxJX0SU9g3VJzdFm7u0VQszHT4CWtFSynao+JwOBKdCzMfp1w1L6iB5oATQ4nXQowoZwDrBMEpwSCsy4n4Wmv8848dDd4EN0N9b+57gIZ9NjU1JTvU7YmWR7dv5FcWV8NZVUurGrXjXtYiH1mbVnWajUc2XA/GP2rKbNxmOhxQNdsyAy20tbVtz8/Pz21PTV1zfBLfnm/s6rlzpKVxq7q2mg1yb69v16wtRFoj98fbIXsreOrUHNPhTuKpPWTT1TweT66rq2sdN3oaPvts2zf19xm0ngAeqpOHhjRaRRTNba6zszOMYxNMx+TSaDEEPOW+kFvyxMSEc2VlpZqRXVVVm/F1ScrV19dvNTQ0bJ85cyZdU1OjmNwXmAGfu4M1u3Ld1P5XIWA/MP1CRL0vcZ5y4cKFZuTcq6Io9qEYeYqmaTMot5Cb165fv374PGVgYOAckvdntbW1fdjKaOMicpABUuSoMjs7O5pKpX6PoJXzlJ6enrbq6upfIIG/iYA2FtlYIEMww9lin4h9F6anpy04dq1inoKzeQMBe1HJiksTstnsfgrhDy2Rg8FgP5roj/D5nbJ5isVieRk/aNnZ2SHpdFo/4HePNRa/338e2z+UzVMQsAPBBASrRGzCf0jL5yk4o1LLLHnx2ZfPUxAs7na7fThT/iN7MBiuZK0SD5Wtra37CNiJ3COMKgx034+98YwujSLgg4p5SiQSGXW5XCGkTTueNEF5Csj4qqIA8nB+aWnpmTxlP2AGfy0m7Xb7MN7/AEGDgiAQm80GbAuQJjSXywGOCSO5h8Ph8J1D5ylIif5AIHDe6/W2V1VVGXlKJpNJbG5uzkaj0ZFEIvFVnvJ/67H/p6WqWvAd/TpY7V0gWxt5qSoC+ex9SK7egbdD64cNAYLzvakTdXV13+v2u7pCDrC0WInAS1V0HNOKoXDqbmI1fhXeOj2D+nqlUlU9xpHv99Y6u845qeyRGCcfl6pIv5NYva3u0LuqIm1cHPojqkTLhQAJGlvP1vvcnT5Bk3ipipYoVYkDzf7u0e0X+9FcLvPfuZKAVnBUn0QvIPJSFS1TqhLbjvheQsC/lgO0IWCAl6r0Q5Sq/DylSx8EaPkPS1XAU+GypapNT2PQdchS1ValUlUBZqeXoKmliZeqoEypiirhmYh5/0oB5mDk8r18c2ub3nkyUKFUtVa4MXyPp7UHml4exj6cy74/OL48fTcezmnUVKoCU6lqY/nK4Fjig0ufm4P8QSGAlQbq4dTZbuh//WtwPFQPgaCLl6p2YGYyBjev3oOpsU+xJ8Yib6UQIHAq+FCO8tZeDNsoLNFZ5W1uv+mVCwHFgobNRI0C3zO2TPWrEGBc/xZgAJyadcoLu6zuAAAAAElFTkSuQmCC
--_ANY_STRING_WILL_DO_AS_A_SEPARATOR
Content-Location:handlenovalue
Content-Transfer-Encoding:base64
iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjVJREFUeNrMVE1PE0EYfmZ32a1ugXVrS0FsoKWKxBps4kFNOBnjybPR/8Nv8A+Y1K+DxgMeIMYLngiBxGyQKK1NWxq3VpGW7tc4O25C024rBw5MMpnknXmfeeZ9nncIpRSnOQSc8jj7gFJYkLCBZxs69IkclHNzkJUk37A6NXTau2jsb+NJvkFDBCC9MYYlRF9sZZLJ5L3rsdFsXoU0qxAhIhByREE//aF2odg0zP3aKh4vfmX53kCGnNlyIa7r+v278ejcnSiVNYnFQOF5FDI7shQl8oX0+MJTxxJ/LBdes5R6N9PeJ4uYTt+c1MczuuBKDRswLfDT/iTBPE8E8UEqll37ffVWBVhhIWcQoAx17AqxbbHYAsoE1KHoq5NEQFwIYiahLzDAtWGACgOc2GvbKLXgDZXTp0ojGs8BWoMAJVYsUjs6oUc8rw+j3zb18i9tekYdY2KMMJd6IZ0pMHY2wzqsVg7M//jQwe52FZdnp1SRYJQVy092vGNRpOCSAxfUKhq17vqFAXaw+sropNIp71ouoUkC0UaYMZlzRLbp+q9kmjdtStulHdP+8MbgOUMALay/L7Xjlza/O4/yyC1elAURkwoH9oFQ7Xi0svP5Z/nd883mystvPGdQp3Bj/1MtgRu3s1h6OI/5fAJTMyo/UNk7hLFRx8e3BrbWv/gV9xl2Gzu09bgfAd8SsWCNBNu+/k02zWC1eluPhH2wAVMxAFa6SuMENfOf6Z7oczhz/+FfAQYATvji+0cdRCAAAAAASUVORK5CYII==
--_ANY_STRING_WILL_DO_AS_A_SEPARATOR--
*/

BIN
static/screenbig.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
static/screenbig_sm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
static/screensmall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
static/screensmall_sm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB