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); + }; + }); } };