summaryrefslogtreecommitdiff
path: root/extensions/firefox
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/firefox')
-rw-r--r--extensions/firefox/chrome.manifest2
-rw-r--r--extensions/firefox/chrome/background.js258
-rw-r--r--extensions/firefox/chrome/inject_on_load.js149
-rw-r--r--extensions/firefox/chrome/overlay.xul6
-rw-r--r--extensions/firefox/install.rdf20
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>