browse.js
Go to the documentation of this file.
1 /*
2  *
3  *=BEGIN SONGBIRD LICENSE
4  *
5  * Copyright(c) 2009-2010 POTI, Inc.
6  * http://www.songbirdnest.com
7  *
8  * For information about the licensing and copyright of this Add-On please
9  * contact POTI, Inc. at customer@songbirdnest.com.
10  *
11  *=END SONGBIRD LICENSE
12  *
13  */
14 
15 if (typeof(Cc) == "undefined")
16  var Cc = Components.classes;
17 if (typeof(Ci) == "undefined")
18  var Ci = Components.interfaces;
19 if (typeof(Cu) == "undefined")
20  var Cu = Components.utils;
21 
22 Cu.import("resource://app/jsmodules/StringUtils.jsm");
23 Cu.import("resource://app/jsmodules/sbProperties.jsm");
24 Cu.import("resource://app/jsmodules/sbLibraryUtils.jsm");
25 
26 if (typeof(songbirdMainWindow) == "undefined")
27  var songbirdMainWindow = Cc["@mozilla.org/appshell/window-mediator;1"]
28  .getService(Ci.nsIWindowMediator)
29  .getMostRecentWindow("Songbird:Main").window;
30 
31 if (typeof(gBrowser) == "undefined")
32  var gBrowser = Cc["@mozilla.org/appshell/window-mediator;1"]
33  .getService(Ci.nsIWindowMediator)
34  .getMostRecentWindow("Songbird:Main").window.gBrowser;
35 
36 #ifdef METRICS_ENABLED
37 if (typeof(gMetrics) == "undefined")
38  var gMetrics = Cc["@songbirdnest.com/Songbird/Metrics;1"]
39  .createInstance(Ci.sbIMetrics);
40 #endif
41 
42 function flushDisplay() {
43  if (typeof(Components) == "undefined")
44  return;
45  while ((typeof(Components) != "undefined") &&
46  Components.classes["@mozilla.org/thread-manager;1"].getService()
47  .currentThread.hasPendingEvents())
48  {
49  Components.classes["@mozilla.org/thread-manager;1"].getService()
50  .currentThread.processNextEvent(true);
51  }
52 }
53 
54 var letterIndices = ['#', 'Z','Y','X','W','V','U','T','S','R','Q','P','O','N',
55  'M','L','K','J','I','H','G','F','E','D','C','B','A'];
56 
57 // Special database ID for new releases in all countries
58 const allEntriesID = "ALL";
59 
60 // If a country name needs an extra word for wording to flow better (ex.
61 // "United States" vs. "the United States") then add it to the following
62 // hash. Currently the logic only adds additional words before the
63 // country name.
64 var customCountries = new Array();
65 customCountries["Netherlands"] = "the";
66 customCountries["Russian Federation"] = "the";
67 customCountries["United Kingdom"] = "the";
68 customCountries["United States"] = "the";
69 
70 if (typeof NewReleaseAlbum == 'undefined') {
71  var NewReleaseAlbum = {};
72 }
73 
74 NewReleaseAlbum.unload = function() {
75  Components.classes["@songbirdnest.com/newreleases;1"]
76  .getService(Ci.nsIClassInfo).wrappedJSObject.unregisterDisplayCallback();
77 }
78 
79 NewReleaseAlbum.init = function() {
80  // Set the tab title
81  var servicePaneStr = Cc["@mozilla.org/intl/stringbundle;1"]
82  .getService(Ci.nsIStringBundleService)
83  .createBundle("chrome://newreleases/locale/overlay.properties");
84  document.title = servicePaneStr.GetStringFromName("servicePane.Name");
85 
86  var self = this;
87 
88  if (typeof(Ci.sbIMediacoreManager) != "undefined")
89  NewReleaseAlbum.newMediaCore = true;
90  else
91  NewReleaseAlbum.newMediaCore = false;
92 
93  this.dateFormat = "%a, %b %e, %Y";
94  if (navigator.platform.substr(0, 3) == "Win")
95  this.dateFormat = "%a, %b %#d, %Y";
96 
97  this.Cc = Components.classes;
98  this.Ci = Components.interfaces;
99 
100  // Load the iframe
101  this.iframe = document.getElementById("newRelease-listings");
102 
103  // Set a pointer to our document so we can update it later as needed
104  this._browseDoc = this.iframe.contentWindow.document;
105 
106  // Set our string bundle for localised string lookups
107  this._strings = document.getElementById("newReleases-strings");
108 
109  // Apply styles to the iframe
110  var headNode = this._browseDoc.getElementsByTagName("head")[0];
111  var cssNode = this._browseDoc.createElementNS(
112  "http://www.w3.org/1999/xhtml", "html:link");
113  cssNode.type = 'text/css';
114  cssNode.rel = 'stylesheet';
115  cssNode.href = 'chrome://songbird/skin/html.css';
116  cssNode.media = 'screen';
117  headNode.appendChild(cssNode);
118 
119  cssNode = this._browseDoc.createElementNS(
120  "http://www.w3.org/1999/xhtml", "html:link");
121  cssNode.type = 'text/css';
122  cssNode.rel = 'stylesheet';
123  cssNode.href = 'chrome://newreleases/skin/browse.css';
124  cssNode.media = 'screen';
125  headNode.appendChild(cssNode);
126 
127  // Get the New Releases XPCOM service
128  this.nrSvc = Cc["@songbirdnest.com/newreleases;1"]
129  .getService(Ci.nsIClassInfo).wrappedJSObject;
130 
131  // Set the checked state for the filter checkbox
132  this.filterLibraryArtists = Application.prefs
133  .getValue("extensions.newreleases.filterLibraryArtists", true);
134 
135  // Get pref to display play link or not
136  this.showPlayLink = Application.prefs
137  .getValue("extensions.newreleases.showplaylink", false);
138  this.showGCal = Application.prefs
139  .getValue("extensions.newreleases.showgcal", false);
140  this.showExtendedInfo = Application.prefs
141  .getValue("extensions.newreleases.showExtendedInfo", false);
142 
143 #ifdef METRICS_ENABLED
144  gMetrics.metricsInc("newReleases", "servicepane.clicked", "");
145 #endif
146  // Register our UI display callback with the newReleases object
147  if (!this.nrSvc.hasDisplayCallback()) {
148  var displayCB = new this.displayCallback(this);
149  displayCB.wrappedJSObject = displayCB;
150  this.nrSvc.registerDisplayCallback(displayCB);
151  }
152 
153  if (Application.prefs.get("extensions.newreleases.firstrun").value) {
154  // Load the first run page in the deck
155  var deck = document.getElementById("newReleases-deck");
156  deck.setAttribute("selectedIndex", 1);
157 
158  // Load location information
159  this.nrSvc.refreshLocations();
160  NewReleaseOptions.init();
161  } else {
162  if (this.nrSvc.newReleasesRefreshRunning) {
163  // If the data refresh is already happening,
164  // switch the deck to the loading page
165  var deck = document.getElementById("newReleases-deck");
166  deck.setAttribute("selectedIndex", 2);
167  var label = document.getElementById("loading-label");
168  var progressbar = document.getElementById("loading-progress");
169  label.value = this.nrSvc.progressString;
170  progressbar.value = this.nrSvc.progressPercentage;
171  this.waitForRefreshToFinish(this);
172  } else {
173  // Otherwise populate the fields in the options page, and continue
174  // to the listing view
175  NewReleaseOptions.init();
176  this.browseNewReleases(this);
177  }
178  }
179 }
180 
181 NewReleaseAlbum.waitForRefreshToFinish = function(self) {
182  if (Components.classes["@songbirdnest.com/newreleases;1"]
183  .getService(Ci.nsIClassInfo).wrappedJSObject.newReleasesRefreshRunning)
184  {
185  setTimeout(this.waitForRefreshToFinish(self), 100);
186  } else {
187  self.browseNewReleases(self);
188  }
189 }
190 
191 NewReleaseAlbum.editLocation = function() {
192  // Save our current selected deck page, so if the user cancels we can
193  // switch back to it
194  var deck = document.getElementById("newReleases-deck");
195  deck.setAttribute("previous-selected-deck", deck.selectedIndex);
196 
197  // Hide the stuff we don't want to display
198  document.getElementById("pref-library").style.visibility = "hidden";
199  document.getElementById("library-newrelease-box")
200  .style.visibility = "collapse";
201 
202  // Not strictly required, but in the event the user hit cancel, we
203  // probably want to reset to their preferred location
204  NewReleaseOptions.init();
205 
206  // Make the cancel button visible (it's hidden by default for first run)
207  var cancel = document.getElementById("pref-cancel-button");
208  cancel.style.visibility="visible";
209 
210  // Switch to the location edit deck page
211  deck.setAttribute("selectedIndex", 1);
212 }
213 
214 NewReleaseAlbum.displayCallback = function(ticketingObj) {
215  this.label = document.getElementById("loading-label"),
216  this.progressbar = document.getElementById("loading-progress");
217  this.newReleaseAlbum = ticketingObj;
218 },
219 NewReleaseAlbum.displayCallback.prototype = {
220  loadingMessage : function(str) {
221  this.label.value = str;
222  },
223  loadingPercentage : function(pct) {
224  this.progressbar.value = pct;
225  },
226  timeoutError : function() {
227  songbirdMainWindow.NewReleases.updateNewReleasesCount(0);
228  var deck = document.getElementById("newReleases-deck");
229  deck.setAttribute("selectedIndex", 4);
230  },
231  showListings : function() {
232  this.newReleaseAlbum.browseNewReleases(this.newReleaseAlbum);
233  },
234  updateNewReleasesCount : function() {
235  songbirdMainWindow.NewReleases.updateNewReleasesCount()
236  },
237  alert : function(str) { window.alert(str); },
238 }
239 
240 // Initiate a synchronous database refresh. This can only be triggered after
241 // first run, or if the user goes and changes their location
242 NewReleaseAlbum.loadNewReleaseData = function(country) {
243  // Switch the deck to the loading page
244  var deck = document.getElementById("newReleases-deck");
245  deck.setAttribute("selectedIndex", 2);
246 
247  // First run is over
248  if (Application.prefs.getValue("extensions.newreleases.firstrun", false)) {
249  // toggle first-run
250  Application.prefs.setValue("extensions.newreleases.firstrun", false);
251 
252  // setup the smart playlist
253  songbirdMainWindow.NewReleases._setupNewReleasePlaylist();
254  }
255 
256  // Load the new data
257  var ret = this.nrSvc.refreshNewReleases(false, country);
258  //songbirdMainWindow.NewReleases.updateNewReleasesCount();
259  if (!ret)
260  this.browseNewReleases(this);
261 }
262 
263 NewReleaseAlbum.showNoNewReleases = function() {
264  var deck = document.getElementById("newReleases-deck");
265  deck.setAttribute("selectedIndex", 3);
266 
267  deck = document.getElementById("no-results-deck");
268  var count = this.nrSvc.getNewReleasesCount(false);
269  var label;
270  var button;
271  var sampletext;
272  if (count == 0) {
273  // no releases found, period
274  label = document.getElementById("noresults-country-1");
275  label.value = this._strings.getString("yourCountrySucks") +
276  " ";
277  // Ugly hack to tack on any special words before the
278  // country name as defined by the hash above
279  var countryName = this.nrSvc.getLocationString(this.pCountry);
280  if (typeof(customCountries[countryName]) != "undefined")
281  label.value += customCountries[countryName] + " ";
282  label.value += countryName;
283  sampletext = document.getElementById("sampleresults");
284  sampletext.style.visibility = "hidden";
285  button = document.getElementById("noresults-seeallreleases-country");
286  button.style.visibility = "hidden";
287  } else {
288  // no library artist releases found
289 
290  // show error messages - "Well that's lame"
291  label = document.getElementById("noresults-country-1");
292  label.value = this._strings.getString("noLibLame");
293 
294  // "None of the artists in your library have an upcoming
295  // release..."
296  var countryStr = " ";
297  var countryName = this.nrSvc.getLocationString(this.pCountry);
298  // Ugly hack to tack on any special words before the
299  // country name as defined by the hash above
300  if (typeof(customCountries[countryName]) != "undefined")
301  countryStr += customCountries[countryName] + " ";
302  countryStr += countryName;
303  label = document.getElementById("noresults-country-2");
304  label.value = this._strings.getString("noLibArtistsReleases");
305  label = document.getElementById("noresults-country-3");
306  label.value = this._strings.getString("noLibArtistsReleases2") +
307  countryStr + ".";
308 
309  // "If that changes..."
310  label = document.getElementById("noresults-country-4");
311  label.value = this._strings.getString("noLibArtistsReleases3");
312  label = document.getElementById("noresults-country-5");
313  label.value = this._strings.getString("noLibArtistsReleases4");
314 
315  // Set the actual button text
316  button = document.getElementById("noresults-seeallreleases-country");
317  button.label = this._strings.getString("seeAllNewReleases") + countryStr;
318 
319  sampletext = document.getElementById("sampleresults");
320  sampletext.style.visibility = "visible";
321  }
322 }
323 
324 NewReleaseAlbum.showTimeoutError = function() {
325  var deck = document.getElementById("newReleases-deck");
326  deck.setAttribute("selectedIndex", 4);
327 }
328 
329 // The following isn't currently used for anything but is saved in case
330 // a provider/URL is ever required
331 NewReleaseAlbum.openProviderPage = function() {
332  var url;
333  if (typeof(this.nrSvc) != "undefined")
334  url = this.nrSvc.providerURL();
335  else
336  url = Cc["@songbirdnest.com/newreleases;1"]
337  .getService(Ci.nsIClassInfo).wrappedJSObject.providerURL();
338 
339  var country = Application.prefs.getValue("extensions.newreleases.country", 0);
340  if (country != 0)
341  url += "?p=21&user_location=" + country;
342  gBrowser.loadOneTab(url);
343 }
344 
345 /***************************************************************************
346  * Called when the user clicks "Play" next to the main artist name in the
347  * artist grouping view of upcoming releases
348  ***************************************************************************/
349 NewReleaseAlbum.playArtist = function(e) {
350  var artistName = this.getAttribute("artistName");
351 
352 #ifdef METRICS_ENABLED
353  gMetrics.metricsInc("newReleases", "browse.view.artist.playartist", "");
354 #endif
355  var list = songbirdMainWindow.NewReleases.releasePlaylist;
356  var view = list.createView();
357  var cfs = view.cascadeFilterSet;
358 
359  cfs.appendSearch(["*"], 1);
360  cfs.appendFilter(SBProperties.genre);
361  cfs.appendFilter(SBProperties.artistName);
362  cfs.appendFilter(SBProperties.albumName);
363 
364  cfs.clearAll();
365  cfs.set(2, [artistName], 1);
366 
367  if (NewReleaseAlbum.newMediaCore)
368  Cc["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
369  .getService(Ci.sbIMediacoreManager)
370  .sequencer.playView(view, 0);
371  else
372  Cc['@songbirdnest.com/Songbird/PlaylistPlayback;1']
373  .getService(Ci.sbIPlaylistPlayback)
374  .sequencer.playView(view, 0);
375 }
376 
377 /***************************************************************************
378  * Scrolls the iframe to the selected index block when the user clicks on
379  * one of the letter or month index links in the chrome
380  ***************************************************************************/
381 NewReleaseAlbum.indexJump = function(e) {
382  var iframe = document.getElementById("newRelease-listings");
383  this._browseDoc = iframe.contentWindow.document;
384 
385  var anchor;
386  if (this.id.indexOf("newReleases-nav-letter-") >= 0) {
387  var letter = this.id.replace("newReleases-nav-letter-", "");
388  anchor = this._browseDoc.getElementById("indexLetter-" + letter);
389  } else {
390  var dateComponent = this.id.split("-")[3];
391  anchor = this._browseDoc.getElementById("indexDate-" + dateComponent);
392  }
393  if (anchor) {
394  var aLeft = anchor.offsetLeft;
395  var aTop = anchor.offsetTop;
396  iframe.contentWindow.scrollTo(0, aTop);
397  }
398 }
399 
400 
401 NewReleaseAlbum.browseNewReleases = function(ticketingObj) {
402  if (this.nrSvc.drawingLock)
403  return;
404  this.nrSvc.drawingLock = true;
405  this.abortDrawing = false;
406 
407  // Switch to the listing view
408  var deck = document.getElementById("newReleases-deck");
409  deck.setAttribute("selectedIndex", 0);
410 
411  this._bodyNode = this._browseDoc.getElementsByTagName("body")[0];
412 
413  // Clear any existing results
414  while (this._bodyNode.firstChild) {
415  this._bodyNode.removeChild(this._bodyNode.firstChild);
416  }
417 
418  var doc = this._browseDoc;
419  var str = this._strings;
420 
421  // Add the header block
422  var headerDiv = this.createBlock("header");
423  var newReleasesImage = doc.createElement("img");
424  newReleasesImage.src =
425  "chrome://newreleases/skin/NewReleases.png";
426  newReleasesImage.id = "newReleases-logo";
427  headerDiv.appendChild(newReleasesImage);
428 
429 /*
430  // The following isn't currently used for anything but is saved
431  // in case a provider/URL should ever be added
432  var songkickImage = doc.createElement("img");
433  songkickImage.src =
434  "chrome://newreleases/content/songkick_left.png";
435  songkickImage.id = "songkick-logo";
436  songkickImage.className = "clickable";
437  songkickImage.addEventListener("click", this.openProviderPage,
438  false);
439  headerDiv.appendChild(songkickImage);
440 */
441 
442  this._bodyNode.appendChild(headerDiv);
443 
444  // Add the subheader block
445  var subHeaderDiv = this.createBlock("sub-header");
446  // This will be populated later when we get the actual new
447  // releases count
448  var numNewReleasesShownDiv = this.createBlock("num-newReleases",
449  true);
450  subHeaderDiv.appendChild(numNewReleasesShownDiv);
451 
452  var locationChangeDiv = this.createBlock("location", true);
453  subHeaderDiv.appendChild(locationChangeDiv);
454  var countryNameBlock = this.createBlock("location-city", true);
455  this.pCountry = Application.prefs.getValue("extensions.newreleases.country",
456  allEntriesID);
457  var locationString = this.nrSvc.getLocationString(this.pCountry);
458  countryNameBlock.appendChild(doc.createTextNode(locationString));
459  locationChangeDiv.appendChild(countryNameBlock);
460  var changeLocation = this.createBlock("location-change", true);
461  changeLocation.appendChild(doc.createTextNode(str.getString("changeLoc")));
462  locationChangeDiv.appendChild(changeLocation);
463  changeLocation.addEventListener("click", NewReleaseAlbum.editLocation,
464  false);
465 
466  var filterDiv = this.createBlock("filter", true);
467  var checkbox = doc.createElement("input");
468  checkbox.setAttribute("type", "checkbox");
469  if (this.filterLibraryArtists)
470  checkbox.setAttribute("checked", true);
471  checkbox.addEventListener("click", function(e)
472  { NewReleaseAlbum.changeFilter(false); }, false);
473  filterDiv.appendChild(checkbox);
474  filterDiv.appendChild(doc.createTextNode(str.getString("filter")));
475  subHeaderDiv.appendChild(filterDiv);
476 
477  var clearDiv = this.createBlock("sub-header-empty");
478  clearDiv.style.clear = "both";
479  subHeaderDiv.appendChild(clearDiv);
480  this._bodyNode.appendChild(subHeaderDiv);
481 
482  var groupBy = Application.prefs.getValue("extensions.newreleases.groupby",
483  "date");
484  // Add the nav block - the View/New Releases/Artists selector
485  var navDiv = this.createBlock("newReleases-nav");
486  var viewSpan = this.createBlock("newReleases-nav-view", true);
487  viewSpan.appendChild(doc.createTextNode(str.getString("navView")));
488 
489  var datesSpan = this.createBlock("newReleases-nav-releases", true);
490  var artistsSpan = this.createBlock("newReleases-nav-artists", true);
491  if (groupBy == "artist") {
492  artistsSpan.className += " newReleases-nav-selected";
493  datesSpan.addEventListener("mouseover", function(e)
494  {
495  datesSpan.className += " newReleases-nav-hover";
496  }, false);
497  datesSpan.addEventListener("mouseout", function(e)
498  {
499  datesSpan.className = datesSpan.className.replace(
500  " newReleases-nav-hover", "");
501  }, false);
502  datesSpan.addEventListener("click", function(e)
503  {
504  NewReleaseAlbum.groupBy("date");
505  }, false);
506  } else {
507  datesSpan.className += " newReleases-nav-selected";
508  artistsSpan.addEventListener("mouseover", function(e)
509  {
510  artistsSpan.className += " newReleases-nav-hover";
511  }, false);
512  artistsSpan.addEventListener("mouseout", function(e)
513  {
514  artistsSpan.className = artistsSpan.className.replace(
515  " newReleases-nav-hover", "");
516  }, false);
517  artistsSpan.addEventListener("click", function(e)
518  {
519  NewReleaseAlbum.groupBy("artist");
520  }, false);
521  }
522 
523  datesSpan.appendChild(doc.createTextNode(str.getString("navDates")));
524  artistsSpan.appendChild(doc.createTextNode(str.getString("navArtists")));
525  navDiv.appendChild(viewSpan);
526  navDiv.appendChild(datesSpan);
527  navDiv.appendChild(artistsSpan);
528 
529  var indexDiv = this.createBlock("newReleases-nav-index");
530  if (groupBy == "artist") {
531  // group by Artist Names
532  // Add the #,A-Z index letters
533  for (var i in letterIndices) {
534  var l = letterIndices[i];
535  var letterSpan = this.createBlock("newReleases-nav-letter",
536  true);
537  letterSpan.id = "newReleases-nav-letter-" + l;
538  letterSpan.appendChild(doc.createTextNode(l));
539  indexDiv.appendChild(letterSpan);
540  }
541  } else {
542  // group by New Release Dates
543  var myDate = new Date();
544  myDate.setDate(1);
545  var forwardMonths =
546  Application.prefs.getValue("extensions.newreleases.futuremonths",
547  4);
548  var dates = [];
549  for (i=0; i<forwardMonths; i++) {
550  var mon = myDate.getMonth();
551  var year = myDate.getFullYear();
552  var dateStr = myDate.toLocaleFormat("%b %Y");
553  var dateSpan = this.createBlock("newReleases-nav-date", true);
554  dateSpan.appendChild(doc.createTextNode(dateStr));
555  dateSpan.id = "newReleases-nav-date-" + mon + year;
556  myDate.setMonth(mon+1);
557  dates.push(dateSpan);
558  }
559  for (var i in dates.reverse()) {
560  indexDiv.appendChild(dates[i], null);
561  }
562  }
563  navDiv.appendChild(indexDiv);
564  clearDiv = this.createBlock("newReleases-nav-empty");
565  clearDiv.style.clear = "both";
566  navDiv.appendChild(clearDiv);
567  this._bodyNode.appendChild(navDiv);
568 
569  flushDisplay();
570 
571  var newReleasesShown;
572  if (groupBy == "artist")
573  newReleasesShown = this.browseByArtists();
574  else
575  newReleasesShown = this.browseByDates();
576 
577  var bundle =
578  new SBStringBundle("chrome://newreleases/locale/overlay.properties");
579  var numNewReleasesStr = bundle.formatCountString("newReleasesShown",
580  newReleasesShown,
581  [ newReleasesShown ],
582  "foo");
583 
584  // Ugliness to hack in articles before country name
585  var customStr = "";
586  if (typeof(customCountries[locationString]) != "undefined")
587  customStr = " " + customCountries[locationString] + " ";
588  numNewReleasesShownDiv.appendChild(doc.createTextNode(numNewReleasesStr + customStr));
589  songbirdMainWindow.NewReleases.updateNewReleasesCount(newReleasesShown);
590 
591  if (newReleasesShown > 0) {
592  var ft = this.createBlock("ft");
593  ft.id = "ft";
594 
595 /*
596  // The following isn't currently used for anything but is saved
597  // in case a provider/URL should ever be added
598  var poweredBy = this.createBlock("poweredBy");
599  var url = this.nrSvc.providerURL();
600  var city = Application.prefs.getValue("extensions.newreleases.city", 0);
601  if (city != 0)
602  url += "?p=21&user_location=" + city;
603  poweredBy.innerHTML = this._strings.getString("poweredBy") +
604  " <a target='_blank' href='" + url + "'>Songkick</a>. "
605  + this._strings.getString("tosPrefix") +
606  " <a target='_blank' href='http://www.songkick.com/terms?p=21'>" +
607  this._strings.getString("tosLink") + "</a>.";
608  ft.appendChild(poweredBy);
609 */
610 
611  var lastUpdated = this.createBlock("lastUpdated");
612  var ts = parseInt(1000*
613  Application.prefs.getValue("extensions.newreleases.lastupdated", 0));
614  if (ts > 0) {
615  var dateString =
616  new Date(ts).toLocaleFormat("%a, %b %e, %Y at %H:%M.");
617  lastUpdated.innerHTML = this._strings.getString("lastUpdated") +
618  " <span class='date'>" + dateString + "</span>";
619  ft.appendChild(lastUpdated);
620  }
621  this._bodyNode.appendChild(ft);
622  }
623 
624  // Debug
625  /*
626  var html = this._bodyNode.innerHTML;
627  var textbox = this._browseDoc.createElement("textarea");
628  textbox.width=50;
629  textbox.height=50;
630  this._bodyNode.appendChild(textbox);
631  textbox.value = html;
632  */
633 
634  this.nrSvc.drawingLock = false;
635 }
636 
637 NewReleaseAlbum.browseByDates = function() {
638  var lastDate = "";
639  var dateBlock = null;
640  var releaseTable = null;
641  var releases = new this.nrSvc.releaseEnumerator("date",
642  this.filterLibraryArtists,
643  this.pCountry);
644  var newReleasesShown = 0;
645 
646  var contentsNode = this._browseDoc.createElement("div");
647  contentsNode.id = "releases-contents";
648  this._bodyNode.appendChild(contentsNode);
649 
650  var today = new Date();
651  var todayMon = today.getMonth();
652  var todayDate = today.getDate();
653  var todayYear = today.getFullYear();
654 
655 #ifdef METRICS_ENABLED
656  gMetrics.metricsInc("newReleases", "browse.view.date", "");
657 #endif
658  while (releases.hasMoreElements()) {
659  if (this.abortDrawing)
660  return;
661  var release = releases.getNext().wrappedJSObject;
662 
663  var thisDateObj = new Date();
664  thisDateObj.setTime(release.ts);
665  var thisMon = thisDateObj.getMonth();
666  var thisYear = thisDateObj.getFullYear();
667  var thisIndex = thisMon + "" + thisYear;
668  var thisDate = thisYear + '-' + thisMon + '-' + thisDateObj.getDate();
669 
670  // Don't show past releases
671  if (((thisMon < todayMon) && (thisYear <= todayYear)) ||
672  (thisMon == todayMon && thisDateObj.getDate() < todayDate)) {
673  continue;
674  }
675 
676  var doc = this._browseDoc;
677  var dateIndex = doc.getElementById("newReleases-nav-date-" + thisIndex);
678  if (dateIndex == null) {
679  continue;
680  }
681  dateIndex.className += " newReleases-nav-date-link";
682  dateIndex.addEventListener("mouseover", function(e)
683  {
684  var el = e.currentTarget;
685  el.className += " newReleases-nav-hover";
686  }, false);
687  dateIndex.addEventListener("mouseout", function(e)
688  {
689  var el = e.currentTarget;
690  el.className = el.className.replace(" newReleases-nav-hover", "");
691  }, false);
692  dateIndex.addEventListener("click", NewReleaseAlbum.indexJump, false);
693 
694  if (thisDate != lastDate) {
695  // Create the block for release listings of the same letter index
696  var dateDiv = this.createBlock("indexDiv");
697 
698  // Create the letter index block
699  var dateIndexContainer = this.createDateBlock(thisDateObj);
700 
701  // Create the actual release listing block for this date
702  dateBlock = this.createBlock("releaseListing");
703  dateBlock.className += " releaseListingDate";
704 
705  // Create a new release listing block
706  var releaseTableBlock = this.createBlock("artistBlock");
707 
708  // Create the table for new releases
709  releaseTable = this.createTableDateView();
710  releaseTableBlock.appendChild(releaseTable);
711 
712  // Assemble the pieces together
713  dateBlock.appendChild(releaseTableBlock);
714  dateDiv.appendChild(dateIndexContainer);
715  dateDiv.appendChild(dateBlock);
716  contentsNode.appendChild(dateDiv);
717  flushDisplay();
718  }
719 
720  // Create the table row representing this release
721  var thisRelease = this.createRowDateView(release);
722  releaseTable.appendChild(thisRelease);
723 
724  lastDate = thisDate;
725  newReleasesShown++;
726  }
727 
728  if (newReleasesShown == 0) {
729  if (Application.prefs.getValue("extensions.newreleases.networkfailure",
730  false))
731  NewReleaseAlbum.showTimeoutError();
732  else
733  NewReleaseAlbum.showNoNewReleases();
734  }
735 
736  return newReleasesShown;
737 }
738 
739 NewReleaseAlbum.browseByArtists = function() {
740  var lastLetter = "";
741  var lastArtist = "";
742  var releaseBlock = null;
743  var artistReleaseBlock = null;
744  var thisMainArtistAnchor = null;
745 
746  // Placeholders for "#" block
747  var othersLetterDiv = null;
748 
749  var releases = this.nrSvc.artistReleaseEnumerator(
750  this.filterLibraryArtists, this.pCountry);
751 
752  var newReleasesShown = 0;
753 
754  var contentsNode = this._browseDoc.createElement("div");
755  contentsNode.id = "releases-contents";
756  this._bodyNode.appendChild(contentsNode);
757 
758  var today = new Date();
759  var todayMon = today.getMonth();
760  var todayDate = today.getDate();
761  var todayYear = today.getFullYear();
762 
763 #ifdef METRICS_ENABLED
764  gMetrics.metricsInc("newReleases", "browse.view.artist", "");
765 #endif
766  while (releases.hasMoreElements()) {
767  if (this.abortDrawing)
768  return;
769 
770  var release = releases.getNext().wrappedJSObject;
771 
772  var thisDateObj = new Date();
773  thisDateObj.setTime(release.ts);
774  var thisMon = thisDateObj.getMonth();
775  var thisYear = thisDateObj.getFullYear();
776  var thisIndex = thisMon + "" + thisYear;
777  var thisDate = thisYear + '-' + thisMon + '-' + thisDateObj.getDate();
778 
779  // Don't show past releases
780  if (((thisMon < todayMon) && (thisYear <= todayYear)) ||
781  (thisMon == todayMon && thisDateObj.getDate() < todayDate))
782  {
783  continue;
784  }
785  var thisLetter = release.artistname[0].toUpperCase();
786  if (thisLetter < 'A' || thisLetter > 'Z')
787  thisLetter = '#';
788 
789  var doc = this._browseDoc;
790  var letterIdx = doc.getElementById("newReleases-nav-letter-" + thisLetter);
791  if (letterIdx == null) {
792  continue;
793  }
794  letterIdx.className += " newReleases-nav-letter-link";
795  letterIdx.addEventListener("mouseover", function(e)
796  {
797  var el = e.currentTarget;
798  el.className += " newReleases-nav-hover";
799  }, false);
800  letterIdx.addEventListener("mouseout", function(e)
801  {
802  var el = e.currentTarget;
803  el.className = el.className.replace(" newReleases-nav-hover", "");
804  }, false);
805  letterIdx.addEventListener("click", NewReleaseAlbum.indexJump, false);
806 
807  if (thisLetter != lastLetter) {
808  // Create the block for release listings of the same letter index
809  var letterDiv = this.createBlock("indexDiv");
810 
811  // Create the letter index block
812  var letterIndexContainer = this.createLetterBlock(thisLetter);
813 
814  // Create the actual release listing block for this letter
815  releaseBlock = this.createBlock("releaseListing");
816  releaseBlock.className += " releaseListingLetter";
817 
818  // Assemble the pieces together
819  letterDiv.appendChild(letterIndexContainer);
820  letterDiv.appendChild(releaseBlock);
821  //this._bodyNode.appendChild(letterDiv);
822  if (thisLetter != '#')
823  contentsNode.appendChild(letterDiv);
824  else
825  {
826  // Wait until all entries have been processed
827  // before tacking on the last "#" block since
828  // these tend to lead to artist links that
829  // don't make sense
830  othersLetterDiv = letterDiv;
831  }
832 
833  flushDisplay();
834  }
835 
836  if (release.artistname != lastArtist) {
837  // Create a new artist block
838  var artistBlock = this.createBlock("artistBlock");
839 
840  // Create the main release title
841  var artistName = this.createBlock("mainArtistName", true);
842  thisMainArtistAnchor = this._browseDoc.createElement("a");
843  var url = "http://www.allmusic.com/cg/amg.dll?P=amg&opt1=1&sql=" + escape(release.artistname);
844  this.makeLink(thisMainArtistAnchor, url, "artist.main");
845  var artistNameText =
846  this._browseDoc.createTextNode(release.artistname);
847  thisMainArtistAnchor.appendChild(artistNameText);
848  artistName.appendChild(thisMainArtistAnchor);
849  artistBlock.appendChild(artistName);
850 
851  if (release.libartist == 1 && this.showPlayLink) {
852  // Create the play link
853  var playBlock = this.createBlock("playBlock", true);
854  var playLink = this._browseDoc.createElement("img");
855  playLink.className = "ticketImage";
856  playLink.src = "chrome://newreleases/skin/icon-play.png";
857  playBlock.className = "playLink";
858  playBlock.appendChild(playLink);
859  playBlock.setAttribute("artistName", release.artistname);
860  playBlock.addEventListener("click", this.playArtist, false);
861  artistBlock.appendChild(playBlock);
862  }
863 
864  // Create the table for releases
865  artistReleaseBlock = this.createTableArtistView();
866  artistBlock.appendChild(artistReleaseBlock);
867 
868  // Attach the artist block to the release listing block
869  releaseBlock.appendChild(artistBlock);
870  }
871 
872  // Create the table row representing this release
873  var thisRelease = this.createRowArtistView(release,
874  thisMainArtistAnchor);
875  artistReleaseBlock.appendChild(thisRelease);
876 
877  lastLetter = thisLetter;
878  lastArtist = release.artistname;
879 
880  newReleasesShown++;
881  }
882 
883  if (othersLetterDiv != null)
884  contentsNode.appendChild(othersLetterDiv);
885 
886  if (newReleasesShown == 0) {
887  if (Application.prefs.getValue("extensions.newreleases.networkfailure",
888  false))
889  NewReleaseAlbum.showTimeoutError();
890  else
891  NewReleaseAlbum.showNoNewReleases();
892  }
893 
894  return newReleasesShown;
895 }
896 
897 // Takes a letter, and returns an index block for it
898 NewReleaseAlbum.createLetterBlock = function(letter) {
899  // letter index (LHS)
900  var letterIndexContainer = this.createBlock("letterIndexContainer");
901  var letterIndex = this.createBlock("letterIndex");
902  letterIndex.id = "indexLetter-" + letter;
903  var letterIndexText = this._browseDoc.createTextNode(letter);
904  letterIndex.appendChild(letterIndexText);
905  letterIndexContainer.appendChild(letterIndex);
906  return (letterIndexContainer);
907 }
908 
909 // Takes a date, and returns an index block for it
910 NewReleaseAlbum.createDateBlock = function(dateObj) {
911  var dateIndexContainer = this.createBlock("dateIndexContainer");
912  var boxBlock = this.createBlock("dateIndex-box");
913  var month = this.createBlock("dateIndex-month");
914  var date = this.createBlock("dateIndex-date");
915  var day = this.createBlock("dateIndex-day");
916  dateIndexContainer.id = "indexDate-" + dateObj.getMonth() +
917  dateObj.getFullYear();
918  var monthText =
919  this._browseDoc.createTextNode(dateObj.toLocaleFormat("%b"));
920  var dateText = this._browseDoc.createTextNode(dateObj.toLocaleFormat("%d"));
921  var dayText = this._browseDoc.createTextNode(dateObj.toLocaleFormat("%a"));
922  month.appendChild(monthText);
923  day.appendChild(dayText);
924  date.appendChild(dateText);
925  boxBlock.appendChild(month);
926  boxBlock.appendChild(date);
927  dateIndexContainer.appendChild(boxBlock);
928  dateIndexContainer.appendChild(day);
929  return (dateIndexContainer);
930 }
931 
932 /***************************************************************************
933  * Routines for building the actual table objects for listing individual
934  * releases within
935  ***************************************************************************/
936 NewReleaseAlbum.createTableArtistView = function() {
937  var table = this.createTable();
938  var headerRow = this._browseDoc.createElement("tr");
939  var dateCol = this.createTableHeader("tableHeaderDate", "date");
940  var titleCol = this.createTableHeader("tableHeaderTitle", "title");
941  var labelCol = this.createTableHeader("tableHeaderLabel", "label");
942  var countryCol = this.createTableHeader("tableHeaderCountry", "country");
943  var typeCol = this.createTableHeader("tableHeaderType", "type");
944  var tracksCol = this.createTableHeader("tableHeaderTracks", "tracks");
945  var gCalCol = this.createTableHeader("tableHeaderGCal", "gcal");
946  headerRow.appendChild(dateCol);
947  headerRow.appendChild(titleCol);
948  headerRow.appendChild(labelCol);
949  if (this.pCountry == allEntriesID)
950  headerRow.appendChild(countryCol);
951  if (this.showExtendedInfo)
952  {
953  headerRow.appendChild(typeCol);
954  headerRow.appendChild(tracksCol);
955  }
956  if (this.showGCal)
957  headerRow.appendChild(gCalCol);
958  table.appendChild(headerRow);
959  return (table);
960 }
961 
962 NewReleaseAlbum.createTableDateView = function() {
963  var table = this.createTable();
964  var headerRow = this._browseDoc.createElement("tr");
965  var artistCol = this.createTableHeader("tableHeaderArtist", "artist");
966  var titleCol = this.createTableHeader("tableHeaderTitle", "title");
967  var labelCol = this.createTableHeader("tableHeaderLabel", "label");
968  var countryCol = this.createTableHeader("tableHeaderCountry", "country");
969  var typeCol = this.createTableHeader("tableHeaderType", "type");
970  var tracksCol = this.createTableHeader("tableHeaderTracks", "tracks");
971  var gCalCol = this.createTableHeader("tableHeaderGCal", "gcal");
972  headerRow.appendChild(artistCol);
973  headerRow.appendChild(titleCol);
974  headerRow.appendChild(labelCol);
975  if (this.pCountry == allEntriesID)
976  headerRow.appendChild(countryCol);
977  if (this.showExtendedInfo)
978  {
979  headerRow.appendChild(typeCol);
980  headerRow.appendChild(tracksCol);
981  }
982  if (this.showGCal)
983  headerRow.appendChild(gCalCol);
984  table.appendChild(headerRow);
985  return (table);
986 }
987 
988 NewReleaseAlbum.createTable = function() {
989  var table = this._browseDoc.createElement("table");
990  table.setAttribute("cellpadding", "0");
991  table.setAttribute("cellspacing", "0");
992  table.setAttribute("border", "0");
993  return (table);
994 }
995 
996 // str is a name in the .properties localised strings
997 // className is the class to apply to the TH cell
998 NewReleaseAlbum.createTableHeader = function(str, className) {
999  var col = this._browseDoc.createElement("th");
1000  col.className = className;
1001  if (str == "tableHeaderTickets" || this._strings.getString(str) == "")
1002  col.innerHTML = "&nbsp;";
1003  else {
1004  var colLabel =
1005  this._browseDoc.createTextNode(this._strings.getString(str));
1006  col.appendChild(colLabel);
1007  }
1008  return (col);
1009 }
1010 /***************************************************************************
1011  * Routines for building the individual release listing rows, i.e. the table
1012  * row representing each individual release for each of the two views
1013  ***************************************************************************/
1014 NewReleaseAlbum.createRowArtistView = function(release, mainAnchor) {
1015  var row = this._browseDoc.createElement("tr");
1016  var dateCol = this.createColumnDate(release);
1017  var titleCol = this.createColumnTitle(release);
1018  var labelCol = this.createColumnLabel(release);
1019  var countryCol = this.createColumnCountry(release);
1020  var typeCol = this.createColumnType(release);
1021  var tracksCol = this.createColumnTracks(release);
1022  var gcalCol = this.createColumnGCal(release);
1023 
1024  row.appendChild(dateCol);
1025  row.appendChild(titleCol);
1026  row.appendChild(labelCol);
1027  if (this.pCountry == allEntriesID)
1028  row.appendChild(countryCol);
1029 
1030  if (this.showExtendedInfo)
1031  {
1032  row.appendChild(typeCol);
1033  row.appendChild(tracksCol);
1034  }
1035  if (this.showGCal)
1036  row.appendChild(gcalCol);
1037  return (row);
1038 }
1039 
1040 NewReleaseAlbum.createRowDateView = function(release) {
1041  var row = this._browseDoc.createElement("tr");
1042  var artistCol = this.createColumnArtist(release);
1043  var titleCol = this.createColumnTitle(release);
1044  var labelCol = this.createColumnLabel(release);
1045  var countryCol = this.createColumnCountry(release);
1046  var typeCol = this.createColumnType(release);
1047  var tracksCol = this.createColumnTracks(release);
1048  var gcalCol = this.createColumnGCal(release);
1049 
1050  row.appendChild(artistCol);
1051  row.appendChild(titleCol);
1052  row.appendChild(labelCol);
1053  if (this.pCountry == allEntriesID)
1054  row.appendChild(countryCol);
1055  if (this.showExtendedInfo)
1056  {
1057  row.appendChild(typeCol);
1058  row.appendChild(tracksCol);
1059  }
1060  if (this.showGCal)
1061  row.appendChild(gcalCol);
1062  return (row);
1063 }
1064 
1065 /***************************************************************************
1066  * Routine for opening links - we need it as a separate routine so we can
1067  * do metrics reporting, in addition to just opening the link
1068  ***************************************************************************/
1069 NewReleaseAlbum.openAndReport = function(e) {
1070 #ifdef METRICS_ENABLED
1071  var metric = this.getAttribute("metric-key");
1072  gMetrics.metricsInc("newReleases", "browse.link." + metric, "");
1073 #endif
1074  gBrowser.loadOneTab(this.href);
1075  e.preventDefault();
1076  return false;
1077 }
1078 NewReleaseAlbum.makeLink = function(el, url, metric) {
1079  el.href = url;
1080  el.setAttribute("metric-key", metric);
1081  el.addEventListener("click", this.openAndReport, true);
1082 }
1083 
1084 // Add's Songbird's partner code only
1085 NewReleaseAlbum.appendPartnerParam = function(url) {
1086  return (url + "?p=21");
1087 }
1088 
1089 /***************************************************************************
1090  * Routines for building the individual column components of each release
1091  * listing table row, e.g. the "Date" column, or the "Artists" column, etc.
1092  ***************************************************************************/
1093 NewReleaseAlbum.createColumnDate = function(release) {
1094  var dateCol = this._browseDoc.createElement("td");
1095  dateCol.className = "date";
1096  var dateObj = new Date();
1097  dateObj.setTime(release.ts);
1098  var dateStr = dateObj.toLocaleFormat(this.dateFormat);
1099  var dateColLabel = this._browseDoc.createTextNode(dateStr);
1100  dateCol.appendChild(dateColLabel);
1101  return (dateCol);
1102 }
1103 
1104 NewReleaseAlbum.createColumnTitle = function(release) {
1105  var titleCol = this._browseDoc.createElement("td");
1106  titleCol.className = "title";
1107  var anchor = this._browseDoc.createElement("a");
1108  var url = "http://www.allmusic.com/cg/amg.dll?P=amg&opt1=2&sql=" +
1109  escape(release.title);
1110  this.makeLink(anchor, url, "title");
1111  var titleColLabel = this._browseDoc.createTextNode(release.title);
1112  anchor.appendChild(titleColLabel);
1113  titleCol.appendChild(anchor);
1114  return (titleCol);
1115 }
1116 
1117 NewReleaseAlbum.createColumnLabel = function(release) {
1118  var labelCol = this._browseDoc.createElement("td");
1119  labelCol.className = "label";
1120  var anchor = this._browseDoc.createElement("a");
1121  var url = "http://www.allmusic.com/cg/amg.dll?P=amg&opt1=5&sql=" +
1122  release.label.replace(/ /g, "+");
1123  this.makeLink(anchor, url, "label");
1124  var labelColLabel = this._browseDoc.createTextNode(release.label);
1125  anchor.appendChild(labelColLabel);
1126  labelCol.appendChild(anchor);
1127  return (labelCol);
1128 }
1129 
1130 NewReleaseAlbum.createColumnCountry = function(release) {
1131  var countryCol = this._browseDoc.createElement("td");
1132  countryCol.className = "country";
1133  var countryColLabel = this._browseDoc.createTextNode(release.country);
1134  countryCol.appendChild(countryColLabel);
1135  return (countryCol);
1136 }
1137 
1138 NewReleaseAlbum.createColumnType = function(release) {
1139  var typeCol = this._browseDoc.createElement("td");
1140  typeCol.className = "type";
1141  var typeColLabel = this._browseDoc.createTextNode(release.type);
1142  typeCol.appendChild(typeColLabel);
1143  return (typeCol);
1144 }
1145 
1146 NewReleaseAlbum.createColumnTracks = function(release) {
1147  var tracksCol = this._browseDoc.createElement("td");
1148  tracksCol.className = "tracks";
1149  var tracksColLabel = this._browseDoc.createTextNode(release.tracks);
1150  tracksCol.appendChild(tracksColLabel);
1151  return (tracksCol);
1152 }
1153 
1154 NewReleaseAlbum.createColumnGCal = function(release) {
1155  var dateObj = new Date(release.ts*1000);
1156  var dateStr = dateObj.toLocaleFormat("%Y%m%d"); // T%H%M%SZ");
1157  var url = "http://www.google.com/calendar/event?action=TEMPLATE";
1158  url += "&text=" + escape(release.title);
1159  url += "&details=";
1160  url += escape(release.artistname) + ",";
1161  // trim the last ,
1162  url = url.substr(0, url.length-1);
1163  url += "&dates=" + dateStr + "/" + dateStr;
1164 
1165  var gCalLinkCol = this._browseDoc.createElement("td");
1166  gCalLinkCol.className = "gcal";
1167  var gCalLink = this._browseDoc.createElement("a");
1168  this.makeLink(gCalLink, url, "gcal");
1169  gCalLink.setAttribute("title", this._strings.getString("tableGCalTooltip"));
1170 
1171  var gCalImage = this._browseDoc.createElement("img");
1172  gCalImage.src = "chrome://newreleases/skin/gcal.png";
1173  gCalImage.className = "gcalImage";
1174  gCalLink.appendChild(gCalImage);
1175 
1176  gCalLinkCol.appendChild(gCalLink);
1177 
1178  return (gCalLinkCol);
1179 }
1180 
1181 NewReleaseAlbum.createColumnArtist = function(release) {
1182  var artistCol = this._browseDoc.createElement("td");
1183  artistCol.className = "artist";
1184  var anchor = this._browseDoc.createElement("a");
1185  var url = "http://www.allmusic.com/cg/amg.dll?P=amg&opt1=1&sql=" +
1186  escape(release.artistname);
1187  this.makeLink(anchor, url, "artistname");
1188  var artistColLabel =
1189  this._browseDoc.createTextNode(release.artistname);
1190  anchor.appendChild(artistColLabel);
1191  artistCol.appendChild(anchor);
1192  return (artistCol);
1193 }
1194 
1195 /* Generic shortcut for creating a DIV or SPAN & assigning it a style class */
1196 NewReleaseAlbum.createBlock = function(blockname, makeSpan) {
1197  var block;
1198  if (makeSpan)
1199  var block = this._browseDoc.createElement("span");
1200  else
1201  var block = this._browseDoc.createElement("div");
1202  block.className=blockname;
1203  return block;
1204 }
1205 
1206 /* Methods connected to the groupby menulist & filter checkbox on the chrome */
1207 NewReleaseAlbum.changeFilter = function(updateCheckbox) {
1208  this.filterLibraryArtists = !this.filterLibraryArtists;
1209  Application.prefs.setValue("extensions.newreleases.filterLibraryArtists",
1210  this.filterLibraryArtists);
1211 #ifdef METRICS_ENABLED
1212  if (this.filterLibraryArtists)
1213  gMetrics.metricsInc("newReleases", "filter.library", "");
1214  else
1215  gMetrics.metricsInc("newReleases", "filter.all", "");
1216 #endif
1217 
1218  /* checks the filter checkbox (for the path taken when there are no
1219  results in the user's country and they click the button to see
1220  all release, rather than checking the checkbox themselves */
1221  if (updateCheckbox) {
1222  var checkbox = document.getElementById("checkbox-library-artists");
1223  checkbox.setAttribute("checked", this.filterLibraryArtists);
1224  }
1225  //songbirdMainWindow.NewReleases.updateNewReleasesCount();
1226  flushDisplay();
1227  if (this.nrSvc.drawingLock) {
1228  // trigger browseByArtists|browseByDates to abort
1229  this.abortDrawing = true;
1230 
1231  // block for release of the lock, and then redraw
1232  this.blockAndBrowseReleases(this);
1233  } else {
1234  this.browseNewReleases(this);
1235  }
1236 }
1237 
1238 NewReleaseAlbum.groupBy = function(group) {
1239  Application.prefs.setValue("extensions.newreleases.groupby", group);
1240 
1241  if (this.nrSvc.drawingLock) {
1242  // trigger browseByArtists|browseByDates to abort
1243  this.abortDrawing = true;
1244 
1245  // block for release of the lock, and then redraw
1246  this.blockAndBrowseReleases(this);
1247  } else {
1248  this.browseNewReleases(this);
1249  }
1250 }
1251 
1252 /* Spins until drawingLock is released, and then triggers a browseNewReleases() */
1253 NewReleaseAlbum.blockAndBrowseReleases = function blockAndBrowseReleases(ct) {
1254  if (Cc["@songbirdnest.com/newreleases;1"]
1255  .getService(Ci.nsIClassInfo).wrappedJSObject.drawingLock)
1256  {
1257  setTimeout(function() { blockAndBrowseReleases(ct);}, 100);
1258  } else {
1259  ct.browseNewReleases(ct);
1260  }
1261 }
const Cu
const Cc
_dialogDatepicker dateText
_setDateDatepicker date
var Application
Definition: sbAboutDRM.js:37
sbOSDControlService prototype className
var letterIndices
Definition: browse.js:45
function doc() browser.contentDocument
var customCountries
Definition: browse.js:64
function flushDisplay()
Definition: browse.js:29
_changeFirstDay day
const allEntriesID
Definition: browse.js:58
getService(Ci.sbIFaceplateManager)
return elem filter &&elem filter indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity
restoreDimensions aLeft
let window
this _contentSandbox label
Definition: FeedWriter.js:814
var bundle
var count
Definition: test_bug7406.js:32
restoreDimensions aTop
Element Properties href
aWindow setTimeout(function(){_this.restoreHistory(aWindow, aTabs, aTabData, aIdMap);}, 0)
function SBStringBundle(aBundle)
return null
Definition: FeedWriter.js:1143
return ret
function url(spec)
return aWindow document documentElement getAttribute(aAttribute)||dimension
const Ci
var NewReleaseOptions
Definition: options.js:29
_getSelectedPageStyle s i
var group