browse.js
Go to the documentation of this file.
1 if (typeof(Cc) == "undefined")
2  var Cc = Components.classes;
3 if (typeof(Ci) == "undefined")
4  var Ci = Components.interfaces;
5 if (typeof(Cu) == "undefined")
6  var Cu = Components.utils;
7 
8 Cu.import("resource://app/jsmodules/StringUtils.jsm");
9 Cu.import("resource://app/jsmodules/sbProperties.jsm");
10 Cu.import("resource://app/jsmodules/sbLibraryUtils.jsm");
11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
12 
13 if (typeof(songbirdMainWindow) == "undefined")
14  var songbirdMainWindow = Cc["@mozilla.org/appshell/window-mediator;1"]
15  .getService(Ci.nsIWindowMediator)
16  .getMostRecentWindow("Songbird:Main").window;
17 
18 if (typeof(gBrowser) == "undefined")
19  var gBrowser = Cc["@mozilla.org/appshell/window-mediator;1"]
20  .getService(Ci.nsIWindowMediator)
21  .getMostRecentWindow("Songbird:Main").window.gBrowser;
22 
23 #ifdef METRICS_ENABLED
24 if (typeof(gMetrics) == "undefined")
25  var gMetrics = Cc["@songbirdnest.com/Songbird/Metrics;1"]
26  .createInstance(Ci.sbIMetrics);
27 #endif
28 
29 function flushDisplay() {
30  if (typeof(Components) == "undefined")
31  return;
32  while ((typeof(Components) != "undefined") &&
33  Components.classes["@mozilla.org/thread-manager;1"].getService()
34  .currentThread.hasPendingEvents())
35  {
36  Components.classes["@mozilla.org/thread-manager;1"].getService()
37  .currentThread.processNextEvent(true);
38  }
39 }
40 
41 var SongkickPartnerCode = "p=" +
42  (new SBStringBundle("chrome://concerts/content/config.properties"))
43  .get('SongkickPartnerCode');
44 
45 var letterIndices = ['Z','Y','X','W','V','U','T','S','R','Q','P','O','N','M',
46  'L','K','J','I','H','G','F','E','D','C','B','A','#'];
47 
48 if (typeof ConcertTicketing == 'undefined') {
49  var ConcertTicketing = {
50  QueryInterface : XPCOMUtils.generateQI([Ci.sbISongkickConcertCountCallback])
51  };
52 }
53 
54 ConcertTicketing.unload = function() {
55  this.abortDrawing = true;
56  this.skSvc.drawingLock = false;
57  Components.classes["@songbirdnest.com/Songbird/Concerts/Songkick;1"]
58  .getService(Components.interfaces.sbISongkick)
59  .unregisterDisplayCallback();
60 }
61 
62 ConcertTicketing.init = function() {
63  // Set the tab title
64  var servicePaneStr = Cc["@mozilla.org/intl/stringbundle;1"]
65  .getService(Ci.nsIStringBundleService)
66  .createBundle("chrome://concerts/locale/overlay.properties");
67  document.title = servicePaneStr.GetStringFromName("servicePane.Name");
68 
69  var self = this;
70 
71  if (typeof(Ci.sbIMediacoreManager) != "undefined")
72  ConcertTicketing.newMediaCore = true;
73  else
74  ConcertTicketing.newMediaCore = false;
75 
76  this.dateFormat = "%a, %b %e, %Y";
77  if (navigator.platform.substr(0, 3) == "Win")
78  this.dateFormat = "%a, %b %#d, %Y";
79 
80  this.Cc = Components.classes;
81  this.Ci = Components.interfaces;
82 
83  // Load the iframe
84  this.iframe = document.getElementById("concert-listings");
85 
86  // Set a pointer to our document so we can update it later as needed
87  this._browseDoc = this.iframe.contentWindow.document;
88 
89  // Set our string bundle for localised string lookups
90  this._strings = document.getElementById("concerts-strings");
91 
92  // Apply styles to the iframe
93  var headNode = this._browseDoc.getElementsByTagName("head")[0];
94  var cssNode = this._browseDoc.createElementNS(
95  "http://www.w3.org/1999/xhtml", "html:link");
96  cssNode.type = 'text/css';
97  cssNode.rel = 'stylesheet';
98  cssNode.href = 'chrome://songbird/skin/html.css';
99  cssNode.media = 'screen';
100  headNode.appendChild(cssNode);
101 
102  cssNode = this._browseDoc.createElementNS(
103  "http://www.w3.org/1999/xhtml", "html:link");
104  cssNode.type = 'text/css';
105  cssNode.rel = 'stylesheet';
106  cssNode.href = 'chrome://concerts/skin/browse.css';
107  cssNode.media = 'screen';
108  headNode.appendChild(cssNode);
109 
110  // Get the Songkick XPCOM service
111  this.skSvc = Cc["@songbirdnest.com/Songbird/Concerts/Songkick;1"]
112  .getService(Ci.sbISongkick);
113 
114  // Set the checked state for the filter checkbox
115  this.filterLibraryArtists = Application.prefs
116  .getValue("extensions.concerts.filterLibraryArtists", true);
117 
118  // Get pref to display play link or not
119  this.showPlayLink = Application.prefs
120  .getValue("extensions.concerts.showplaylink", false);
121  this.showGCal = Application.prefs
122  .getValue("extensions.concerts.showgcal", false);
123 
124  // Set the last saved concert count
125  this.skSvc.getConcertCount(
126  this.filterLibraryArtists, this);
127 
128 #ifdef METRICS_ENABLED
129  gMetrics.metricsInc("concerts", "servicepane.clicked", "");
130 #endif
131  // Register our UI display callback with the Songkick object
132  if (!this.skSvc.hasDisplayCallback()) {
133  var displayCB = new this.displayCallback(this);
134  displayCB.wrappedJSObject = displayCB;
135  this.skSvc.registerDisplayCallback(displayCB);
136  }
137 
138  if (Application.prefs.get("extensions.concerts.firstrun").value) {
139  // Load the first run page in the deck
140  var deck = document.getElementById("concerts-deck");
141  deck.setAttribute("selectedIndex", 1);
142  ConcertOptions.init();
143  } else {
144  if (this.skSvc.concertRefreshRunning) {
145  // If the data refresh is already happening, switch to the progress
146  // Switch the deck to the loading page
147  var deck = document.getElementById("concerts-deck");
148  deck.setAttribute("selectedIndex", 2);
149  var label = document.getElementById("loading-label");
150  var progressbar = document.getElementById("loading-progress");
151  label.value = this.skSvc.progressString;
152  progressbar.value = this.skSvc.progressPercentage;
153  this.waitForRefreshToFinish(this);
154  } else {
155  // Otherwise populate the fields in the options page, and continue
156  // to the listing view
157  ConcertOptions.init();
158  this.browseConcerts(this);
159  }
160  }
161 }
162 
163 ConcertTicketing.onConcertCountEnd = function(aConcertCount) {
164  this.lastConcertCount = aConcertCount;
165 
166  // Attempt to update the count element if it exists.
167  var numConcertsNode = this._browseDoc.getElementById("num-concerts");
168  if (numConcertsNode) {
169  var bundle = new SBStringBundle(
170  "chrome://concerts/locale/overlay.properties");
171  var numConcertsStr = bundle.formatCountString("concertsShown",
172  aConcertCount,
173  [ aConcertCount ],
174  "foo");
175  numConcertsNode.innerHTML = numConcertsStr;
176  }
177 }
178 
179 ConcertTicketing.createFooter = function(aConcertCount) {
180  if (this._browseDoc.getElementById("ft") != null) {
181  this._browseDoc.removeChild(this._browseDoc.getElementById("ft"));
182  }
183  if (aConcertCount > 0) {
184  var ft = this.createBlock("ft");
185  ft.id = "ft";
186  var poweredBy = this.createBlock("poweredBy");
187  var url = this.skSvc.providerURL();
188  var city = Application.prefs.getValue("extensions.concerts.city", 0);
189  if (city != 0)
190  url += "?" + SongkickPartnerCode + "&user_location=" + city;
191  poweredBy.innerHTML = this._strings.getString("poweredBy") +
192  " <a target='_blank' href='" + url + "'>Songkick</a>. "
193  + this._strings.getString("tosPrefix") +
194  " <a target='_blank' href='http://www.songkick.com/info/terms/?" +
195  SongkickPartnerCode + "'>" +
196  this._strings.getString("tosLink") + "</a>.";
197  ft.appendChild(poweredBy);
198 
199  var lastUpdated = this.createBlock("lastUpdated");
200  var ts = parseInt(1000*
201  Application.prefs.getValue("extensions.concerts.lastupdated", 0));
202  if (ts > 0) {
203  var dateString =
204  new Date(ts).toLocaleFormat("%a, %b %e, %Y at %H:%M.");
205  lastUpdated.innerHTML = this._strings.getString("lastUpdated") +
206  " <span class='date'>" + dateString + "</span>";
207  ft.appendChild(lastUpdated);
208  }
209  this._bodyNode.appendChild(ft);
210  }
211 }
212 
213 ConcertTicketing.waitForRefreshToFinish = function(self) {
214  if (Components.classes["@songbirdnest.com/Songbird/Concerts/Songkick;1"]
215  .getService(Components.interfaces.sbISongkick).concertRefreshRunning)
216  {
217  setTimeout(this.waitForRefreshToFinish(self), 100);
218  } else {
219  self.browseConcerts(self);
220  }
221 
222 }
223 
224 ConcertTicketing.editLocation = function() {
225  // Save our current selected deck page, so if the user cancels we can
226  // switch back to it
227  var deck = document.getElementById("concerts-deck");
228  deck.setAttribute("previous-selected-deck", deck.selectedIndex);
229 
230  // Hide the stuff we don't want to display
231  document.getElementById("pref-library").style.visibility = "hidden";
232  document.getElementById("library-ontour-box").style.visibility ="collapse";
233 
234  // Not strictly required, but in the event the user hit cancel, we
235  // probably want to reset to their preferred location
236  ConcertOptions.init();
237 
238  // Make the cancel button visible (it's hidden by default for first run)
239  var cancel = document.getElementById("pref-cancel-button");
240  cancel.style.visibility="visible";
241 
242  // Switch to the location edit deck page
243  deck.setAttribute("selectedIndex", 1);
244 }
245 
246 ConcertTicketing.displayCallback = function(ticketingObj) {
247  this.label = document.getElementById("loading-label"),
248  this.progressbar = document.getElementById("loading-progress");
249  this.concertTicketing = ticketingObj;
250 },
251 ConcertTicketing.displayCallback.prototype = {
252  loadingMessage : function(str) {
253  this.label.value = str;
254  },
255  loadingPercentage : function(pct) {
256  this.progressbar.value = pct;
257  },
258  timeoutError : function() {
259  songbirdMainWindow.Concerts.updateConcertCount(0);
260  var deck = document.getElementById("concerts-deck");
261  deck.setAttribute("selectedIndex", 4);
262  },
263  showListings : function() {
264  this.concertTicketing.browseConcerts(this.concertTicketing);
265  },
266  updateConcertCount : function() {
267  songbirdMainWindow.Concerts.updateConcertCount()
268  },
269  alert : function(str) { window.alert(str); },
270 }
271 
272 // Initiate a synchronous database refresh. This can only be triggered after
273 // first run, or if the user goes and changes their location
274 ConcertTicketing.loadConcertData = function(city) {
275  // Switch the deck to the loading page
276  var deck = document.getElementById("concerts-deck");
277  deck.setAttribute("selectedIndex", 2);
278 
279  // First run is over
280  if (Application.prefs.getValue("extensions.concerts.firstrun", false)) {
281  // toggle first-run
282  Application.prefs.setValue("extensions.concerts.firstrun", false);
283 
284  // setup the smart playlist
285  songbirdMainWindow.Concerts._setupOnTourPlaylist();
286  }
287 
288  // Load the new data
289  var ret = this.skSvc.refreshConcerts(false, city);
290  songbirdMainWindow.Concerts.updateConcertCount();
291  if (!ret)
292  this.browseConcerts(this);
293 }
294 
295 ConcertTicketing.showNoConcerts = function() {
296  var deck = document.getElementById("concerts-deck");
297  deck.setAttribute("selectedIndex", 3);
298 
299  deck = document.getElementById("no-results-deck");
300  var easterEgg = Application.prefs.getValue("extensions.concerts.epic", 0);
301  if (easterEgg == "9x6") {
302  deck.setAttribute("selectedIndex", 1);
303  var label = document.getElementById("epic-city");
304  label.value += this.skSvc.getLocationString(this.pCountry, this.pState,
305  this.pCity);
306 
307  // Set the actual button text
308  var button = document.getElementById("noresults-seeallconcerts-city-e");
309  button.label = this._strings.getString("seeAllConcerts") + " " +
310  this.skSvc.getCityString(this.pCity);
311  } else {
312  var label;
313  var button;
314  if (easterEgg == "54") {
315  // no concerts found, period
316  deck.setAttribute("selectedIndex", 0);
317  label = document.getElementById("noresults-city");
318  label.value = this._strings.getString("yourCitySucks") +
319  " " + this.skSvc.getLocationString(this.pCountry,
320  this.pState, this.pCity);
321  button = document.getElementById("noresults-seeallconcerts-city");
322  button.style.visibility = "hidden";
323  } else {
324  // no library artist concerts found, select the right deck
325  deck.setAttribute("selectedIndex", 0);
326 
327  // show error messages
328  // "Well that's lame"
329  label = document.getElementById("noresults-city-1");
330  label.value = this._strings.getString("noLibLame");
331 
332  // "None of the artists in your library are touring..."
333  var city = " " + this.skSvc.getCityString(this.pCity);
334  // ugly hack because SF Bay Area is the only one that needs "the"
335  // in front of it since it's not a proper city name. This was
336  // causing problems for Babelzilla translators, so I've
337  // hardcoded it to assume that if you're using SF Bay Area, then
338  // you're also going to get it in English
339  if (this.pCity == 26330)
340  city = " the " + city;
341  label = document.getElementById("noresults-city-2");
342  label.value = this._strings.getString("noLibArtistsTouring") +
343  city + ".";
344 
345  // "If that changes..."
346  label = document.getElementById("noresults-city-3");
347  label.value = this._strings.getString("noLibArtistsTouring2");
348  label = document.getElementById("noresults-city-4");
349  label.value = this._strings.getString("noLibArtistsTouring3");
350 
351  // Set the actual button text
352  button = document.getElementById("noresults-seeallconcerts-city");
353  button.label = this._strings.getString("seeAllConcerts") + city;
354  }
355  }
356 }
357 
358 ConcertTicketing.showTimeoutError = function() {
359  var deck = document.getElementById("concerts-deck");
360  deck.setAttribute("selectedIndex", 4);
361 }
362 
363 ConcertTicketing.openCityPage = function() {
364  var url = Application.prefs.getValue("extensions.concerts.citypage", "");
365  gBrowser.loadOneTab(url + "?" + SongkickPartnerCode);
366 }
367 
368 ConcertTicketing.openProviderPage = function() {
369  var url;
370  if (typeof(this.skSvc) != "undefined")
371  url = this.skSvc.providerURL();
372  else
373  url = Cc["@songbirdnest.com/Songbird/Concerts/Songkick;1"]
374  .getService(Ci.sbISongkick).providerURL();
375 
376  var city = Application.prefs.getValue("extensions.concerts.city", 0);
377  if (city != 0)
378  url += "?" + SongkickPartnerCode + "&user_location=" + city;
379  gBrowser.loadOneTab(url);
380 }
381 
382 /***************************************************************************
383  * Called when the user clicks "Play" next to the main artist name in the
384  * artist grouping view of upcoming concerts
385  ***************************************************************************/
386 ConcertTicketing.playArtist = function(e) {
387  var artistName = this.getAttribute("artistName");
388 
389 #ifdef METRICS_ENABLED
390  gMetrics.metricsInc("concerts", "browse.view.artist.playartist", "");
391 #endif
392  var list = songbirdMainWindow.Concerts.touringPlaylist;
393  var view = list.createView();
394  var cfs = view.cascadeFilterSet;
395 
396  cfs.appendSearch(["*"], 1);
397  cfs.appendFilter(SBProperties.genre);
398  cfs.appendFilter(SBProperties.artistName);
399  cfs.appendFilter(SBProperties.albumName);
400 
401  cfs.clearAll();
402  cfs.set(2, [artistName], 1);
403 
404  if (ConcertTicketing.newMediaCore)
405  Cc["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
406  .getService(Ci.sbIMediacoreManager)
407  .sequencer.playView(view, 0);
408  else
409  Cc['@songbirdnest.com/Songbird/PlaylistPlayback;1']
410  .getService(Ci.sbIPlaylistPlayback)
411  .sequencer.playView(view, 0);
412 }
413 
414 /***************************************************************************
415  * Scrolls the iframe to the selected index block when the user clicks on
416  * one of the letter or month index links in the chrome
417  ***************************************************************************/
418 ConcertTicketing.indexJump = function(e) {
419  var iframe = document.getElementById("concert-listings");
420  this._browseDoc = iframe.contentWindow.document;
421 
422  var anchor;
423  if (this.id.indexOf("concerts-nav-letter-") >= 0) {
424  var letter = this.id.replace("concerts-nav-letter-", "");
425  anchor = this._browseDoc.getElementById("indexLetter-" + letter);
426  } else {
427  var dateComponent = this.id.split("-")[3];
428  anchor = this._browseDoc.getElementById("indexDate-" + dateComponent);
429  }
430  if (anchor) {
431  var aLeft = anchor.offsetLeft;
432  var aTop = anchor.offsetTop;
433  iframe.contentWindow.scrollTo(0, aTop);
434  }
435 }
436 
437 
438 ConcertTicketing.browseConcerts = function(ticketingObj) {
439  if (this.skSvc.drawingLock)
440  return;
441  this.skSvc.drawingLock = true;
442  this.abortDrawing = false;
443 
444  // Switch to the listing view
445  var deck = document.getElementById("concerts-deck");
446  deck.setAttribute("selectedIndex", 0);
447 
448  /*
449  * bug 13347 - disabling this in favour of going to 'edit location' page
450  * instead
451  regionLabel.addEventListener("click",
452  self.ConcertTicketing.openCityPage, false);
453  */
454 
455  this._bodyNode = this._browseDoc.getElementsByTagName("body")[0];
456 
457  // Clear any existing results
458  while (this._bodyNode.firstChild) {
459  this._bodyNode.removeChild(this._bodyNode.firstChild);
460  }
461 
462  /*
463  // Add the Songkick logo
464  var skDiv = this.createBlock("powered-by-songkick");
465  skDiv.style.width = "100%";
466  skDiv.style.textAlign = "right";
467  var skImg = this._browseDoc.createElement("img");
468  skImg.src = "chrome://concerts/content/songkick.png";
469  skImg.className = "clickable";
470  skImg.addEventListener("click", this.openProviderPage, false);
471  skDiv.appendChild(skImg);
472  this._bodyNode.appendChild(skDiv);
473  */
474 
475  var easterEgg = Application.prefs.getValue("extensions.concerts.epic", 0);
476  if (easterEgg == "doctorwho") {
477  ConcertTicketing.showTimeoutError();
478  return;
479  }
480 
481  var doc = this._browseDoc;
482  var str = this._strings;
483 
484  // Add the header block
485  var headerDiv = this.createBlock("header");
486  var concertsImage = doc.createElement("img");
487  concertsImage.src = "chrome://concerts/skin/Concerts.png";
488  concertsImage.id = "concerts-logo";
489  headerDiv.appendChild(concertsImage);
490  var songkickImage = doc.createElement("img");
491  songkickImage.src = "chrome://concerts/content/songkick-logo-concerts-home.png";
492  songkickImage.id = "songkick-logo";
493  songkickImage.className = "clickable";
494  songkickImage.addEventListener("click", this.openProviderPage, false);
495  headerDiv.appendChild(songkickImage);
496  this._bodyNode.appendChild(headerDiv);
497 
498  // Add the subheader block
499  var subHeaderDiv = this.createBlock("sub-header");
500  var numConcertsShownDiv = this.createBlock("num-concerts", true);
501  numConcertsShownDiv.id = "num-concerts";
502  subHeaderDiv.appendChild(numConcertsShownDiv);
503  this.skSvc.getConcertCount(this.filterLibraryArtists, this);
504  songbirdMainWindow.Concerts.updateConcertCount();
505 
506 
507  var locationChangeDiv = this.createBlock("location", true);
508  subHeaderDiv.appendChild(locationChangeDiv);
509  var cityName = this.createBlock("location-city", true);
510  this.pCountry = Application.prefs.getValue("extensions.concerts.country",0);
511  this.pState = Application.prefs.getValue("extensions.concerts.state",0);
512  this.pCity = Application.prefs.getValue("extensions.concerts.city", 0);
513  var locationString = this.skSvc.getLocationString(this.pCountry,
514  this.pState,
515  this.pCity);
516  cityName.appendChild(doc.createTextNode(locationString));
517  locationChangeDiv.appendChild(cityName);
518  var changeLocation = this.createBlock("location-change", true);
519  changeLocation.appendChild(doc.createTextNode(str.getString("changeLoc")));
520  locationChangeDiv.appendChild(changeLocation);
521  changeLocation.addEventListener("click", ConcertTicketing.editLocation,
522  false);
523 
524  var filterDiv = this.createBlock("filter", true);
525  var checkbox = doc.createElement("input");
526  checkbox.setAttribute("type", "checkbox");
527  if (this.filterLibraryArtists)
528  checkbox.setAttribute("checked", true);
529  checkbox.addEventListener("click", function(e)
530  { ConcertTicketing.changeFilter(false); }, false);
531  filterDiv.appendChild(checkbox);
532  filterDiv.appendChild(doc.createTextNode(str.getString("filter")));
533  subHeaderDiv.appendChild(filterDiv);
534 
535  var clearDiv = this.createBlock("sub-header-empty");
536  clearDiv.style.clear = "both";
537  subHeaderDiv.appendChild(clearDiv);
538  this._bodyNode.appendChild(subHeaderDiv);
539 
540 
541  var groupBy = Application.prefs.getValue("extensions.concerts.groupby",
542  "artist");
543  // Add the nav block - the View/Concerts/Artists selector
544  var navDiv = this.createBlock("concerts-nav");
545  var viewSpan = this.createBlock("concerts-nav-view", true);
546  viewSpan.appendChild(doc.createTextNode(str.getString("navView")));
547 
548  var datesSpan = this.createBlock("concerts-nav-concerts", true);
549  var artistsSpan = this.createBlock("concerts-nav-artists", true);
550  if (groupBy == "artist") {
551  artistsSpan.className += " concerts-nav-selected";
552  datesSpan.addEventListener("mouseover", function(e)
553  {
554  datesSpan.className += " concerts-nav-hover";
555  }, false);
556  datesSpan.addEventListener("mouseout", function(e)
557  {
558  datesSpan.className = datesSpan.className.replace(
559  " concerts-nav-hover", "");
560  }, false);
561  datesSpan.addEventListener("click", function(e)
562  {
563  ConcertTicketing.groupBy("date");
564  }, false);
565  } else {
566  datesSpan.className += " concerts-nav-selected";
567  artistsSpan.addEventListener("mouseover", function(e)
568  {
569  artistsSpan.className += " concerts-nav-hover";
570  }, false);
571  artistsSpan.addEventListener("mouseout", function(e)
572  {
573  artistsSpan.className = artistsSpan.className.replace(
574  " concerts-nav-hover", "");
575  }, false);
576  artistsSpan.addEventListener("click", function(e)
577  {
578  ConcertTicketing.groupBy("artist");
579  }, false);
580  }
581 
582  datesSpan.appendChild(doc.createTextNode(str.getString("navDates")));
583  artistsSpan.appendChild(doc.createTextNode(str.getString("navArtists")));
584  navDiv.appendChild(viewSpan);
585  navDiv.appendChild(datesSpan);
586  navDiv.appendChild(artistsSpan);
587 
588  var indexDiv = this.createBlock("concerts-nav-index");
589  if (groupBy == "artist") {
590  // group by Artist Names
591  // Add the #,A-Z index letters
592  for (var i in letterIndices) {
593  var l = letterIndices[i];
594  var letterSpan = this.createBlock("concerts-nav-letter", true);
595  letterSpan.id = "concerts-nav-letter-" + l;
596  letterSpan.appendChild(doc.createTextNode(l));
597  indexDiv.appendChild(letterSpan);
598  }
599  } else {
600  // group by Concert Dates
601  var myDate = new Date();
602  myDate.setDate(1);
603  var dates = [];
604  for (let i=0; i<6; i++) {
605  var mon = myDate.getMonth();
606  var year = myDate.getFullYear();
607  var dateStr = myDate.toLocaleFormat("%b %Y");
608  var dateSpan = this.createBlock("concerts-nav-date", true);
609  dateSpan.appendChild(doc.createTextNode(dateStr));
610  dateSpan.id = "concerts-nav-date-" + mon + year;
611  myDate.setMonth(mon+1);
612  dates.push(dateSpan);
613  }
614  for (var i in dates.reverse()) {
615  indexDiv.appendChild(dates[i], null);
616  }
617  }
618  navDiv.appendChild(indexDiv);
619  clearDiv = this.createBlock("concerts-nav-empty");
620  clearDiv.style.clear = "both";
621  navDiv.appendChild(clearDiv);
622  this._bodyNode.appendChild(navDiv);
623 
624  flushDisplay();
625 
626  if (groupBy == "artist")
627  this.browseArtists();
628  else
629  this.browseDates();
630 
631  // Debug
632  /*
633  var html = this._bodyNode.innerHTML;
634  var textbox = this._browseDoc.createElement("textarea");
635  textbox.width=50;
636  textbox.height=50;
637  this._bodyNode.appendChild(textbox);
638  textbox.value = html;
639  */
640 
641  this.skSvc.drawingLock = false;
642 }
643 
644 ConcertTicketing.browseDates = function() {
645  this._isWaitingOnBrowseDates = true;
646  this.skSvc.concertEnumerator(
647  "date", this.filterLibraryArtists, this);
648 }
649 
650 ConcertTicketing.onBrowseDatesReady = function(concerts) {
651  var lastDate = "";
652  var dateBlock = null;
653  var concertTable = null;
654  var venueConcertBlock = null;
655  var concertsShown = 0;
656 
657  var contentsNode = this._browseDoc.getElementById("concerts-contents");
658 
659  var today = new Date();
660  var todayMon = today.getMonth();
661  var todayDate = today.getDate();
662  var todayYear = today.getFullYear();
663 
664 #ifdef METRICS_ENABLED
665  gMetrics.metricsInc("concerts", "browse.view.date", "");
666 #endif
667  while (concerts.hasMoreElements()) {
668  if (this.abortDrawing)
669  return;
670  var concert = concerts.getNext().QueryInterface(Ci.sbISongkickConcertInfo);
671 
672  var thisDateObj = new Date(concert.ts * 1000);
673  var thisMon = thisDateObj.getMonth();
674  var thisYear = thisDateObj.getFullYear();
675  var thisIndex = thisMon + "" + thisYear;
676  var thisDate = thisYear + '-' + thisMon + '-' + thisDateObj.getDate();
677 
678  // Don't show past concerts
679  if (((thisMon < todayMon) && (thisYear <= todayYear)) ||
680  (thisMon == todayMon && thisDateObj.getDate() < todayDate)) {
681  continue;
682  }
683 
684  var doc = this._browseDoc;
685  var dateIndex = doc.getElementById("concerts-nav-date-" + thisIndex);
686  if (dateIndex == null) {
687  continue;
688  }
689  dateIndex.className += " concerts-nav-date-link";
690  dateIndex.addEventListener("mouseover", function(e)
691  {
692  var el = e.currentTarget;
693  el.className += " concerts-nav-hover";
694  }, false);
695  dateIndex.addEventListener("mouseout", function(e)
696  {
697  var el = e.currentTarget;
698  el.className = el.className.replace(" concerts-nav-hover", "");
699  }, false);
700  dateIndex.addEventListener("click", ConcertTicketing.indexJump, false);
701 
702  if (thisDate != lastDate) {
703  // Create the block for concert listings of the same letter index
704  var dateDiv = this.createBlock("indexDiv");
705 
706  // Create the letter index block
707  var dateIndexContainer = this.createDateBlock(thisDateObj);
708 
709  // Create the actual concert listing block for this date
710  dateBlock = this.createBlock("concertListing");
711  dateBlock.className += " concertListingDate";
712 
713  // Create a new concert listing block
714  var concertTableBlock = this.createBlock("artistBlock");
715 
716  // Create the table for concerts
717  concertTable = this.createTableDateView();
718  concertTableBlock.appendChild(concertTable);
719 
720  // Assemble the pieces together
721  dateBlock.appendChild(concertTableBlock);
722  dateDiv.appendChild(dateIndexContainer);
723  dateDiv.appendChild(dateBlock);
724  contentsNode.appendChild(dateDiv);
725  flushDisplay();
726  }
727 
728  // Create the table row representing this concert
729  var thisConcert = this.createRowDateView(concert);
730  concertTable.appendChild(thisConcert);
731 
732  lastDate = thisDate;
733  concertsShown++;
734  }
735 
736  if (concertsShown == 0) {
737  if (Application.prefs.getValue("extensions.concerts.networkfailure",
738  false))
739  ConcertTicketing.showTimeoutError();
740  else
741  ConcertTicketing.showNoConcerts();
742  }
743 
744  this.createFooter(concertsShown);
745  return concertsShown;
746 }
747 
748 // XXX KREEGER + STEVEL == THIS IS DEAD CODE ///////////////////////////////////
749 ConcertTicketing.browseVenues = function(concerts) {
750  var lastLetter = "";
751  var lastVenue = "";
752  var concertBlock = null;
753  var venueConcertBlock = null;
754  var thisMainVenueAnchor = null;
755 
756  var concertsShown = 0;
757  while (concerts.hasMoreElements()) {
758  var concert = concerts.getNext().wrappedJSObject;
759 
760  var thisLetter = concert.venue[0].toUpperCase();
761  var letterIdx = document.getElementById("letter-index-" + thisLetter);
762  letterIdx.className = "index-letter text-link";
763  letterIdx.addEventListener("click", ConcertTicketing.indexJump, false);
764  if (thisLetter != lastLetter) {
765  // Create the block for concert listings of the same letter index
766  var letterDiv = this.createBlock("indexDiv");
767 
768  // Create the letter index block
769  var letterIndexContainer = this.createLetterBlock(thisLetter);
770 
771  // Create the actual concert listing block for this letter
772  concertBlock = this.createBlock("concertListing");
773  concertBlock.className += " concertListingLetter";
774 
775  // Assemble the pieces together
776  letterDiv.appendChild(letterIndexContainer);
777  letterDiv.appendChild(concertBlock);
778  this._bodyNode.appendChild(letterDiv);
779  }
780  if (concert.venue != lastVenue) {
781  // Create a new venue block
782  var venueBlock = this.createBlock("artistBlock");
783 
784  // Create the main venue title
785  var venueName = this.createBlock("mainArtistName");
786  thisMainVenueAnchor = this._browseDoc.createElement("a");
787  thisMainVenueAnchor.setAttribute("target", "_blank");
788  var venueNameText = this._browseDoc.createTextNode(concert.venue);
789  thisMainVenueAnchor.appendChild(venueNameText);
790  venueName.appendChild(thisMainVenueAnchor);
791  venueBlock.appendChild(venueName);
792 
793  // Create the table for concerts
794  venueConcertBlock = this.createTableVenueView();
795  venueBlock.appendChild(venueConcertBlock);
796 
797  // Attach the venue block to the concert listing block
798  concertBlock.appendChild(venueBlock);
799  }
800 
801  // Create the table row representing this concert
802  var thisConcert = this.createRowVenueView(concert);
803  venueConcertBlock.appendChild(thisConcert);
804 
805  lastLetter = thisLetter;
806  lastVenue = concert.venue;
807 
808  concertsShown++;
809  }
810 
811  if (concertsShown == 0) {
812  if (Application.prefs.getValue("extensions.concerts.networkfailure",
813  false))
814  ConcertTicketing.showTimeoutError();
815  else
816  ConcertTicketing.showNoConcerts();
817  }
818 
819  return concertsShown;
820 }
822 
823 ConcertTicketing.browseArtists = function() {
824  this._isWatingOnBrowseArtists = true;
825  this.skSvc.artistConcertEnumerator(
826  this.filterLibraryArtists, this);
827 }
828 
829 ConcertTicketing.onBrowseArtistsReady = function(concerts) {
830  var lastLetter = "";
831  var lastArtist = "";
832  var concertBlock = null;
833  var artistConcertBlock = null;
834  var thisMainArtistAnchor = null;
835  var concertsShown = 0;
836 
837  var contentsNode = this._browseDoc.getElementById("concerts-contents");
838 
839  var today = new Date();
840  var todayMon = today.getMonth();
841  var todayDate = today.getDate();
842  var todayYear = today.getFullYear();
843 
844 #ifdef METRICS_ENABLED
845  gMetrics.metricsInc("concerts", "browse.view.artist", "");
846 #endif
847  while (concerts.hasMoreElements()) {
848  if (this.abortDrawing)
849  return;
850  var concert = concerts.getNext().QueryInterface(Ci.sbISongkickConcertInfo);
851 
852  var thisDateObj = new Date(concert.ts * 1000);
853  var thisMon = thisDateObj.getMonth();
854  var thisYear = thisDateObj.getFullYear();
855  var thisIndex = thisMon + "" + thisYear;
856  var thisDate = thisYear + '-' + thisMon + '-' + thisDateObj.getDate();
857 
858  // Don't show past concerts
859  if (((thisMon < todayMon) && (thisYear <= todayYear)) ||
860  (thisMon == todayMon && thisDateObj.getDate() < todayDate))
861  {
862  continue;
863  }
864 
865  var artistname = unescape(decodeURIComponent(concert.artistname));
866  var thisLetter = artistname[0].toUpperCase();
867  if (thisLetter < 'A' || thisLetter > 'Z')
868  thisLetter = '#';
869 
870  var doc = this._browseDoc;
871  var letterIdx = doc.getElementById("concerts-nav-letter-" + thisLetter);
872  if (letterIdx == null) {
873  continue;
874  }
875  letterIdx.className += " concerts-nav-letter-link";
876  letterIdx.addEventListener("mouseover", function(e)
877  {
878  var el = e.currentTarget;
879  el.className += " concerts-nav-hover";
880  }, false);
881  letterIdx.addEventListener("mouseout", function(e)
882  {
883  var el = e.currentTarget;
884  el.className = el.className.replace(" concerts-nav-hover", "");
885  }, false);
886  letterIdx.addEventListener("click", ConcertTicketing.indexJump, false);
887 
888  if (thisLetter != lastLetter) {
889  // Create the block for concert listings of the same letter index
890  var letterDiv = this.createBlock("indexDiv");
891 
892  // Create the letter index block
893  var letterIndexContainer = this.createLetterBlock(thisLetter);
894 
895  // Create the actual concert listing block for this letter
896  concertBlock = this.createBlock("concertListing");
897  concertBlock.className += " concertListingLetter";
898 
899  // Assemble the pieces together
900  letterDiv.appendChild(letterIndexContainer);
901  letterDiv.appendChild(concertBlock);
902  //this._bodyNode.appendChild(letterDiv);
903  contentsNode.appendChild(letterDiv);
904  flushDisplay();
905  }
906  if (artistname != lastArtist) {
907  // Create a new artist block
908  var artistBlock = this.createBlock("artistBlock");
909 
910  // Create the main concert title
911  var artistNameBlock = this.createBlock("mainArtistName", true);
912  thisMainArtistAnchor = this._browseDoc.createElement("a");
913  this.makeLink(thisMainArtistAnchor, "", "artist.main");
914  var artistNameText =
915  this._browseDoc.createTextNode(artistname);
916  thisMainArtistAnchor.appendChild(artistNameText);
917  artistNameBlock.appendChild(thisMainArtistAnchor);
918  artistBlock.appendChild(artistNameBlock);
919 
920  if (concert.libartist == 1 && this.showPlayLink) {
921  // Create the play link
922  var playBlock = this.createBlock("playBlock", true);
923  var playLink = this._browseDoc.createElement("img");
924  playLink.className = "ticketImage";
925  playLink.src = "chrome://concerts/skin/icon-play.png";
926  playBlock.className = "playLink";
927  playBlock.appendChild(playLink);
928  playBlock.setAttribute("artistName", artistname);
929  playBlock.addEventListener("click", this.playArtist, false);
930  artistBlock.appendChild(playBlock);
931  }
932 
933  // Create the table for concerts
934  artistConcertBlock = this.createTableArtistView();
935  artistBlock.appendChild(artistConcertBlock);
936 
937  // Attach the artist block to the concert listing block
938  concertBlock.appendChild(artistBlock);
939  }
940 
941  // Create the table row representing this concert
942  var thisConcert = this.createRowArtistView(concert,
943  thisMainArtistAnchor);
944  artistConcertBlock.appendChild(thisConcert);
945 
946  lastLetter = thisLetter;
947  lastArtist = artistname;
948 
949  concertsShown++;
950  }
951 
952  if (concertsShown == 0) {
953  if (Application.prefs.getValue("extensions.concerts.networkfailure",
954  false))
955  ConcertTicketing.showTimeoutError();
956  else
957  ConcertTicketing.showNoConcerts();
958  }
959 
960  this.createFooter(concertsShown);
961  return concertsShown;
962 }
963 
964 ConcertTicketing.onEnumerationStart = function() {
965  // Add the loading dialog in.
966  var contentsNode = this._browseDoc.createElement("div");
967  contentsNode.id = "concerts-contents";
968  this._bodyNode.appendChild(contentsNode);
969 
970  var strBundle = Cc["@mozilla.org/intl/stringbundle;1"]
971  .getService(Ci.nsIStringBundleService)
972  .createBundle("chrome://concerts/locale/songkick.properties");
973 
974  var loadingNode = this._browseDoc.createElement("div");
975  loadingNode.id = "concerts-loading";
976  loadingNode.innerHTML = strBundle.GetStringFromName("preparing");
977  contentsNode.appendChild(loadingNode);
978 }
979 
980 ConcertTicketing.onEnumerationEnd = function(aConcertsEnum) {
981  // Remove the loading block.
982  var loadingNode = this._browseDoc.getElementById("concerts-loading");
983  var contentsNode = this._browseDoc.getElementById("concerts-contents");
984  contentsNode.removeChild(loadingNode);
985 
986  if (this._isWatingOnBrowseArtists) {
987  this._isWatingOnBrowseArtists = false;
988  this.onBrowseArtistsReady(aConcertsEnum);
989  }
990  if (this._isWaitingOnBrowseDates) {
991  this._isWaitingOnBrowseDates = false;
992  this.onBrowseDatesReady(aConcertsEnum);
993  }
994 }
995 
996 // Takes a letter, and returns an index block for it
997 ConcertTicketing.createLetterBlock = function(letter) {
998  // letter index (LHS)
999  var letterIndexContainer = this.createBlock("letterIndexContainer");
1000  var letterIndex = this.createBlock("letterIndex");
1001  letterIndex.id = "indexLetter-" + letter;
1002  var letterIndexText = this._browseDoc.createTextNode(letter);
1003  letterIndex.appendChild(letterIndexText);
1004  letterIndexContainer.appendChild(letterIndex);
1005  return (letterIndexContainer);
1006 }
1007 
1008 ConcertTicketing.openDateLink = function(e) {
1009  var citypage=Application.prefs.getValue("extensions.concerts.citypage", "");
1010 #ifdef METRICS_ENABLED
1011  gMetrics.metricsInc("concerts", "browse.link.datebox", "");
1012 #endif
1013  if (citypage) {
1014  var year = this.getAttribute("year");
1015  var month = parseInt(this.getAttribute("month")) + 1;
1016  var date = this.getAttribute("date");
1017  gBrowser.loadOneTab(citypage + "?" + SongkickPartnerCode +
1018  "&d=" + year + "-" + month + "-" + date);
1019  }
1020 }
1021 
1022 // Takes a date, and returns an index block for it
1023 ConcertTicketing.createDateBlock = function(dateObj) {
1024  var dateIndexContainer = this.createBlock("dateIndexContainer");
1025  var boxBlock = this.createBlock("dateIndex-box");
1026  boxBlock.addEventListener("click", ConcertTicketing.openDateLink, false);
1027  boxBlock.setAttribute("year", dateObj.getFullYear());
1028  boxBlock.setAttribute("month", dateObj.getMonth());
1029  boxBlock.setAttribute("date", dateObj.getDate());
1030  boxBlock.style.cursor = "pointer";
1031  var month = this.createBlock("dateIndex-month");
1032  var date = this.createBlock("dateIndex-date");
1033  var day = this.createBlock("dateIndex-day");
1034  dateIndexContainer.id = "indexDate-" + dateObj.getMonth() +
1035  dateObj.getFullYear();
1036  var monthText =
1037  this._browseDoc.createTextNode(dateObj.toLocaleFormat("%b"));
1038  var dateText = this._browseDoc.createTextNode(dateObj.toLocaleFormat("%d"));
1039  var dayText = this._browseDoc.createTextNode(dateObj.toLocaleFormat("%a"));
1040  month.appendChild(monthText);
1041  day.appendChild(dayText);
1042  date.appendChild(dateText);
1043  boxBlock.appendChild(month);
1044  boxBlock.appendChild(date);
1045  dateIndexContainer.appendChild(boxBlock);
1046  dateIndexContainer.appendChild(day);
1047  return (dateIndexContainer);
1048 }
1049 
1050 /***************************************************************************
1051  * Routines for building the actual table objects for listing individual
1052  * concerts within
1053  ***************************************************************************/
1054 ConcertTicketing.createTableArtistView = function() {
1055  var table = this.createTable();
1056  var headerRow = this._browseDoc.createElement("tr");
1057  var dateCol = this.createTableHeader("tableHeaderDate", "date");
1058  var gCalCol = this.createTableHeader("tableHeaderGCal", "gcal");
1059  var artistsCol = this.createTableHeader("tableHeaderOtherArtists",
1060  "artists");
1061  var venueCol = this.createTableHeader("tableHeaderVenue", "venue");
1062  var ticketCol = this.createTableHeader("tableHeaderTickets", "tickets");
1063  headerRow.appendChild(dateCol);
1064  if (this.showGCal)
1065  headerRow.appendChild(gCalCol);
1066  headerRow.appendChild(artistsCol);
1067  headerRow.appendChild(venueCol);
1068  headerRow.appendChild(ticketCol);
1069  table.appendChild(headerRow);
1070  return (table);
1071 }
1072 
1073 ConcertTicketing.createTableVenueView = function() {
1074  var table = this.createTable();
1075  var headerRow = this._browseDoc.createElement("tr");
1076  var ticketCol = this.createTableHeader("tableHeaderTickets", "tickets");
1077  var dateCol = this.createTableHeader("tableHeaderDate", "date");
1078  var artistsCol = this.createTableHeader("tableHeaderArtists", "artists");
1079  headerRow.appendChild(ticketCol);
1080  headerRow.appendChild(dateCol);
1081  headerRow.appendChild(artistsCol);
1082  table.appendChild(headerRow);
1083  return (table);
1084 }
1085 
1086 ConcertTicketing.createTableDateView = function() {
1087  var table = this.createTable();
1088  var headerRow = this._browseDoc.createElement("tr");
1089  var gCalCol = this.createTableHeader("tableHeaderGCal", "gcal");
1090  var artistsCol = this.createTableHeader("tableHeaderArtists", "artists");
1091  var venueCol = this.createTableHeader("tableHeaderVenue", "venue");
1092  var ticketCol = this.createTableHeader("tableHeaderTickets", "tickets");
1093  if (this.showGCal)
1094  headerRow.appendChild(gCalCol);
1095  headerRow.appendChild(artistsCol);
1096  headerRow.appendChild(venueCol);
1097  headerRow.appendChild(ticketCol);
1098  table.appendChild(headerRow);
1099  return (table);
1100 }
1101 
1102 ConcertTicketing.createTable = function() {
1103  var table = this._browseDoc.createElement("table");
1104  table.setAttribute("cellpadding", "0");
1105  table.setAttribute("cellspacing", "0");
1106  table.setAttribute("border", "0");
1107  return (table);
1108 }
1109 
1110 // str is a name in the .properties localised strings
1111 // className is the class to apply to the TH cell
1112 ConcertTicketing.createTableHeader = function(str, className) {
1113  var col = this._browseDoc.createElement("th");
1114  col.className = className;
1115  if (str == "tableHeaderTickets" || this._strings.getString(str) == "")
1116  col.innerHTML = "&nbsp;";
1117  else {
1118  var colLabel =
1119  this._browseDoc.createTextNode(this._strings.getString(str));
1120  col.appendChild(colLabel);
1121  }
1122  return (col);
1123 }
1124 /***************************************************************************
1125  * Routines for building the individual concert listing rows, i.e. the table
1126  * row representing each individual concert for each of the 3 views
1127  ***************************************************************************/
1128 ConcertTicketing.createRowArtistView = function(concert, mainAnchor) {
1129  var row = this._browseDoc.createElement("tr");
1130  var ticketCol = this.createColumnTickets(concert);
1131  var dateCol = this.createColumnDate(concert);
1132  var gcalCol = this.createColumnGCal(concert);
1133  var venueCol = this.createColumnVenue(concert);
1134 
1135  // Artists playing - logic is slightly different from the other venue &
1136  // date views since we don't want to show the artist that we're already
1137  // listed under (for the "main index"), and we need to update the main
1138  // index anchor href
1139  var otherArtistsCol = this._browseDoc.createElement("td");
1140  otherArtistsCol.className = "artists";
1141  var first = true;
1142  var artists = concert.artistsConcertInfo;
1143  var artistname = unescape(decodeURIComponent(concert.artistname));
1144  for (var i = 0; i < artists.length; i++) {
1145  var curArtist = artists.queryElementAt(i, Ci.sbISongkickArtistConcertInfo);
1146  let curArtistName = unescape(decodeURIComponent(curArtist.artistname));
1147  if (curArtistName == artistname) {
1148  mainAnchor.setAttribute("href",
1149  this.appendCityParam(curArtist.artisturl));
1150  continue;
1151  }
1152  if (!first) {
1153  otherArtistsCol.appendChild(this._browseDoc.createTextNode(", "));
1154  } else {
1155  first = false;
1156  }
1157  var anchor = this._browseDoc.createElement("a");
1158  this.makeLink(anchor, this.appendCityParam(curArtist.artisturl),
1159  "artist.other");
1160  var anchorLabel = this._browseDoc.createTextNode(curArtistName);
1161  anchor.appendChild(anchorLabel);
1162  otherArtistsCol.appendChild(anchor);
1163  }
1164  if (otherArtistsCol.firstChild == null)
1165  otherArtistsCol.innerHTML = "&nbsp;";
1166 
1167  row.appendChild(dateCol);
1168  if (this.showGCal)
1169  row.appendChild(gcalCol);
1170  row.appendChild(otherArtistsCol);
1171  row.appendChild(venueCol);
1172  row.appendChild(ticketCol);
1173  return (row);
1174 }
1175 
1176 ConcertTicketing.createRowVenueView = function(concert) {
1177  var row = this._browseDoc.createElement("tr");
1178  var ticketCol = this.createColumnTickets(concert);
1179  var dateCol = this.createColumnDate(concert);
1180  var artistsCol = this.createColumnArtists(concert);
1181  row.appendChild(ticketCol);
1182  row.appendChild(dateCol);
1183  row.appendChild(artistsCol);
1184  return (row);
1185 }
1186 
1187 ConcertTicketing.createRowDateView = function(concert) {
1188  var row = this._browseDoc.createElement("tr");
1189  var ticketCol = this.createColumnTickets(concert);
1190  var gcalCol = this.createColumnGCal(concert);
1191  var artistsCol = this.createColumnArtists(concert);
1192  var venueCol = this.createColumnVenue(concert);
1193  if (this.showGCal)
1194  row.appendChild(gcalCol);
1195  row.appendChild(artistsCol);
1196  row.appendChild(venueCol);
1197  row.appendChild(ticketCol);
1198  return (row);
1199 }
1200 
1201 /***************************************************************************
1202  * Routine for opening links - we need it as a separate routine so we can
1203  * do metrics reporting, in addition to just opening the link
1204  ***************************************************************************/
1205 ConcertTicketing.openAndReport = function(e) {
1206 #ifdef METRICS_ENABLED
1207  var metric = this.getAttribute("metric-key");
1208  gMetrics.metricsInc("concerts", "browse.link." + metric, "");
1209 #endif
1210  gBrowser.loadOneTab(this.href);
1211  e.preventDefault();
1212  return false;
1213 }
1214 ConcertTicketing.makeLink = function(el, url, metric) {
1215  el.href = url;
1216  el.setAttribute("metric-key", metric);
1217  el.addEventListener("click", this.openAndReport, true);
1218 }
1219 
1220 // Adds Songbird's partner code & user location
1221 ConcertTicketing.appendCityParam = function(url) {
1222  return (url + "?" + SongkickPartnerCode + "&user_location=" + this.pCity);
1223 }
1224 // Add's Songbird's partner code only
1225 ConcertTicketing.appendPartnerParam = function(url) {
1226  return (url + "?" + SongkickPartnerCode);
1227 }
1228 
1229 /***************************************************************************
1230  * Routines for building the individual column components of each concert
1231  * listing table row, e.g. the "Date" column, or the "Artists" column, etc.
1232  ***************************************************************************/
1233 ConcertTicketing.createColumnTickets = function(concert) {
1234  var ticketCol = this._browseDoc.createElement("td");
1235  ticketCol.className = "tickets";
1236  if (concert.tickets) {
1237  var ticketAnchor = this._browseDoc.createElement("a");
1238  this.makeLink(ticketAnchor, this.appendPartnerParam(concert.url),
1239  "tickets");
1240  ticketAnchor.setAttribute("title",
1241  this._strings.getString("tableTicketsTooltip"));
1242  ticketAnchor.className = "get-tickets";
1243  ticketAnchor.innerHTML = this._strings.getString("ticketButtonLabel");
1244  ticketCol.appendChild(ticketAnchor);
1245  } else {
1246  ticketCol.innerHTML = "&nbsp;"
1247  }
1248 
1249  return (ticketCol);
1250 }
1251 
1252 ConcertTicketing.createColumnDate = function(concert) {
1253  var dateCol = this._browseDoc.createElement("td");
1254  dateCol.className = "date";
1255  var dateObj = new Date(concert.ts*1000);
1256  var dateStr = dateObj.toLocaleFormat(this.dateFormat);
1257  var dateColLabel = this._browseDoc.createTextNode(dateStr);
1258  var citypage = Application.prefs.getValue("extensions.concerts.citypage",
1259  "");
1260  if (citypage != "") {
1261  var dateFormat = dateObj.toLocaleFormat("%Y-%m-%d");
1262  var dateAnchor = this._browseDoc.createElement("a");
1263  this.makeLink(dateAnchor, this.appendPartnerParam(concert.url),
1264  "tickets");
1265  dateAnchor.setAttribute("title",
1266  this._strings.getString("tableTicketsTooltip"));
1267  dateAnchor.appendChild(dateColLabel);
1268  dateCol.appendChild(dateAnchor);
1269  } else {
1270  dateCol.appendChild(dateColLabel);
1271  }
1272 
1273  return (dateCol);
1274 }
1275 
1276 ConcertTicketing.createColumnGCal = function(concert) {
1277  var dateObj = new Date(concert.ts*1000);
1278  var dateStr = dateObj.toLocaleFormat("%Y%m%d"); // T%H%M%SZ");
1279  var url = "http://www.google.com/calendar/event?action=TEMPLATE";
1280  url += "&text=" + escape(concert.title);
1281  url += "&details=";
1282  for (let artist in concert.artists) {
1283  url += escape(concert.artists[artist].name) + ",";
1284  }
1285  // trim the last ,
1286  url = url.substr(0, url.length-1);
1287  url += "&location=" + escape(concert.venue) + "," + escape(concert.city);
1288  url += "&dates=" + dateStr + "/" + dateStr;
1289 
1290  var gCalLinkCol = this._browseDoc.createElement("td");
1291  gCalLinkCol.className = "gcal";
1292  var gCalLink = this._browseDoc.createElement("a");
1293  this.makeLink(gCalLink, url, "gcal");
1294  gCalLink.setAttribute("title", this._strings.getString("tableGCalTooltip"));
1295 
1296  var gCalImage = this._browseDoc.createElement("img");
1297  gCalImage.src = "chrome://concerts/skin/gcal.png";
1298  gCalImage.className = "gcalImage";
1299  gCalLink.appendChild(gCalImage);
1300 
1301  gCalLinkCol.appendChild(gCalLink);
1302 
1303  return (gCalLinkCol);
1304 }
1305 
1306 ConcertTicketing.createColumnArtists = function(concert) {
1307  /*
1308  * Construct the artists playing string
1309  * Take the concert title, and for each artist listed as playing at this
1310  * concert, mask it out of the concert title. After all the artists have
1311  * been masked out, test to see if the concert title consists of anything
1312  * other than punctuation. If it doesn't, then the title was just a list
1313  * of artists - and don't display it. If it's got non-punctuation chars,
1314  * then we'll classify it as a concert/festival name and list it as
1315  * "Festival Name with Artist1, Artist2"
1316  */
1317  var artists = concert.artistsConcertInfo;
1318  var artistsCol = this._browseDoc.createElement("td");
1319  artistsCol.className = "artists";
1320  var concertTitle = concert.title;
1321  var first = true;
1322  var headlinerFound = false;
1323  var headlinerNode;
1324  for (var i = 0; i < artists.length; i++) {
1325  // Comma separate the list of artists
1326  var commaNode = null;
1327  if (!first) {
1328  commaNode = this._browseDoc.createTextNode(", ");
1329  }
1330 
1331  var curArtistConcertInfo =
1332  artists.queryElementAt(i, Ci.sbISongkickArtistConcertInfo);
1333  // Linkify the artist name
1334  var anchor = this._browseDoc.createElement("a");
1335  this.makeLink(
1336  anchor,
1337  this.appendCityParam(curArtistConcertInfo.artisturl),
1338  "artist.other");
1339  var artistName =
1340  unescape(decodeURIComponent(curArtistConcertInfo.artistname));
1341  var anchorLabel = this._browseDoc.createTextNode(artistName);
1342  anchor.appendChild(anchorLabel);
1343 
1344  // Test to see if the concert title is the exact same string as this
1345  // artist name - if so, then this artist is the headliner and should
1346  // be called out first, otherwise append the artist name/link to the
1347  // column
1348  if (artistName == concert.title) {
1349  headlinerFound = true;
1350  headlinerNode = anchor;
1351  } else {
1352  if (commaNode != null)
1353  artistsCol.appendChild(commaNode);
1354  artistsCol.appendChild(anchor);
1355  first = false;
1356  }
1357 
1358  // Mask it out of the concert title
1359  concertTitle = concertTitle.replace(artistName, "");
1360  }
1361  /*
1362  * XXX Taking this logic out for now - it'd work really nicely if we had
1363  * consistently clean concert titles, but we don't
1364  */
1365  /*
1366  if (!headlinerFound) {
1367  concertTitle = concertTitle.replace(/[\s!\.:\&;\(\)\/]/gi, "");
1368  if (concertTitle.length > 0) {
1369  var concertNode = this._browseDoc.createTextNode(concert.title +
1370  " featuring ");
1371  artistsCol.insertBefore(concertNode, artistsCol.firstChild);
1372  }
1373  } else {
1374  if (!first) {
1375  var withNode = this._browseDoc.createTextNode(" with ");
1376  artistsCol.insertBefore(withNode, artistsCol.firstChild);
1377  }
1378  artistsCol.insertBefore(headlinerNode, artistsCol.firstChild);
1379  }
1380  */
1381  if (headlinerFound) {
1382  if (!first) {
1383  var withNode = this._browseDoc.createTextNode(" with ");
1384  artistsCol.insertBefore(withNode, artistsCol.firstChild);
1385  }
1386  artistsCol.insertBefore(headlinerNode, artistsCol.firstChild);
1387  }
1388 
1389  return (artistsCol);
1390 }
1391 
1392 ConcertTicketing.createColumnVenue = function(concert) {
1393  var venueCol = this._browseDoc.createElement("td");
1394  venueCol.className = "venue";
1395 
1396  if (concert.venue != "<generic />") {
1397  var venueName = concert.venue;
1398  venueName = unescape(decodeURIComponent(concert.venue));
1399  var anchor = this._browseDoc.createElement("a");
1400  this.makeLink(anchor, this.appendPartnerParam(concert.venueURL), "venue");
1401  var venueColLabel = this._browseDoc.createTextNode(venueName);
1402  anchor.appendChild(venueColLabel);
1403  venueCol.appendChild(anchor);
1404  }
1405 
1406  return (venueCol);
1407 }
1408 
1409 /* Generic shortcut for creating a DIV or SPAN & assigning it a style class */
1410 ConcertTicketing.createBlock = function(blockname, makeSpan) {
1411  var block;
1412  if (makeSpan)
1413  var block = this._browseDoc.createElement("span");
1414  else
1415  var block = this._browseDoc.createElement("div");
1416  block.className=blockname;
1417  return block;
1418 }
1419 
1420 /* Methods connected to the groupby menulist & filter checkbox on the chrome */
1421 ConcertTicketing.changeFilter = function(updateCheckbox) {
1422  this.filterLibraryArtists = !this.filterLibraryArtists;
1423  Application.prefs.setValue("extensions.concerts.filterLibraryArtists",
1424  this.filterLibraryArtists);
1425 #ifdef METRICS_ENABLED
1426  if (this.filterLibraryArtists)
1427  gMetrics.metricsInc("concerts", "filter.library", "");
1428  else
1429  gMetrics.metricsInc("concerts", "filter.all", "");
1430 #endif
1431  /* checks the filter checkbox (for the path taken when there are no
1432  results in the user's city and they click the button to see
1433  all concerts, rather than checking the checkbox themselves */
1434  if (updateCheckbox) {
1435  var checkbox = document.getElementById("checkbox-library-artists");
1436  checkbox.setAttribute("checked", this.filterLibraryArtists);
1437  }
1438  songbirdMainWindow.Concerts.updateConcertCount();
1439  flushDisplay();
1440  if (this.skSvc.drawingLock) {
1441  // trigger browseArtists|browseDates to abort
1442  this.abortDrawing = true;
1443 
1444  // block for release of the lock, and then redraw
1445  this.blockAndBrowseConcerts(this);
1446  } else {
1447  this.browseConcerts(this);
1448  }
1449 }
1450 
1451 ConcertTicketing.groupBy = function(group) {
1452  Application.prefs.setValue("extensions.concerts.groupby", group);
1453 
1454  if (this.skSvc.drawingLock) {
1455  // trigger browseArtists|browseDates to abort
1456  this.abortDrawing = true;
1457 
1458  // block for release of the lock, and then redraw
1459  this.blockAndBrowseConcerts(this);
1460  } else {
1461  this.browseConcerts(this);
1462  }
1463 }
1464 
1465 /* Spins until drawingLock is released, and then triggers a browseConcerts() */
1466 ConcertTicketing.blockAndBrowseConcerts = function blockAndBrowseConcerts(ct) {
1467  if (Cc["@songbirdnest.com/Songbird/Concerts/Songkick;1"]
1468  .getService(Ci.sbISongkick).drawingLock)
1469  {
1470  setTimeout(function() { blockAndBrowseConcerts(ct);}, 100);
1471  } else {
1472  ct.browseConcerts(ct);
1473  }
1474 }
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
function flushDisplay()
Definition: browse.js:29
_changeFirstDay day
sbOSDControlService prototype QueryInterface
getService(Ci.sbIFaceplateManager)
return elem filter &&elem filter indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity
restoreDimensions aLeft
let window
var getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('Songbird SBProperties artist
Definition: tuner2.js:40
this _contentSandbox label
Definition: FeedWriter.js:814
var bundle
var SongkickPartnerCode
Definition: browse.js:41
restoreDimensions aTop
Element Properties href
var ConcertOptions
Definition: options.js:14
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
_getSelectedPageStyle s i
var group
return first