/* * RdioTab is an EventEmitter, emitting 'ready', and 'closed' events * respetively when: * - a tab which URL matches rdio.com's is opened, and ready (DOM loaded...) * - a tab which URL matches rdio.com's is closed. */ const tabs = require('tabs'); const { EventEmitter } = require('events'); const rdioURLRegex = /https?:\/\/(www\.)?rdio\.com\/?/; const RdioTab = EventEmitter.compose({ constructor: function() { var self = this; tabs.on('ready', function (tab) { if ( tab.url.match(rdioURLRegex) ) { self._emit('ready', tab); } }); tabs.on('close', function (tab) { if ( tab.url.match(rdioURLRegex) ) { self._emit('closed', tab); } }); } }); exports.rdioTab = new RdioTab();
var SearchEngines = EventEmitter.compose({ _emit: EventEmitter.required, on: EventEmitter.required, _debug : function _debug(isDebug) { if (isDebug) { this.geolocation = true; } }, // Cached list of engines _engines : null, constructor : function SearchEngines() { if (!storage.engines) { // list of currently used engines according to user order storage.engines = this._engines = []; // list of found engines according to how often they are seen storage.other = []; this._first_run(); } else { this._engines = storage.engines.map(function (engine) { //console.log("engine", JSON.stringify(engine)); return this._engineFromJSON(engine); }.bind(this)); //storage.other.forEach(function(e) { console.log(JSON.stringify(e)); }); this._upgrade(); } this.on("defaults.added", function (engine) { StatisticsReporter.send("defaults.added", engine); }); this.on("defaults.removed", function (engine) { StatisticsReporter.send("defaults.removed", engine); }); this.on("others.added", function (engine) { StatisticsReporter.send("others.added", engine); }); this.on("others.removed", function (engine) { StatisticsReporter.send("others.removed", engine); }); ObserverService.add("search:debug", this._debug.bind(this), this); SearchEnginesCollector.on("engine", this._collector.bind(this)); SimpleStorage.on("OverQuota", this._overQuota.bind(this)); require("unload").ensure(this); }, // this is designed to run after the add-on has reinitialized and there are // some slight data issues we need to deal with _upgrade : function _upgrade() { var yelp = this.get("http://www.yelp.com/opensearch"); if (yelp.suggestionURL !== "http://www.yelp.com/search_suggest/json?prefix={searchTerms}&src=firefox&loc={geo:name}") { yelp.suggestionURL = "http://www.yelp.com/search_suggest/json?prefix={searchTerms}&src=firefox&loc={geo:name}"; this._update(yelp); } }, // The first run initialization to pull in some default engines from Firefox _first_run : function _first_run() { var BrowserSearchEngines = require("browser-search-engine") .BrowserSearchEngines; // Add in some suggestions for engines we know work but aren't listed BrowserSearchEngines.get("Amazon.com").addSuggest("http://completion.amazon.com/search/complete?method=completion&search-alias=aps&mkt=1&q={searchTerms}"); // Our default order of engines as an array of ids var order = [ "https://www.google.com/", "http://d2lo25i6d3q8zm.cloudfront.net/browser-plugins/AmazonSearchSuggestionsOSD.Firefox.xml", "http://www.yelp.com/opensearch", "http://en.wikipedia.org/w/opensearch_desc.php" ]; // Add LinkedIn to the list of other engines this.others.add(new SearchEngine("http://www.linkedin.com/search/fpsearch", "LinkedIn", "http://www.linkedin.com/search/fpsearch?keywords={searchTerms}", "http://www.linkedin.com/ta/federator?query={searchTerms}&types=mynetwork,company,group,sitefeature,skill", "http://static01.linkedin.com/scds/common/u/img/favicon_v3.ico")); // Add Yelp to our list of other engines // We'll try to add this to the defaults afterward this.others.add(new SearchEngine("http://www.yelp.com/opensearch", "Yelp", "http://www.yelp.com/search?find_desc={searchTerms}&src=firefox&find_loc={geo:name}", "http://www.yelp.com/search_suggest/json?prefix={searchTerms}&src=firefox&loc={geo:name}", "http://media2.ak.yelpcdn.com/static/201012161623981098/img/ico/favicon.ico")); // Provide a mapping of the OpenSearch descriptors to our default engines // (which either don't have them or are incorrect) var sites = { "Wikipedia (en)" : "http://en.wikipedia.org/w/opensearch_desc.php", "Amazon.com" : "http://d2lo25i6d3q8zm.cloudfront.net/browser-plugins/AmazonSearchSuggestionsOSD.Firefox.xml" }; BrowserSearchEngines.getVisible().forEach(function (engine) { var queryURL = decodeURIComponent(engine.getSubmission("{searchTerms}")), suggestionURL = decodeURIComponent(engine.getSuggestion("{searchTerms}") || ""), id = sites[engine.name] || engine.searchForm, se = new SearchEngine(id, engine.name, queryURL, suggestionURL, engine.icon); if (order.indexOf(id) >= 0) { this.defaults.add(se); } else { this.others.add(se); } }, this); // set our intial default sort order this.defaults.sort(order); StatisticsReporter.once("allowed", function () { // Ask to move Yelp to to the defaults once we've // gotten permission to send statistics this.defaults.add(this.get("http://www.yelp.com/opensearch")) .then(function (added) { this.defaults.sort(order); }.bind(this)); }.bind(this)); }, get defaults() { var self = this; return { remove : function remove(engine) { if (!(engine instanceof SearchEngine)) { engine = self._engineFromJSON(engine); } var index = -1, result = null; self._engines.every(function (e, i) { if (engine.equals(e)) { index = i; return false; } return true; }); if (index >= 0) { result = self._engines.splice(index, 1)[0]; if (result) { // Save the engine in the other engines self.others.add(result); // Save our new default engines storage.engines = self._engines; // send out the removed event self._emit("defaults.removed", engine); } } // If we're removing an engine that used geolocation // lets double check that we still need it running if (engine.usesGeoLocation) { Geolocation.allowed = this.usingGeolocation(); } return engine; }, add : function add(engine) { if (!(engine instanceof SearchEngine)) { engine = self._engineFromJSON(engine); } var d = defer(), promise = d.promise, resolve = d.resolve, reject = d.reject; // if we already have this engine, don't add it again if (self._engines.some(function (e) { return engine.equals(e); })) { resolve(engine); return promise; } var actuallyAdd = function (engine) { // Only add this engine if it doesn't already exist if (!self._engines.some(function (e) { return engine.equals(e); })) { self._engines.push(engine); } // Save new default engines to storage storage.engines = self._engines; // Remove this engine from the others list if it exists self.others.remove(engine); // Send out the add event self._emit("defaults.added", engine); resolve(engine); }; if (engine.usesGeoLocation) { GeoPermissionPanel.port.once("click", function click(data) { searchbar.getSearchTextBox().focus(); GeoPermissionPanel.hide(); if (data === "ok") { actuallyAdd(engine); // If geolocation isn't alredy turned on we can turn it on now if (!Geolocation.allowed) { Geolocation.allowed = true; } } else { // try to add this to the others list if it doesn't exist self.others.add(engine); reject(engine); } }); // Our permission panel will overrun others who are asking GeoPermissionPanel.port.emit("engine", engine); GeoPermissionPanel.show(searchbar.getSearchTextBox()); return promise; } else { // If this engine doesn't require geolocation just add it actuallyAdd(engine); return promise; } }, get : function get(id) { var engine = null; self._engines.every(function (e, i) { if (e.id === id) { engine = e; return false; } return true; }); return engine; }, // Returns the list of all default engines get all() { return self._engines; }, // Examine the ids of our new list order and sort the engines by that sort : function sort(newOrder) { self._engines.sort(function (a, b) { return newOrder.indexOf(a.id) > newOrder.indexOf(b.id); }); // There is a new order in town. Regulators! Mount Up! self._emit("defaults.sorted", self._engines.map(function (engine) { return engine.id; })); }, usingGeolocation : function usingGeolocation() { return self._engines.some(function (engine) { return engine.usesGeoLocation; }); } }; }, // Others doesn't check that items are in the default list get others() { var self = this; return { remove : function remove(engine) { if (!(engine instanceof SearchEngine)) { engine = self._engineFromJSON(engine); } storage.other.every(function (e, i) { if (e.id === engine.id) { // remove the engine from our others list storage.other.splice(i, 1); return false; } return true; }); self._emit("others.removed", engine); return engine; }, add : function add(engine) { if (!(engine instanceof SearchEngine)) { engine = self._engineFromJSON(engine); } // Only add this engine if it doesn't already exist if (!storage.other.some(function (e) { return e.id === engine.id; })) { storage.other.push(engine); } self._emit("others.added", engine); }, get : function get(id) { var engine = null; storage.other.every(function (e) { if (e.id === id) { engine = self._engineFromJSON(e); return false; } return true; }); return engine; }, get all() { return storage.other; } }; }, _engineFromJSON : function _engineFromJSON(engine) { if (engine === null) { return null; } var e = null; try { e = new SearchEngine(engine.id, engine.name, engine.queryURL, engine.suggestionURL, engine.icon, engine.type); } catch (ex) { console.error(ex); console.log("ENGINE:", JSON.stringify(engine)); } return e; }, // Delete an engine from defaults and others remove : function remove(engine) { this.defaults.remove(engine); this.others.remove(engine); this._emit("removed", engine); return engine; }, // Get an engine no matter what list it's in get : function get(id) { var result = null; result = this.defaults.get(id); if (result) { return result; } return this.others.get(id); }, _update : function _update(engine) { if (this._engines.some(function (e, i, a) { if (e.id === engine.id) { e = engine; return true; } return false; } )) { storage.engines = this._engines; return; } storage.other.every(function (e, i, a) { if (engine.id === e.id) { e = engine; return false; } return true; }); }, /** * Listener function for the SearchEnginesCollector module * * This listener should have been set in the constructor * * @param engine {Object} * an object that represents and engine pulled from the site offering it * See SearchEnginesCollector._parse * * Existing search engines are updated with new data * New search engines are added with the FOUND_TAG * */ _collector : function _collector(collected) { var engine = this.get(collected.url.toString()); //console.log("_collector", collected.url); // if this engine already exists lets just update our records for it // SECURITY : this should have some kind of security review here if (engine) { // DATA should be tracking this update to track when sites add suggestions if (engine.name !== collected.name || engine.queryURL !== collected.queryURL || engine.suggestionURL !== collected.suggestionURL || engine.icon !== collected.icon) { //console.log(engine.name," != ",collected.name, "\n", // engine.queryURL," != ",collected.queryURL, "\n", // engine.suggestionURL," != ",collected.suggestionURL); engine.name = collected.name; engine.icon = collected.icon; // XXX for now don't let search engines update their URLs, // only name and icon // we'll send out the fact that they wanted to so we can examine // that need from the cloud //engine.queryURL = collected.queryURL; //engine.suggestionURL = collected.suggestionURL; StatisticsReporter.send("update", engine); this._update(engine); } } else { this.others.add(new SearchEngine(collected.url, collected.name, collected.queryURL, collected.suggestionURL, collected.icon)); } }, // XXX Totally untested _overQuota: function _overQuota() { while (SimpleStorage.quotaUsage > 1) { // remove all the items from other starting with the least seen ones if (storage.other.length > 0) { storage.other.pop(); } else { // if we don't have any other items left remove used engines storage.engines.pop(); } } console.warn("_overQuota"); }, unload : function unload(reason) { storage.engines = this._engines; ObserverService.remove("search:debug", this._debug, this); SearchEnginesCollector.removeListener("engine", this._collector); SimpleStorage.removeListener("OverQuota", this._overQuota); } })();
let Bindings = EventEmitter.compose({ _bindings: undefined, _cookieManager: undefined, length: 0, constructor: function(options) { this._cookieManager = options.cookieManager; this.clear(); unload.ensure(this, "teardown"); }, teardown: function() { this.clear(); this._cookieManager = null; this._bindings = null; }, add: function(binding) { let host = binding.host; this.remove(host); let name = binding.bound_to.name; // Save this off so that we can remove it later. binding.remove = this.remove.bind(this, host); this._cookieManager.watch(host, name, binding.remove); this._bindings[host] = binding; this.length++; }, get: function(host) { let binding = this._bindings[host]; return binding; }, remove: function(host) { let binding = this.get(host); if(binding) { this._bindings[host] = null; delete this._bindings[host]; this.length--; this._cookieManager.unwatch(host, binding.bound_to.name, binding.remove); this._emit("remove", binding); } return binding; }, clear: function() { for(let host in this._bindings) { this.remove(host); } this._bindings = {}; this.length = 0; } });
const LoginManager = EventEmitter.compose({ constructor: function(options) { let {document} = options; this.box = createNode(document, "box", { id: "identity-session-box" }); this.signIn = createNode(document, "label", { id: "identity-session-signin", value: "Sign in", parentNode: this.box }); this.signIn.addEventListener("click", this._emit.bind(this, "login"), false); this.userInfo = createNode(document, "label", { id: "identity-session-userinfo", value: "", parentNode: this.box }); this.userInfo.addEventListener("click", this._emit.bind(this, "userinfo"), false); this.image = createNode(document, "image", { id: "identity-session-icon", parentNode: this.box }); this.image.hide(); this.siteName = createNode(document, "label", { id: "identity-session-site", value: "site name", parentNode: this.box }); this.siteName.hide(); let insertBefore = document.getElementById("notification-popup-box"); if(insertBefore) { insertBefore.parentNode.insertBefore(this.box, insertBefore); } }, show: function(host) { this.box.show(); this.siteName.setAttribute("value", host); }, login: function(host) { this.show(host); this.signIn.show(); this.userInfo.hide(); }, loggedIn: function(username, host) { this.show(host); this.signIn.hide(); this.userInfo.setAttribute("value", username); this.userInfo.show(); }, none: function() { this.box.hide(); }, setURL: function(url) { this.siteName.setAttribute("value", url); } });
var RdioWebApp = EventEmitter.compose({ constructor: function constructor (rdioTab) { this.state = { playerReady: false }; this._tabEvents(rdioTab); return this; }, /* * Wether we're ready to accept commands * @return {Boolean} */ isReady: function isReady () { return this.state.playerReady; }, /* * FIXME should accept a callback triggered through worker confirmation. * @return {Boolean} */ playPause: function playPause () { if ( ! this.state.playerReady ) { return false; } this.worker.port.emit('playPause'); return true; }, volume: function (value, cb) { // Get volume if ( 'function' == typeof value ) { this.worker.port.once('volume:got', value); this.worker.port.emit('volume:get'); // Set volume } else { if ( cb ) { this.worker.port.once('volume:set', cb); } this.worker.port.emit('volume:update', value); } return this; }, /* * Initialize browser's content-script when tabs on rdio.com are loaded. */ _tabEvents: function _tabEvents (rdioTab) { var self = this; rdioTab.on("closed", function (tab) { self.setUnavailable(); }); rdioTab.on("ready", function (tab) { console.debug("Loaded: " + tab.url); self.playerReady = false; self._attachWorker(tab); }); return self; }, /* * Attach worker's content-script on tag, and bind worker-events * * @param {Tab} browser tab */ _attachWorker: function _attachWorker (tab) { var self = this; // Attaching content-script this.worker = tab.attach({ contentScriptWhen: 'end', contentScriptFile: data.url('worker.js') }); // Check for player on the latest attached tab... this.worker.port.on('ready', function () { return self.setAvailable(); }); this.worker.port.on('noplayer', function () { return self.setUnavailable(); }); return this; }, // Update add-on state, player unavailable setUnavailable: function setUnavailable () { this.state.playerReady = false; this._emit('closed'); return this; }, // Update add-on state, player available setAvailable: function setAvailable () { this.state.playerReady = true; this._emit('ready'); return this; } });
const SessionDisplay = EventEmitter.compose({ constructor: function(options) { let {document, session} = options; this.session = session; this.document = document; createUI.call(this, document); attachSessionEvents.call(this); let identityBox = getIdentityBox.call(this); this.origLeft = identityBox && identityBox.style.paddingLeft; this.hide(); unload.ensure(this, 'teardown'); }, teardown: function() { if (this.box && this.box.parentNode) { this.box.parentNode.removeChild(this.box); } let identityBox = getIdentityBox.call(this); if (identityBox) { identityBox.style.paddingLeft = this.origLeft; } }, show: function() { this.box.show(); let identityBox = getIdentityBox.call(this); if (identityBox) { identityBox.style.paddingLeft = "10px"; } this._emit("show"); }, hide: function() { this.box.hide(); let identityBox = getIdentityBox.call(this); if (identityBox) { identityBox.style.paddingLeft = this.origLeft; } this._emit("hide"); }, setStatus: setStatus });
const {Cu} = require("chrome"); const self = require("self"); const helper = require("helpers"); const {EventEmitter} = require("events"); const search = require("search"); let sr = new search.search(); /* this is used to communicate with the async search script*/ let EventWorker = EventEmitter.compose({ postMessage: function(data) { //console.log("emitting: " + JSON.stringify(data)); this._emit("searchresults", data); } }); let worker = EventWorker(); /* worker = { postMessage: function(){}, on: function(){} } */ /**/ exports.getEventWorker = function() { return worker; } exports.predictSearch = function(latestTitle) { let RE_NOUN_VERB = new RegExp(/(^NN)|(^VB)|(^JJ)/);
var SearchEnginesCollector = EventEmitter.compose({ _emit: EventEmitter.required, on: EventEmitter.required, _allowed : simpleprefs.prefs[ALLOW_COLLECT_PREF], get allowed() { return this._allowed; }, set allowed(allow) { this._allowed = simpleprefs.prefs[ALLOW_COLLECT_PREF] = allow; }, get isOk() { // Do not collect links when the user is in private browsing mode if (PrivateBrowsing.isActive) { console.debug("PrivateBrowsing enabled - not collecting search engines"); } if (!this._allowed) { console.debug("Search Engines Collector is not enabled"); } return (!PrivateBrowsing.isActive && this._allowed); }, _onallowed : function _onallowed(subject) { this._allowed = simpleprefs.prefs[ALLOW_COLLECT_PREF]; this._emit("allowed", this._allowed); }, constructor : function SearchEnginesCollector() { simpleprefs.on(ALLOW_COLLECT_PREF, this._onallowed.bind(this), this); require("unload").ensure(this); }, unload: function _destructor() { this._removeAllListeners(); simpleprefs.removeListener(ALLOW_COLLECT_PREF, this._onallowed); }, /** * Receives <link> elements from the PageMod * * **Note** Will not continue if `PrivateBrowsing.isActive` returns true to * avoid collecting OpenSearch description documents while the users is in * private browsing mode. * * @param links {Array} * an array of link objects * where objects are { site : document.URL, * name : <link title>, * opensearch : <link href> } * * emits the "link" event * */ collect : function collect(links) { if (!this.isOk) { return null; } links.forEach(function (link, i, a) { // site (may) = "http://google.com/search/path/?q=" var site = URL.URL(link.site); // host = "http://google.com/" var host = link.site.replace(site.path, ""); // opensearch URL could be relative so use the host as a base // example: href="/search.xml" var href = URL.URL(link.opensearch, host); // emit that we found a URL for anyone who wants to listen this._emit("link", host, href); // retrieve this engine from the URL this.getEngineByXMLURL(href.toString()); }.bind(this)); return links; }, /** * Retrieves and parses OpenSearch engine XML descriptors * * @param url {String} * Absolute URL pointing to an OpenSearch XML file * * emits the "engine" event * */ getEngineByXMLURL : function getEngineByXMLURL(url) { var request = new xhr.XMLHttpRequest(), collector = this; request.open('GET', url, true); // Force document parsing in case we get a weird response type request.overrideMimeType("text/xml"); request.onreadystatechange = function (aEvt) { if (request.readyState === 4) { if (request.status === HTTP_OK || request.status === HTTP_LOCAL_FILE) { if (request.responseXML) { if (collector.isOk) { collector._buildEngine(url, request.responseXML); } } } } }; request.send(null); }, /** * Wraps up the Async download of the Open Search image */ _buildEngine : function _buildEngine(url, doc) { var collector = this, engine = this._parse(url, doc), href = null; try { href = URL.URL(engine.icon); fetchImageDataASync(href.toString()).then(function (datauri) { engine.icon = datauri; collector._emit("engine", engine); }); } catch (e) { // if there's a problem with our engine icon lets just send this one off // without an icon collector._emit("engine", engine); } }, /** * Lightweight parsing of an XML OpenSearch description document * * @param url {String} * Absolute URL pointing to an OpenSearch XML file * * @param doc {Object} * XML document object from request.responseXML xhr call * * @returns {Object} * { "url", "name", "queryURL", "suggestionURL", "icon" } * */ _parse : function _parse(url, doc) { var opensearch = { "url" : url, "name" : "", "queryURL" : "", "suggestionURL" : "", "icon" : "" }, urls = doc.getElementsByTagName("Url"), queryObj = null, queryMap = function (key) { return (key + "=" + queryObj[key]); }; for (var i = 0, item; item = urls.item(i); i += 1) { //var method = item.getAttribute("method"); var template = URL.URL(item.getAttribute(ATTR_TEMPLATE)), type = item.getAttribute("type"), params = item.getElementsByTagName(TAG_PARAM), split = template.path.split("?"), path = split[0], query = split[1]; queryObj = querystring.parse(query); for (var j = 0, p; p = params.item(j); j += 1) { queryObj[p.getAttribute(ATTR_NAME)] = p.getAttribute(ATTR_VALUE); } // remove the original path from our template and make this a string now template = template.toString().replace(template.path, ""); // add back the path with query string template += path + "?" + Object.keys(queryObj).map(queryMap).join("&"); if (URLTYPE_SEARCH_HTML === type) { opensearch.queryURL = template; } else if (URLTYPE_SUGGEST_JSON === type) { opensearch.suggestionURL = template; } } try { opensearch.name = doc.getElementsByTagName(TAG_SHORT_NAME) .item(0).textContent; } catch (noname) { console.error("engine has no name", noname); } try { opensearch.icon = doc.getElementsByTagName(TAG_IMAGE).item(0).textContent; } catch (noicon) { // try to get the favicon using the Favicon service // falls back to the default icon if none is found opensearch.icon = getFavicon(url); } return opensearch; } })();