From ee86ef713022a6afb8ed8807cebe2fda80ce6d47 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Sat, 11 Jul 2015 01:52:41 -0400 Subject: [PATCH] starting "tv" mode --- tv/index.html | 26 ++++ tv/js/audio.js | 26 ++++ tv/js/vendor/fetch.js | 330 +++++++++++++++++++++++++++++++++++++++++ tv/js/vendor/loader.js | 86 +++++++++++ tv/js/vendor/util.js | 6 + tv/js/ytmnd.js | 49 ++++++ tv/js/zoomtext.js | 56 +++++++ 7 files changed, 579 insertions(+) create mode 100644 tv/index.html create mode 100644 tv/js/audio.js create mode 100644 tv/js/vendor/fetch.js create mode 100644 tv/js/vendor/loader.js create mode 100644 tv/js/vendor/util.js create mode 100644 tv/js/ytmnd.js create mode 100644 tv/js/zoomtext.js diff --git a/tv/index.html b/tv/index.html new file mode 100644 index 0000000..834bef3 --- /dev/null +++ b/tv/index.html @@ -0,0 +1,26 @@ + + + +ytmnd-tv + + + +
+ + + + + + + + + \ No newline at end of file diff --git a/tv/js/audio.js b/tv/js/audio.js new file mode 100644 index 0000000..127680d --- /dev/null +++ b/tv/js/audio.js @@ -0,0 +1,26 @@ +(function(){ + var hasWebKit = ('webkitAudioContext' in window) && !('chrome' in window) + var context = new webkitAudioContext() + var request = new XMLHttpRequest() + var source + + request.open('GET', url, true) + request.responseType = 'arraybuffer' + request.onload = function() { + context.decodeAudioData(request.response, function(response) { + (function loop(){ + if (source) { + source.start(0) + setTimeout(loop, source.buffer.duration * 1000 - (source.buffer.duration < 2 ? 0 : 60) ) + } + else { + setTimeout(loop, 0) + } + source = context.createBufferSource() + source.connect(context.destination) + source.buffer = response + })() + }, function () { console.error('The request failed.') } ) + } + request.send() +})() diff --git a/tv/js/vendor/fetch.js b/tv/js/vendor/fetch.js new file mode 100644 index 0000000..1b4c275 --- /dev/null +++ b/tv/js/vendor/fetch.js @@ -0,0 +1,330 @@ +(function() { + 'use strict'; + + if (self.fetch) { + return + } + + function normalizeName(name) { + if (typeof name !== 'string') { + name = name.toString(); + } + if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { + throw new TypeError('Invalid character in header field name') + } + return name.toLowerCase() + } + + function normalizeValue(value) { + if (typeof value !== 'string') { + value = value.toString(); + } + return value + } + + function Headers(headers) { + this.map = {} + + if (headers instanceof Headers) { + headers.forEach(function(value, name) { + this.append(name, value) + }, this) + + } else if (headers) { + Object.getOwnPropertyNames(headers).forEach(function(name) { + this.append(name, headers[name]) + }, this) + } + } + + Headers.prototype.append = function(name, value) { + name = normalizeName(name) + value = normalizeValue(value) + var list = this.map[name] + if (!list) { + list = [] + this.map[name] = list + } + list.push(value) + } + + Headers.prototype['delete'] = function(name) { + delete this.map[normalizeName(name)] + } + + Headers.prototype.get = function(name) { + var values = this.map[normalizeName(name)] + return values ? values[0] : null + } + + Headers.prototype.getAll = function(name) { + return this.map[normalizeName(name)] || [] + } + + Headers.prototype.has = function(name) { + return this.map.hasOwnProperty(normalizeName(name)) + } + + Headers.prototype.set = function(name, value) { + this.map[normalizeName(name)] = [normalizeValue(value)] + } + + Headers.prototype.forEach = function(callback, thisArg) { + Object.getOwnPropertyNames(this.map).forEach(function(name) { + this.map[name].forEach(function(value) { + callback.call(thisArg, value, name, this) + }, this) + }, this) + } + + function consumed(body) { + if (body.bodyUsed) { + return Promise.reject(new TypeError('Already read')) + } + body.bodyUsed = true + } + + function fileReaderReady(reader) { + return new Promise(function(resolve, reject) { + reader.onload = function() { + resolve(reader.result) + } + reader.onerror = function() { + reject(reader.error) + } + }) + } + + function readBlobAsArrayBuffer(blob) { + var reader = new FileReader() + reader.readAsArrayBuffer(blob) + return fileReaderReady(reader) + } + + function readBlobAsText(blob) { + var reader = new FileReader() + reader.readAsText(blob) + return fileReaderReady(reader) + } + + var support = { + blob: 'FileReader' in self && 'Blob' in self && (function() { + try { + new Blob(); + return true + } catch(e) { + return false + } + })(), + formData: 'FormData' in self + } + + function Body() { + this.bodyUsed = false + + + this._initBody = function(body) { + this._bodyInit = body + if (typeof body === 'string') { + this._bodyText = body + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body + } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { + this._bodyFormData = body + } else if (!body) { + this._bodyText = '' + } else { + throw new Error('unsupported BodyInit type') + } + } + + if (support.blob) { + this.blob = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as blob') + } else { + return Promise.resolve(new Blob([this._bodyText])) + } + } + + this.arrayBuffer = function() { + return this.blob().then(readBlobAsArrayBuffer) + } + + this.text = function() { + var rejected = consumed(this) + if (rejected) { + return rejected + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob) + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as text') + } else { + return Promise.resolve(this._bodyText) + } + } + } else { + this.text = function() { + var rejected = consumed(this) + return rejected ? rejected : Promise.resolve(this._bodyText) + } + } + + if (support.formData) { + this.formData = function() { + return this.text().then(decode) + } + } + + this.json = function() { + return this.text().then(JSON.parse) + } + + return this + } + + // HTTP methods whose capitalization should be normalized + var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] + + function normalizeMethod(method) { + var upcased = method.toUpperCase() + return (methods.indexOf(upcased) > -1) ? upcased : method + } + + function Request(url, options) { + options = options || {} + this.url = url + + this.credentials = options.credentials || 'omit' + this.headers = new Headers(options.headers) + this.method = normalizeMethod(options.method || 'GET') + this.mode = options.mode || null + this.referrer = null + + if ((this.method === 'GET' || this.method === 'HEAD') && options.body) { + throw new TypeError('Body not allowed for GET or HEAD requests') + } + this._initBody(options.body) + } + + function decode(body) { + var form = new FormData() + body.trim().split('&').forEach(function(bytes) { + if (bytes) { + var split = bytes.split('=') + var name = split.shift().replace(/\+/g, ' ') + var value = split.join('=').replace(/\+/g, ' ') + form.append(decodeURIComponent(name), decodeURIComponent(value)) + } + }) + return form + } + + function headers(xhr) { + var head = new Headers() + var pairs = xhr.getAllResponseHeaders().trim().split('\n') + pairs.forEach(function(header) { + var split = header.trim().split(':') + var key = split.shift().trim() + var value = split.join(':').trim() + head.append(key, value) + }) + return head + } + + Body.call(Request.prototype) + + function Response(bodyInit, options) { + if (!options) { + options = {} + } + + this._initBody(bodyInit) + this.type = 'default' + this.url = null + this.status = options.status + this.ok = this.status >= 200 && this.status < 300 + this.statusText = options.statusText + this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers) + this.url = options.url || '' + } + + Body.call(Response.prototype) + + self.Headers = Headers; + self.Request = Request; + self.Response = Response; + + self.fetch = function(input, init) { + // TODO: Request constructor should accept input, init + var request + if (Request.prototype.isPrototypeOf(input) && !init) { + request = input + } else { + request = new Request(input, init) + } + + return new Promise(function(resolve, reject) { + var xhr = new XMLHttpRequest() + + function responseURL() { + if ('responseURL' in xhr) { + return xhr.responseURL + } + + // Avoid security warnings on getResponseHeader when not allowed by CORS + if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) { + return xhr.getResponseHeader('X-Request-URL') + } + + return; + } + + xhr.onload = function() { + var status = (xhr.status === 1223) ? 204 : xhr.status + if (status < 100 || status > 599) { + reject(new TypeError('Network request failed')) + return + } + var options = { + status: status, + statusText: xhr.statusText, + headers: headers(xhr), + url: responseURL() + } + var body = 'response' in xhr ? xhr.response : xhr.responseText; + resolve(new Response(body, options)) + } + + xhr.onerror = function() { + reject(new TypeError('Network request failed')) + } + + xhr.open(request.method, request.url, true) + + if (request.credentials === 'include') { + xhr.withCredentials = true + } + + if ('responseType' in xhr && support.blob) { + xhr.responseType = 'blob' + } + + request.headers.forEach(function(value, name) { + xhr.setRequestHeader(name, value) + }) + + xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) + }) + } + self.fetch.polyfill = true +})(); \ No newline at end of file diff --git a/tv/js/vendor/loader.js b/tv/js/vendor/loader.js new file mode 100644 index 0000000..e0a193a --- /dev/null +++ b/tv/js/vendor/loader.js @@ -0,0 +1,86 @@ +var Loader = Loader || (function(){ + function Loader (readyCallback, view){ + this.assets = {}; + this.images = []; + this.readyCallback = readyCallback; + this.count = 0 + this.view = view + this.loaded = false + } + + // Register an asset as loading + Loader.prototype.register = function(s){ + this.assets[s] = false; + this.count += 1 + } + + // Signal that an asset has loaded + Loader.prototype.ready = function(s){ + window.debug && console.log("ready >> " + s); + + this.assets[s] = true; + if (this.loaded) return; + + this.view && this.view.update( this.percentRemaining() ) + + if (! this.isReady()) return; + + this.loaded = true; + if (this.view) { + this.view && this.view.finish(this.readyCallback) + } + else { + this.readyCallback && this.readyCallback(); + } + } + + // (boolean) Is the loader ready? + Loader.prototype.isReady = function(){ + for (var s in this.assets) { + if (this.assets.hasOwnProperty(s) && this.assets[s] != true) { + return false; + } + } + return true; + } + + // (float) Percentage of assets remaining + Loader.prototype.percentRemaining = function(){ + return this.remainingAssets() / this.count + } + + // (int) Number of assets remaining + Loader.prototype.remainingAssets = function(){ + var n = 0; + for (var s in this.assets) { + if (this.assets.hasOwnProperty(s) && this.assets[s] != true) { + n++; + // console.log('remaining: ' + s); + } + } + return n; + } + + // Preload the images in config.images + Loader.prototype.preloadImages = function(images){ + this.register("preload"); + for (var i = 0; i < images.length; i++) { + this.preloadImage(images[i]); + } + this.ready("preload"); + } + Loader.prototype.preloadImage = function(src){ + if (! src || src == "none") return; + var _this = this; + this.register(src); + var img = new Image(); + img.onload = function(){ + _this.ready(src); + } + img.src = src; + if (img.complete) img.onload(); + _this.images.push(img); + } + + return Loader; +})(); \ No newline at end of file diff --git a/tv/js/vendor/util.js b/tv/js/vendor/util.js new file mode 100644 index 0000000..6d3d5c4 --- /dev/null +++ b/tv/js/vendor/util.js @@ -0,0 +1,6 @@ +function random(){ return Math.random() } +function rand(n){ return (Math.random()*n) } +function randint(n){ return rand(n)|0 } +function randrange(a,b){ return a + rand(b-a) } +function randsign(){ return random() >= 0.5 ? -1 : 1 } +function choice(a){ return a[randint(a.length)] } \ No newline at end of file diff --git a/tv/js/ytmnd.js b/tv/js/ytmnd.js new file mode 100644 index 0000000..7617f06 --- /dev/null +++ b/tv/js/ytmnd.js @@ -0,0 +1,49 @@ +(function(){ + + var ytmnd = {} + var sites = [] + + ytmnd.init = function(names){ + var loader = new Loader(ytmnd.ready) + loader.register('init') + names.forEach(function(name){ + loader.register(name) + fetch(name + '.json').then(function(rows){ + sites = sites.concat(rows) + loader.ready(name) + }) + }) + loader.ready('init') + } + + ytmnd.ready = function(){ + var next = ytmnd.next() + } + + ytmnd.play = function(data){ + } + + ytmnd.stop = function(){ + } + + return ytmnd +})() + + +/* + simplified_info = { + 'domain': domain, + 'title': title, + 'username': username, + 'work_safe': work_safe, + 'bgcolor': bgcolor, + 'placement': placement, + 'zoom_text': zoom_text, + 'image': domain + "." + gif_type, + 'sound': domain + "." + wav_type, + 'image_type': gif_type, + 'sound_type': wav_type, + 'image_origin': image_origin, + 'sound_origin': sound_origin, + } +*/ \ No newline at end of file diff --git a/tv/js/zoomtext.js b/tv/js/zoomtext.js new file mode 100644 index 0000000..69c7c42 --- /dev/null +++ b/tv/js/zoomtext.js @@ -0,0 +1,56 @@ +var zoomtext = (function(){ + + var zoomtext = {} + + var el = document.querySelctor("#zoomtext") + + zoomtext.empty = function(){ + el.innerHTML = "" + } + + zoomtext.render = function(site){ + if (site.zoom_text.line_1.length == 0) { + return zoomtext.empty() + } + + var text = ytmnd_info['site']['zoom_text'] + + var offset = 100, rows = "" + if ("line_3" in zoom_text and zoom_text["line_3"].length > 0) { + rows += zoomtext.add_row( zoom_text['line_3'], offset, 500 ) + offset += 50 + } + if ("line_2" in zoom_text and zoom_text["line_2"].length > 0) { + rows += zoomtext.add_row( zoom_text['line_2'], offset, 250 ) + offset += 50 + } + if ("line_1" in zoom_text and zoom_text["line_1"].length > 0) { + rows += zoomtext.add_row( zoom_text['line_1'], offset, 500 ) + } + + el.innerHTML = rows.join("") + } + zoomtext.add_row = function(text, offset, top){ + var z_index, row_left, row_top, font_size, color + var row = "" + for (var i = 1; i < 51; i++) { + z_index = offset + i + row_left = i * 2 + row_top = top + i + font_size = i * 2 + if (i == 50) { + color = 0 + } + else { + color = i * 4 + } + + row += "
" + text + "
" + } + return row + } + + return zoomtext +})() \ No newline at end of file