From 932a5a255f4817e80f874779db697c48c8f066e1 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sat, 12 Dec 2015 19:49:50 -0800 Subject: [PATCH] Create DOM in Javascript This allows us to develop locally without a server. --- js/embed.js | 6 +- js/main-window-dom.js | 111 ++++++++++++ js/main.js | 6 +- rjs/text.js | 390 ------------------------------------------ 4 files changed, 117 insertions(+), 396 deletions(-) create mode 100644 js/main-window-dom.js delete mode 100644 rjs/text.js diff --git a/js/embed.js b/js/embed.js index 786d5af1..596c9a53 100644 --- a/js/embed.js +++ b/js/embed.js @@ -3,7 +3,7 @@ var scriptTag = document.currentScript; require([ 'browser', - '../rjs/text!../html/main-window.html', + 'main-window-dom', '../rjs/css!../css/cleanslate.css', '../rjs/css!../css/winamp.css', 'winamp', @@ -11,7 +11,7 @@ require([ 'hotkeys' ], function( Browser, - mainWindowHtml, + mainWindowDom, cleanslateCss, winampCss, Winamp, @@ -28,7 +28,7 @@ require([ var hotKeys = typeof options.hotkeys !== 'undefined'; if (Browser.isCompatible()) { - node.innerHTML = mainWindowHtml; + node.appendChild(mainWindowDom); node.setAttribute('id', 'winamp2-js'); var winamp = Winamp.init({ diff --git a/js/main-window-dom.js b/js/main-window-dom.js new file mode 100644 index 00000000..6936e82c --- /dev/null +++ b/js/main-window-dom.js @@ -0,0 +1,111 @@ +define([], function() { + function el(tagName, attributes, content) { + tagName = tagName || 'div'; + attributes = attributes || {}; + content = content || []; + var tag = document.createElement(tagName); + if (typeof content === 'string') { + content = [content]; + } + for (var attr in attributes) { + tag.setAttribute(attr, attributes[attr]); + } + for (var i = 0; i < content.length; i++) { + if (typeof content[i] === 'string') { + tag.appendChild(document.createTextNode(content[i])); + } else { + tag.appendChild(content[i]); + } + } + return tag; + } + + return el('div', {id: 'main-window', class: 'loading stop'}, [ + el('div', {id: 'loading'}, 'Loading...'), + el('div', {id: 'title-bar', class: 'selected'}, [ + el('div', {id: 'option'}, [ + el('ul', {id: 'context-menu'}, [ + el('li', {}, [ + el('a', {href: 'https://github.com/captbaritone/winamp2-js', target: '_blank'}, 'Winamp2-js...') + ]), + el('li', {class: 'hr'}, [ el('hr') ]), + el('li', {id: 'context-play-file'}, 'Play File...'), + el('li', {class: 'parent'}, [ + el('ul', {}, [ + el('li', {id: 'context-load-skin'}, 'Load Skin...'), + el('li', {class: 'hr'}, [ el('hr') ]), + el('li', {'class': 'skin-select', 'data-skin-url': 'https://cdn.rawgit.com/captbaritone/winamp-skins/master/v2/base-2.91.wsz'}, ''), + el('li', {'class': 'skin-select', 'data-skin-url': 'https://cdn.rawgit.com/captbaritone/winamp-skins/master/v2/MacOSXAqua1-5.wsz'}, 'Mac OSX v1.5 (Aqua)'), + el('li', {'class': 'skin-select', 'data-skin-url': 'https://cdn.rawgit.com/captbaritone/winamp-skins/master/v2/TopazAmp1-2.wsz'}, 'TopazAmp'), + el('li', {'class': 'skin-select', 'data-skin-url': 'https://cdn.rawgit.com/captbaritone/winamp-skins/master/v2/Vizor1-01.wsz'}, 'Vizor'), + el('li', {'class': 'skin-select', 'data-skin-url': 'https://cdn.rawgit.com/captbaritone/winamp-skins/master/v2/XMMS-Turquoise.wsz'}, 'XMMS Turquoise '), + el('li', {'class': 'skin-select', 'data-skin-url': 'https://cdn.rawgit.com/captbaritone/winamp-skins/master/v2/ZaxonRemake1-0.wsz'}, 'Zaxon Remake') + // More here + ]), + 'Skins' + ]), + el('li', {class: 'hr'}, [ el('hr') ]), + el('li', {id: 'context-exit'}, 'Exit') + ]) + ]), + el('div', {id: 'shade-time'}, [ + el('div', {id: 'shade-minus-sign'}), + el('div', {id: 'shade-minute-first-digit', class: 'character'}), + el('div', {id: 'shade-minute-second-digit', class: 'character'}), + el('div', {id: 'shade-second-first-digit', class: 'character'}), + el('div', {id: 'shade-second-second-digit', class: 'character'}) + ]), + el('div', {id: 'minimize'}), + el('div', {id: 'shade'}), + el('div', {id: 'close'}) + ]), + el('div', {class: 'status'}, [ + el('div', {id: 'clutter-bar'}, [ + el('div', {id: 'button-o'}), + el('div', {id: 'button-a'}), + el('div', {id: 'button-i'}), + el('div', {id: 'button-d'}), + el('div', {id: 'button-v'}) + ]), + el('div', {id: 'play-pause'}), + el('div', {id: 'work-indicator'}), + el('div', {id: 'time'}, [ + el('div', {id: 'minus-sign'}), + el('div', {id: 'minute-first-digit'}), + el('div', {id: 'minute-second-digit'}), + el('div', {id: 'second-first-digit'}), + el('div', {id: 'second-second-digit'}) + ]), + el('canvas', {id: 'visualizer', width: '152', height: '32'}) + ]), + el('div', {class: 'media-info'}, [ + el('div', {id: 'song-title', class: 'text'}), + el('div', {id: 'kbps'}), + el('div', {id: 'khz'}), + el('div', {class: 'mono-stereo'}, [ + el('div', {id: 'mono'}), + el('div', {id: 'stereo'}) + ]) + ]), + el('input', {id: 'volume', type: 'range', min: '0', max: '100', step: '1', value: '50'}), + el('input', {id: 'balance', type: 'range', min: '-100', max: '100', step: '2', value: '0'}), + el('div', {class: 'windows'}, [ + el('div', {id: 'equalizer-button'}), + el('div', {id: 'playlist-button'}) + ]), + el('input', {id: 'position', type: 'range', min: '0', max: '100', step: '1', value: '0'}), + el('div', {class: 'actions'}, [ + el('div', {id: 'previous'}), + el('div', {id: 'play'}), + el('div', {id: 'pause'}), + el('div', {id: 'stop'}), + el('div', {id: 'next'}) + ]), + el('div', {id: 'eject'}), + el('div', {class: 'shuffle-repeat'}, [ + el('div', {id: 'shuffle'}), + el('div', {id: 'repeat'}) + ]), + el('a', {id: 'about', target: 'blank', href: 'https://github.com/captbaritone/winamp2-js'}) + ]); +}); diff --git a/js/main.js b/js/main.js index 9ebe0b5a..cdd9f16a 100644 --- a/js/main.js +++ b/js/main.js @@ -1,13 +1,13 @@ require([ 'browser', - '../rjs/text!../html/main-window.html', + 'main-window-dom', '../rjs/css!../css/winamp.css', 'winamp', 'context', 'hotkeys' ], function( Browser, - mainWindowHtml, + mainWindowDom, pageCss, Winamp, Context, @@ -20,7 +20,7 @@ require([ }; if (Browser.isCompatible()) { var mainWindowElement = document.createElement('div'); - mainWindowElement.innerHTML = mainWindowHtml; + mainWindowElement.appendChild(mainWindowDom); document.getElementById('winamp2-js').appendChild(mainWindowElement); var winamp = Winamp.init({ diff --git a/rjs/text.js b/rjs/text.js deleted file mode 100644 index 17921b6e..00000000 --- a/rjs/text.js +++ /dev/null @@ -1,390 +0,0 @@ -/** - * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/requirejs/text for details - */ -/*jslint regexp: true */ -/*global require, XMLHttpRequest, ActiveXObject, - define, window, process, Packages, - java, location, Components, FileUtils */ - -define(['module'], function (module) { - 'use strict'; - - var text, fs, Cc, Ci, xpcIsWindows, - progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], - xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, - bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, - hasLocation = typeof location !== 'undefined' && location.href, - defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), - defaultHostName = hasLocation && location.hostname, - defaultPort = hasLocation && (location.port || undefined), - buildMap = {}, - masterConfig = (module.config && module.config()) || {}; - - text = { - version: '2.0.12', - - strip: function (content) { - //Strips declarations so that external SVG and XML - //documents can be added to a document without worry. Also, if the string - //is an HTML document, only the part inside the body tag is returned. - if (content) { - content = content.replace(xmlRegExp, ""); - var matches = content.match(bodyRegExp); - if (matches) { - content = matches[1]; - } - } else { - content = ""; - } - return content; - }, - - jsEscape: function (content) { - return content.replace(/(['\\])/g, '\\$1') - .replace(/[\f]/g, "\\f") - .replace(/[\b]/g, "\\b") - .replace(/[\n]/g, "\\n") - .replace(/[\t]/g, "\\t") - .replace(/[\r]/g, "\\r") - .replace(/[\u2028]/g, "\\u2028") - .replace(/[\u2029]/g, "\\u2029"); - }, - - createXhr: masterConfig.createXhr || function () { - //Would love to dump the ActiveX crap in here. Need IE 6 to die first. - var xhr, i, progId; - if (typeof XMLHttpRequest !== "undefined") { - return new XMLHttpRequest(); - } else if (typeof ActiveXObject !== "undefined") { - for (i = 0; i < 3; i += 1) { - progId = progIds[i]; - try { - xhr = new ActiveXObject(progId); - } catch (e) {} - - if (xhr) { - progIds = [progId]; // so faster next time - break; - } - } - } - - return xhr; - }, - - /** - * Parses a resource name into its component parts. Resource names - * look like: module/name.ext!strip, where the !strip part is - * optional. - * @param {String} name the resource name - * @returns {Object} with properties "moduleName", "ext" and "strip" - * where strip is a boolean. - */ - parseName: function (name) { - var modName, ext, temp, - strip = false, - index = name.indexOf("."), - isRelative = name.indexOf('./') === 0 || - name.indexOf('../') === 0; - - if (index !== -1 && (!isRelative || index > 1)) { - modName = name.substring(0, index); - ext = name.substring(index + 1, name.length); - } else { - modName = name; - } - - temp = ext || modName; - index = temp.indexOf("!"); - if (index !== -1) { - //Pull off the strip arg. - strip = temp.substring(index + 1) === "strip"; - temp = temp.substring(0, index); - if (ext) { - ext = temp; - } else { - modName = temp; - } - } - - return { - moduleName: modName, - ext: ext, - strip: strip - }; - }, - - xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, - - /** - * Is an URL on another domain. Only works for browser use, returns - * false in non-browser environments. Only used to know if an - * optimized .js version of a text resource should be loaded - * instead. - * @param {String} url - * @returns Boolean - */ - useXhr: function (url, protocol, hostname, port) { - var uProtocol, uHostName, uPort, - match = text.xdRegExp.exec(url); - if (!match) { - return true; - } - uProtocol = match[2]; - uHostName = match[3]; - - uHostName = uHostName.split(':'); - uPort = uHostName[1]; - uHostName = uHostName[0]; - - return (!uProtocol || uProtocol === protocol) && - (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && - ((!uPort && !uHostName) || uPort === port); - }, - - finishLoad: function (name, strip, content, onLoad) { - content = strip ? text.strip(content) : content; - if (masterConfig.isBuild) { - buildMap[name] = content; - } - onLoad(content); - }, - - load: function (name, req, onLoad, config) { - //Name has format: some.module.filext!strip - //The strip part is optional. - //if strip is present, then that means only get the string contents - //inside a body tag in an HTML string. For XML/SVG content it means - //removing the declarations so the content can be inserted - //into the current doc without problems. - - // Do not bother with the work if a build and text will - // not be inlined. - if (config && config.isBuild && !config.inlineText) { - onLoad(); - return; - } - - masterConfig.isBuild = config && config.isBuild; - - var parsed = text.parseName(name), - nonStripName = parsed.moduleName + - (parsed.ext ? '.' + parsed.ext : ''), - url = req.toUrl(nonStripName), - useXhr = (masterConfig.useXhr) || - text.useXhr; - - // Do not load if it is an empty: url - if (url.indexOf('empty:') === 0) { - onLoad(); - return; - } - - //Load the text. Use XHR if possible and in a browser. - if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { - text.get(url, function (content) { - text.finishLoad(name, parsed.strip, content, onLoad); - }, function (err) { - if (onLoad.error) { - onLoad.error(err); - } - }); - } else { - //Need to fetch the resource across domains. Assume - //the resource has been optimized into a JS module. Fetch - //by the module name + extension, but do not include the - //!strip part to avoid file system issues. - req([nonStripName], function (content) { - text.finishLoad(parsed.moduleName + '.' + parsed.ext, - parsed.strip, content, onLoad); - }); - } - }, - - write: function (pluginName, moduleName, write, config) { - if (buildMap.hasOwnProperty(moduleName)) { - var content = text.jsEscape(buildMap[moduleName]); - write.asModule(pluginName + "!" + moduleName, - "define(function () { return '" + - content + - "';});\n"); - } - }, - - writeFile: function (pluginName, moduleName, req, write, config) { - var parsed = text.parseName(moduleName), - extPart = parsed.ext ? '.' + parsed.ext : '', - nonStripName = parsed.moduleName + extPart, - //Use a '.js' file name so that it indicates it is a - //script that can be loaded across domains. - fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; - - //Leverage own load() method to load plugin value, but only - //write out values that do not have the strip argument, - //to avoid any potential issues with ! in file names. - text.load(nonStripName, req, function (value) { - //Use own write() method to construct full module value. - //But need to create shell that translates writeFile's - //write() to the right interface. - var textWrite = function (contents) { - return write(fileName, contents); - }; - textWrite.asModule = function (moduleName, contents) { - return write.asModule(moduleName, fileName, contents); - }; - - text.write(pluginName, nonStripName, textWrite, config); - }, config); - } - }; - - if (masterConfig.env === 'node' || (!masterConfig.env && - typeof process !== "undefined" && - process.versions && - !!process.versions.node && - !process.versions['node-webkit'])) { - //Using special require.nodeRequire, something added by r.js. - fs = require.nodeRequire('fs'); - - text.get = function (url, callback, errback) { - try { - var file = fs.readFileSync(url, 'utf8'); - //Remove BOM (Byte Mark Order) from utf8 files if it is there. - if (file.indexOf('\uFEFF') === 0) { - file = file.substring(1); - } - callback(file); - } catch (e) { - if (errback) { - errback(e); - } - } - }; - } else if (masterConfig.env === 'xhr' || (!masterConfig.env && - text.createXhr())) { - text.get = function (url, callback, errback, headers) { - var xhr = text.createXhr(), header; - xhr.open('GET', url, true); - - //Allow plugins direct access to xhr headers - if (headers) { - for (header in headers) { - if (headers.hasOwnProperty(header)) { - xhr.setRequestHeader(header.toLowerCase(), headers[header]); - } - } - } - - //Allow overrides specified in config - if (masterConfig.onXhr) { - masterConfig.onXhr(xhr, url); - } - - xhr.onreadystatechange = function (evt) { - var status, err; - //Do not explicitly handle errors, those should be - //visible via console output in the browser. - if (xhr.readyState === 4) { - status = xhr.status || 0; - if (status > 399 && status < 600) { - //An http 4xx or 5xx error. Signal an error. - err = new Error(url + ' HTTP status: ' + status); - err.xhr = xhr; - if (errback) { - errback(err); - } - } else { - callback(xhr.responseText); - } - - if (masterConfig.onXhrComplete) { - masterConfig.onXhrComplete(xhr, url); - } - } - }; - xhr.send(null); - }; - } else if (masterConfig.env === 'rhino' || (!masterConfig.env && - typeof Packages !== 'undefined' && typeof java !== 'undefined')) { - //Why Java, why is this so awkward? - text.get = function (url, callback) { - var stringBuffer, line, - encoding = "utf-8", - file = new java.io.File(url), - lineSeparator = java.lang.System.getProperty("line.separator"), - input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), - content = ''; - try { - stringBuffer = new java.lang.StringBuffer(); - line = input.readLine(); - - // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 - // http://www.unicode.org/faq/utf_bom.html - - // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: - // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 - if (line && line.length() && line.charAt(0) === 0xfeff) { - // Eat the BOM, since we've already found the encoding on this file, - // and we plan to concatenating this buffer with others; the BOM should - // only appear at the top of a file. - line = line.substring(1); - } - - if (line !== null) { - stringBuffer.append(line); - } - - while ((line = input.readLine()) !== null) { - stringBuffer.append(lineSeparator); - stringBuffer.append(line); - } - //Make sure we return a JavaScript string and not a Java string. - content = String(stringBuffer.toString()); //String - } finally { - input.close(); - } - callback(content); - }; - } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && - typeof Components !== 'undefined' && Components.classes && - Components.interfaces)) { - //Avert your gaze! - Cc = Components.classes; - Ci = Components.interfaces; - Components.utils['import']('resource://gre/modules/FileUtils.jsm'); - xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); - - text.get = function (url, callback) { - var inStream, convertStream, fileObj, - readData = {}; - - if (xpcIsWindows) { - url = url.replace(/\//g, '\\'); - } - - fileObj = new FileUtils.File(url); - - //XPCOM, you so crazy - try { - inStream = Cc['@mozilla.org/network/file-input-stream;1'] - .createInstance(Ci.nsIFileInputStream); - inStream.init(fileObj, 1, 0, false); - - convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] - .createInstance(Ci.nsIConverterInputStream); - convertStream.init(inStream, "utf-8", inStream.available(), - Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); - - convertStream.readString(inStream.available(), readData); - convertStream.close(); - inStream.close(); - callback(readData.value); - } catch (e) { - throw new Error((fileObj && fileObj.path || '') + ': ' + e); - } - }; - } - return text; -});