diff options
Diffstat (limited to 'extensions/firefox')
-rw-r--r-- | extensions/firefox/chrome.manifest | 2 | ||||
-rw-r--r-- | extensions/firefox/chrome/background.js | 258 | ||||
-rw-r--r-- | extensions/firefox/chrome/inject_on_load.js | 149 | ||||
-rw-r--r-- | extensions/firefox/chrome/overlay.xul | 6 | ||||
-rw-r--r-- | extensions/firefox/install.rdf | 20 |
5 files changed, 435 insertions, 0 deletions
diff --git a/extensions/firefox/chrome.manifest b/extensions/firefox/chrome.manifest new file mode 100644 index 0000000..1d59370 --- /dev/null +++ b/extensions/firefox/chrome.manifest @@ -0,0 +1,2 @@ +content vkpc chrome/ +overlay chrome://browser/content/browser.xul chrome://vkpc/content/overlay.xul diff --git a/extensions/firefox/chrome/background.js b/extensions/firefox/chrome/background.js new file mode 100644 index 0000000..2795a8c --- /dev/null +++ b/extensions/firefox/chrome/background.js @@ -0,0 +1,258 @@ +var VKPC = new function() { + +function init() { + window.addEventListener("load", function load(event) { + window.removeEventListener("load", load, false); + injectOnLoad(); + }, false); + + WSClient.go(); +} +function extend(dest, source) { + for (var i in source) { + dest[i] = source[i]; + } +} +function remove(element) { + element.parentNode.removeChild(element); +} +function createCData(data) { + var docu = new DOMParser().parseFromString('<xml></xml>', "application/xml"); + var cdata = docu.createCDATASection(data); + docu.getElementsByTagName('xml')[0].appendChild(cdata); + return cdata; +} +function getWebSocket() { + return window.WebSocket || window.MozWebSocket; +} +function print() { + var msgs = [], i, tmp; + for (i = 0; i < arguments.length; i++) { + if (arguments[i] instanceof Error) tmp = [arguments[i], arguments[i].stack]; + else tmp = arguments[i]; + msgs.push(tmp); + } + + try { + console.log.apply(console, msgs); + } catch(e) {} +} +function injectOnLoad() { + function onPageLoaded(e) { + var doc = e.originalTarget, loc = doc.location; + if (!loc.href.match(/^https?:\/\/vk.com\/.*$/)) return; + + doc.addEventListener("VKPCInjectedMessage", function(e) { + var target = e.target, json = JSON.parse(target.data || "{}"), doc = target.ownerDocument; + receiveMessage(json, doc, target); + }, false); + + var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader); + loader.loadSubScript("chrome://vkpc/content/inject_on_load.js", doc); + } + + var appcontent = document.getElementById("appcontent"); + if (appcontent) { + appcontent.addEventListener("DOMContentLoaded", onPageLoaded, true); + } +} +function receiveMessage(json, doc, target) { + switch (json.cmd) { + case "register": + Documents.add(doc); + break; + + case "params": + var id = json.id; + var obj = Injections.get(id); + if (obj) { + obj.addResponse(doc, json.data); + } + break; + } + + try { + remove(target); + } catch (e) {} +} +function executeCommand(cmd) { + var injId = Injections.getNextId(); + + var tabsCount = Documents.getCount(); + if (!tabsCount) return; + + var injResponses; + var onDone = function() { + var ok = {nowPlaying: null, lsSource: null, recentlyPlayed: null, active: null, last: null}; + var results = injResponses.results, lsSource = injResponses.lsSource; + + for (var i = 0; i < results.length; i++) { + var data = results[i].data, doc = results[i].tab; + ok.last = doc; + + if (data.havePlayer && (data.isPlaying || typeof data.trackId == 'string')) { + ok.recentlyPlayed = doc; + } + if (data.isPlaying) { + ok.nowPlaying = doc; + } + if (lsSource && lsSource == data.instanceId) { + ok.lsSource = doc; + } + if (data.isFocused) { + ok.active = doc; + } + } + injResponses.unregister(); + + var rightDoc = ok.nowPlaying || ok.lsSource || ok.recentlyPlayed || ok.active || ok.last; + if (rightDoc) { + Documents.sendToDoc(rightDoc, { + cmd: "audioCommand", + command: cmd + }); + } + }; + + injResponses = new InjectionResponses(injId, tabsCount, onDone); + + Documents.send({ + cmd: "getParams", + id: injId + }); +} + +var Injections = { + id: 0, + objs: {}, + getNextId: function() { + return ++this.id; + }, + get: function(id) { + return this.objs[id] || false; + }, + register: function(id, obj) { + this.objs[id] = obj; + }, + unregister: function(id) { + if (this.objs[id] !== undefined) delete this.objs[id]; + } +}; + +var Documents = { + list: [], + add: function(doc) { + this.cleanup(); + this.list.push(doc); + }, + cleanup: function() { + this.list = this.list.filter(function(t) { + return Object.prototype.toString.call(t) != '[object DeadObject]'; + }); + }, + send: function(json) { + var self = this; + this.cleanup(); + + this.list.forEach(function(doc) { + self.sendToDoc(doc, json); + }); + }, + sendToDoc: function(doc, json) { + var cdata = createCData(JSON.stringify(json)); + doc.getElementById('utils').appendChild(cdata); + + var evt = doc.createEvent("Events"); + evt.initEvent("VKPCBgMessage", true, false); + cdata.dispatchEvent(evt); + }, + getCount: function() { + this.cleanup(); + return this.list.length; + } +}; + +var WSClient = new function() { + var STATUS_NONE = 0, STATUS_OK = 1, STATUS_ERR = 2; + var _ws = getWebSocket(), ws; + var _status = STATUS_NONE; + var ping_timer, reconnect_timer; + + if (!_ws) return; + + function setTimers() { + ping_timer = setInterval(function() { + if (ws) ws.send("PING"); + }, 30000); + } + function unsetTimers() { + clearInterval(ping_timer); + } + + function connect() { + _status = STATUS_NONE; + + print("[connect]"); + ws = new _ws("ws://localhost:52178", "signaling-protocol"); + ws.onopen = function() { + _status = STATUS_OK; + setTimers(); + }; + ws.onerror = function() { + unsetTimers(); + if (_status != STATUS_ERR) { + _status = STATUS_ERR; + tryToReconnect(); + } + } + ws.onclose = function() { + unsetTimers(); + if (_status != STATUS_ERR) { + _status = STATUS_ERR; + tryToReconnect(); + } + }; + ws.onmessage = function(e) { + onCommand(e.data); + }; + } + function tryToReconnect() { + print("[tryToReconnect]"); + + clearTimeout(reconnect_timer); + reconnect_timer = setTimeout(connect, 5000); + } + function onCommand(msg) { + executeCommand(msg); + } + + this.go = function() { + connect(); + } +}; + +function InjectionResponses(id, count, callback) { + this.id = id; + this.results = []; + this.lsSource = null; + this.maxCount = count; + this.callback = callback || function() {}; + + Injections.register(this.id, this); +} +extend(InjectionResponses.prototype, { + addResponse: function(doc, response) { + this.results.push({tab: doc, data: response}); + if (!this.lsSource && response && response.lastInstanceId) this.lsSource = response.lastInstanceId; + if (this.results.length == this.maxCount) { + this.callback(); + } + }, + unregister: function() { + Injections.unregister(this.id); + } +}); + +init(); + +}; diff --git a/extensions/firefox/chrome/inject_on_load.js b/extensions/firefox/chrome/inject_on_load.js new file mode 100644 index 0000000..faa4dbf --- /dev/null +++ b/extensions/firefox/chrome/inject_on_load.js @@ -0,0 +1,149 @@ +(function() { + var isFocused = true; + + function vkAudio__getLastInstanceId() { + var id = null, pp = ls.get('pad_playlist'); + if (pp && pp.source) id = pp.source; + return id; + } + function vkAudio__getParams() { + var data = {}; + try { + var havePlayer = window.audioPlayer !== undefined; + var havePlaylist = havePlayer && !!padAudioPlaylist(); + + data = { + havePlayer: havePlayer, + havePlaylist: havePlaylist, + isPlaying: havePlayer && window.audioPlayer.player && !window.audioPlayer.player.paused(), + instanceId: window.curNotifier && curNotifier.instance_id, + trackId: havePlayer && audioPlayer.id, + lastInstanceId: vkAudio__getLastInstanceId() + }; + } catch(e) {} + + return data; + } + function vkAudio__getPlayFirstId() { + var id = currentAudioId() || ls.get('audio_id') || (window.audioPlaylist && audioPlaylist.start); + return id || null; + } + function vkAudio__executeAfterPadLoading(f) { + Pads.show('mus'); + window.onPlaylistLoaded = function() { + if (f) { + try { + f(); + } catch(e) {} + } + setTimeout(function() { + Pads.show('mus'); + }, 10); + } + } + function vkAudio__next() { + console.log("Next"); + window.audioPlayer && audioPlayer.nextTrack(true, !window.audioPlaylist); + } + function vkAudio__prev() { + console.log("Prev"); + window.audioPlayer && audioPlayer.prevTrack(true, !window.audioPlaylist); + } + function vkAudio__playPause() { + console.log("PlayPause"); + if (!window.audioPlayer || !padAudioPlaylist()) { + stManager.add(['audioplayer.js'], function() { + vkAudio__executeAfterPadLoading(function() { + var plist = padAudioPlaylist(), id = vkAudio__getPlayFirstId(); + if (id) { + playAudioNew(id); + } else if (plist && plist.start) { + playAudioNew(plist.start); + } + }); + }); + } else { + if (window.audioPlayer && audioPlayer.player) { + if (audioPlayer.player.paused()) { + audioPlayer.playTrack(); + } else { + audioPlayer.pauseTrack(); + } + } + } + } + + function createCData(data) { + var docu = new DOMParser().parseFromString('<xml></xml>', "application/xml"); + var cdata = docu.createCDATASection(data); + docu.getElementsByTagName('xml')[0].appendChild(cdata); + return cdata; + } + function sendMessage(json) { + // Fucking crazy. + json.bg = 1; + + var cdata = createCData(JSON.stringify(json)); + document.getElementById('utils').appendChild(cdata); + + var evt = document.createEvent("Events"); + evt.initEvent("VKPCInjectedMessage", true, false); + cdata.dispatchEvent(evt); + } + function remove() { + remove.parentNode.removeChild(remove); + } + function receiveCommand(e) { + var target = e.target, json = JSON.parse(target.data || "{}"); + + switch (json.cmd) { + case "getParams": + var params = vkAudio__getParams(); + params.isFocused = isFocused; + sendMessage({ + data: params, + cmd: "params", + id: json.id + }); + break; + + case "audioCommand": + switch (json.command) { + case "play": + case "pause": + vkAudio__playPause(); + break; + + case "next": + vkAudio__next(); + break; + + case "prev": + vkAudio__prev(); + break; + } + break; + } + + try { + _VKPC.remove(target); + } catch (e) {} + } + + window.addEventListener("DOMContentLoaded", function(e) { + if (window.vk) { + document.addEventListener("VKPCBgMessage", receiveCommand, false); + + sendMessage({ + cmd: "register" + }); + } + }); + + window.addEventListener("focus", function(e) { + isFocused = true; + }, false); + window.addEventListener("blur", function(e) { + isFocused = false + }, false); +})(); diff --git a/extensions/firefox/chrome/overlay.xul b/extensions/firefox/chrome/overlay.xul new file mode 100644 index 0000000..7cf7229 --- /dev/null +++ b/extensions/firefox/chrome/overlay.xul @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!DOCTYPE overlay > +<overlay id="vkpc-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://vkpc/content/background.js"/> +</overlay> diff --git a/extensions/firefox/install.rdf b/extensions/firefox/install.rdf new file mode 100644 index 0000000..30690ef --- /dev/null +++ b/extensions/firefox/install.rdf @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + <Description about="urn:mozilla:install-manifest"> + <em:id>vkpc@ch1p.org</em:id> + <em:name>VK Player Controller Client</em:name> + <em:version>0.1</em:version> + <em:type>2</em:type> + <em:creator>ch1p</em:creator> + <em:description>Firefox client for VK Player Controller app.</em:description> + + <em:targetApplication> + <Description> + <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> + <em:minVersion>4.0</em:minVersion> + <em:maxVersion>30.0</em:maxVersion> + </Description> + </em:targetApplication> + </Description> +</RDF> |