snowl/content/subscribe.js

292 строки
9.9 KiB
JavaScript

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Snowl.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Myk Melez <myk@mozilla.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
// modules that come with Firefox
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
// modules that are generic
Cu.import("resource://snowl/modules/log4moz.js");
Cu.import("resource://snowl/modules/Observers.js");
Cu.import("resource://snowl/modules/URI.js");
// Snowl-specific modules
Cu.import("resource://snowl/modules/service.js");
Cu.import("resource://snowl/modules/datastore.js");
Cu.import("resource://snowl/modules/feed.js");
Cu.import("resource://snowl/modules/twitter.js");
window.addEventListener("load", function() { Subscriber.init() }, false);
function SubscriptionListener(subject, topic, data) {
// FIXME: figure out why the SubscriptionListener hangs around and gets called
// sometimes after the page has been unloaded and Subscriber is not defined.
if (typeof Subscriber == "undefined") {
Log4Moz.Service.getLogger("Snowl.SubscriptionListener").warn(
"called without Subscriber; subject: " + subject + "; topic: " + topic +
"; data: " + data);
return;
}
let source = Subscriber.feed;
// Don't track the status of subscriptions happening in other windows/tabs.
if (subject != source)
return;
let statusBox = document.getElementById("statusBox");
let statusMessage = document.getElementById("statusMessage");
function setStatus(code, message) {
statusBox.setAttribute("status", code);
while (statusMessage.hasChildNodes())
statusMessage.removeChild(statusMessage.firstChild);
// Append a child node so it wraps if it's too long to fit on one line.
// XXX Is there something we can do so the UI doesn't resize midstream?
statusMessage.appendChild(document.createTextNode(message));
}
let identity = source.name ||
(source.humanURI ? source.humanURI.spec : null) ||
(source.machineURI ? source.machineURI.spec : null) ||
"unnamed source";
switch(topic) {
case "snowl:subscribe:connect:start":
setStatus("active", "Connecting to " + identity);
break;
case "snowl:subscribe:connect:end":
let code, message;
if (data == "duplicate") {
code = "error";
message = "Already subscribed to " + identity;
}
else if (data < 200 || data > 299) {
code = "error";
message = "Error connecting to " + identity;
if (data == 401) {
message += ": your credentials were not accepted. Please check " +
"your username and password and try again.";
}
}
else {
// Under most circumstances, this message will be replaced immediately
// by the "getting messages" message.
code = "complete";
message = "Connected to " + identity;
}
setStatus(code, message);
break;
case "snowl:subscribe:get:start":
setStatus("active", "Getting messages for " + identity);
break;
case "snowl:subscribe:get:progress":
break;
case "snowl:subscribe:get:end":
setStatus("complete", "You have subscribed to " + identity);
break;
}
}
let Subscriber = {
_log: Log4Moz.Service.getLogger("Snowl.Subscriber"),
//**************************************************************************//
// Initialization & Destruction
init: function() {
Observers.add(SubscriptionListener, "snowl:subscribe:connect:start");
Observers.add(SubscriptionListener, "snowl:subscribe:connect:end");
Observers.add(SubscriptionListener, "snowl:subscribe:get:start");
Observers.add(SubscriptionListener, "snowl:subscribe:get:progress");
Observers.add(SubscriptionListener, "snowl:subscribe:get:end");
// Parse URL parameters
let paramString = window.location.search.substr(1);
let params = {};
for each (let param in paramString.split("&")) {
let [name, value] = param.split("=");
if (value)
params[name] = decodeURIComponent(value);
else
params[name] = value;
}
if (params.feed) {
document.getElementById("locationTextbox").value = params.feed;
this.subscribeFeed();
}
else if (params.tab) {
let tabbox = document.getElementById("tabbox");
switch (params.tab) {
// The feed tab is selected by default.
case "twitter":
tabbox.selectedTab = document.getElementById("twitterTab");
break;
case "opml":
tabbox.selectedTab = document.getElementById("opmlTab");
break;
}
}
},
destroy: function() {
Observers.remove(SubscriptionListener, "snowl:subscribe:connect:start");
Observers.remove(SubscriptionListener, "snowl:subscribe:connect:end");
Observers.remove(SubscriptionListener, "snowl:subscribe:get:start");
Observers.remove(SubscriptionListener, "snowl:subscribe:get:progress");
Observers.remove(SubscriptionListener, "snowl:subscribe:get:end");
},
//**************************************************************************//
// Event Handlers
subscribeFeed: function() {
let uri = URI.get(document.getElementById("locationTextbox").value);
let feed = new SnowlFeed(null, null, null, uri);
this._subscribe(feed);
},
importOPML: function() {
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
fp.init(window, "Import OPML", Ci.nsIFilePicker.modeOpen);
fp.appendFilter("OPML Files", "*.opml");
fp.appendFilters(Ci.nsIFilePicker.filterXML);
fp.appendFilters(Ci.nsIFilePicker.filterAll);
let rv = fp.show();
if (rv != Ci.nsIFilePicker.returnOK)
return;
// FIXME: use a file utility to open the file instead of XMLHttpRequest.
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance(Ci.nsIXMLHttpRequest);
request.open("GET", fp.fileURL.spec, false);
// Since the file probably ends in .opml, we have to force XHR to treat it
// as XML by overriding the MIME type it would otherwise select.
request.overrideMimeType("text/xml");
request.send(null);
let xmlDocument = request.responseXML;
let outline = xmlDocument.getElementsByTagName("body")[0];
this._importOutline(outline);
},
showTwitterPassword: function() {
if (document.getElementById("showTwitterPassword").checked)
document.getElementById("twitterPassword").removeAttribute("type");
else
document.getElementById("twitterPassword").setAttribute("type", "password");
},
subscribeTwitter: function() {
let credentials = {
username: document.getElementById("twitterUsername").value,
password: document.getElementById("twitterPassword").value,
remember: document.getElementById("rememberTwitterPassword").checked
};
let twitter = new SnowlTwitter();
// FIXME: call this "source" instead of "feed".
this.feed = twitter;
twitter.subscribe(credentials);
},
//**************************************************************************//
// OPML Import
_importOutline: strand(function(aOutline) {
// If this outline represents a feed, subscribe the user to the feed.
let uri = URI.get(aOutline.getAttribute("xmlUrl"));
if (uri) {
let name = aOutline.getAttribute("title") || aOutline.getAttribute("text");
let feed = new SnowlFeed(null, name || "untitled", uri);
let future = new Future();
this._subscribe(feed, future.fulfill);
yield future.result();
}
if (aOutline.hasChildNodes()) {
let children = aOutline.childNodes;
for (let i = 0; i < children.length; i++) {
let child = children[i];
// Only deal with "outline" elements; ignore text, etc. nodes.
if (child.nodeName != "outline")
continue;
yield this._importOutline(child);
}
}
}),
_subscribe: strand(function(feed, callback) {
// Store a reference to the feed to which we are currently subscribing
// so the progress listener can filter out events for some other feed.
this.feed = feed;
if (SnowlService.hasSource(feed.machineURI.spec)) {
Observers.notify(this.feed, "snowl:subscribe:connect:end", "duplicate");
return;
}
let future = new Future();
feed.subscribe(future.fulfill);
yield future.result();
this.feed = null;
if (callback)
callback();
})
};