diff --git a/css/main.css b/css/main.css
index 0ff5aede..2df55a44 100755
--- a/css/main.css
+++ b/css/main.css
@@ -64,23 +64,9 @@
left: 0;
height: 14px;
width: 275px;
- /* background-image: TITLEBAR.BMP via Javascript */
- background-position: -27px -14px;
cursor:url('../cursors/TITLEBAR.PNG?inline'), auto;
}
-#title-bar.selected {
- background-position: -27px 0px;
-}
-
-.llama #title-bar {
- background-position: -27px -61px;
-}
-
-.llama #title-bar.selected {
- background-position: -27px -57px;
-}
-
#option,
#minimize,
#shade,
@@ -101,11 +87,6 @@
top: 12px;
left: 0px;
}
-#title-bar #option:active,
-#title-bar #option.selected
-{
- background-position: 0 -9px;
-}
#title-bar #option:active #context-menu,
#title-bar #option.selected #context-menu
{
@@ -114,44 +95,20 @@
#title-bar #minimize {
left: 244px;
- /* background-image: TITLEBAR.BMP via Javascript */
- background-position: -9px 0;
}
-#title-bar #minimize:active {
- background-position: -9px -9px;
-}
-
#title-bar #shade {
left: 254px;
- /* background-image: TITLEBAR.BMP via Javascript */
- background-position: 0px -18px;
}
-#title-bar #shade:active {
- background-position: -9px -18px;
-}
-
#title-bar #close {
left: 264px;
- /* background-image: TITLEBAR.BMP via Javascript */
- background-position: -18px 0px;
cursor:url('../cursors/CLOSE.PNG?inline'), auto;
}
-#title-bar #close:active {
- background-position: -18px -9px;
-}
-
-.status #clutter-bar {
+#clutter-bar {
position: absolute;
top: 22px;
left: 10px;
height: 43px;
width: 8px;
- /* background-image: TITLEBAR.BMP via Javascript */
- background-position: -304px 0;
-}
-
-.status #clutter-bar.disabled {
- background-position: -312px 0;
}
#clutter-bar div {
@@ -165,38 +122,21 @@
top: 3px;
height: 8px;
}
-#clutter-bar #button-o:active {
- background-position: -304px -47px;
-}
#clutter-bar #button-a {
top: 11px;
}
-#clutter-bar #button-a:active,
-#clutter-bar #button-a.selected {
- background-position: -312px -55px;
-}
#clutter-bar #button-i {
top: 18px;
}
-#clutter-bar #button-i:active {
- background-position: -320px -62px;
-}
#clutter-bar #button-d {
top: 25px;
height: 8px;
}
-#clutter-bar #button-d:active,
-#clutter-bar #button-d.selected {
- background-position: -328px -69px;
-}
#clutter-bar #button-v {
top: 33px;
}
-#clutter-bar #button-v:active {
- background-position: -336px -77px;
-}
-.status #play-pause {
+#play-pause {
position: absolute;
top: 28px;
left: 26px;
@@ -206,7 +146,7 @@
background-repeat: no-repeat;
}
-.play .status #work-indicator {
+.play #work-indicator {
position: absolute;
top: 28px;
left: 24px;
@@ -215,13 +155,6 @@
/* background-image: PLAYPAUS.BMP via Javascript */
background-position: -36px 0;
}
-.play .status #work-indicator.selected {
- background-position: -39px 0;
-}
-
-.play .status #play-pause { }
-.pause .status #play-pause { background-position: -9px 0; }
-.stop .status #play-pause { background-position: -18px 0; }
.status #time {
position: absolute;
left: 39px;
@@ -243,23 +176,14 @@
left: -1px;
width: 5px;
height: 1px;
- /* background-image: NUMBERS.BMP via Javascript */
- background-position: -9px -6px;
-}
-.status #time.countdown #minus-sign {
- background-position: -20px -6px;
}
+
/* Alternate format for minus sign, when skin supports it */
.status #time.ex #minus-sign {
top: 0px;
left: -1px;
width: 9px;
height: 13px;
- /* background-image: NUMS_EX.BMP via Javascript */
- background-position: -90px 0px;
-}
-.status #time.ex.countdown #minus-sign {
- background-position: -99px 0px;
}
.status #time #minute-first-digit {
@@ -658,20 +582,9 @@
display: none;
}
.shade #title-bar {
- background-position: -27px -42px;
cursor:url('../cursors/MAINMENU.PNG?inline'), auto;
}
-.shade #title-bar.selected {
- background-position: -27px -29px;
-}
-
-.shade #title-bar #shade {
- background-position: 0px -27px;
-}
-.shade #title-bar #shade:active {
- background-position: -9px -27px;
-}
.shade .actions div {
background: none;
position: absolute;
diff --git a/index.html b/index.html
index ec25004d..2d13cbbe 100755
--- a/index.html
+++ b/index.html
@@ -117,6 +117,7 @@
+
diff --git a/js/font.js b/js/font.js
index dbaba716..dbfa196f 100644
--- a/js/font.js
+++ b/js/font.js
@@ -32,13 +32,9 @@ Font = {
// Get a
containing a digit
digitNode: function(digit) {
- var horizontalOffset = digit * 9;
var div = document.createElement('div');
div.classList.add('digit');
- // Ex rules superseed if nums_ex.bmp is present
- div.classList.add('digit-ex');
- div.style.backgroundPosition = '-' + horizontalOffset + 'px 0';
- div.innerHTML = digit;
+ div.classList.add('digit-' + digit);
return div;
},
diff --git a/js/skin-sprites.js b/js/skin-sprites.js
new file mode 100644
index 00000000..09107f4c
--- /dev/null
+++ b/js/skin-sprites.js
@@ -0,0 +1,92 @@
+var SKIN_SPRITES = [
+ {
+ img: "MAIN",
+ sprites: [
+ { selectors: ["#main-window"], x: 0, y: 0, width: 275, height: 116}
+ ]
+ },
+ {
+ img: "NUMBERS",
+ sprites: [
+ { selectors: ["#time #minus-sign"], x: 9, y: 6, width: 5, height: 1},
+ { selectors: ["#time.countdown #minus-sign"], x: 20, y: 6, width: 5, height: 1},
+ { selectors: [".digit-0"], x: 0, y: 0, width: 9, height: 13},
+ { selectors: [".digit-1"], x: 9, y: 0, width: 9, height: 13},
+ { selectors: [".digit-2"], x: 18, y: 0, width: 9, height: 13},
+ { selectors: [".digit-3"], x: 27, y: 0, width: 9, height: 13},
+ { selectors: [".digit-4"], x: 36, y: 0, width: 9, height: 13},
+ { selectors: [".digit-5"], x: 45, y: 0, width: 9, height: 13},
+ { selectors: [".digit-6"], x: 54, y: 0, width: 9, height: 13},
+ { selectors: [".digit-7"], x: 63, y: 0, width: 9, height: 13},
+ { selectors: [".digit-8"], x: 72, y: 0, width: 9, height: 13},
+ { selectors: [".digit-9"], x: 81, y: 0, width: 9, height: 13},
+ ]
+ },
+ {
+ img: "NUMS_EX",
+ sprites: [
+ { selectors: ["#time.ex #minus-sign"], x: 90, y: 0, width: 9, height: 13},
+ { selectors: ["#time.ex.countdown #minus-sign"], x: 99, y: 0, width: 9, height: 13},
+ { selectors: [".digit-0"], x: 0, y: 0, width: 9, height: 13},
+ { selectors: [".digit-1"], x: 9, y: 0, width: 9, height: 13},
+ { selectors: [".digit-2"], x: 18, y: 0, width: 9, height: 13},
+ { selectors: [".digit-3"], x: 27, y: 0, width: 9, height: 13},
+ { selectors: [".digit-4"], x: 36, y: 0, width: 9, height: 13},
+ { selectors: [".digit-5"], x: 45, y: 0, width: 9, height: 13},
+ { selectors: [".digit-6"], x: 54, y: 0, width: 9, height: 13},
+ { selectors: [".digit-7"], x: 63, y: 0, width: 9, height: 13},
+ { selectors: [".digit-8"], x: 72, y: 0, width: 9, height: 13},
+ { selectors: [".digit-9"], x: 81, y: 0, width: 9, height: 13},
+ ]
+ },
+ {
+ img: "PLAYPAUS",
+ sprites: [
+ { selectors: [".play #play-pause"], x: 0, y: 0, width: 9, height: 9},
+ { selectors: [".pause #play-pause"], x: 9, y: 0, width: 9, height: 9},
+ { selectors: [".stop #play-pause"], x: 18, y: 0, width: 9, height: 9},
+ { selectors: [".play #work-indicator"], x: 36, y: 0, width: 9, height: 9},
+ { selectors: [".play #work-indicator.selected"], x: 39, y: 0, width: 9, height: 9}
+ ]
+ },
+ {
+ img: "PLEDIT",
+ sprites: [
+ { selectors: [".playlist-top-tile"], x: 127, y: 21, width: 25, height: 20},
+ { selectors: [".selected .playlist-top-tile"], x: 127, y: 0, width: 25, height: 20},
+ { selectors: [".playlist-left-tile"], x: 0, y: 42, width: 25, height: 29},
+ { selectors: [".playlist-right-tile"], x: 27, y: 42, width: 25, height: 29},
+ { selectors: [".playlist-bottom-tile"], x: 179, y: 0, width: 25, height: 38},
+ { selectors: ["#playlist.shade"], x: 72, y: 57, width: 25, height: 14}
+ ]
+ },
+ {
+ img: "TITLEBAR",
+ sprites: [
+ { selectors: ["#title-bar"], x: 27, y: 15, width: 275, height: 14},
+ { selectors: ["#title-bar.selected"], x: 27, y: 0, width: 275, height: 14},
+ { selectors: [".lllama #title-bar"], x: 27, y: 61, width: 275, height: 14},
+ { selectors: [".lllama #title-bar.selected"], x: 27, y: 57, width: 275, height: 14},
+ { selectors: ["#title-bar #option"], x: 0, y: 0, width: 9, height: 9},
+ { selectors: ["#title-bar #option"], x: 0, y: 0, width: 9, height: 9},
+ { selectors: ["#title-bar #option:active", "#title-bar #option:selected"], x: 0, y: 9, width: 9, height: 9},
+ { selectors: ["#title-bar #minimize"], x: 9, y: 0, width: 9, height: 9},
+ { selectors: ["#title-bar #minimize:active"], x: 9, y: 9, width: 9, height: 9},
+ { selectors: ["#title-bar #shade"], x: 0, y: 18, width: 9, height: 9},
+ { selectors: ["#title-bar #shade:active"], x: 9, y: 18, width: 9, height: 9},
+ { selectors: ["#title-bar #close"], x: 18, y: 0, width: 9, height: 9},
+ { selectors: ["#title-bar #close:active"], x: 18, y: 9, width: 9, height: 9},
+ { selectors: ["#clutter-bar"], x: 304, y: 0, width: 8, height: 43},
+ { selectors: ["#clutter-bar.disabled"], x: 312, y: 0, width: 8, height: 43},
+ { selectors: ["#button-o:active", "#button-0:selected"], x: 304, y: 47, width: 8, height: 8},
+ { selectors: ["#button-a:active", "#button-a.selected"], x: 312, y: 55, width: 8, height: 7},
+ { selectors: ["#button-i:active", "#button-i.selected"], x: 320, y: 62, width: 8, height: 7},
+ { selectors: ["#button-d:active", "#button-d.selected"], x: 328, y: 69, width: 8, height: 8},
+ { selectors: ["#button-v:active", "#button-v.selected"], x: 336, y: 77, width: 8, height: 7},
+ { selectors: [".shade #title-bar"], x: 27, y: 42, width: 275, height: 14},
+ { selectors: [".shade #title-bar.selected"], x: 27, y: 29, width: 275, height: 14},
+ { selectors: [".shade #title-bar #shade"], x: 0, y: 27, width: 9, height: 9},
+ { selectors: [".shade #title-bar #shade:active"], x: 9, y: 27, width: 9, height: 9}
+ ]
+ },
+];
diff --git a/js/skin.js b/js/skin.js
index fbb47b79..7fed44ba 100644
--- a/js/skin.js
+++ b/js/skin.js
@@ -8,19 +8,6 @@ SkinManager = {
},
_skinImages: {
- "#main-window": "MAIN.BMP",
- "#title-bar": "TITLEBAR.BMP",
- "#title-bar #option": "TITLEBAR.BMP",
- "#title-bar #minimize": "TITLEBAR.BMP",
- "#title-bar #shade": "TITLEBAR.BMP",
- "#title-bar #close": "TITLEBAR.BMP",
- ".status #clutter-bar": "TITLEBAR.BMP",
- ".status #clutter-bar div:active": "TITLEBAR.BMP",
- ".status #clutter-bar div.selected": "TITLEBAR.BMP",
- ".status #play-pause": "PLAYPAUS.BMP",
- ".play .status #work-indicator": "PLAYPAUS.BMP",
- ".status #time #minus-sign": "NUMBERS.BMP",
- ".status #time.ex #minus-sign": "NUMS_EX.BMP",
".media-info .mono-stereo div": "MONOSTER.BMP",
"#volume": "VOLUME.BMP",
"#volume::-webkit-slider-thumb": "VOLUME.BMP",
@@ -36,7 +23,6 @@ SkinManager = {
"#eject": "CBUTTONS.BMP",
".shuffle-repeat div": "SHUFREP.BMP",
".character": "TEXT.BMP",
- ".digit": "NUMBERS.BMP",
// Put this second, since it will trump .digit
".digit-ex": "NUMS_EX.BMP",
".shade #position": "TITLEBAR.BMP",
@@ -44,6 +30,9 @@ SkinManager = {
".shade #position::-moz-range-thumb": "TITLEBAR.BMP",
},
+ // For sprites that tile, we need to use just the sprite, not the whole image
+ _skinSprites: SKIN_SPRITES,
+
// Given a file of an original Winamp WSZ file, set the current skin
setSkinByFile: function(file, completedCallback) {
this.completedCallback = completedCallback;
@@ -56,16 +45,14 @@ SkinManager = {
var zip = new JSZip(buffer);
document.getElementById('time').classList.remove('ex');
- this._createNewStyleNode();
-
- var cssRules = '';
- for(var selector in SkinManager._skinImages) {
- var fileName = SkinManager._skinImages[selector];
+ var promisedCssRules = [];
+ for(var selector in this._skinImages) {
+ var fileName = this._skinImages[selector];
var file = this._findFileInZip(fileName, zip);
if (file) {
var value = "background-image: url(data:image/bmp;base64," + btoa(file.asBinary()) + ")";
- cssRules += selector + "{" + value + "}\n";
+ promisedCssRules.push(selector + "{" + value + "}");
// CSS has to change if this file is present
if(fileName == 'NUMS_EX.BMP') {
@@ -74,9 +61,24 @@ SkinManager = {
}
}
- this.styleNode.appendChild(document.createTextNode(cssRules));
- this._parseVisColors(zip);
- this.completedCallback();
+ Array.prototype.push.apply(promisedCssRules, this._skinSprites.map(function(spriteObj) {
+ var file = this._findFileInZip(spriteObj.img, zip);
+ if (file) {
+ var src = "data:image/bmp;base64," + btoa(file.asBinary());
+ return this._spriteCssRule(src, spriteObj);
+ }
+ }, this));
+
+
+ // Extract sprite images
+ Promise.all(promisedCssRules).then(function(newCssRules) {
+ this._createNewStyleNode();
+ cssRules = newCssRules.join('\n');
+ console.log(cssRules.length);
+ this.styleNode.appendChild(document.createTextNode(cssRules));
+ this._parseVisColors(zip);
+ this.completedCallback();
+ }.bind(this));
},
_parseVisColors: function(zip) {
@@ -109,5 +111,32 @@ SkinManager = {
}
this.styleNode = document.createElement('style');
document.head.appendChild(this.styleNode);
+ },
+
+ // Given an image URL and coordinates, returns a data url for a sub-section
+ // of that image
+ _spriteCssRule: function(src, spriteObj) {
+ return new Promise(function(resolve, reject) {
+ var imageObj = new Image();
+ imageObj.src = src;
+
+ imageObj.onload = function() {
+ var skinImage = this;
+ var cssRules = '';
+ var canvas = document.createElement('canvas');
+ spriteObj.sprites.forEach(function(sprite) {
+ canvas.height = sprite.height;
+ canvas.width = sprite.width;
+
+ var context = canvas.getContext('2d');
+ context.drawImage(skinImage, -sprite.x, -sprite.y);
+ var value = "background-image: url(" + canvas.toDataURL() + ")";
+ sprite.selectors.forEach(function(selector) {
+ cssRules += selector + "{" + value + "}\n";
+ });
+ });
+ resolve(cssRules);
+ };
+ });
}
};