home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Freelog 112
/
FreelogNo112-NovembreDecembre2012.iso
/
Multimedia
/
Songbird
/
Songbird_2.0.0-2311_windows-i686-msvc8.exe
/
components
/
sbMediaPageManager.js
< prev
next >
Wrap
Text File
|
2012-06-08
|
17KB
|
534 lines
/*
*=BEGIN SONGBIRD GPL
*
* This file is part of the Songbird web player.
*
* Copyright(c) 2005-2010 POTI, Inc.
* http://www.songbirdnest.com
*
* This file may be licensed under the terms of of the
* GNU General Public License Version 2 (the ``GPL'').
*
* Software distributed under the License is distributed
* on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the GPL for the specific language
* governing rights and limitations.
*
* You should have received a copy of the GPL along with this
* program. If not, go to http://www.gnu.org/licenses/gpl.html
* or write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*=END SONGBIRD GPL
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Ce = Components.Exception;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://app/jsmodules/ArrayConverter.jsm");
Cu.import("resource://app/jsmodules/RDFHelper.jsm");
Cu.import("resource://app/jsmodules/sbProperties.jsm");
function MediaPageManager() {
}
MediaPageManager.prototype = {
classDescription: "Songbird MediaPage Manager",
classID: Components.ID("{e63463d0-357c-4035-af33-db670ee1b7f2}"),
contractID: "@songbirdnest.com/Songbird/MediaPageManager;1",
QueryInterface: XPCOMUtils.generateQI([Ci.sbIMediaPageManager]),
_pageInfoArray: [],
// PageInfo objects for the fallback mediapages. Set by _registerDefaults.
_defaultPlaylistPage: null,
_defaultFilteredPlaylistPage: null,
registerPage: function(aName, aURL, aIcon, aMatchInterface) {
// Make sure we don't already have a page with
// the given url
var pageInfo;
for each (pageInfo in this._pageInfoArray) {
if (pageInfo.contentUrl == aURL) {
throw new Error("Page URL already registered: " + aURL);
}
}
// Make a PageInfo object
pageInfo = {
get contentTitle() {
return aName;
},
get contentUrl() {
return aURL;
},
get contentIcon() {
return aIcon;
},
get matchInterface() {
return aMatchInterface;
},
QueryInterface: function(iid) {
if (!iid.equals(Ci.sbIMediaPageInfo) &&
!iid.equals(Ci.nsISupports))
throw Cr.NS_ERROR_NO_INTERFACE;
return this;
}
};
// Store the page into our page array
this._pageInfoArray.push(pageInfo);
// And return it to the caller so he may use that to unregister
return pageInfo;
},
unregisterPage: function(aPageInfo) {
// If unregistering the default pages, must remove shortcut pointers
if (this._defaultFilteredPlaylistPage &&
this._defaultFilteredPlaylistPage.contentUrl == aPageInfo.contentUrl) {
this._defaultFilteredPlaylistPage = null;
} else if (this._defaultPlaylistPage &&
this._defaultPlaylistPage.contentUrl == aPageInfo.contentUrl) {
this._defaultPlaylistPage = null;
}
// Search the array for the matching page
for (var i in this._pageInfoArray) {
if (aPageInfo.contentUrl == this._pageInfoArray[i].contentUrl) {
// found, remove it and stop
this._pageInfoArray.splice(i, 1);
return;
}
}
// Page not found, throw!
throw new Error("Page " + aPageInfo.contentTitle + " not found in unregisterPage");
},
getAvailablePages: function(aList, aConstraint) {
this._ensureMediaPageRegistration();
// If no list is provided, return the entire set
if (!aList) {
return ArrayConverter.enumerator(this._pageInfoArray);
}
// Otherwise, make a list of what matches the list
var tempArray = [];
for (var i in this._pageInfoArray) {
var pageInfo = this._pageInfoArray[i];
if (pageInfo.matchInterface.match(aList, aConstraint)) {
tempArray.push(pageInfo);
}
}
// ... and return that.
return ArrayConverter.enumerator(tempArray);
},
getPage: function(aList, aConstraint, aType) {
this._ensureMediaPageRegistration();
// use the outermost list
aList = this._getOutermostList(aList);
// Read the saved state
var remote = Cc["@songbirdnest.com/Songbird/DataRemote;1"]
.createInstance(Ci.sbIDataRemote);
var baseKey = "mediapages." + aList.guid;
var key = baseKey;
if (aType)
key = baseKey + "." + aType;
remote.init(key, null);
var savedPageURL = remote.stringValue;
if (savedPageURL && savedPageURL != "") {
// Check that the saved url is still registered
// and still supports this list
let pageInfo = this._checkPageForList(aList, aConstraint, savedPageURL);
if (pageInfo) return pageInfo;
}
// fall back to prefs with no type
else if (aType) {
let remote = Cc["@songbirdnest.com/Songbird/DataRemote;1"]
.createInstance(Ci.sbIDataRemote);
remote.init(baseKey, null);
let savedOldPageURL = remote.stringValue;
if (savedOldPageURL && savedOldPageURL != "") {
// Check that the saved url is still registered
// and still supports this list
let oldPageInfo = this._checkPageForList(aList,
aConstraint,
savedOldPageURL);
if (oldPageInfo) return oldPageInfo;
}
}
// Read the list's default
var defaultPageURL = aList.getProperty(SBProperties.defaultMediaPageURL);
if (defaultPageURL && defaultPageURL != "") {
// Check that the saved url is still registered
// and still supports this list
var pageInfo = this._checkPageForList(aList, aConstraint, defaultPageURL);
if (pageInfo) return pageInfo;
}
// No saved state and no default, this is either the first time this list
// is shown, or its previous saved/default page isn't valid anymore, so
// pick a new one
// Hardcoded first run logic:
// Everybody gets the listview (playlistPage)
if (this._defaultPlaylistPage) {
return this._defaultPlaylistPage;
} else {
// No hardcoded defaults. Look for anything
// that matches.
for (var i in this._pageInfoArray) {
var pageInfo = this._pageInfoArray[i];
if (pageInfo.matchInterface.match(aList, aConstraint)) {
return pageInfo;
}
}
}
// Oh crap.
throw new Error("MediaPageManager unable to determine a page for " + aList.guid);
// keep js happy ?
return null;
},
setPage: function(aList, aPageInfo, aType) {
// use the outermost list
aList = this._getOutermostList(aList);
// Save the state
var remote = Cc["@songbirdnest.com/Songbird/DataRemote;1"]
.createInstance(Ci.sbIDataRemote);
var key = "mediapages." + aList.guid;
if (aType)
key = key + "." + aType;
remote.init(key, null);
remote.stringValue = aPageInfo.contentUrl;
},
// internal, get the outermost list for a given list. for instance, when
// given a smart medialist's storage list, this returns the original
// smart medialist.
_getOutermostList: function(aList) {
var outerGuid = aList.getProperty(SBProperties.outerGUID);
if (outerGuid)
aList = aList.library.getMediaItem(outerGuid);
return aList;
},
// internal. checks that a url is registered in the list of pages, and that
// its matching test succeeds for a given list
_checkPageForList: function(aList, aConstraint, aUrl) {
for (var i in this._pageInfoArray) {
var pageInfo = this._pageInfoArray[i];
if (pageInfo.contentUrl != aUrl) continue;
if (!pageInfo.matchInterface.match(aList, aConstraint)) continue;
return pageInfo;
}
return null;
},
_ensureMediaPageRegistration: function() {
if(this._registrationComplete) { return };
this._registerDefaults();
MediaPageMetadataReader.loadMetadata(this);
this._registrationComplete = true;
},
_registerDefaults: function() {
var playlistString = "mediapages.playlistpage";
var filteredPlaylistString = "mediapages.filteredplaylistpage";
try {
var stringBundleService = Cc["@mozilla.org/intl/stringbundle;1"]
.getService(Ci.nsIStringBundleService);
var stringBundle = stringBundleService.createBundle(
"chrome://songbird/locale/songbird.properties" );
playlistString = stringBundle.GetStringFromName(playlistString);
filteredPlaylistString = stringBundle.GetStringFromName(
filteredPlaylistString);
stringBundleService = null;
stringBundle = null;
} catch (e) {
Component.utils.reportError("MediaPageManager: Couldn't localize default media page name.\n")
}
// the default page matches everything
var matchAll = {match: function() true};
// Register the playlist with filters
this._defaultFilteredPlaylistPage =
this.registerPage( filteredPlaylistString,
"chrome://songbird/content/mediapages/filtersPage.xul",
null,
matchAll);
// And the playlist without filters
this._defaultPlaylistPage =
this.registerPage( playlistString,
"chrome://songbird/content/mediapages/playlistPage.xul",
null,
matchAll);
},
}; // MediaPageManager.prototype
/**
* MediaPageMetadataReader
* Reads the Add-on Metadata RDF datasource for Media Page declarations.
*/
var MediaPageMetadataReader = {
loadMetadata: function(manager) {
this._manager = manager;
var addons = RDFHelper.help(
"rdf:addon-metadata",
"urn:songbird:addon:root",
RDFHelper.DEFAULT_RDF_NAMESPACES
);
for (var i = 0; i < addons.length; i++) {
// skip addons with no panes.
if (!addons[i].mediaPage)
continue;
try {
var pages = addons[i].mediaPage;
for (var j = 0; j < pages.length; j++) {
this._registerMediaPage(addons[i], pages[j])
}
} catch (e) {
this._reportErrors("", [ "An error occurred while processing " +
"extension " + addons[i].Value + ". Exception: " + e ]);
}
}
},
/**
* Extract pane metadata and register it with the manager.
*/
_registerMediaPage: function _registerMediaPage(addon, page) {
// create and validate our page info
var errorList = [];
var warningList = [];
var info = {};
for (property in page) {
if (page[property])
info[property] = page[property][0];
}
this._validateProperties(info, errorList, warningList);
// create a match function
var matchFunction;
if (page.match) {
var matchList = this._createMatchList(page, warningList);
matchFunction = this._createMatchFunction(matchList);
}
else {
matchFunction = this._createMatchAllFunction();
}
// If errors were encountered, then do not submit
if (warningList.length > 0){
this._reportErrors(
"Warning: " + addon.Value + " install.rdf loading media page: " , warningList);
}
if (errorList.length > 0) {
this._reportErrors(
"ERROR: " + addon.Value + " install.rdf IGNORED media page: ", errorList);
return;
}
// Submit description
this._manager.registerPage( info.contentTitle,
info.contentUrl,
info.contentIcon,
{match: matchFunction}
);
//dump("MediaPageMetadataReader: registered pane " + info.contentTitle
// + " at " + info.contentUrl + " from addon " + addon.Value + " \n");
},
_validateProperties: function(info, errorList, warningList) {
var requiredProperties = ["contentTitle", "contentUrl"];
var optionalProperties = ["contentIcon", "match"];
// check for required properties
for (var p in requiredProperties) {
if (!info[requiredProperties[p]]) {
errorList.push("Missing required property " + requiredProperties[p] + ".\n")
}
}
// check for unused RDF nodes and warn about them.
var template = {};
for(var i in requiredProperties) {
template[requiredProperties[i]] = "required";
}
for(var i in optionalProperties) {
template[optionalProperties[i]] = "optional";
}
for (var p in info) {
if (!template[p]) {
warningList.push("Unrecognized property " + p + ".\n")
}
}
},
_createMatchList: function(page, warningList) {
// create a set of property-comparison objects
// one for each <match>. a mediapage will work for medialists which match
// all the properties in a hash.
var matchList = [];
for (var i = 0; i < page.match.length; i++) {
var fields = page.match[i].split(/\s+/);
var properties = {}
for(var f in fields) {
var key; var value;
[key, value] = fields[f].split(":");
if(!value) { value = key; key = "type" };
// TODO: quoted string values ala name:"My Funky List"
// TODO: check if the key is actually a legal key-type and warn if not
if(properties[key]) {
warningList.push("Attempting to match two values for "
+key+": "+properties[key]+" and "+value+".");
}
properties[key] = value;
}
matchList.push(properties);
}
return matchList;
},
_createMatchFunction: function(matchList) {
// create a match function that uses the match options
var matchFunction = function(mediaList, aConstraint) {
// just in case someone's passing in bad values
if(!mediaList) {
return false;
}
// check each <match/>'s values
// if any one set works, this is a good media page for the list
for (var m in matchList) {
let match = matchList[m];
// first, see if we just want to opt out of this match
// our definition of an opt-out-able list is:
// one that is so unspecific as to only target by "type"
// or to target everything. (see below)
// (this is a bit natty)
var numProperties = 0;
for (var i in match) {
numProperties++
}
// if we *only* target the "type" of the list
// and the list wants to opt out
if (numProperties == 1 && match["type"]) {
if (mediaList.getProperty(SBProperties.onlyCustomMediaPages) == "1") {
return false;
}
}
var thisListMatches = true;
for (var i in match) {
switch(i) {
case "contentType":
// this is set on the constraint instead
if (aConstraint) {
for (let group in ArrayConverter.JSEnum(aConstraint.groups)) {
if (!(group instanceof Ci.sbILibraryConstraintGroup)) {
continue;
}
if (!group.hasProperty(SBProperties.contentType)) {
continue;
}
let contentTypes =
ArrayConverter.JSArray(group.getValues(SBProperties.contentType));
if (contentTypes.indexOf(match[i]) == -1) {
thisListMatches = false;
break;
}
}
}
break;
default:
if (i in mediaList) {
thisListMatches &= (match[i] == mediaList[i]);
}
else {
// Use getProperty notation if the desired field is not
// specified as a true JS property on the object.
// TODO: This should be improved.
thisListMatches &= (match[i] == mediaList.getProperty(SBProperties[i]));
}
}
if (!thisListMatches) {
break;
}
}
if (thisListMatches) {
return true;
}
}
// arriving here means none of the <match>
// elements fit the medialist passed in
return false;
}
return matchFunction;
},
_createMatchAllFunction: function() {
var matchFunction = function(mediaList) {
// opt-out lists will also exclude completely generic pages
// this detail must be clearly communicated to the MP dev'rs!
return(mediaList && mediaList.getProperty(SBProperties.onlyCustomMediaPages) != "1");
};
return matchFunction;
},
/**
* \brief Dump a list of errors to the console and jsconsole
*
* \param contextMessage Additional prefix to use before every line
* \param errorList Array of error messages
*/
_reportErrors: function _reportErrors(contextMessage, errorList) {
var consoleService = Cc["@mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService);
for (var i = 0; i < errorList.length; i++) {
Cu.reportError("MediaPage Addon Metadata: " + contextMessage + errorList[i]);
}
}
}
function NSGetModule(compMgr, fileSpec) {
return XPCOMUtils.generateModule([MediaPageManager]);
}