Initial commit

This commit is contained in:
Jordan Eldredge 2014-11-03 18:51:47 -08:00
commit f5c0dca37d
50 changed files with 1029 additions and 0 deletions

61
README.md Executable file
View file

@ -0,0 +1,61 @@
# Winamp.js
A reimplementation of Winamp 2.x in jQuery and css. We use the actual skin
assets so it *should* be compatible with other themes.
It comes preloaded with one track, but you can click to "eject" button to
select your own mp3 file.
Looks the most polished in Chrome but works fine in modern versions of Safari
and Firefox. Only tested on OS X.
## TODO
- Investigate real space text
- Blog post
- Test on IE 10
- Handle "working" icon
- Minimized "shade" view
- Better presentation around it
- Test other themes
- Tool for selecting a theme
- Maybe http://gildas-lormeau.github.io/zip.js/demos/demo2.html
- Control loading state so it's not visible until it's loaded
- Marquee effect when the title is too long
- Check native app for behavior
- When you load a track, do we auto-play? Do we update the time?
- What does the scrubber do when we fastforward
- Does it pause when we scrub?
- What does the scrubber do when we get to the end of the file?
- What does the top left button do?
- What are the visualizer options?
- What is the line graph version?
- What is the bar graph version?
- What do the clutter bar letters do?
## Someday Maybe
- Playlist window
- Visualizer? (http://w-labs.at/experiments/audioviz/)
- Actually read header info
- Actually get kbps and khz
- Actually get streo/mono
- Drag and drop file selection
- Make the window draggable
- Responsive css so it looks reasonable on my phone
## Currently Impossible
- Equalizer and balance?
## Tested in modern versions of
- Chrome (OS X, Ubuntu) - Perfect
- Firefox (OS X) - Perfect
- Safari (OS X), Chrome & Safari (iOS) - Small glitch with the position handle
## Thanks to
- [skinspecs.pdf](http://members.xoom.it/skinart/tutorial/skinspecs..pdf)
- After I started, I found someone else who did the same thing in 2002(!).
Doesn't seem to work play in my modern browsers. http://forums.winamp.com/showthread.php?threadid=91850

63
index.html Executable file
View file

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<title>Winamp.js - It really rips off the llama's ass</title>
<link rel='stylesheet' type='text/css' href='winamp.css' />
</head>
<body>
<audio id='player'></audio>
<div id='winamp'>
<div id='title-bar' class='selected'>
<div id='option'></div>
<div id='minimize'></div>
<div id='shade'></div>
<div id='close'></div>
</div>
<div class='status'>
<div id='clutter-bar' class='disabled'></div>
<div id='play-pause' class='stop'>
<div id='work-indicator' class='selected'></div>
</div>
<div id='time'>
<div id='minus-sign'></div>
<div id='minute-first-digit'></div>
<div id='minute-second-digit'></div>
<div id='second-first-digit'></div>
<div id='second-second-digit'></div>
</div>
<div id='visualization'></div>
</div>
<div class='media-info'>
<div id='song-title'></div>
<div id='kbps'></div>
<div id='khz'></div>
<div class='mono-stereo'>
<div id='mono' class=''></div>
<div id='stereo' class='selected'></div>
</div>
</div>
<input id='volume' type='range' min='0' max='100' step='1' value='50' />
<input id='balance' type='range' min='0' max='100' step='1' value='50' />
<div class='windows'>
<div id='equalizer'></div>
<div id='playlist'></div>
</div>
<input id='position' type='range' min='0' max='100' step='1' value='0' />
<div class='actions'>
<div id='previous'></div>
<div id='play'></div>
<div id='pause'></div>
<div id='stop'></div>
<div id='next'></div>
</div>
<div id='eject'></div>
<div class='shuffle-repeat'>
<div id="shuffle"></div>
<div id="repeat"></div>
</div>
<a id='about' target='_blank' href='http://jordaneldredge.com'></a>
</div>
<input id="file-input" type="file" />
<script src="winamp.js"></script>
</body>
</html>

BIN
skins/default/AVS.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
skins/default/BALANCE.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
skins/default/CBUTTONS.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
skins/default/CLOSE.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/EQCLOSE.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/EQMAIN.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
skins/default/EQNORMAL.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/EQSLID.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/EQTITLE.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/EQ_EX.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
skins/default/MAIN.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
skins/default/MAINMENU.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/MB.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
skins/default/MIN.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/MONOSTER.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
skins/default/NORMAL.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/NUMBERS.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

BIN
skins/default/PCLOSE.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/PLAYPAUS.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
skins/default/PLEDIT.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

6
skins/default/PLEDIT.TXT Executable file
View file

@ -0,0 +1,6 @@
[Text]
Normal=#00FF00
Current=#FFFFFF
NormalBG=#000000
SelectedBG=#0000FF
Font=Arial

BIN
skins/default/PNORMAL.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/POSBAR.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
skins/default/POSBAR.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/PSIZE.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/PTBAR.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/PVSCROLL.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/PWINBUT.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/PWSNORM.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/PWSSIZE.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

105
skins/default/REGION.TXT Executable file
View file

@ -0,0 +1,105 @@
; I stole this from the Complex skin, cause hey I thought it was cool :)
; (and cause I'm too lazy to document it myself. :)
; The original author is Adam Kennedy <adamk@iname.com>
;
; Hope you don't mind Adam :)
;
;
; -Justin
;
;
; P.S. you can use the section names [WindowShade] and [Equalizer]
; for obvious purposes =)
;
;
; REGION.TXT STRUCTURE FOR DEFINING SKIN MASKS
;
;The region information comes without any supporting documentation
;so I thought I might as well make some.
;
;The region.txt in WinAmp 1.92 allows us to some interesting things with transparencies
;But how does it work?
;
;Well, basically, it lets you define a set of polygons. Anything inside a polygon is drawn. Anything not in a polygon is not drawn. Simple heh?
;For how to define it have a look below at the first block. Un-comment it now. Don't worry about commenting the actual one lower down, if WinAmp finds multiple definitions, it only does the first and ignores the rest
;
;How to make a mask
;1. Type [Normal]. Real simple that one. (My guess is it is just for forward compatibility)
;2. Just type the NumPoints= and leave the number blank, we'll fill it in later
;3. OK, now for the fun part.
; The co-ordinates you should type in are the OUTSIDE pixel co-ordinates in x,y format.
; Start at the top lefthand corner and work your way CLOCKWISE around you polygon.
; Now WinAmp ONLY accepts the PointList as one line of comma seperated variables.
; You can use spaces, but DONT GO ONTO A NEW LINE. Clear? Good. :)
; One last thing, don't type the first position again at the end, WinAmp joins them.
; Putting a space between each pair is simply common sense, right?
;4. Once your done, count the number of co-ords, and fill in that number for NumPoints.
;
;Oh, as a side note, the x variables go from 0 to 275, and the y from 0 to 116.
;So if you look at the first example you can see I've gone across the second top line, gone down a bit, ducked in one pixel, gone down to the bottom, and across and back up the other side, putting another ledge on the other side.
;This does the outside border, and chops some bits out on the top and edges
;[Normal]
;NumPoints=8 ;Do this LAST!
;PointList=0,1, 275,1, 275,14, 274,14, 274,115, 1,115, 1,14, 0,14
;Cool heh? Very subtle effect. Now lets try a more complex one, with multiple polygons
;For your first hard(ish) mask, I suggest defining each element as a seperate mask.
;It makes them a bit easier to think about.
;First, lets define an area that JUST does the titlebar.
;[Normal]
;NumPoints=4
;PointList=0,1, 275,1, 275,14, 0,14
;Simple as can be :)
;Go ahead, uncomment it and have a look. Just remember to recomment everything above it
;Doing your areas one at a time does speed the process up a bit
;Now lets define JUST the area sort of inside the outer ring
;[Normal]
;NumPoints = 4
;PointList = 3,15, 272,15, 272,113, 3,113
;Right, so say we want a mask that has, the titlebar AND the main area.
;We just add them together
; [Normal]
; NumPoints = 4, 4 ;Make sure to get the order right(although it doesnt matter here =P )
; PointList = 0,1, 275,1, 275,14, 0,14, 3,15, 272,15, 272,113, 3,113
;Dont forget to add that extra comma after the first set.
; Having that bigger gap that lets you "keep it in your head" easier, it's a good idea
;OK, now lets try something tricky. We are going to make a mask for just one green line around the outside of the skin
;This introduces the other "big thing" with masks. Have a look at the first three co-ordinates below. You can see the first two moves we make are to go one to the side and then go down to the bottom. Looking at the last co-ordinate you can see we will come back up at the end, making a line two wide, right?
;WRONG!! Here comes the big important phrase.
;IF YOU ARNT FOLLOWING THE OUTSIDE EDGE, YOUR CO-ORDINATES MARK TRANSPARENT SPACE LIMITS
;That's right. Because I don't make a square, because I have to turn "inside" the box, I am only marking space, and what I end up with is a mask with one green line.
;WARNING: Leave your skins dialog open when you do this one. :)
;[Normal]
;NumPoints = 8
;PointList = 1,14, 2,14, 2,114, 273,114, 273,14, 274,14, 274,115, 1,115
;OK, as the final touch lets add lets the border we just made to the other two.
;You should be able to work this out on your own
;[Normal]
;NumPoints = 4, 4, 8
;PointList = 0,1, 275,1, 275,14, 0,14, 3,15, 272,15, 272,113, 3,113, 1,14, 2,14, 2,114, 273,114, 273,14, 274,14, 274,115, 1,115
;hmm... that still looks a bit tacky down the bottom right
;So I'm going to modify it a bit
;See if you can work out what I've had to alter just by looking at the display of it
;[Normal]
;NumPoints = 4, 4, 8
;PointList = 0,1, 275,1, 275,14, 0,14, 3,15, 272,15, 272,80, 3,80, 1,14, 2,14, 2,81, 273,81, 273,14, 274,14, 274,115, 1,115
; Justin's whacked trans skin :) in one, big lame messy line
; [Normal]
; NumPoints=20,4,4,4,8,4,4,6,6,4,4,4,4
; PointList=0,0,19,0,19,11,114,11,114,0,156,0,156,11,243,11,243,0,275,0,275,13,266,13,266,22,264,22,264,13,111,13,111,22,109,22,109,13,0,13, 109,22,266,22,266,36,109,36, 16,88,130,88,130,105,16,105, 136,89,157,89,157,104,136,104, 22,13,22,62,102,62,102,13,100,13,100,24,24,24,24,13, 0,0,275,0,275,3,0,3, 16,72,264,72,264,81,16,81, 0,13,0,78,16,78,16,75,4,75,4,13, 275,13,275,78,264,78,264,75,272,75,272,13, 14,78,16,78,16,105,14,105, 130,81,132,81,132,105,130,105, 146,81,146,89,148,89,148,81, 130,96,136,96,136,98,130,98
;THE END
;
;Post-Script
;The other good thing about doing your mask in bits and pieces like this is that can make multiple versions so you can change as your whims change

BIN
skins/default/SHUFREP.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
skins/default/SONGNAME.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/TEXT.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
skins/default/TITLEBAR.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
skins/default/TITLEBAR.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

24
skins/default/VISCOLOR.TXT Executable file
View file

@ -0,0 +1,24 @@
0,0,0, // color 0 = black
24,33,41, // color 1 = grey for dots
239,49,16, // color 2 = top of spec
206,41,16, // 3
214,90,0, // 4
214,102,0, // 5
214,115,0, // 6
198,123,8, // 7
222,165,24, // 8
214,181,33, // 9
189,222,41, // 10
148,222,33, // 11
41,206,16, // 12
50,190,16, // 13
57,181,16, // 14
49,156,8, // 15
41,148,0, // 16
24,132,8, // 17 = bottom of spec
255,255,255, // 18 = osc 1
214,214,222, // 19 = osc 2 (slightly dimmer)
181,189,189, // 20 = osc 3
160,170,175, // 21 = osc 4
148,156,165, // 22 = osc 4
150, 150, 150, // 23 = analyzer peak dots

BIN
skins/default/VOLBAL.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/VOLBAR.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/VOLUME.BMP Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
skins/default/WINBUT.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/WSCLOSE.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/WSMIN.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/WSNORMAL.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/WSPOSBAR.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

BIN
skins/default/WSWINBUT.CUR Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

461
winamp.css Executable file
View file

@ -0,0 +1,461 @@
/* Range input css reset */
input[type=range]{ -webkit-appearance: none; margin: 0; padding: 0; background: none; }
input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; border: none; border-radius: 0; background: none; }
input[type=range]::-moz-range-thumb { border: none; border-radius: 0; background: none; }
input[type=range]::-moz-range-track { border: none; background: none; }
input[type=range]:focus { outline: none; }
input[type=range]::-moz-focus-outer { border: 0; }
#winamp {
position: relative;
background-image: url('skins/default/MAIN.BMP');
height: 116px;
width: 275px;
cursor:url('skins/default/MAINMENU.CUR'), auto;
}
.actions {
position: absolute;
top: 88px;
left: 16px;
height: 18px;
width: 114px;
}
#title-bar {
position: absolute;
top: 0;
left: 0;
height: 14px;
width: 275px;
background-image: url('skins/default/TITLEBAR.BMP');
background-position: -27px -14px;
cursor:url('skins/default/TITLEBAR.CUR'), auto;
}
#title-bar.selected {
background-position: -27px 0px;
}
.llama #title-bar {
background-position: -27px -61px;
}
.llama #title-bar.selected {
background-position: -27px -57px;
}
#title-bar div {
position: absolute;
height: 9px;
width: 9px;
top: 3px;
cursor:url('skins/default/MAINMENU.CUR'), auto;
}
#title-bar #option {
left: 6px;
background-image: url('skins/default/TITLEBAR.BMP');
}
#title-bar #option:active {
background-position: 0 -9px;
}
#title-bar #minimize {
left: 244px;
background-image: url('skins/default/TITLEBAR.BMP');
background-position: -9px 0;
}
#title-bar #minimize:active {
background-position: -9px -9px;
}
#title-bar #shade {
left: 254px;
background-image: url('skins/default/TITLEBAR.BMP');
background-position: 0px -18px;
}
#title-bar #shade:active {
background-position: -9px -18px;
}
#title-bar #close {
left: 264px;
background-image: url('skins/default/TITLEBAR.BMP');
background-position: -18px 0px;
cursor:url('skins/default/CLOSE.CUR'), auto;
}
#title-bar #close:active {
background-position: -18px -9px;
}
.status #clutter-bar {
position: absolute;
top: 22px;
left: 10px;
height: 43px;
width: 8px;
background-image: url('skins/default/TITLEBAR.BMP');
background-position: -304px 0;
}
.status #clutter-bar.disabled {
background-position: -312px 0;
}
.status #play-pause {
position: absolute;
top: 28px;
left: 24px;
height: 9px;
width: 9px;
background-image: url('skins/default/PLAYPAUS.BMP');
}
.status #play-pause.play #work-indicator {
position: absolute;
top: 0px;
left: 0px;
height: 9px;
width: 3px;
background-image: url('skins/default/PLAYPAUS.BMP');
background-position: -39px 0;
}
.status #play-pause.play #work-indicator.selected {
background-position: -36px 0;
}
.status #play-pause.play { }
.status #play-pause.pause { background-position: -9px 0; }
.status #play-pause.stop { background-position: -18px 0; }
.status #time {
position: absolute;
left: 48px;
left: 40px;
top: 26px;
/* Just to make it clickable */
height: 13px;
width: 59px;
}
.status #time #minus-sign {
position: absolute;
top: 6px;
background-color: red;
width: 5px;
height: 1px;
background-image: url('skins/default/NUMBERS.BMP');
background-position: -9px -6px;
}
.status #time.countdown #minus-sign {
background-position: -20px -6px;
}
.status #time #minute-first-digit {
position: absolute;
left: 8px;
height: 13px;
width: 9px;
}
.status #time #minute-second-digit {
position: absolute;
left: 20px;
height: 13px;
width: 9px;
}
.status #time #second-first-digit {
position: absolute;
left: 38px;
height: 13px;
width: 9px;
}
.status #time #second-second-digit {
position: absolute;
left: 50px;
height: 13px;
width: 9px;
}
.media-info #song-title {
position: absolute;
left: 112px;
top: 27px;
width: 152px;
height: 6px;
overflow: hidden;
}
.media-info #kbps {
position: absolute;
left: 111px;
top: 43px;
width: 15px;
height: 6px;
overflow: hidden;
}
.media-info #khz {
position: absolute;
left: 156px;
top: 43px;
width: 10px;
height: 6px;
overflow: hidden;
}
.media-info .mono-stereo {
position: absolute;
left: 212px;
top: 41px;
width: 57px;
height: 12px;
}
.media-info .mono-stereo div {
position: absolute;
height: 12px;
background-image: url('skins/default/MONOSTER.BMP');
}
.media-info .mono-stereo #mono {
width: 29px;
background-position: -29px -12px;
}
.media-info .mono-stereo #mono.selected {
background-position: -29px 0;
}
.media-info .mono-stereo #stereo {
left: 27px;
width: 29px;
background-position: 0 -12px;
}
.media-info .mono-stereo #stereo.selected {
background-position: 0 0;
}
#volume {
position: absolute;
left: 107px;
top: 57px;
height: 13px;
width: 68px;
background-image: url('skins/default/VOLUME.BMP');
background-position: 0 0;
}
#volume::-webkit-slider-thumb {
margin-top: 1px;
height: 11px;
width: 14px;
background-image: url('skins/default/VOLUME.BMP');
background-position: -15px -422px;
cursor:url('skins/default/VOLBAR.CUR'), auto;
}
#volume::-moz-range-thumb {
margin-top: 1px;
height: 11px;
width: 14px;
background-image: url('skins/default/VOLUME.BMP');
background-position: -15px -422px;
cursor:url('skins/default/VOLBAR.CUR'), auto;
}
#volume:active::-webkit-slider-thumb {
background-position: 0 -422px;
}
#volume:active::-moz-range-thumb {
background-position: 0 -422px;
}
#balance {
position: absolute;
left: 177px;
top: 57px;
height: 13px;
width: 38px;
background-image: url('skins/default/BALANCE.BMP');
background-position: -9px 0;
}
#balance::-webkit-slider-thumb {
top: 1px;
height: 11px;
width: 14px;
background-image: url('skins/default/VOLUME.BMP');
background-position: -15px -422px;
cursor:url('skins/default/POSBAR.CUR'), auto;
}
#balance::-moz-range-thumb {
top: 1px;
height: 11px;
width: 14px;
background-image: url('skins/default/VOLUME.BMP');
background-position: -15px -422px;
cursor:url('skins/default/POSBAR.CUR'), auto;
}
#balance:active::-webkit-slider-thumb {
background-position: 0 -422px;
}
#balance:active::-moz-range-thumb {
background-position: 0 -422px;
}
.windows {
position: absolute;
left: 219px;
top: 58px;
width: 46px;
height: 13px;
}
.windows div {
position: absolute;
width: 23px;
height: 13px;
background-image: url('skins/default/SHUFREP.BMP');
}
.windows #equalizer {
left: 0;
background-position: 0 -61px;
}
.windows #equalizer:active {
background-position: -46px -61px;
}
.windows #playlist {
left: 23px;
background-position: -23px -61px;
}
.windows #playlist:active {
background-position: -69px -61px;
}
#position {
position: absolute;
left: 16px;
top: 72px;
width: 248px;
height: 10px;
background-image: url('skins/default/POSBAR.BMP');
}
#position::-webkit-slider-thumb {
height: 10px;
width: 29px;
background-position: -248px 0;
background-image: url('skins/default/POSBAR.BMP');
cursor:url('skins/default/POSBAR.CUR'), auto;
}
#position::-moz-range-thumb {
height: 10px;
width: 29px;
background-position: -248px 0;
background-image: url('skins/default/POSBAR.BMP');
cursor:url('skins/default/POSBAR.CUR'), auto;
}
#position:active::-webkit-slider-thumb {
background-position: -278px 0;
}
#position:active::-moz-range-thumb {
background-position: -278px 0;
}
.actions div {
background-image: url('skins/default/CBUTTONS.BMP');
height: 18px;
width: 23px;
float: left;
}
.actions #previous { background-position: 0 0; }
.actions #previous:active { background-position: 0 -18px; }
.actions #play { background-position: -23px 0; }
.actions #play:active { background-position: -23px -18px; }
.actions #pause { background-position: -46px 0; }
.actions #pause:active { background-position: -46px -18px; }
.actions #stop { background-position: -69px 0; }
.actions #stop:active { background-position: -69px -18px; }
.actions #next { background-position: -92px 0; width: 22px; }
.actions #next:active { background-position: -92px -18px; }
#eject {
position: absolute;
top: 88px;
left: 136px;
background-image: url('skins/default/CBUTTONS.BMP');
height: 16px;
width: 22px;
}
#eject { background-position: -114px 0; }
#eject:active { background-position: -114px -16px; }
.shuffle-repeat {
position: absolute;
top: 89px;
left: 164px;
width: 74px;
}
.shuffle-repeat div {
position: absolute;
height: 15px;
background-image: url('skins/default/SHUFREP.BMP');
}
.shuffle-repeat #shuffle {
width: 47px;
background-position: -28px 0;
}
.shuffle-repeat #shuffle:active {
background-position: -28px -15px;
}
.shuffle-repeat #shuffle.selected {
background-position: -28px -30px;
}
.shuffle-repeat #shuffle.selected:active {
background-position: -28px -45px;
}
.shuffle-repeat #repeat {
left: 47px;
width: 28px;
}
.shuffle-repeat #repeat:active {
background-position: 0 -15px;
}
.shuffle-repeat #repeat.selected {
background-position: 0 -30px;
}
.shuffle-repeat #repeat.selected:active {
background-position: 0 -45px;
}
#about {
position: absolute;
top: 91px;
left: 253px;
height: 15px;
width: 13px;
}
.character {
display: block;
float: left; /* Safari does not do inline-block well */
width: 5px;
height: 6px;
background-image: url('skins/default/TEXT.BMP');
text-indent: -9999px;
}
.digit {
display: inline-block;
width: 9px;
height: 13px;
background-image: url('skins/default/NUMBERS.BMP');
text-indent: -9999px;
}
#file-input { display: none; }

309
winamp.js Executable file
View file

@ -0,0 +1,309 @@
/* Helpful wrapper for the native <audio> element */
function Media (audioId) {
this.audio = document.getElementById(audioId);
/* Properties */
this.timeElapsed = function() {
return this.audio.currentTime;
}
this.timeRemaining = function() {
return this.audio.duration - this.audio.currentTime;
}
this.timeElapsedObject = function() {
return this._timeObject(this.timeElapsed());
}
this.timeRemainingObject = function() {
return this._timeObject(this.timeRemaining());
}
this.percentComplete = function() {
return (this.audio.currentTime / this.audio.duration) * 100;
}
/* Actions */
this.previous = function() {
this.audio.currentTime = 0;
};
this.play = function() {
this.audio.play();
};
this.pause = function() {
this.audio.pause();
};
this.stop = function() {
this.audio.pause();
this.audio.currentTime = 0;
};
this.next = function() {
this.audio.currentTime = this.audio.duration;
};
this.toggleRepeat = function() {
this.audio.loop = !this.audio.loop;
};
this.toggleShuffle = function() {
// Not implemented
};
/* Actions with arguments */
this.seekToPercentComplete = function(percent) {
this.audio.currentTime = this.audio.duration * (percent/100);
this.audio.play();
};
this.setVolume = function(volume) {
this.audio.volume = volume;
};
this.loadFile = function(file) {
this.audio.setAttribute('src', file);
};
/* Listeners */
this.addEventListener = function(event, callback) {
this.audio.addEventListener(event, callback);
};
/* Helpers */
this._timeObject = function(seconds) {
var minutes = seconds / 60;
var seconds = seconds % 60;
return [
Math.floor(minutes / 10),
Math.floor(minutes % 10),
Math.floor(seconds / 10),
Math.floor(seconds % 10)
];
}
}
function Winamp () {
self = this;
this.media = new Media('player');
this.media.setVolume(.5);
this.nodes = {
'close': document.getElementById('close'),
'position': document.getElementById('position'),
'fileInput': document.getElementById('file-input'),
'time': document.getElementById('time'),
'previous': document.getElementById('previous'),
'play': document.getElementById('play'),
'pause': document.getElementById('pause'),
'stop': document.getElementById('stop'),
'next': document.getElementById('next'),
'eject': document.getElementById('eject'),
'repeat': document.getElementById('repeat'),
'shuffle': document.getElementById('shuffle'),
'volume': document.getElementById('volume'),
'balance': document.getElementById('balance'),
'playPause': document.getElementById('play-pause'),
'winamp': document.getElementById('winamp'),
};
this.nodes.close.onclick = function() {
}
this.media.addEventListener('timeupdate', function() {
self.nodes.position.value = self.media.percentComplete();
self.updateTime();
});
this.media.addEventListener('ended', function() {
self.setStatus('stop');
self.media.previous();
});
this.nodes.time.onclick = function() {
this.classList.toggle('countdown');
self.updateTime();
}
this.nodes.previous.onclick = function() {
self.media.previous();
}
this.nodes.play.onclick = function() {
self.media.play();
self.setStatus('play');
}
this.nodes.pause.onclick = function() {
self.media.pause();
self.setStatus('pause');
}
this.nodes.stop.onclick = function() {
self.media.stop();
self.setStatus('stop');
}
this.nodes.next.onclick = function() {
self.media.next();
}
this.nodes.eject.onclick = function() {
self.nodes.fileInput.click();
}
this.nodes.fileInput.onchange = function(e){
var file = e.target.files[0];
var objectUrl = URL.createObjectURL(file);
self.loadFile(objectUrl, file.name);
self.media.play();
self.setStatus('play');
}
this.nodes.volume.oninput = function() {
setVolume( this.value / 100);
}
this.nodes.position.onmousedown = function() {
self.media.pause();
}
this.nodes.position.onchange = function() {
self.media.seekToPercentComplete(this.value);
}
this.nodes.balance.oninput = function() {
setBalance( this.value / 100);
}
this.nodes.repeat.onclick = function() {
toggleRepeat();
}
this.nodes.shuffle.onclick = function() {
toggleShuffle();
}
this.setStatus = function(className) {
self.nodes.playPause.removeAttribute("class");
self.nodes.playPause.classList.add(className);
}
function setVolume(volume) {
sprite = Math.round(volume * 28);
offset = (sprite - 1) * 15;
self.media.setVolume(volume);
self.nodes.volume.style.backgroundPosition = '0 -' + offset + 'px';
}
function setBalance(balance) {
offset = Math.abs(balance - .5) * 2;
sprite = Math.floor(offset * 28);
offset = (sprite - 1) * 15;
console.log(self.nodes.balance.style);
self.nodes.balance.style.backgroundPosition = '-9px -' + offset + 'px';
}
function toggleRepeat() {
self.media.toggleRepeat();
self.nodes.repeat.classList.toggle('selected');
}
function toggleShuffle() {
self.media.toggleShuffle();
self.nodes.shuffle.classList.toggle('selected');
}
this.updateTime = function() {
if(this.nodes.time.classList.contains('countdown')) {
digits = this.media.timeRemainingObject();
} else {
digits = this.media.timeElapsedObject();
}
html = digitHtml(digits[0]);
document.getElementById('minute-first-digit').innerHTML = '';
document.getElementById('minute-first-digit').appendChild(html);
html = digitHtml(digits[1]);
document.getElementById('minute-second-digit').innerHTML = '';
document.getElementById('minute-second-digit').appendChild(html);
html = digitHtml(digits[2]);
document.getElementById('second-first-digit').innerHTML = '';
document.getElementById('second-first-digit').appendChild(html);
html = digitHtml(digits[3]);
document.getElementById('second-second-digit').innerHTML = '';
document.getElementById('second-second-digit').appendChild(html);
}
this.loadFile = function(file, fileName) {
this.media.loadFile(file);
html = fontHtml(fileName);
document.getElementById('song-title').innerHTML = '';
document.getElementById('song-title').appendChild(html);
html = fontHtml("128");
document.getElementById('kbps').innerHTML = '';
document.getElementById('kbps').appendChild(html);
html = fontHtml("44");
document.getElementById('khz').innerHTML = '';
document.getElementById('khz').appendChild(html);
this.updateTime();
}
function digitHtml(digit) {
horizontalOffset = digit * 9;
div = document.createElement('div');
div.classList.add('digit');
div.style.backgroundPosition = '-' + horizontalOffset + 'px 0';
div.innerHTML = digit;
return div;
}
function fontHtml(string) {
parentDiv = document.createElement('div');
for (var i = 0, len = string.length; i < len; i++) {
char = string[i].toLowerCase();
parentDiv.appendChild(charHtml(char));
}
return parentDiv;
}
function charHtml(char) {
position = charPosition(char);
row = position[0];
column = position[1];
verticalOffset = row * 6;
horizontalOffset = column * 5;
div = document.createElement('div');
div.classList.add('character');
x = '-' + horizontalOffset + 'px';
y = '-' + verticalOffset + 'px'
div.style.backgroundPosition = x + ' ' + y;
div.innerHTML = char;
return div;
}
function charPosition(char) {
position = fontLookup[char];
if(!position) {
return fontLookup[' '];
}
return position;
}
/* XXX There are too many " " and "_" characters */
var fontLookup = {
"a": [0,0], "b": [0,1], "c": [0,2], "d": [0,3], "e": [0,4], "f": [0,5],
"g": [0,6], "h": [0,7], "i": [0,8], "j": [0,9], "k": [0,10],
"l": [0,11], "m": [0,12], "n": [0,13], "o": [0,14], "p": [0,15],
"q": [0,16], "r": [0,17], "s": [0,18], "t": [0,19], "u": [0,20],
"v": [0,21], "w": [0,22], "x": [0,23], "y": [0,24], "z": [0,25],
"\"": [0,26], "@": [0,27], " ": [0,29], "0": [1,0], "1": [1,1],
"2": [1,2], "3": [1,3], "4": [1,4], "5": [1,5], "6": [1,6], "7": [1,7],
"8": [1,8], "9": [1,9], " ": [1,10], "_": [1,11], ":": [1,12],
"(": [1,13], ")": [1,14], "-": [1,15], "'": [1,16], "!": [1,17],
"_": [1,18], "+": [1,19], "\\": [1,20], "/": [1,21], "[": [1,22],
"]": [1,23], "^": [1,24], "&": [1,25], "%": [1,26], ".": [1,27],
"=": [1,28], "$": [1,29], "#": [1,30], "Å": [2,0], "Ö": [2,1],
"Ä": [2,2], "?": [2,3], "*": [2,4], " ": [2,5]
};
}
keylog = [];
trigger = [78,85,76,27,76,27,83,79,70,84];
// Easter Egg
document.onkeyup = function(e){
keylog.push(e.which);
keylog = keylog.slice(-10);
if(keylog.toString() == trigger.toString()) {
document.getElementById('winamp').classList.toggle('llama');
}
}
winamp = new Winamp();
winamp.loadFile('http://jordaneldredge.com/projects/winamp.js/llama.mp3', 'llama.mp3');