mashTape.js
Go to the documentation of this file.
1 const Cc = Components.classes;
2 const CcID = Components.classesByID;
3 const Ci = Components.interfaces;
4 const Cr = Components.results;
5 const Cu = Components.utils;
6 
7 // Photo image height size for cutoff between small/medium
8 const PANE_CUTOFF = 275;
9 
10 // Number of photos to attempt to preload before loading any others
11 const PHOTO_PRELOAD = 6;
12 
13 if (typeof(gMM) == "undefined")
14  var gMM = Cc["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
15  .getService(Ci.sbIMediacoreManager);
16 
17 if (typeof(gBrowser) == "undefined")
18  var gBrowser = Cc['@mozilla.org/appshell/window-mediator;1']
19  .getService(Ci.nsIWindowMediator).getMostRecentWindow('Songbird:Main')
20  .window.gBrowser;
21 
22 #ifdef METRICS_ENABLED
23 if (typeof(gMetrics) == "undefined")
24  var gMetrics = Cc["@songbirdnest.com/Songbird/Metrics;1"]
25  .createInstance(Ci.sbIMetrics);
26 #endif
27 
28 if (typeof(SBProperties) == "undefined") {
29  Cu.import("resource://app/jsmodules/sbProperties.jsm");
30  if (!SBProperties)
31  throw new Error("Import of sbProperties module failed");
32 }
33 
34 if (typeof(mtUtils) == "undefined")
35  Cu.import("resource://mashTape/mtUtils.jsm");
36 
37 if (typeof mashTape == "undefined")
38  var mashTape = {
39  initialised : false,
40  expanded: false,
41  height: null,
42  }
43 
44 /*
45  * Run once the first time mashTape is loaded - this seeds the metrics
46  * counts for the defaults
47  */
48 mashTape.firstRun = function() {
49  Application.prefs.setValue("extensions.mashTape.firstrun", false);
50 
51 #ifdef METRICS_ENABLED
52  // By default, the info pane is selected, autohide is true, and all
53  // individual tabs are enabled
54  gMetrics.metricsInc("mashtape", "defaultpane", "info");
55  gMetrics.metricsInc("mashtape", "autohide", "enabled");
56  gMetrics.metricsInc("mashtape", "info", "tab.disabled");
57  gMetrics.metricsInc("mashtape", "review", "tab.disabled");
58  gMetrics.metricsInc("mashtape", "rss", "tab.disabled");
59  gMetrics.metricsInc("mashtape", "photo", "tab.disabled");
60  gMetrics.metricsInc("mashtape", "flash", "tab.disabled");
61 #endif
62 }
63 
64 mashTape.log = function(msg) {
65  mtUtils.log("mashTape", msg);
66 }
67 
68 /*
69  * Initialisation routine to get our various providers and initialise our
70  * tab panels
71  */
72 mashTape.init = function(e) {
73  // remove our listener
74  window.removeEventListener("DOMContentLoaded", mashTape.init, false);
75 
76  if (Application.prefs.getValue("extensions.mashTape.firstrun", false))
77  mashTape.firstRun();
78 
79  mashTape.compMgr =
80  Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
81 
82  mashTape.prevArtist = "";
83 
84  mashTape.npDeck = 1;
85  var count = Application.prefs.getValue("extensions.mashTape.firstRunCount",0);
86  if (count < 10) {
87  mashTape.npDeck = 0;
88  Application.prefs.setValue("extensions.mashTape.firstRunCount", ++count);
89  }
90  document.getElementById("mashTape-deck").selectedIndex = mashTape.npDeck;
91 
92  // Save a reference to our display pane we were loaded in
93  var displayPaneMgr = Cc["@songbirdnest.com/Songbird/DisplayPane/Manager;1"]
94  .getService(Ci.sbIDisplayPaneManager);
95  var dpInstantiator = displayPaneMgr.getInstantiatorForWindow(window);
96  mashTape.displayPane = dpInstantiator.displayPane;
97 
98  // Load our strings
99  mashTape.strings = Components.classes["@mozilla.org/intl/stringbundle;1"]
100  .getService(Components.interfaces.nsIStringBundleService)
101  .createBundle("chrome://mashtape/locale/mashtape.properties");
102 
103  // Add our listener for location changes
104  gBrowser.addProgressListener(mashTape.locationListener,
105  Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
106 
107  // Save a reference to the mashTapeManager
108  mashTape.mgr = Cc["@songbirdnest.com/mashTape/manager;1"]
109  .getService(Ci.sbIMashTapeManager);
110 
111  // Setup our preferences observer
112  mashTape._prefBranch = Cc["@mozilla.org/preferences-service;1"]
113  .getService(Ci.nsIPrefService).getBranch("extensions.mashTape.")
114  .QueryInterface(Ci.nsIPrefBranch2);
115  mashTape._prefBranch.addObserver("", mashTape.prefObserver, false);
116 
117  // class names for our index listings (RSS & Flash)
118  mashTape.classes = ["row-odd", "row-even"];
119 
120  // Array to hold our mashTape providers
121  mashTape.providers = new Array();
122  mashTape.providers["info"] = new Array();
123  mashTape.providers["review"] = new Array();
124  mashTape.providers["rss"] = new Array();
125  mashTape.providers["photo"] = new Array();
126  mashTape.providers["flash"] = new Array();
127 
128  // Keep track of our enabled providers
129  mashTape.enabledProviders = new Array();
130 
131  var oldProviders = Application.prefs.getValue(
132  "extensions.mashTape.providers.all_previous", "").split(",");
133  var newProviders = new Array();
134  // Scan all XPCOM components to inventory our mashTape providers
135  for (var component in Cc) {
136  if (component.indexOf("mashTape/provider") >= 0) {
137  var mtClass = component.substring(
138  component.indexOf("mashTape/provider") + 18, component.length);
139  mtClass = mtClass.substring(0, mtClass.indexOf("/"));
140  mashTape.providers[mtClass].push(component);
141  mashTape.log("Found " + mtClass + " provider: " + component);
142 
143  var existedPrior = false;
144  var clsid = mashTape.compMgr.contractIDToCID(component);
145  clsid = clsid.toString();
146  newProviders.push(clsid);
147  for each (var prev in oldProviders) {
148  if (prev == clsid)
149  existedPrior = true;
150  }
151  if (!existedPrior) {
152  mashTape.log(component + " is new, auto-enabling");
153  mashTape.enableProvider(clsid, mtClass);
154  }
155  }
156  }
157  Application.prefs.setValue("extensions.mashTape.providers.all_previous",
158  newProviders.join(","));
159 
160  // set some shortcuts
161  mashTape.infoTab = document.getElementById("mashTape-nav-tab-info");
162  mashTape.reviewTab = document.getElementById("mashTape-nav-tab-review");
163  mashTape.rssTab = document.getElementById("mashTape-nav-tab-rss");
164  mashTape.photoTab = document.getElementById("mashTape-nav-tab-photo");
165  mashTape.flashTab = document.getElementById("mashTape-nav-tab-flash");
166 
167  // add our listener to change tabs upon radio selection
168  var radioGroup = document.getElementById("mashTape-nav-radiogroup");
169  radioGroup.addEventListener("click", mashTape.navChange, false);
170 
171  // select the default tab
172  var defTab = Application.prefs.getValue("extensions.mashTape.defaultpane",
173  "info");
174  radioGroup.selectedItem = document.getElementById("mashTape-nav-tab-"+defTab);
175  mashTape.selectPane();
176 
177  // initialise our auto-hidden value
178  mashTape.autoHidden = false;
179 
180  // update the nav to reflect enabled/disabled tabs/providers
181  mashTape.updateNav();
182 
183  mashTape.photoFrame = document.getElementById("mashTape-frame-photo");
184 
185  gMM.addListener(mashTape);
186 
187  var SB_NewDataRemote = Components.Constructor(
188  "@songbirdnest.com/Songbird/DataRemote;1", "sbIDataRemote", "init");
189  mashTape.pausedDr = SB_NewDataRemote("faceplate.paused", null);
190  mashTape.pausedDr.bindObserver(mashTape.playingObserver, true);
191 
192  /*
193  if (Application.prefs.getValue("extensions.mashTape.expanded", false))
194  mashTape.maximiseDisplayPane(null);
195  */
196 
197  mashTape.setupAlreadyPlaying();
198 }
199 
200 // Switch tabs/decks/panels
201 mashTape.navChange = function(e) {
202  var selectedTab = e.currentTarget.selectedItem.id;
203  var newTab = selectedTab.replace(/^mashTape-nav-tab-/, '');
204  mashTape.selectPane(newTab);
205  e.preventDefault();
206  e.stopPropagation();
207  mashTape.playPausePhotoListener(newTab == "photo");
208  mashTape.selectedTab = newTab;
209 }
210 
211 // Update the navigation block (enabling/disabling visibility of headers
212 // such as 'Reviews' based on whether or not there are providers enabled
213 // for that header
214 mashTape.updateNav = function() {
215  if (mashTape.providers["info"].length == 0 ||
216  !Application.prefs.getValue("extensions.mashTape.info.enabled", true))
217  mashTape.infoTab.style.visibility = "collapse";
218  else
219  mashTape.infoTab.style.visibility = "visible";
220  if (mashTape.providers["review"].length == 0 ||
221  !Application.prefs.getValue("extensions.mashTape.review.enabled", true))
222  mashTape.reviewTab.style.visibility = "collapse";
223  else
224  mashTape.reviewTab.style.visibility = "visible";
225  if (mashTape.providers["rss"].length == 0 ||
226  !Application.prefs.getValue("extensions.mashTape.rss.enabled", true))
227  mashTape.rssTab.style.visibility = "collapse";
228  else
229  mashTape.rssTab.style.visibility = "visible";
230  if (mashTape.providers["photo"].length == 0 ||
231  !Application.prefs.getValue("extensions.mashTape.photo.enabled", true))
232  mashTape.photoTab.style.visibility = "collapse";
233  else
234  mashTape.photoTab.style.visibility = "visible";
235  if (mashTape.providers["flash"].length == 0 ||
236  !Application.prefs.getValue("extensions.mashTape.flash.enabled", true))
237  mashTape.flashTab.style.visibility = "collapse";
238  else
239  mashTape.flashTab.style.visibility = "visible";
240 }
241 
242 // Given a pane, switch the deck to it. If none was given, then select the
243 // default one.
244 mashTape.selectPane = function(pane) {
245  var contentDeck = document.getElementById("mashTape-content-deck");
246  if (pane == null) {
247  pane = Application.prefs.getValue("extensions.mashTape.defaultpane","info");
248 
249  // also need to select the tab nav element itself
250  document.getElementById("mashTape-nav-radiogroup").selectedItem =
251  document.getElementById("mashTape-nav-tab-" + pane);
252  }
253  switch(pane) {
254  case "info":
255  if (mashTape.infoTab.style.visibility != "collapse") {
256  contentDeck.selectedIndex = 0;
257  break;
258  }
259  case "review":
260  if (mashTape.reviewTab.style.visibility != "collapse") {
261  contentDeck.selectedIndex = 1;
262  break;
263  }
264  case "rss":
265  if (mashTape.rssTab.style.visibility != "collapse") {
266  contentDeck.selectedIndex = 2;
267  break;
268  }
269  case "photo":
270  if (mashTape.photoTab.style.visibility != "collapse") {
271  contentDeck.selectedIndex = 3;
272  break;
273  }
274  case "flash":
275  if (mashTape.flashTab.style.visibility != "collapse") {
276  contentDeck.selectedIndex = 4;
277  break;
278  }
279  default:
280  break;
281  }
282 }
283 
284 mashTape.unload = function() {
285  // remove thyself!
286  window.removeEventListener("unload", mashTape.unload, false);
287 
288  // clean up other listeners
289  gBrowser.removeProgressListener(mashTape.locationListener);
290  mashTape._prefBranch.removeObserver("", mashTape.prefObserver);
291  gMM.removeListener(mashTape);
292 
293  // Save our expanded state to a pref
294  Application.prefs.setValue("extensions.mashTape.expanded", mashTape.expanded);
295 
296  // if we're expanded, then unexpand
297  /*
298  if (mashTape.expanded)
299  mashTape.maximiseDisplayPane(null);
300  */
301 
302  // destroy the dataremotes
303  mashTape.pausedDr.unbind();
304 }
305 
306 mashTape.setupAlreadyPlaying = function() {
307  if ((gMM.status.state == Ci.sbIMediacoreStatus.STATUS_PLAYING) ||
308  (gMM.status.state == Ci.sbIMediacoreStatus.STATUS_PAUSED) ||
309  (gMM.status.state == Ci.sbIMediacoreStatus.STATUS_BUFFERING)
310  )
311  {
312  mashTape.iframeLoadCount = 0;
313  for each (var iframe in document.getElementsByTagName("iframe")) {
314  if (typeof(iframe) == "object") {
315  iframe.addEventListener("DOMContentLoaded",
316  mashTape.iframeLoadListener, false);
317  mashTape.iframeLoadCount++;
318  }
319  }
320  }
321 }
322 
323 mashTape.iframeLoadListener = function(e) {
324  mashTape.iframeLoadCount--;
325  if (mashTape.iframeLoadCount == 0) {
326  mashTape.showTabPanels();
327 
328  // in the event we're already playing a track we should trigger the
329  // update, but we need to wait for all the iframe DOMs to finish
330  // loading first
331  var i = gMM.sequencer.view.getItemByIndex(
332  gMM.sequencer.viewPosition);
333  var artist = i.getProperty(SBProperties.artistName);
334  var album = i.getProperty(SBProperties.albumName);
335  var track = i.getProperty(SBProperties.trackName);
336 
337  if (artist == null || artist == "") {
338  mashTape.noDataTab("info");
339  mashTape.noDataTab("review");
340  mashTape.noDataTab("rss");
341  mashTape.noDataTab("photo");
342  mashTape.noDataTab("flash");
343  }
344 
345  mashTape.update(artist, album, track);
346  }
347 }
348 
349 
350 /*
351  * Show all the tab panels, switch to the actual content panes
352  */
353 mashTape.showTabPanels = function() {
354  var deck = document.getElementById("mashTape-deck");
355  deck.selectedIndex = 2;
356 
357  for each (var prov in ["info", "review", "rss", "photo", "flash"]) {
358  var provDeck = document.getElementById("mashTape-panel-" + prov);
359  provDeck.selectedIndex = 0;
360  }
361 }
362 
363 mashTape.onMediacoreEvent = function(ev) {
364  switch (ev.type) {
365  case Ci.sbIMediacoreEvent.TRACK_CHANGE:
366  mashTape.onTrackChange(ev.data);
367  break;
368  case Ci.sbIMediacoreEvent.STREAM_END:
369  case Ci.sbIMediacoreEvent.STREAM_STOP:
370  // workaround for STREAM_END being fired in between track changes
371  // check the media core status to see if it's actually stopped or
372  // not. when STREAM_END is fired in between track changes, the
373  // media core status is still playing, not STOPPED
374  if (gMM.status.state == Ci.sbIMediacoreStatus.STATUS_STOPPED)
375  mashTape.onStop();
376  break;
377  default:
378  break;
379  }
380 }
381 
382 /*
383  * PlaylistPlaybackService observers for track change & stop
384  */
385 // Show the "Nothing playing" stack pane when the user stops playback
386 mashTape.onStop = function() {
387  var deck = document.getElementById("mashTape-deck");
388  deck.selectedIndex = mashTape.npDeck;
389 }
390 
391 mashTape.onTrackChange = function(item, view, index) {
392  mashTape.showTabPanels();
393  var artist = item.getProperty(SBProperties.artistName);
394  var album = item.getProperty(SBProperties.albumName);
395  var track = item.getProperty(SBProperties.trackName);
396 
397  if (artist == null || artist == "") {
398  mashTape.noDataTab("info");
399  mashTape.noDataTab("review");
400  mashTape.noDataTab("rss");
401  mashTape.noDataTab("photo");
402  mashTape.noDataTab("flash");
403  } else
404  mashTape.update(artist, album, track);
405 }
406 
407 mashTape.playingObserver = {
408  observe: function(subject, topic, data) {
409  if (gMM.status.state == Ci.sbIMediacoreStatus.STATUS_PLAYING
410  && topic == "faceplate.paused" && data == "0")
411  {
412  // we were previously paused and we just unpaused
413  var detailFrame = document.getElementById("mashTape-panel-flash-detail");
414  var videoWindow = detailFrame.contentWindow;
415  var event = videoWindow.document.createEvent("Events");
416  event.initEvent("songbirdPlaybackResumed", false, false);
417  videoWindow.dispatchEvent(event);
418  }
419  }
420 }
421 
422 /*
423  * Called when a tab has no given data to display
424  */
425 mashTape.noDataTab = function(providerType) {
426  var thisDeck = document.getElementById("mashTape-panel-" + providerType);
427  thisDeck.selectedIndex = 1;
428  /*
429  var panel = mashTape.tabPanels[providerType];
430  var deck = mashTape.tabPanels[providerType].firstChild;
431  deck.selectedIndex = 0;
432  var key = "extensions.mashTape.msg.no_" + providerType + "_found";
433  panel.infoMessage.setAttribute("value", mashTape.strings.GetStringFromName(key));
434  */
435 }
436 
437 /*
438  * Check to make sure that all enabled providers are still installed
439  */
440 mashTape.updateEnabledProviders = function(providerType) {
441  var prefKey = "extensions.mashTape." + providerType + ".providers";
442  mashTape.enabledProviders[providerType] =
443  Application.prefs.getValue(prefKey, "").split(",");
444  var i=0;
445  var changed = false;
446  while (i<mashTape.enabledProviders[providerType].length) {
447  var clsid = mashTape.enabledProviders[providerType][i];
448  if (typeof(CcID[clsid]) == "undefined") {
449  // remove this from the enabled providers list
450  mashTape.log("provider " + clsid + " is uninstalled, disabling");
451  mashTape.enabledProviders[providerType].splice(i, 1);
452  changed = true;
453  } else {
454  i++;
455  }
456  }
457  if (changed)
458  Application.prefs.setValue(prefKey,
459  mashTape.enabledProviders[providerType].join(","));
460 }
461 
462 /*
463  * Enable one provider
464  */
465 mashTape.enableProvider = function(clsid, providerType) {
466  var prefKey = "extensions.mashTape." + providerType + ".providers";
467  mashTape.enabledProviders[providerType] =
468  Application.prefs.getValue(prefKey, "").split(",");
469 
470  switch (providerType) {
471  // multiple providers allowed
472  case "rss":
473  case "review":
474  case "flash":
475  mashTape.enabledProviders[providerType].push(clsid);
476  Application.prefs.setValue(prefKey,
477  mashTape.enabledProviders[providerType].join(","));
478  break;
479  // only one provider allowed
480  case "photo":
481  case "info":
482  mashTape.enabledProviders[providerType] = new Array(clsid);
483  Application.prefs.setValue(prefKey, clsid);
484  break;
485  }
486 }
487 
488 /*
489  * The main routine; this gets triggered everytime mashTape should refresh
490  * its data and redraw
491  */
492 mashTape.update = function(artist, album, track) {
493  mashTape.pendingCallbacks = new Array();
494  mashTape.pendingCallbacks["info"] = new Object;
495  mashTape.pendingCallbacks["review"] = new Object;
496  mashTape.pendingCallbacks["rss"] = new Object;
497  mashTape.pendingCallbacks["photo"] = new Object;
498  mashTape.pendingCallbacks["flash"] = new Object;
499  mashTape.pendingCallbacks["info"].pending = 0;
500  mashTape.pendingCallbacks["review"].pending = 0;
501  mashTape.pendingCallbacks["rss"].pending = 0;
502  mashTape.pendingCallbacks["photo"].pending = 0;
503  mashTape.pendingCallbacks["flash"].pending = 0;
504  mashTape.pendingCallbacks["info"].valid = 0;
505  mashTape.pendingCallbacks["review"].valid = 0;
506  mashTape.pendingCallbacks["rss"].valid = 0;
507  mashTape.pendingCallbacks["photo"].valid = 0;
508  mashTape.pendingCallbacks["flash"].valid = 0;
509 
510  // i'm using the artist name as the UID for now, if/when we evolve to
511  // having track-specific providers, we'll do something trickier
512  // XXX is it time to evolve?
513  var uid = encodeURIComponent(artist + album + track);
514 
515  if (mashTape.prevArtist != artist) {
516  mashTape.resetInfo();
517  mashTape.updateEnabledProviders("info");
518  if (mashTape.enabledProviders["info"].length > 0 &&
519  Application.prefs.getValue("extensions.mashTape.info.enabled",
520  true))
521  {
522  // Since only one info provider is allowed at a time, we'll just
523  // grab the first one (since the prefs UI will do the restriction of
524  // 'only one' for us
525  var clsid = mashTape.enabledProviders["info"][0];
526  var infoProvider = CcID[clsid].createInstance(
527  Ci.sbIMashTapeInfoProvider);
528  var callback = new mashTape.displayCallback(uid);
529  mashTape.pendingCallbacks["info"].pending +=
530  infoProvider.numSections;
531  infoProvider.query(artist, callback);
532  }
533  }
534 
535  if (mashTape.providers["review"].length > 0) {
536  mashTape.resetReviewFrame();
537  mashTape.updateEnabledProviders("review");
538  if (Application.prefs.getValue("extensions.mashTape.review.enabled", true))
539  {
540  for (var i=0; i<mashTape.enabledProviders["review"].length; i++) {
541  var clsid = mashTape.enabledProviders["review"][i];
542  var prov = CcID[clsid].createInstance(Ci.sbIMashTapeReviewProvider);
543  var callback = new mashTape.displayCallback(uid);
544  prov.queryFull(artist, album, track, callback);
545  mashTape.pendingCallbacks["review"].pending++;
546  }
547  }
548  }
549 
550  mashTape.resetRssFrame();
551  mashTape.updateEnabledProviders("rss");
552  if (Application.prefs.getValue("extensions.mashTape.rss.enabled", true))
553  {
554  for (var i=0; i<mashTape.enabledProviders["rss"].length; i++) {
555  var clsid = mashTape.enabledProviders["rss"][i];
556  var prov = CcID[clsid].createInstance(Ci.sbIMashTapeRSSProvider);
557  var callback = new mashTape.displayCallback(uid);
558  prov.query(artist, callback);
559  mashTape.pendingCallbacks["rss"].pending++;
560  }
561  }
562 
563  if (mashTape.prevArtist != artist || !mashTape.photoFrameLoaded) {
564  mashTape.photoFrameLoaded = false;
565  mashTape.photosReady = null;
566  mashTape.resetPhotoFrame();
567  mashTape.updateEnabledProviders("photo");
568  if (Application.prefs.getValue("extensions.mashTape.photo.enabled",
569  true))
570  {
571  for (var i=0; i<mashTape.enabledProviders["photo"].length; i++) {
572  var clsid = mashTape.enabledProviders["photo"][i];
573  var provider = CcID[clsid].createInstance(
574  Ci.sbIMashTapePhotoProvider);
575  var callback = new mashTape.displayCallback(uid);
576  provider.query(artist, callback);
577  mashTape.pendingCallbacks["photo"].pending++;
578  }
579  }
580  }
581 
582  mashTape.resetFlashFrame();
583  mashTape.updateEnabledProviders("flash");
584  if (Application.prefs.getValue("extensions.mashTape.flash.enabled", true))
585  {
586  setTimeout(mashTape.loadFirstFlashFeed, 10000);
587  for (var i=0; i<mashTape.enabledProviders["flash"].length; i++) {
588  var clsid = mashTape.enabledProviders["flash"][i];
589  var prov = CcID[clsid].createInstance(Ci.sbIMashTapeFlashProvider);
590  var callback = new mashTape.displayCallback(uid);
591  prov.query(artist, callback);
592  mashTape.pendingCallbacks["flash"].pending++;
593  }
594  }
595 
596  mashTape.prevArtist = artist;
597 
598  /*
599  dump("Outgoing callbacks:\n");
600  dump(" info : " + mashTape.pendingCallbacks["info"].pending + "\n");
601  dump(" review: " + mashTape.pendingCallbacks["review"].pending + "\n");
602  dump(" rss : " + mashTape.pendingCallbacks["rss"].pending + "\n");
603  dump(" photo : " + mashTape.pendingCallbacks["photo"].pending + "\n");
604  dump(" flash : " + mashTape.pendingCallbacks["flash"].pending + "\n");
605  */
606 }
607 
608 mashTape.displayCallback = function(uid) {
609  this.wrappedJSObject = this;
610  this.uid = uid;
611 }
612 mashTape.displayCallback.prototype = {
613  update: function(contractId, results, section) {
614  // this happens when the window has been closed (e.g. display pane
615  // hidden/unloaded) before a callback has returned
616  if (typeof(gMM) == "undefined")
617  return;
618  var i = gMM.sequencer.view.getItemByIndex(
619  gMM.sequencer.viewPosition);
620  var thisUID = encodeURIComponent(i.getProperty(SBProperties.artistName)
621  + i.getProperty(SBProperties.albumName)
622  + i.getProperty(SBProperties.trackName));
623  if (this.uid != thisUID) {
624  //dump("> callback triggered with a different UID, aborting.\n");
625  return;
626  }
627  var clsid = mashTape.compMgr.contractIDToCID(contractId);
628  var provider = Cc[contractId].createInstance(Ci.sbIMashTapeProvider);
629 
630  var classPending = mashTape.pendingCallbacks[provider.providerType];
631  if (results != null &&
632  (provider.providerType == "info" || results.length > 0))
633  {
634  //if (provider.providerType == "info")
635  // dump("valid data for : " + section + "\n");
636  classPending.valid++;
637  }
638  classPending.pending--;
639 
640  /*
641  if (provider.providerType == "info") {
642  dump(section + " returned\n");
643  dump("info pending: " + classPending.pending +
644  " / " + classPending.valid + "\n");
645  } else if (provider.providerType == "rss") {
646  dump("rss: " + provider.providerName + " returned, pending: " +
647  classPending.pending + "\n");
648  }
649  */
650 
651  if (classPending.pending == 0 && classPending.valid == 0) {
652  // All our pending callbacks completed, let's check to see if
653  // we actually got any valid results for this providerType
654  mashTape.noDataTab(provider.providerType);
655  }
656 
657  switch(provider.providerType) {
658  case "info":
659  mashTape.updateInfo(provider, results, section);
660  break;
661  case "review":
662  var splitter =
663  document.getElementById("mashTape-panel-review-splitter");
664  mashTape.updateReviewFeeds(provider, results);
665  if (results != null &&
666  splitter.getAttribute("state") == "collapsed")
667  {
668  splitter.setAttribute("state", "open");
669  }
670  if (classPending.pending == 0)
671  mashTape.loadFirstReviewFeed()
672  break;
673  case "rss":
674  var splitter = document.getElementById("mashTape-panel-rss-splitter");
675  mashTape.updateRssFeeds(provider, results);
676  if (results != null &&
677  splitter.getAttribute("state") == "collapsed")
678  {
679  splitter.setAttribute("state", "open");
680  }
681  if (classPending.pending == 0)
682  mashTape.loadFirstRssFeed()
683  break;
684  case "photo":
685  mashTape.updatePhoto(provider, results);
686  break;
687  case "flash":
688  var splitter =
689  document.getElementById("mashTape-panel-flash-splitter");
690  mashTape.updateFlash(provider, results);
691  if (results != null && results.length > 0 && !mashTape.expanded
692  && splitter.getAttribute("state") == "collapsed")
693  {
694  splitter.setAttribute("state", "open");
695  }
696  if (classPending.pending == 0)
697  mashTape.loadFirstFlashFeed()
698  break;
699  default:
700  alert("Unrecognised provider callback: " + providerType);
701  }
702  }
703 }
704 
705 /****************************************************************************
706  * ARTIST INFO PROVIDER FUNCTIONS
707  ****************************************************************************/
708 mashTape.resetInfo = function() {
709  var infoFrame = document.getElementById("mashTape-frame-info");
710  infoFrame.contentWindow.scrollTo(0,0);
711  var doc = infoFrame.contentWindow.document;
712  var photoDiv = doc.getElementById("artist-photo");
713  while (photoDiv.firstChild)
714  photoDiv.removeChild(photoDiv.firstChild);
715  var discoDiv = doc.getElementById("discography-text");
716  while (discoDiv.firstChild)
717  discoDiv.removeChild(discoDiv.firstChild);
718  var membersDiv = doc.getElementById("members-text");
719  while (membersDiv.firstChild)
720  membersDiv.removeChild(membersDiv.firstChild);
721  var tagsDiv = doc.getElementById("tags-text");
722  while (tagsDiv.firstChild)
723  tagsDiv.removeChild(tagsDiv.firstChild);
724  var linksDiv = doc.getElementById("links-text");
725  while (linksDiv.firstChild)
726  linksDiv.removeChild(linksDiv.firstChild);
727  var bioText = doc.getElementById("bio-text");
728  while (bioText.firstChild)
729  bioText.removeChild(bioText.firstChild);
730 
731  bioText.style.height = "auto";
732  discoDiv.style.height = "auto";
733  bioText.setAttribute("mashTape-collapsed", true);
734  discoDiv.setAttribute("mashTape-collapsed", true);
735  doc.getElementById("bio-toggle-text").innerHTML =
736  mashTape.strings.GetStringFromName("extensions.mashTape.msg.readmore");
737  doc.getElementById("discography-toggle-text").innerHTML =
738  mashTape.strings.GetStringFromName("extensions.mashTape.msg.viewmore");
739 
740  // Hide sections
741  doc.getElementById("bio").style.display = "none";
742  doc.getElementById("artist-photo").style.display = "none";
743  doc.getElementById("discography").style.display = "none";
744  doc.getElementById("members").style.display = "none";
745  doc.getElementById("tags").style.display = "none";
746  doc.getElementById("links").style.display = "none";
747 }
748 
749 mashTape.updateInfo = function(provider, results, section) {
750  if (results == null && section != "photo")
751  return;
752  var infoFrame = document.getElementById("mashTape-frame-info");
753  var doc = infoFrame.contentWindow.document;
754  var body = doc.getElementsByTagName("body")[0];
755 
756  provider = provider.QueryInterface(Ci.sbIMashTapeInfoProvider);
757  var faviconSrc;
758  var faviconUrl;
759  var faviconTitle;
760 
761  switch(section) {
762  case "bio":
763  doc.getElementById("bio").style.display = "block";
764  var bioDiv = doc.getElementById("bio-text");
765  faviconSrc = provider.providerIconBio;
766  faviconUrl = results.bioUrl;
767 
768  if (typeof(results.bioText) == "undefined" ||
769  results.bioText == null)
770  {
771  var noBioFound = mashTape.strings.formatStringFromName(
772  "extensions.mashTape.info.no_bio", [mashTape.prevArtist],1);
773  var bioText = noBioFound;
774  if (typeof(results.bioEditUrl) != "undefined")
775  bioText += " <a href='" +
776  results.bioEditUrl + "'>" +
777  mashTape.strings.GetStringFromName(
778  "extensions.mashTape.info.create") + "</a>";
779  bioDiv.innerHTML = bioText;
780  doc.getElementById("bio-toggle-text").style.display = "none";
781  break;
782  }
783  bioDiv.innerHTML = results.bioText.toString();
784  bioDiv.setAttribute("mashTape-full-height", bioDiv.scrollHeight);
785 
786  // 98px is the line height (14px) * 7 rows of text. if we've got more
787  // than 7 lines of text, then use the "Read More" link to only show
788  // an initial amount
789  if (bioDiv.scrollHeight > 98) {
790  // determine where to set initial height to (bug 19980)
791  var height = 0;
792  for (var i=0; i<bioDiv.childNodes.length; i++) {
793  if (bioDiv.childNodes[i].offsetTop <= 100)
794  height = bioDiv.childNodes[i].offsetTop + 5;
795  }
796  if (height == 0)
797  height = 98;
798  doc.getElementById("bio-toggle-text").style.display = "block";
799  bioDiv.style.height = height + "px";
800  } else
801  doc.getElementById("bio-toggle-text").style.display = "none";
802 
803  // notify listeners
804  mashTape.mgr.updateInfo("bio", results.bioText.toString());
805 
806  var bioUrl = results.bioUrl;
807  if (typeof(bioUrl) != "string")
808  bioUrl = bioUrl.toString();
809  mashTape.mgr.updateInfo("biourl", bioUrl);
810  break;
811 
812  case "photo":
813  doc.getElementById("artist-photo").style.display = "block";
814  var photoDiv = doc.getElementById("artist-photo");
815  var artistImage = doc.createElement("img");
816  // adjust width
817  artistImage.addEventListener("load", function() {
818  var width = this.naturalWidth + "px";
819  if (this.naturalWidth > 150)
820  width = "150px";
821  doc.getElementById("artist-photo").style.width = width;
822  doc.getElementById("content").style.marginLeft = width;
823 
824  // notify listeners
825  mashTape.mgr.updateInfo("photo", this.src);
826  }, false);
827  if (results != null)
828  artistImage.src = results;
829  else {
830  artistImage.onerror = function() {
831  this.src =
832  "chrome://mashtape/skin/generic-artist-photo.jpg";
833  }
834  artistImage.src =
835  "chrome://songbird/skin/artist-info/default-photo.png";
836  }
837  artistImage.className = "artistImage";
838  photoDiv.appendChild(artistImage);
839  break;
840 
841  case "discography":
842  doc.getElementById("discography").style.display = "block";
843  var discoDiv = doc.getElementById("discography-text");
844 
845  // Sort in chronological order
846  results.discography.sort(function(a,b) {
847  var a = (a.release_date == null) ? "" : a.release_date;
848  var b = (b.release_date == null) ? "" : b.release_date;
849  return a < b;
850  });
851 
852  results.discography.forEach(function(val, i, arr) {
853  var albumName = val.title;
854  var albumImage = val.artwork;
855  var url = val.link;
856 
857  var albumDiv = doc.createElement("div");
858  albumDiv.className = "discography-album";
859 
860  var imageLink = doc.createElement("a");
861  if (url)
862  imageLink.href = url;
863  var image = doc.createElement("img");
864  image.className = "album-art";
865  image.setAttribute("title", val.tooltip);
866  if (albumImage)
867  image.src = albumImage;
868  else
869  image.src = //"chrome://mashtape/skin/no-cover.png";
870  "chrome://songbird/skin/album-art/default-cover.png";
871  image.width = 64;
872  image.height = 64;
873  imageLink.appendChild(image);
874  albumDiv.appendChild(imageLink);
875 
876  var name = doc.createElement("span");
877  name.class = "album-name";
878  name.innerHTML = albumName;
879  if (url) {
880  var link = doc.createElement("a");
881  link.href = url;
882  link.setAttribute("title", val.tooltip);
883  link.appendChild(name);
884  albumDiv.appendChild(link);
885  } else
886  albumDiv.appendChild(name);
887 
888  if (val.release_date != null) {
889  var release = doc.createElement("span");
890  release.className = "release-date";
891  release.innerHTML = "<br />(" +
892  val.release_date.substr(0,4) + ")";
893  albumDiv.appendChild(release);
894  }
895 
896  discoDiv.appendChild(albumDiv);
897  });
898 
899  discoDiv.setAttribute("mashTape-full-height",discoDiv.scrollHeight);
900  if (discoDiv.scrollHeight > 70) {
901  doc.getElementById("discography-toggle-text").style.display =
902  "block";
903  discoDiv.style.height = "70px";
904  } else
905  doc.getElementById("discography-toggle-text").style.display =
906  "none";
907 
908  faviconSrc = provider.providerIconDiscography;
909  faviconUrl = results.url;
910  break;
911 
912  case "members":
913  if (results.members.length == 0)
914  return;
915  doc.getElementById("members").style.display = "block";
916  var membersDiv = doc.getElementById("members-text");
917  var list = doc.createElement("ul");
918  list.className = "flat";
919  results.members.forEach(function(val, i, arr) {
920  var li = doc.createElement("li");
921  var str = val;
922  if (i == arr.length-1)
923  li.className = "last";
924  li.innerHTML = str;
925  list.appendChild(li);
926  });
927  membersDiv.appendChild(list);
928 
929  faviconSrc = provider.providerIconMembers;
930  faviconUrl = results.url;
931  break;
932 
933  case "tags":
934  doc.getElementById("tags").style.display = "block";
935  var tagsDiv = doc.getElementById("tags-text");
936  var list = doc.createElement("ul");
937  list.className = "flat";
938  results.tags.forEach(function(val, i, arr) {
939  if (val.count >= 10) {
940  var li = doc.createElement("li");
941  var link = doc.createElement("a");
942  link.href = val.url;
943  var tagName = doc.createTextNode(val.name);
944  link.appendChild(tagName);
945  li.appendChild(link);
946  if (i == arr.length-1 ||
947  (i < arr.length-1 && arr[i+1].count < 10))
948  li.className = "last";
949  list.appendChild(li);
950  }
951  });
952  tagsDiv.appendChild(list);
953 
954  faviconSrc = provider.providerIconTags;
955  faviconUrl = results.url;
956  break;
957 
958  case "links":
959  if (results.links.length == 0)
960  return;
961  doc.getElementById("links").style.display = "block";
962  var linksDiv = doc.getElementById("links-text");
963  results.links.sort(function(a,b) {
964  if (a.name == "Homepage")
965  return -1;
966  else if (b.name == "Homepage")
967  return 1;
968  else
969  return a.name < b.name;
970  });
971  var table = doc.createElement("table");
972  table.className = "table-links";
973  results.links.forEach(function(val, i, arr) {
974  var tr = doc.createElement("tr");
975  var colName = doc.createElement("td");
976  var colUrl = doc.createElement("td");
977  colUrl.className = "url";
978  var textNode = doc.createElement("span");
979  textNode.className = "linkType";
980  textNode.innerHTML = decodeURIComponent(val.name) + ":";
981  colName.appendChild(textNode);
982  var link = doc.createElement("a");
983  link.href = val.url;
984  link.innerHTML = val.url;
985  colUrl.appendChild(link);
986  tr.appendChild(colName);
987  tr.appendChild(colUrl);
988  table.appendChild(tr);
989  });
990  linksDiv.appendChild(table);
991 
992  faviconSrc = provider.providerIconLinks;
993  faviconUrl = results.url;
994  break;
995 
996  default:
997  dump("*** updateInfo called for unknown section: "+section+"\n");
998  }
999 
1000  if (section != "photo") {
1001  var favicon = doc.getElementById("favicon-" + section);
1002  favicon.src = faviconSrc;
1003  favicon.setAttribute("title", mashTape.strings.formatStringFromName(
1004  "extensions.mashTape.msg.provider_tooltip", [results.provider],
1005  1));
1006  var link = doc.getElementById("link-" + section);
1007  link.href = faviconUrl;
1008  }
1009  return;
1010 }
1011 
1012 /****************************************************************************
1013  * REVIEW PROVIDER FUNCTIONS
1014  ****************************************************************************/
1015 mashTape.resetReviewFrame = function() {
1016  // Clear existing data
1017  var indexFrame = document.getElementById("mashTape-panel-review-index");
1018  var body = indexFrame.contentWindow.document.getElementsByTagName("body")[0];
1019  while (body.firstChild)
1020  body.removeChild(body.firstChild);
1021 
1022  var detailFrame = document.getElementById("mashTape-panel-review-detail");
1023  var doc = detailFrame.contentWindow.document;
1024 
1025  // Hide any existing content
1026  var content = doc.getElementById("actual-content");
1027  content.style.display = "none";
1028 
1029  // Collapse the splitter
1030  var splitter = document.getElementById("mashTape-panel-review-splitter");
1031  splitter.setAttribute("state", "collapsed");
1032 
1033  // Put in the loading image for the detail frame
1034  var loading = doc.getElementById("loading");
1035  loading.style.display = "block";
1036  var paneHeight = doc.getElementsByTagName("html")[0].clientHeight;
1037  // 64 is the height of the load.gif
1038  loading.style.marginTop = (paneHeight-64)/2 + "px";
1039 }
1040 
1041 mashTape.loadFirstReviewFeed = function() {
1042  var detailFrame = document.getElementById("mashTape-panel-review-detail");
1043  var indexFrame = document.getElementById("mashTape-panel-review-index");
1044  // only load if the loading is still showing (meaning the user hasn't
1045  // clicked on something else to load in the meantime)
1046  if (detailFrame.contentWindow.document
1047  .getElementById("loading").style.display == "block")
1048  {
1049  var doc = indexFrame.contentWindow.document;
1050  var body = doc.getElementsByTagName("body")[0];
1051  var first = body.getElementsByTagName("div")[0];
1052  mashTape.loadReviewDetail(first);
1053  }
1054 }
1055 
1056 mashTape.loadReviewDetail = function(entry) {
1057  if (typeof(entry) == "undefined")
1058  return;
1059  while (!entry.hasAttribute("mashTape-title"))
1060  entry = entry.parentNode;
1061  var title = entry.getAttribute("mashTape-title");
1062  var author = entry.getAttribute("mashTape-author");
1063  var authorUrl = entry.getAttribute("mashTape-authorUrl");
1064  var src = entry.getAttribute("mashTape-src");
1065  var time = entry.getAttribute("mashTape-time");
1066  var url = entry.getAttribute("mashTape-url");
1067  var favicon = entry.getAttribute("mashTape-favicon");
1068  var content = entry.getAttribute("mashTape-content");
1069  var rating = entry.getAttribute("mashTape-rating");
1070 
1071  var detailFrame = document.getElementById("mashTape-panel-review-detail");
1072  var doc = detailFrame.contentWindow.document;
1073  detailFrame.contentWindow.scrollTo(0,0);
1074  var detailTitle = doc.getElementById("title");
1075  var detailFavicon = doc.getElementById("favicon");
1076  var detailFaviconLink = doc.getElementById("link");
1077  var detailSubtitle = doc.getElementById("subtitle");
1078  var detailContent = doc.getElementById("content");
1079  var detailMore = doc.getElementById("write-review");
1080 
1081  // hide the loading div
1082  var loading = doc.getElementById("loading");
1083  loading.style.display = "none";
1084 
1085  // display the content divs
1086  var actualContent = doc.getElementById("actual-content");
1087  actualContent.style.display = "block";
1088 
1089  var containingDiv = entry;
1090  if (mashTape.selectedReview)
1091  mashTape.selectedReview.className =
1092  mashTape.selectedReview.className.replace("row-selected",
1093  "row-unselected");
1094  while (containingDiv.className.indexOf("row-") != 0)
1095  containingDiv = containingDiv.parentNode;
1096  containingDiv.className += " row-selected";
1097  mashTape.selectedReview = containingDiv;
1098 
1099  detailTitle.innerHTML = title;
1100  detailFavicon.src = favicon;
1101  detailFavicon.setAttribute("title", mashTape.strings.formatStringFromName(
1102  "extensions.mashTape.msg.review_tooltip", [src], 1));
1103  detailFaviconLink.href = url;
1104 
1105  while (detailSubtitle.firstChild)
1106  detailSubtitle.removeChild(detailSubtitle.firstChild);
1107  detailSubtitle.appendChild(doc.createTextNode(
1108  mashTape.strings.GetStringFromName("extensions.mashTape.msg.by") +" "));
1109  var provider = doc.createElement("a");
1110  provider.href = authorUrl;
1111  provider.innerHTML = author;
1112  detailSubtitle.appendChild(provider);
1113 
1114  if (time > 0) {
1115  var dateObj = new Date(parseInt(time));
1116  var timestamp = doc.createElement("span");
1117  timestamp.className = "time";
1118  timestamp.innerHTML = dateObj.ago();
1119  detailSubtitle.appendChild(timestamp);
1120  }
1121 
1122  detailSubtitle.appendChild(doc.createElement("br"));
1123 
1124  if (rating > -1) {
1125  var ratings = doc.createElement("div");
1126  ratings.className = "ratings rate" + rating.toString();
1127  detailSubtitle.appendChild(ratings);
1128  }
1129 
1130 
1131  detailContent.innerHTML = content;
1132  detailMore.innerHTML = "<a href='" + url + "'>" +
1133  mashTape.strings.GetStringFromName("extensions.mashTape.msg.review") +
1134  "</a>";
1135 }
1136 
1137 mashTape.updateReviewFeeds = function(provider, results) {
1138  if (results == null)
1139  return;
1140  var indexFrame = document.getElementById("mashTape-panel-review-index");
1141  var doc = indexFrame.contentWindow.document;
1142  var body = doc.getElementsByTagName("body")[0];
1143 
1144  var favicon = provider.QueryInterface(Ci.sbIMashTapeReviewProvider)
1145  .providerIcon;
1146  for (var i=0; i<results.length; i++) {
1147  var entryDiv = doc.createElement("div");
1148  entryDiv.className = "row-unselected";
1149  var img = doc.createElement("img");
1150  img.className = "favicon";
1151  img.src = favicon;
1152  img.setAttribute("title", provider.providerName);
1153  entryDiv.appendChild(img);
1154 
1155  var metadata = doc.createElement("div");
1156  metadata.className = "metadata-review";
1157  var link = doc.createElement("span");
1158  entryDiv.setAttribute("mashTape-title", results[i].title);
1159  entryDiv.setAttribute("mashTape-favicon", favicon);
1160  entryDiv.setAttribute("mashTape-author", results[i].author);
1161  entryDiv.setAttribute("mashTape-authorUrl", results[i].authorUrl);
1162  entryDiv.setAttribute("mashTape-src", results[i].provider);
1163  entryDiv.setAttribute("mashTape-url", results[i].url);
1164  entryDiv.setAttribute("mashTape-time", results[i].time);
1165  entryDiv.setAttribute("mashTape-rating", results[i].rating);
1166  entryDiv.setAttribute("mashTape-content", results[i].content);
1167  entryDiv.addEventListener("click", function(e) {
1168  mashTape.loadReviewDetail(e.target);
1169  e.stopPropagation();
1170  e.preventDefault();
1171  }, false);
1172  link.innerHTML = results[i].title;
1173  metadata.appendChild(link);
1174 
1175  if (results[i].time > 0) {
1176  var dateObj = new Date(results[i].time);
1177  var dateSpan = doc.createElement("span");
1178  dateSpan.className = "time";
1179  dateSpan.innerHTML = dateObj.ago();
1180  metadata.appendChild(dateSpan);
1181  }
1182 
1183  if (results[i].rating > -1) {
1184  var ratings = doc.createElement("div");
1185  ratings.className = "ratings rate" + results[i].rating.toString();
1186  metadata.appendChild(ratings);
1187  }
1188 
1189  entryDiv.appendChild(metadata);
1190 
1191  // Figure out where in the DOM we want to insert this node
1192  entryDiv.setAttribute("mashTape-timestamp", results[i].time);
1193  entryDiv.setAttribute("mashTape-title", results[i].title);
1194  var divs = body.getElementsByTagName("div");
1195  var inserted = false;
1196  //var nextClass = 0;
1197  for (var j=0; j<divs.length; j++) {
1198  if (divs[j].className.indexOf("row-") == -1)
1199  continue;
1200  //divs[j].className = mashTape.classes[nextClass];
1201  //nextClass = Math.abs(nextClass-1);
1202 
1203  var otherTimestamp = divs[j].getAttribute("mashTape-timestamp");
1204  if ((otherTimestamp < results[i].time && !inserted &&
1205  otherTimestamp > 0)
1206  || (results[i].time == 0 && !inserted))
1207  {
1208  /*
1209  if (divs[j].className == "row-even") {
1210  entryDiv.className = "row-even";
1211  divs[j].className = "row-odd";
1212  } else {
1213  entryDiv.className = "row-odd";
1214  divs[j].className = "row-even";
1215  }
1216  */
1217  body.insertBefore(entryDiv, divs[j]);
1218  inserted = true;
1219  }
1220  }
1221  if (!inserted) {
1222  //entryDiv.className = mashTape.classes[nextClass];
1223  body.appendChild(entryDiv);
1224  }
1225  }
1226 }
1227 
1228 /****************************************************************************
1229  * RSS PROVIDER FUNCTIONS
1230  ****************************************************************************/
1231 mashTape.resetRssFrame = function() {
1232  // Clear existing data
1233  var indexFrame = document.getElementById("mashTape-panel-rss-index");
1234  var detailFrame = document.getElementById("mashTape-panel-rss-detail");
1235  var doc = indexFrame.contentWindow.document;
1236  var body = doc.getElementsByTagName("body")[0];
1237  while (body.firstChild)
1238  body.removeChild(body.firstChild);
1239 
1240  doc = detailFrame.contentWindow.document;
1241 
1242  // Hide any existing content
1243  var content = doc.getElementById("actual-content");
1244  content.style.display = "none";
1245 
1246  // Collapse the splitter
1247  var splitter = document.getElementById("mashTape-panel-rss-splitter");
1248  splitter.setAttribute("state", "collapsed");
1249 
1250  // Put in the loading image for the detail frame
1251  var loading = doc.getElementById("loading");
1252  loading.style.display = "block";
1253  var paneHeight = doc.getElementsByTagName("html")[0].clientHeight;
1254  // 64 is the height of the load.gif
1255  loading.style.marginTop = (paneHeight-64)/2 + "px";
1256 }
1257 
1258 mashTape.loadFirstRssFeed = function() {
1259  // only load if the loading is still showing (meaning the user hasn't
1260  // clicked on something else to load in the meantime)
1261  var indexFrame = document.getElementById("mashTape-panel-rss-index");
1262  var detailFrame = document.getElementById("mashTape-panel-rss-detail");
1263  if (detailFrame.contentWindow.document
1264  .getElementById("loading").style.display == "block")
1265  {
1266  var doc = indexFrame.contentWindow.document;
1267  var body = doc.getElementsByTagName("body")[0];
1268  var first = body.getElementsByTagName("div")[0];
1269  mashTape.loadRssDetail(first);
1270  }
1271 }
1272 
1273 mashTape.loadRssDetail = function(entry) {
1274  if (typeof(entry) == "undefined")
1275  return;
1276  while (!entry.hasAttribute("mashTape-title"))
1277  entry = entry.parentNode;
1278  var title = entry.getAttribute("mashTape-title");
1279  var src = entry.getAttribute("mashTape-src");
1280  var srcUrl = entry.getAttribute("mashTape-srcUrl");
1281  var time = entry.getAttribute("mashTape-time");
1282  var url = entry.getAttribute("mashTape-url");
1283  var favicon = entry.getAttribute("mashTape-favicon");
1284  var content = entry.getAttribute("mashTape-content");
1285 
1286  var detailFrame = document.getElementById("mashTape-panel-rss-detail");
1287  var doc = detailFrame.contentWindow.document;
1288  detailFrame.contentWindow.scrollTo(0,0);
1289  var detailTitle = doc.getElementById("title");
1290  var detailFavicon = doc.getElementById("favicon");
1291  var detailFaviconLink = doc.getElementById("link");
1292  var detailSubtitle = doc.getElementById("subtitle");
1293  var detailContent = doc.getElementById("content");
1294  var detailMore = doc.getElementById("read-more");
1295 
1296  // hide the loading div
1297  var loading = doc.getElementById("loading");
1298  loading.style.display = "none";
1299 
1300  // display the content divs
1301  var actualContent = doc.getElementById("actual-content");
1302  actualContent.style.display = "block";
1303 
1304  var containingDiv = entry;
1305  if (mashTape.selectedRss)
1306  mashTape.selectedRss.className =
1307  mashTape.selectedRss.className.replace("row-selected", "row-unselected");
1308  while (containingDiv.className.indexOf("row-") != 0)
1309  containingDiv = containingDiv.parentNode;
1310  containingDiv.className += " row-selected";
1311  mashTape.selectedRss = containingDiv;
1312 
1313  detailTitle.innerHTML = title;
1314  detailFavicon.src = favicon;
1315  detailFavicon.setAttribute("title", mashTape.strings.formatStringFromName(
1316  "extensions.mashTape.msg.rss_tooltip", [src], 1));
1317  detailFaviconLink.href = url;
1318 
1319  while (detailSubtitle.firstChild)
1320  detailSubtitle.removeChild(detailSubtitle.firstChild);
1321  detailSubtitle.appendChild(doc.createTextNode(
1322  mashTape.strings.GetStringFromName("extensions.mashTape.msg.by") + " "));
1323  var provider = doc.createElement("a");
1324  provider.href = srcUrl;
1325  provider.innerHTML = src;
1326  detailSubtitle.appendChild(provider);
1327  var dateObj = new Date(parseInt(time));
1328  var timestamp = doc.createElement("span");
1329  timestamp.className = "time";
1330  timestamp.innerHTML = dateObj.ago();
1331  detailSubtitle.appendChild(timestamp);
1332 
1333  detailContent.innerHTML = content;
1334  detailMore.innerHTML = "<a href='" + url + "'>" +
1335  mashTape.strings.GetStringFromName("extensions.mashTape.msg.readorig") + "</a>";
1336 
1337 }
1338 
1339 mashTape.updateRssFeeds = function(provider, results) {
1340  if (results == null)
1341  return;
1342  var indexFrame = document.getElementById("mashTape-panel-rss-index");
1343  var doc = indexFrame.contentWindow.document;
1344  var body = doc.getElementsByTagName("body")[0];
1345 
1346  var favicon = provider.QueryInterface(Ci.sbIMashTapeRSSProvider)
1347  .providerIcon;
1348  for (var i=0; i<results.length; i++) {
1349  var entryDiv = doc.createElement("div");
1350  entryDiv.className = "row-unselected";
1351  var img = doc.createElement("img");
1352  img.className = "favicon";
1353  img.src = favicon;
1354  img.setAttribute("title", provider.providerName);
1355  entryDiv.appendChild(img);
1356 
1357  var metadata = doc.createElement("div");
1358  metadata.className = "metadata-rss";
1359  var link = doc.createElement("span");
1360  entryDiv.setAttribute("mashTape-title", results[i].title);
1361  entryDiv.setAttribute("mashTape-favicon", favicon);
1362  entryDiv.setAttribute("mashTape-src", results[i].provider);
1363  entryDiv.setAttribute("mashTape-srcUrl", results[i].providerUrl);
1364  entryDiv.setAttribute("mashTape-time", results[i].time);
1365  entryDiv.setAttribute("mashTape-url", results[i].url);
1366  entryDiv.setAttribute("mashTape-content", results[i].content);
1367  entryDiv.addEventListener("click", function(e) {
1368  mashTape.loadRssDetail(e.target);
1369  e.stopPropagation();
1370  e.preventDefault();
1371  }, false);
1372  link.innerHTML = results[i].title;
1373  metadata.appendChild(link);
1374 
1375  var dateObj = new Date(results[i].time);
1376  var dateSpan = doc.createElement("span");
1377  dateSpan.className = "time";
1378  dateSpan.innerHTML = dateObj.ago();
1379  metadata.appendChild(dateSpan);
1380  if (results[i].content.indexOf("<img") >= 0) {
1381  var img = doc.createElement("img");
1382  img.src = "chrome://mashtape/skin/photos.png";
1383  img.setAttribute("title", mashTape.strings.GetStringFromName(
1384  "extensions.mashTape.rss.photo"));
1385  img.className = "legend";
1386  metadata.appendChild(img);
1387  }
1388  if (results[i].content.indexOf("<object") >= 0 ||
1389  results[i].content.indexOf("<embed") >= 0) {
1390  var img = doc.createElement("img");
1391  img.src = "chrome://mashtape/skin/video.png";
1392  img.setAttribute("title", mashTape.strings.GetStringFromName(
1393  "extensions.mashTape.rss.video"));
1394  img.className = "legend";
1395  metadata.appendChild(img);
1396  }
1397  entryDiv.appendChild(metadata);
1398 
1399  // Figure out where in the DOM we want to insert this node
1400  entryDiv.setAttribute("mashTape-timestamp", results[i].time);
1401  entryDiv.setAttribute("mashTape-title", results[i].title);
1402  var divs = body.getElementsByTagName("div");
1403  var inserted = false;
1404  var nextClass = 0;
1405  for (var j=0; j<divs.length; j++) {
1406  if (divs[j].className.indexOf("row-") == -1)
1407  continue;
1408  //divs[j].className = mashTape.classes[nextClass];
1409  nextClass = Math.abs(nextClass-1);
1410 
1411  var otherTimestamp = divs[j].getAttribute("mashTape-timestamp");
1412  if (otherTimestamp < results[i].time && !inserted) {
1413  /*
1414  if (divs[j].className == "row-even") {
1415  entryDiv.className = "row-even";
1416  divs[j].className = "row-odd";
1417  } else {
1418  entryDiv.className = "row-odd";
1419  divs[j].className = "row-even";
1420  }
1421  */
1422  body.insertBefore(entryDiv, divs[j]);
1423  inserted = true;
1424  }
1425  }
1426  if (!inserted) {
1427  //entryDiv.className = mashTape.classes[nextClass];
1428  body.appendChild(entryDiv);
1429  }
1430  }
1431 }
1432 
1433 /****************************************************************************
1434  * PHOTO PROVIDER FUNCTIONS
1435  ****************************************************************************/
1436 mashTape.resetPhotoFrame = function() {
1437  // delete the iframe child, create a new one, and reinsert it
1438  // before the no-data-found hbox
1439  var photoPanel = document.getElementById("mashTape-panel-photo");
1440  var iframe = document.getElementById("mashTape-frame-photo");
1441  photoPanel.removeChild(iframe);
1442 
1443  var iframe = document.createElement("iframe");
1444  iframe.id = "mashTape-frame-photo";
1445  iframe.className = "content-frame";
1446  iframe.setAttribute("flex", 1);
1447  iframe.setAttribute("tooltip", "aHTMLTooltip");
1448  iframe.setAttribute("src","chrome://mashtape/content/iframePhoto.html");
1449 
1450  iframe.addEventListener("DOMContentLoaded", mashTape.photoLoadListener,false);
1451  photoPanel.insertBefore(iframe, photoPanel.firstChild);
1452  mashTape.photoFrame = iframe;
1453 }
1454 mashTape.photoLoadListener = function(e) {
1455  var iframe = e.currentTarget;
1456  iframe.removeEventListener("DOMContentLoaded",
1457  mashTape.photoLoadListener, false);
1458 
1459  // Put in the loading image for the photo frame
1460  var doc = mashTape.photoFrame.contentWindow.document;
1461  var loading = doc.getElementById("loading");
1462  loading.style.display = "block";
1463  var paneHeight = doc.getElementsByTagName("html")[0].clientHeight;
1464  // 32 is the height of the load.gif
1465  loading.style.marginTop = (paneHeight-32)/2 + "px";
1466  mashTape.photoFrameLoaded = true;
1467  if (mashTape.photosReady != null)
1468  mashTape.drawPhotoStream(mashTape.photosReady.provider,
1469  mashTape.photosReady.results);
1470 }
1471 
1472 mashTape.updatePhoto = function(provider, results) {
1473  if (!mashTape.photoFrameLoaded) {
1474  mashTape.photosReady = {
1475  provider : provider,
1476  results : results,
1477  }
1478  } else
1479  mashTape.drawPhotoStream(provider, results);
1480 
1481  // trigger mashTapeListener updates
1482  var photos = new Array();
1483  for (var i=0; i<results.length; i++) {
1484  var result = results[i];
1485  photos.push({
1486  imageUrl : result.medium,
1487  imageTitle : result.title,
1488  authorName : result.owner,
1489  authorUrl : result.ownerUrl,
1490  timestamp: result.time
1491  });
1492  }
1493 
1494  mashTape.mgr.updatePhotos(photos, photos.length);
1495 }
1496 
1497 mashTape.drawPhotoStream = function(provider, results) {
1498  var doc = mashTape.photoFrame.contentWindow.document;
1499  var body = doc.getElementsByTagName("body")[0];
1500 
1501  var paneWidth = doc.width;
1502  var paneHeight = doc.getElementsByTagName("html")[0].clientHeight;
1503 
1504  // hide the loading graphic
1505  var loading = doc.getElementById("loading");
1506  loading.style.display = "none";
1507 
1508  // unhide the buttons
1509  doc.getElementById("buttons").style.visibility = "visible";
1510 
1511  var images = doc.getElementById("box");
1512 
1513  // Set the mask height to the height of the display pane
1514  doc.getElementById("mask").style.height = paneHeight + "px";
1515  doc.getElementById("mask").style.display = "block";
1516 
1517  // Add resize handler for photostream
1518  mashTape.photoFrame.contentWindow.addEventListener("resize", function(e) {
1519  e.preventDefault();
1520  e.stopPropagation();
1521 
1522  var mTWindow = mashTape.photoFrame.contentWindow;
1523 
1524  var nS5 = mTWindow.nS5;
1525  //nS5.stop();
1526 
1527  var frameWidth = doc.getElementsByTagName("html")[0].clientWidth;
1528  var frameHeight = doc.getElementsByTagName("html")[0].clientHeight;
1529  //dump("New frame size: " + frameWidth + "x" + frameHeight + "\n");
1530 
1531  doc.getElementById("mask").style.height = frameHeight + "px";
1532  var imageElements = images.getElementsByTagName("img");
1533  for (var i=0; i<imageElements.length; i++) {
1534  var img = imageElements[i];
1535  //dump("img " + i + " - " + img.width + "x" + img.height + "\n");
1536  var ratio = img.height / frameHeight;
1537  img.height = frameHeight;
1538 
1539  // Check to see if images should be reloaded, only if we loaded
1540  // the small images, and our resized height is greater than the
1541  // display pane height cutoff
1542  if (mTWindow.imageItems[i].small == img.src &&
1543  frameHeight > PANE_CUTOFF)
1544  {
1545  img.src = mTWindow.imageItems[i].medium;
1546  }
1547  }
1548 
1549  // Offset recalculation for the photostream
1550  if (typeof(nS5) == "undefined")
1551  return;
1552  var current = nS5.currentIndex;
1553  //dump("current index: " + current + "\n");
1554 
1555  // Invalidate offsets for all subsequent images
1556  for (var i=current; i<imageElements.length; i++)
1557  mTWindow.imageItems[i].offset = null;
1558 
1559  // Recalculate offsets for all photos from start to current
1560  mTWindow.imageItems[0].offset = 0;
1561  for (var i=1; i<=current; i++) {
1562  var previousOffset = mTWindow.imageItems[i-1].offset;
1563  var currentOffset = previousOffset - imageElements[i-1].width;
1564  //dump("reset: "+ (i-1).toString() + " = "+ previousOffset + "\n");
1565  mTWindow.imageItems[i].offset = currentOffset;
1566  }
1567 
1568  // Reset the stream
1569  mTWindow.nS5.gotoSlide(current);
1570 
1571  }, false);
1572 
1573  // Build our iframe's JS image array
1574  mashTape.photoFrame.contentWindow.imageItems = new Array();
1575  if (results.length > PHOTO_PRELOAD)
1576  mashTape.imageLoadCount = PHOTO_PRELOAD;
1577  else
1578  mashTape.imageLoadCount = results.length;
1579  mashTape.imageResults = results;
1580  mashTape.photoStreamReady = false;
1581  mashTape.photoStreamRunning = false;
1582  for (var i=0; i<mashTape.imageLoadCount; i++) {
1583  var item = results[i];
1584  var imgcontainer = doc.createElement("span");
1585  var img = doc.createElement("img");
1586  // add a load listener for the first N items so we don't start
1587  // scrolling and loading the rest until those N are loaded
1588  img.addEventListener("load", mashTape.imageLoadListener, false);
1589  if (paneHeight < PANE_CUTOFF)
1590  img.src = item.small;
1591  else
1592  img.src = item.medium;
1593  img.setAttribute("mashTape-href", item.url);
1594  img.addEventListener("click", function() {
1595  mashTape.photoFrame.contentWindow.mashTape_openLink(
1596  this.getAttribute("mashTape-href"));
1597  }, false);
1598  img.height = paneHeight;
1599  imgcontainer.appendChild(img);
1600  images.appendChild(imgcontainer);
1601 
1602  var title = item.title.replace(/'/g, "\\'");
1603  var author = item.owner.replace(/'/g, "\\'");
1604  var timestamp = item.time;
1605  mashTape.photoFrame.contentWindow.imageItems.push({
1606  title: title,
1607  author: author,
1608  authorUrl: item.ownerUrl,
1609  date: timestamp,
1610  el: img,
1611  link: item.url,
1612  small: item.small,
1613  medium: item.medium
1614  });
1615  }
1616 }
1617 
1618 mashTape.imageLoadListener = function(e) {
1619  e.target.removeEventListener("load", mashTape.imageLoadListener, false);
1620  mashTape.imageLoadCount--;
1621  var doc = mashTape.photoFrame.contentWindow.document;
1622 
1623  if (mashTape.imageLoadCount > 0)
1624  return;
1625 
1626  // once we hit 0, we've loaded all our initial batch of images
1627  // check to see if we have remaining images we should start queuing up
1628  // to be loaded
1629  if (mashTape.imageResults.length > PHOTO_PRELOAD) {
1630  var images = doc.getElementById("box");
1631  var paneHeight = doc.getElementsByTagName("html")[0].clientHeight;
1632 
1633  for (var i=PHOTO_PRELOAD; i<mashTape.imageResults.length; i++) {
1634  var item = mashTape.imageResults[i];
1635  var imgcontainer = doc.createElement("span");
1636  var img = doc.createElement("img");
1637  if (paneHeight < PANE_CUTOFF)
1638  img.src = item.small;
1639  else
1640  img.src = item.medium;
1641  img.setAttribute("mashTape-href", item.url);
1642  img.addEventListener("click", function() {
1643  mashTape.photoFrame.contentWindow.mashTape_openLink(
1644  this.getAttribute("mashTape-href"));
1645  }, false);
1646  img.height = paneHeight;
1647  imgcontainer.appendChild(img);
1648  images.appendChild(imgcontainer);
1649 
1650  mashTape.photoFrame.contentWindow.imageItems.push({
1651  title: item.title.replace(/'/g, "\\'"),
1652  author: item.owner.replace(/'/g, "\\'"),
1653  authorUrl: item.ownerUrl,
1654  date: item.time,
1655  el: img,
1656  link: item.url,
1657  small: item.small,
1658  medium: item.medium
1659  });
1660  }
1661  }
1662 
1663  // if the Photos tab is currently focused, then start the photo stream
1664  // otherwise add a listener to delay triggering the photo stream until
1665  // the tab is in focus
1666  if (document.getElementById("mashTape-nav-radiogroup").selectedItem ==
1667  document.getElementById("mashTape-nav-tab-photo"))
1668  {
1669  mashTape.photoFrame.contentWindow.mashTape_triggerPhotoStream(
1670  mashTape.photoFrame.contentWindow.imageItems, doc.width);
1671  } else {
1672  mashTape.photoStreamReady = true;
1673  mashTape.photoStreamRunning = false;
1674  }
1675 }
1676 
1677 mashTape.playPausePhotoListener = function(inPhotoTab) {
1678  if (inPhotoTab) {
1679  // switched to the photo tab, need to resume or start it
1680  if (!mashTape.photoStreamRunning) {
1681  mashTape.photoStreamRunning = true;
1682  var doc = mashTape.photoFrame.contentWindow.document;
1683  mashTape.photoFrame.contentWindow.mashTape_triggerPhotoStream(
1684  mashTape.photoFrame.contentWindow.imageItems, doc.width);
1685  } else {
1686  var mTWindow = mashTape.photoFrame.contentWindow;
1687  mTWindow.nS5.playpause(mTWindow.nS5.interval, 'next');
1688  }
1689  } else if (mashTape.selectedTab == "photo") {
1690  // previously selected tab was the photo tab, need to pause it
1691  var mTWindow = mashTape.photoFrame.contentWindow;
1692  mTWindow.nS5.playpause(mTWindow.nS5.interval, 'next');
1693  }
1694 }
1695 
1696 /****************************************************************************
1697  * FLASH PROVIDER FUNCTIONS
1698  ****************************************************************************/
1699 mashTape.resetFlashFrame = function() {
1700  // Reset the index frame contents
1701  var indexFrame = document.getElementById("mashTape-panel-flash-index");
1702  var detailFrame = document.getElementById("mashTape-panel-flash-detail");
1703  var doc = indexFrame.contentWindow.document;
1704  var body = doc.getElementsByTagName("body")[0];
1705  while (body.firstChild)
1706  body.removeChild(body.firstChild);
1707 
1708  doc = detailFrame.contentWindow.document;
1709 
1710  // Hide any existing content
1711  var content = doc.getElementById("actual-content");
1712  content.style.display = "none";
1713 
1714  // Collapse the splitter
1715  var splitter = document.getElementById("mashTape-panel-flash-splitter");
1716  splitter.setAttribute("state", "collapsed");
1717 
1718  // Put in the loading image for the detail frame
1719  var loading = doc.getElementById("loading");
1720  loading.style.display = "block";
1721  var paneHeight = doc.getElementsByTagName("html")[0].clientHeight;
1722  // 32 is the height of the load.gif
1723  loading.style.marginTop = (paneHeight-32)/2 + "px";
1724 }
1725 
1726 mashTape.durationFromSeconds = function(seconds) {
1727  // Calculate the duration
1728  var minutes = parseInt(seconds / 60);
1729  seconds = seconds % 60;
1730  var hours = parseInt(minutes / 60);
1731  minutes = minutes % 60;
1732 
1733  var str = seconds;
1734  if (parseInt(seconds) < 10)
1735  str = "0" + seconds.toString();
1736  if (minutes > 0 || hours > 0) {
1737  if (minutes < 10)
1738  minutes = "0" + minutes.toString();
1739  str = minutes + ":" + str;
1740  if (hours > 0)
1741  str = hours.toString() + ":" + str;
1742  }
1743  return (str);
1744 }
1745 
1746 mashTape.loadFirstFlashFeed = function() {
1747  // only load if the loading is still showing (meaning the user hasn't
1748  // clicked on something else to load in the meantime)
1749  var indexFrame = document.getElementById("mashTape-panel-flash-index");
1750  var detailFrame = document.getElementById("mashTape-panel-flash-detail");
1751  if (detailFrame.contentWindow.document
1752  .getElementById("loading").style.display == "block")
1753  {
1754  var doc = indexFrame.contentWindow.document;
1755  var body = doc.getElementsByTagName("body")[0];
1756  // Only load something if we have something
1757  if (body.getElementsByTagName("div").length <= 0) {
1758  mashTape.noDataTab("flash");
1759  return;
1760  }
1761  var first = body.getElementsByTagName("div")[0];
1762  mashTape.loadFlashDetail(first);
1763  }
1764 }
1765 
1766 
1767 mashTape.loadFlashDetail = function(el) {
1768  if (typeof(el) == "undefined")
1769  return;
1770  var title = el.getAttribute("mashTape-title");
1771  var time = el.getAttribute("mashTape-time");
1772  var url = el.getAttribute("mashTape-url");
1773  var favicon = el.getAttribute("mashTape-favicon");
1774  var swfUrl = el.getAttribute("mashTape-video");
1775  var description = el.getAttribute("mashTape-description");
1776  var author = el.getAttribute("mashTape-author");
1777  var authorUrl = el.getAttribute("mashTape-author-url");
1778  var duration = el.getAttribute("mashTape-duration");
1779  var width = el.getAttribute("mashTape-width");
1780  var ratio = el.getAttribute("mashTape-ratio");
1781  var height = el.getAttribute("mashTape-height");
1782  var provider = el.getAttribute("mashTape-provider");
1783  var flashVars;
1784  if (el.hasAttribute("mashTape-flashvars"))
1785  flashVars = el.getAttribute("mashTape-flashvars");
1786 
1787  /* Add privileged JS to the remote window DOM */
1788  var indexFrame = document.getElementById("mashTape-panel-flash-index");
1789  var detailFrame = document.getElementById("mashTape-panel-flash-detail");
1790  var flashWindow = detailFrame.contentWindow;
1791  var doc = detailFrame.contentWindow.document;
1792 
1793  var script = doc.createElement("script");
1794  script.setAttribute("src", "chrome://mashtape/content/iframe-flash.js");
1795  doc.body.appendChild(script);
1796 
1797  doc.setUserData("mashTapeRatio", ratio, null);
1798 
1799  function openLink(e) {
1800  e.preventDefault();
1801  e.stopPropagation();
1802  var target = e.target;
1803  while (target != null && !target.href) {
1804  target = target.parentNode;
1805  }
1806  if (target == null)
1807  return;
1808  if (target.href) {
1809  Components.classes['@mozilla.org/appshell/window-mediator;1']
1810  .getService(Components.interfaces.nsIWindowMediator)
1811  .getMostRecentWindow('Songbird:Main').gBrowser
1812  .loadOneTab(target.href);
1813  }
1814  }
1815 
1816  if (typeof(mashTape.flashListenerAdded) == "undefined") {
1817  flashWindow.addEventListener('click', openLink, false);
1818  mashTape.flashListenerAdded = true;
1819  }
1820 
1821  var detailVideo = doc.getElementById("video");
1822  var detailFavicon = doc.getElementById("favicon");
1823  var detailFaviconLink = doc.getElementById("link");
1824  var detailTitle = doc.getElementById("title");
1825  var detailTime = doc.getElementById("time");
1826  var detailAuthor = doc.getElementById("author");
1827  var detailDescr = doc.getElementById("description");
1828 
1829  // hide the loading div
1830  var loading = doc.getElementById("loading");
1831  loading.style.display = "none";
1832 
1833  // display the content divs
1834  var actualContent = doc.getElementById("actual-content");
1835  actualContent.style.display = "block";
1836  doc.getElementById("caption").href = url;
1837 
1838  var containingDiv = el;
1839  if (mashTape.selectedFlash)
1840  mashTape.selectedFlash.className =
1841  mashTape.selectedFlash.className.replace("row-selected","");
1842  while (containingDiv.className.indexOf("row-") != 0)
1843  containingDiv = containingDiv.parentNode;
1844  containingDiv.className += " row-selected";
1845  mashTape.selectedFlash = containingDiv;
1846 
1847  // Reset some divs
1848  while (detailTitle.firstChild)
1849  detailTitle.removeChild(detailTitle.firstChild);
1850  while (detailAuthor.firstChild)
1851  detailAuthor.removeChild(detailAuthor.firstChild);
1852  while (detailVideo.firstChild)
1853  detailVideo.removeChild(detailVideo.firstChild);
1854 
1855  // Set the video
1856  var swf = doc.createElement("object");
1857  swf.setAttribute("data", swfUrl);
1858 
1859  mashTape.log("Loading movie from: " + swfUrl, true);
1860 
1861  swf.setAttribute("type", "application/x-shockwave-flash");
1862  swf.setAttribute("id", "mTFlashObject");
1863  swf.setAttribute("name", "mTFlashObject");
1864  swf.setAttribute("width", parseInt(width));
1865  swf.setAttribute("height", parseInt(height));
1866  if (el.hasAttribute("mashTape-flashvars"))
1867  swf.setAttribute("flashVars", flashVars);
1868  swf.setAttribute("mashTape-provider", provider);
1869 
1870  var param = doc.createElement("param");
1871  param.setAttribute("name", "allowScriptAccess");
1872  param.setAttribute("value", "always");
1873  swf.appendChild(param);
1874 
1875  param = doc.createElement("param");
1876  param.setAttribute("name", "quality");
1877  param.setAttribute("value", "high");
1878  swf.appendChild(param);
1879 
1880  param = doc.createElement("param");
1881  param.setAttribute("name", "allowFullScreen");
1882  param.setAttribute("value", "true");
1883  swf.appendChild(param);
1884 
1885  param = doc.createElement("param");
1886  param.setAttribute("name", "bgcolor");
1887  param.setAttribute("value", "#000000");
1888  swf.appendChild(param);
1889 
1890  detailVideo.appendChild(swf);
1891 
1892  // Set the title
1893  var detailLink = doc.createElement("a");
1894  detailLink.href = url;
1895  //detailLink.appendChild(doc.createTextNode(title));
1896  detailLink.innerHTML = title;
1897  detailTitle.appendChild(detailLink);
1898 
1899  // Set the favicon, & duration
1900  detailFavicon.src = favicon;
1901  detailFavicon.setAttribute("title", mashTape.strings.formatStringFromName(
1902  "extensions.mashTape.msg.flash_tooltip", [provider], 1));
1903  detailFaviconLink.href = url;
1904  if (duration > 0)
1905  detailTime.innerHTML = mashTape.durationFromSeconds(duration);
1906 
1907  // Set the author information
1908  detailAuthor.appendChild(doc.createTextNode(
1909  mashTape.strings.GetStringFromName("extensions.mashTape.msg.by") +" "));
1910  var authorPage = doc.createElement("a");
1911  authorPage.href = authorUrl;
1912  authorPage.innerHTML = author;
1913  detailAuthor.appendChild(authorPage);
1914 
1915  // Dispatch a resize event to trigger the video resize
1916  var e = doc.createEvent("UIEvents");
1917  e.initUIEvent("resize", true, true, window, 1);
1918  doc.dispatchEvent(e);
1919 
1920  // Set the description
1921  //detailDescr.innerHTML = description;
1922 }
1923 
1924 mashTape.updateFlash = function(provider, results) {
1925  if (results == null)
1926  return;
1927 
1928  var indexFrame = document.getElementById("mashTape-panel-flash-index");
1929  var doc = indexFrame.contentWindow.document;
1930  var body = doc.getElementsByTagName("body")[0];
1931 
1932  var favicon = provider.QueryInterface(Ci.sbIMashTapeFlashProvider)
1933  .providerIcon;
1934  for (var i=0; i<results.length; i++) {
1935  var entryDiv = doc.createElement("div");
1936  entryDiv.className = "row-unselected";
1937  var video = results[i];
1938 
1939  entryDiv.setAttribute("mashTape-title", video.title);
1940  entryDiv.setAttribute("mashTape-favicon", favicon);
1941  entryDiv.setAttribute("mashTape-time", video.time);
1942  entryDiv.setAttribute("mashTape-url", video.url);
1943  entryDiv.setAttribute("mashTape-video", video.swfUrl);
1944  entryDiv.setAttribute("mashTape-description", video.description);
1945  entryDiv.setAttribute("mashTape-author", video.author);
1946  entryDiv.setAttribute("mashTape-author-url", video.authorUrl);
1947  entryDiv.setAttribute("mashTape-duration", video.duration);
1948  entryDiv.setAttribute("mashTape-width", video.width);
1949  entryDiv.setAttribute("mashTape-ratio", video.ratio);
1950  entryDiv.setAttribute("mashTape-height", video.height);
1951  entryDiv.setAttribute("mashTape-provider", provider.providerName);
1952 
1953  if (video.flashVars)
1954  entryDiv.setAttribute("mashTape-flashvars", video.flashVars);
1955  entryDiv.addEventListener("click", function(e) {
1956  var node = e.target;
1957  while (!node.hasAttribute("mashTape-title"))
1958  node = node.parentNode;
1959  mashTape.loadFlashDetail(node);
1960  e.stopPropagation();
1961  e.preventDefault();
1962  }, false);
1963 
1964  var thumbLink = doc.createElement("span");
1965  var thumbImg = doc.createElement("img");
1966  thumbImg.className = "thumbnail";
1967  thumbImg.src = results[i].thumbnail;
1968  thumbImg.width = "60";
1969  thumbLink.appendChild(thumbImg);
1970  entryDiv.appendChild(thumbLink);
1971  thumbImg.addEventListener("load", function() {
1972  // in the event we've changed tracks and this img is no longer
1973  // attached to the DOM then just bail out
1974  if (this.parentNode == null)
1975  return;
1976  var theDiv = this.parentNode.parentNode;
1977  var theImg = this;
1978  if (theDiv.clientHeight-10 < theImg.clientHeight)
1979  theDiv.style.height = (theImg.clientHeight + 5) + "px";
1980  }, false);
1981 
1982  var faviconImg = doc.createElement("img");
1983  faviconImg.className = "favicon favicon-flash";
1984  faviconImg.src = favicon;
1985  faviconImg.setAttribute("title", provider.providerName);
1986  entryDiv.appendChild(faviconImg);
1987 
1988  var metadata = doc.createElement("div");
1989  metadata.className = "metadata-flash";
1990  var link = doc.createElement("span");
1991  link.innerHTML = results[i].title;
1992  metadata.appendChild(link);
1993  metadata.appendChild(doc.createElement("br"));
1994 
1995  if (results[i].time != 0) {
1996  var dateObj = new Date(results[i].time);
1997  var dateSpan = doc.createElement("span");
1998  dateSpan.className = "time";
1999  dateSpan.innerHTML = dateObj.ago();
2000  metadata.appendChild(dateSpan);
2001  }
2002 
2003  entryDiv.appendChild(metadata);
2004 
2005  // Figure out where in the DOM we want to insert this node
2006  entryDiv.setAttribute("mashTape-timestamp", results[i].time);
2007  entryDiv.setAttribute("mashTape-title", results[i].title);
2008  var divs = body.getElementsByTagName("div");
2009  var inserted = false;
2010  var nextClass = 0;
2011  for (var j=0; j<divs.length; j++) {
2012  if (divs[j].className.indexOf("row-") == -1)
2013  continue;
2014  //divs[j].className = mashTape.classes[nextClass];
2015  nextClass = Math.abs(nextClass-1);
2016 
2017  var otherTimestamp = divs[j].getAttribute("mashTape-timestamp");
2018  if (otherTimestamp < results[i].time && !inserted) {
2019  /*
2020  if (divs[j].className == "row-even") {
2021  entryDiv.className = "row-even";
2022  divs[j].className = "row-odd";
2023  } else {
2024  entryDiv.className = "row-odd";
2025  divs[j].className = "row-even";
2026  }
2027  */
2028  body.insertBefore(entryDiv, divs[j]);
2029  inserted = true;
2030  }
2031  }
2032  if (!inserted) {
2033  //entryDiv.className = mashTape.classes[nextClass];
2034  body.appendChild(entryDiv);
2035  }
2036  }
2037 }
2038 
2039 /****************************************************************************
2040  * Miscellaneous routines
2041  ****************************************************************************/
2042 // Maximise the display pane
2043 mashTape.maximiseDisplayPane = function(ev) {
2044  var flashSplitter = document.getElementById("mashTape-panel-flash-splitter");
2045 
2046  var mainDoc = Cc['@mozilla.org/appshell/window-mediator;1']
2047  .getService(Ci.nsIWindowMediator).getMostRecentWindow('Songbird:Main')
2048  .window.document;
2049  var dp = mashTape.displayPane;
2050  var splitterId = mashTape.displayPane.getAttribute("splitter");
2051  var dpSplitter = mainDoc.getElementById(splitterId);
2052 
2053  if (mashTape.expanded) {
2054  // we're already expanded, so restore to the non-expanded state
2055  mashTape.expanded = false;
2056 
2057  dp.setAttribute("flex", 0);
2058 
2059  // open the splitter back up
2060  flashSplitter.setAttribute("state", "open");
2061 
2062  // reset the display pane splitter
2063  dpSplitter.setAttribute("collapse", "after");
2064  dpSplitter.setAttribute("state", "open");
2065 
2066  dp.height = mashTape.height;
2067 
2068  mashTape.displayPaneMaxButton.style.listStyleImage =
2069  "url('chrome://songbird/skin/display-pane/button-maximize.png')";
2070  } else {
2071  // expand!
2072  mashTape.expanded = true;
2073 
2074  mashTape.height = dp.height;
2075 
2076  dp.setAttribute("flex", 1);
2077  // collapse the splitter
2078  flashSplitter.setAttribute("state", "collapsed");
2079 
2080  // collapse the display pane splitter
2081  dpSplitter.setAttribute("collapse", "before");
2082  dpSplitter.setAttribute("state", "collapsed");
2083 
2084  mashTape.displayPaneMaxButton.style.listStyleImage =
2085  "url('chrome://songbird/skin/display-pane/button-restore.png')";
2086  }
2087 }
2088 
2089 // Our listener for auto-hiding mashTape when switching to web views
2090 mashTape.locationListener = {
2091  QueryInterface: function(aIID) {
2092  if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
2093  aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
2094  aIID.equals(Components.interfaces.nsISupports))
2095  return this;
2096  throw Components.results.NS_NOINTERFACE;
2097  },
2098  onStateChange: function(aProgress, aRequest, aFlag, aStatus) { return(0); },
2099  onLocationChange:function(a, request, url) {
2100  if (url == null)
2101  return;
2102 
2103  if (!Application.prefs.getValue("extensions.mashTape.autohide", false))
2104  return;
2105 
2106  // don't do anything for firstrun
2107  if (url.spec == "chrome://songbird/content/mediapages/firstrun.xul")
2108  return;
2109  if (typeof(mashTape) == "undefined") {
2110  return;
2111  }
2112  var mainDoc = Cc['@mozilla.org/appshell/window-mediator;1']
2113  .getService(Ci.nsIWindowMediator)
2114  .getMostRecentWindow('Songbird:Main').window.document;
2115  var splitterId = mashTape.displayPane.getAttribute("splitter");
2116  var dpSplitter = mainDoc.getElementById(splitterId);
2117 
2118  if ((gBrowser.currentMediaListView &&
2119  gBrowser.selectedTab == gBrowser.mediaTab) ||
2120  url.spec.indexOf("chrome://shoutcast-radio/") == 0)
2121  {
2122  // expose the mashTape display pane only if we auto-hid it
2123  if (mashTape.autoHidden) {
2124  mashTape.displayPane.collapsed = false;
2125  mashTape.autoHidden = false;
2126  }
2127  } else {
2128  // collapse the mashTape display pane if it's not already
2129  if (!mashTape.displayPane.collapsed) {
2130  mashTape.displayPane.collapsed = true;
2131  mashTape.autoHidden = true;
2132  }
2133  }
2134  },
2135  onProgressChange: function() {return 0;},
2136  onStatusChange: function() {return 0;},
2137  onSecurityChange: function() {return 0;},
2138  onLinkIconAvailable: function() {return 0;}
2139 }
2140 
2141 // Our observer for watching the tab-enabled/disabled preferences and hiding
2142 // the tabs appropriately
2143 mashTape.prefObserver = {
2144  observe: function(subject, topic, data) {
2145  if (subject instanceof Components.interfaces.nsIPrefBranch) {
2146  var pref = data.split(".");
2147  if (pref.length == 2 && pref[1] == "enabled") {
2148  var tab;
2149  var enabled = subject.getBoolPref(data);
2150  switch (pref[0]) {
2151  case "info":
2152  tab = mashTape.infoTab;
2153  break;
2154  case "review":
2155  tab = mashTape.reviewTab;
2156  break;
2157  case "rss":
2158  tab = mashTape.rssTab;
2159  break;
2160  case "photo":
2161  tab = mashTape.photoTab;
2162  break;
2163  case "flash":
2164  tab = mashTape.flashTab;
2165  break;
2166  }
2167  if (enabled) {
2168  tab.style.visibility = "visible";
2169 #ifdef METRICS_ENABLED
2170  gMetrics.metricsInc("mashtape", pref[0], "tab.enabled");
2171 #endif
2172  mashTape.noDataTab(pref[0]);
2173  } else {
2174  tab.style.visibility = "collapse";
2175 #ifdef METRICS_ENABLED
2176  gMetrics.metricsInc("mashtape", pref[0], "tab.disabled");
2177 #endif
2178  // if we're currently on the tab, then hide it and select some
2179  // other tab
2180  mashTape.selectPane();
2181  }
2182  } else if (data == "autohide") {
2183 #ifdef METRICS_ENABLED
2184  var enabled = subject.getBoolPref(data);
2185  if (enabled)
2186  gMetrics.metricsInc("mashtape", "autohide", "enabled");
2187  else
2188  gMetrics.metricsInc("mashtape", "autohide", "disabled");
2189 #endif
2190  } else if (data == "defaultpane") {
2191  var which = subject.getCharPref(data);
2192 #ifdef METRICS_ENABLED
2193  gMetrics.metricsInc("mashtape", "defaultpane", which);
2194 #endif
2195  mashTape.selectPane();
2196  } else if (data == "photo.speed") {
2197  mashTape.photoFrame.contentWindow.mashTape_updatePhotoStreamSpeed();
2198  }
2199  }
2200  }
2201 }
2202 
2203 window.addEventListener("DOMContentLoaded", mashTape.init, false);
2204 window.addEventListener("unload", mashTape.unload, false);
2205 
2206 mashTape.tooltip = function(tipElement)
2207 {
2208  var retVal = false;
2209  if (tipElement.namespaceURI ==
2210  "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")
2211  return retVal;
2212 
2213  const XLinkNS = "http://www.w3.org/1999/xlink";
2214 
2215 
2216  var titleText = null;
2217  var XLinkTitleText = null;
2218  var direction = tipElement.ownerDocument.dir;
2219 
2220  while (!titleText && !XLinkTitleText && tipElement) {
2221  if (tipElement.nodeType == Node.ELEMENT_NODE) {
2222  titleText = tipElement.getAttribute("title");
2223  XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
2224  var defView = tipElement.ownerDocument.defaultView;
2225  // XXX Work around bug 350679:
2226  // "Tooltips can be fired in documents with no view".
2227  if (!defView)
2228  return retVal;
2229  direction = defView.getComputedStyle(tipElement, "")
2230  .getPropertyValue("direction");
2231  }
2232  tipElement = tipElement.parentNode;
2233  }
2234 
2235  var tipNode = document.getElementById("aHTMLTooltip");
2236  tipNode.style.direction = direction;
2237 
2238  for each (var t in [titleText, XLinkTitleText]) {
2239  if (t && /\S/.test(t)) {
2240 
2241  // Per HTML 4.01 6.2 (CDATA section), literal CRs and tabs should be
2242  // replaced with spaces, and LFs should be removed entirely.
2243  // XXX Bug 322270: We don't preserve the result of entities like &#13;,
2244  // which should result in a line break in the tooltip, because we can't
2245  // distinguish that from a literal character in the source by this point.
2246  t = t.replace(/[\r\t]/g, ' ');
2247  t = t.replace(/\n/g, '');
2248 
2249  tipNode.setAttribute("label", t);
2250  retVal = true;
2251  }
2252  }
2253 
2254  return retVal;
2255 }
2256 
2257 
classDescription entry
Definition: FeedWriter.js:1427
const CcID
Definition: mashTape.js:2
var gMM
Definition: windowUtils.js:62
nsString encodeURIComponent(const nsString &c)
var Application
Definition: sbAboutDRM.js:37
dataSBHighestRatedArtists SBProperties rating
Definition: tuner2.js:867
sbOSDControlService prototype className
const Ci
Definition: mashTape.js:3
var pref
Definition: openLocation.js:44
function doc() browser.contentDocument
for(let i=0;i< aHistory.count;i++)
var nS5
Definition: iframe-photo.js:3
var event
sbOSDControlService prototype QueryInterface
while((node=formNodes.iterateNext()))
var tab
function width(ele) rect(ele).width
let window
const SB_NewDataRemote
const Cr
Definition: mashTape.js:4
var getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('Songbird SBProperties artist
Definition: tuner2.js:40
var t
_hideDatepicker duration
var count
Definition: test_bug7406.js:32
var loaded
this _dialogInput val(dateText)
aWindow setTimeout(function(){_this.restoreHistory(aWindow, aTabs, aTabData, aIdMap);}, 0)
grep callback
return null
Definition: FeedWriter.js:1143
_updateDatepicker height
let node
if(typeof(gMM)=="undefined") var gMM
Definition: mashTape.js:28
const PHOTO_PRELOAD
Definition: mashTape.js:11
function url(spec)
const XLinkNS
Definition: pageInfo.js:219
return aWindow document documentElement getAttribute(aAttribute)||dimension
observe topic
Definition: FeedWriter.js:1326
ContinuingWebProgressListener prototype onStateChange
function msg
const PANE_CUTOFF
Definition: mashTape.js:8
observe data
Definition: FeedWriter.js:1329
_getSelectedPageStyle s i
const Cc
Definition: mashTape.js:1
return first
function next()
sbDeviceFirmwareAutoCheckForUpdate prototype observe
const Cu
Definition: mashTape.js:5