smartPlaylist.js
Go to the documentation of this file.
1 
27 Components.utils.import("resource://app/jsmodules/sbSmartMediaListColumnSpecUpdater.jsm");
28 
30  Ci.sbILocalDatabaseSmartMediaList;
31 
32 var SB_NS = "http://songbirdnest.com/data/1.0#";
33 var SB_PROPERTY_UILIMITTYPE = SB_NS + "uiLimitType";
34 
35 var USECS_PER_MINUTE = 60 * 1000 * 1000;
36 var USECS_PER_HOUR = USECS_PER_MINUTE * 60;
37 var BYTES_PER_MB = 1000 * 1000;
38 var BYTES_PER_GB = BYTES_PER_MB * 1000;
39 
40 var selectByList = [
41  {
42  value: "album",
43  property: SB_NS + "albumName",
44  direction: true
45  },
46  {
47  value: "artist",
48  property: SB_NS + "artistName",
49  direction: true
50  },
51  {
52  value: "genre",
53  property: SB_NS + "genre",
54  direction: true
55  },
56  {
57  value: "title",
58  property: SB_NS + "trackName",
59  direction: true
60  },
61  {
62  value: "high_rating",
63  property: SB_NS + "rating",
64  direction: false
65  },
66  {
67  value: "low_rating",
68  property: SB_NS + "rating",
69  direction: true
70  },
71  {
72  value: "most_recent",
73  property: SB_NS + "lastPlayTime",
74  direction: false
75  },
76  {
77  value: "least_recent",
78  property: SB_NS + "lastPlayTime",
79  direction: true
80  },
81  {
82  value: "most_often",
83  property: SB_NS + "playCount",
84  direction: false
85  },
86  {
87  value: "least_often",
88  property: SB_NS + "playCount",
89  direction: true
90  },
91  {
92  value: "most_added",
93  property: SB_NS + "created",
94  direction: false
95  },
96  {
97  value: "least_added",
98  property: SB_NS + "created",
99  direction: true
100  }
101 ];
102 
103 function updateOkButton() {
104  var dialog = document.documentElement;
105  var ok = dialog.getButton("accept");
106  ok.disabled = !isConfigurationValid();
107 }
108 
110  var check = document.getElementById("smart_match_check");
111  var checkwhat = document.getElementById("smart_any_list");
112  var following = document.getElementById("smart_following_label");
113 
114  var smartConditions = document.getElementById("smart_conditions");
115  var conditions = smartConditions.conditions;
116 
117  if (conditions.length == 1) {
118  check.setAttribute("label", check.getAttribute("labelsingle"));
119  checkwhat.hidden = true;
120  following.hidden = true;
121  } else {
122  check.setAttribute("label", check.getAttribute("labelmultiple"));
123  checkwhat.hidden = false;
124  following.hidden = false;
125  }
126 }
127 
128 function onAddCondition() {
130  updateOkButton();
131 }
132 
133 function onRemoveCondition() {
135  updateOkButton();
136 }
137 
138 function onUserInput() {
139  updateOkButton();
140 }
141 
142 function doLoad()
143 {
145 
146  var smartConditions = document.getElementById("smart_conditions");
147  smartConditions.addEventListener("input", onUserInput, false);
148  smartConditions.addEventListener("select", onUserInput, false);
149  smartConditions.addEventListener("additem", onAddCondition, false);
150  smartConditions.addEventListener("removeitem", onRemoveCondition, false);
151 
152  var limit = document.getElementById("smart_songs_count");
153  limit.addEventListener("blur", onLimitBlur, false);
154 }
155 
156 function doUnLoad()
157 {
158  var smartConditions = document.getElementById("smart_conditions");
159  smartConditions.removeEventListener("input", onUserInput, false);
160  smartConditions.removeEventListener("select", onUserInput, false);
161  smartConditions.removeEventListener("additem", onAddCondition, false);
162  smartConditions.removeEventListener("removeitem", onRemoveCondition, false);
163 
164  var limit = document.getElementById("smart_songs_count");
165  limit.removeEventListener("blur", onLimitBlur, false);
166 }
167 
168 function loadConditions()
169 {
170  var list = window.arguments[0];
171 
172  if (list instanceof sbILocalDatabaseSmartMediaList) {
173 
174  // Set up conditions
175  var smartConditions = document.getElementById("smart_conditions");
176  if (list.conditionCount > 0) {
177  var conditions = [];
178  for (var i = 0; i < list.conditionCount; i++) {
179  var condition = list.getConditionAt(i);
180  conditions.push({
181  metadata: condition.propertyID,
182  condition: condition.operator.operator,
183  value: condition.leftValue,
184  value2: condition.rightValue,
185  unit: condition.displayUnit,
186  listguid: list.guid,
187  source: list.sourceLibraryGuid
188  });
189  }
190  smartConditions.conditions = conditions;
191  }
192  else {
193  smartConditions.newCondition();
194  }
195 
196  // Set match type
197  var matchSomething = document.getElementById("smart_match_check");
198  var matchAnyAll = document.getElementById("smart_any_list");
199  switch(list.matchType) {
200  case sbILocalDatabaseSmartMediaList.MATCH_TYPE_ANY:
201  matchAnyAll.value = "any";
202  matchSomething.checked = true;
203  break;
204  case sbILocalDatabaseSmartMediaList.MATCH_TYPE_ALL:
205  matchAnyAll.value = "all";
206  matchSomething.checked = true;
207  break;
208  case sbILocalDatabaseSmartMediaList.MATCH_TYPE_NONE:
209  matchAnyAll.value = "any";
210  matchSomething.checked = false;
211  break;
212  }
213 
214  // Set limit. Get the "ui" limit from a list property
215  var uiLimitType = list.getProperty(SB_PROPERTY_UILIMITTYPE) || "songs";
216 
217  // Set the limit based on the ui limit. Convert the units from the smart
218  // playlist to the units needed to display the ui limit
219 
220  var limit = document.getElementById("smart_songs_check");
221  var count = document.getElementById("smart_songs_count");
222  var limitType = document.getElementById("smart_songs_list");
223  if (list.limitType == sbILocalDatabaseSmartMediaList.LIMIT_TYPE_NONE) {
224  limit.checked = false;
225  count.value = "0";
226  limitType.value = "songs";
227  }
228  else {
229  var mismatch = false;
230  switch(uiLimitType) {
231  case "songs":
232  if (list.limitType != sbILocalDatabaseSmartMediaList.LIMIT_TYPE_ITEMS) {
233  mismatch = true;
234  break;
235  }
236  count.value = list.limit;
237  break;
238  case "minutes":
239  if (list.limitType != sbILocalDatabaseSmartMediaList.LIMIT_TYPE_USECS) {
240  mismatch = true;
241  break;
242  }
243  count.value = list.limit / USECS_PER_MINUTE;
244  break;
245  case "hours":
246  if (list.limitType != sbILocalDatabaseSmartMediaList.LIMIT_TYPE_USECS) {
247  mismatch = true;
248  break;
249  }
250  count.value = list.limit / USECS_PER_HOUR;
251  break;
252  case "MB":
253  if (list.limitType != sbILocalDatabaseSmartMediaList.LIMIT_TYPE_BYTES) {
254  mismatch = true;
255  break;
256  }
257  count.value = list.limit / BYTES_PER_MB;
258  break;
259  case "GB":
260  if (list.limitType != sbILocalDatabaseSmartMediaList.LIMIT_TYPE_BYTES) {
261  mismatch = true;
262  break;
263  }
264  count.value = list.limit / BYTES_PER_GB;
265  break;
266  }
267  if (mismatch) {
268  limit.checked = false;
269  count.value = "0";
270  limitType.value = "songs";
271  }
272  else {
273  limit.checked = true;
274  limitType.value = uiLimitType;
275  }
276  }
277 
278  // Set select by
279  var selectBy = document.getElementById("smart_selected_list");
280  if (list.randomSelection) {
281  selectBy.value = "random";
282  }
283  else {
284  var value = getValueForSelectBy(list);
285  selectBy.value = value;
286  }
287 
288  // Set autoupdate
289  var autoUpdate = document.getElementById("smart_autoupdate_check");
290  autoUpdate.checked =
291  list.autoUpdate == true;
292 
293  } else { // if (list instanceof smart)
294 
295  // defaults for new lists
296 
297  var smartConditions = document.getElementById("smart_conditions");
298 
299  smartConditions.newCondition();
300 
301  var matchSomething = document.getElementById("smart_match_check");
302  var matchAnyAll = document.getElementById("smart_any_list");
303 
304  matchAnyAll.value = "all";
305  matchSomething.checked = true;
306 
307  var limit = document.getElementById("smart_songs_check");
308  var count = document.getElementById("smart_songs_count");
309  var limitType = document.getElementById("smart_songs_list");
310 
311  limit.checked = false;
312  count.value = "25";
313  limitType.value = "songs";
314 
315  var selectBy = document.getElementById("smart_selected_list");
316  selectBy.value = "artist";
317 
318  var autoUpdate = document.getElementById("smart_autoupdate_check");
319  autoUpdate.checked = true;
320  }
321 
322  // immediately update the match controls, so we don't have to wait for the drawer items
324  // immediately update ok button, in case one of the loaded setting was actually invalid
325  updateOkButton();
326 }
327 
329  var smartConditions = document.getElementById("smart_conditions");
330  var check = document.getElementById("smart_match_check");
331  var check_limit = document.getElementById("smart_songs_check");
332  var count = document.getElementById("smart_songs_count");
333  var check_match = document.getElementById("smart_match_check");
334  return ((smartConditions.isValid || !check.checked) &&
335  (!check_limit.checked || parseInt(count.value) > 0) &&
336  (check_limit.checked || check_match.checked));
337 }
338 
339 function doOK()
340 {
341  checkLimit();
342  if (!isConfigurationValid())
343  return true;
344 
345  var smart_conditions = document.getElementById("smart_conditions");
346  var conditions = smart_conditions.conditions;
347  var check = document.getElementById("smart_match_check");
348 
349  // the rules themselves are valid, but some values do not make sense to
350  // make playlists for (eg. contains "" ?), so we take care of this here,
351  // by showing those fields as invalid after the user clicks ok.
352  if (check.checked &&
353  !testAdditionalRestrictions(conditions, smart_conditions)) {
354  updateOkButton();
355  return false;
356  }
357 
358  // Get the smart playlist, creating one if necessary
359  var list = window.arguments[0];
360  var paramObject = null;
361  var newSmartPlaylist = null;
362  if (!(list instanceof sbILocalDatabaseSmartMediaList)) {
363  // Get the window parameters object
364  paramObject = list;
365 
366  // If a new smart playlist has already been created, use it. Otherwise,
367  // create a new one.
368  if (paramObject.newSmartPlaylist)
369  list = paramObject.newSmartPlaylist;
370  else
371  list = paramObject.newPlaylistFunction();
372  newSmartPlaylist = list;
373  }
374 
375  // Try configuring the smart playlist
376  var success;
377  try {
378  success = configureList(list);
379  } catch (ex) {
380  success = false;
381  }
382 
383  // Remove new smart playlist on failure
384  if (!success && newSmartPlaylist) {
385  newSmartPlaylist.library.remove(newSmartPlaylist);
386  newSmartPlaylist = null;
387  }
388 
389  // Return new smart playlist
390  if (paramObject)
391  paramObject.newSmartPlaylist = newSmartPlaylist;
392 
393  return success;
394 }
395 
396 function configureList(list)
397 {
398  var pm = Components
399  .classes["@songbirdnest.com/Songbird/Properties/PropertyManager;1"]
400  .getService(Ci.sbIPropertyManager);
401 
402  // Save conditions
403  var smart_conditions = document.getElementById("smart_conditions");
404  var conditions = smart_conditions.conditions;
405 
406  list.clearConditions();
407  var check = document.getElementById("smart_match_check");
408  if (check.checked) {
409  conditions.forEach(function(condition) {
410  var info = pm.getPropertyInfo(condition.metadata);
411  // access specialized operators
412  switch (info.type) {
413  case "datetime":
414  info.QueryInterface(Ci.sbIDatetimePropertyInfo);
415  break;
416  case "boolean":
417  info.QueryInterface(Ci.sbIBooleanPropertyInfo);
418  break;
419  }
420  var op = info.getOperator(condition.condition);
421  var unit;
422  var leftValue;
423  var rightValue;
424  if (op.operator != info.OPERATOR_ISTRUE &&
425  op.operator != info.OPERATOR_ISFALSE &&
426  op.operator != info.OPERATOR_ISSET &&
427  op.operator != info.OPERATOR_ISNOTSET)
428  leftValue = condition.value;
429  if (op.operator == info.OPERATOR_BETWEEN ||
430  op.operator == info.OPERATOR_BETWEENDATES)
431  rightValue = condition.value2;
432  if (condition.useunits)
433  unit = condition.unit;
434  list.appendCondition(condition.metadata,
435  op,
436  leftValue,
437  rightValue,
438  unit);
439  });
440  }
441 
442  // Save match
443  var matchSomething = document.getElementById("smart_match_check");
444  var matchAnyAll = document.getElementById("smart_any_list");
445  if (matchSomething.checked) {
446  if (matchAnyAll.value == "all") {
447  list.matchType = sbILocalDatabaseSmartMediaList.MATCH_TYPE_ALL;
448  }
449  else {
450  list.matchType = sbILocalDatabaseSmartMediaList.MATCH_TYPE_ANY;
451  }
452  }
453  else {
454  list.matchType = sbILocalDatabaseSmartMediaList.MATCH_TYPE_NONE;
455  }
456 
457  // Save limit
458  var limit = document.getElementById("smart_songs_check");
459  var count = document.getElementById("smart_songs_count");
460  var limitType = document.getElementById("smart_songs_list");
461 
462  list.setProperty(SB_PROPERTY_UILIMITTYPE, limitType.value);
463 
464  if (limit.checked) {
465  switch(limitType.value) {
466  case "songs":
467  list.limitType = sbILocalDatabaseSmartMediaList.LIMIT_TYPE_ITEMS;
468  list.limit = count.value;
469  break;
470  case "minutes":
471  list.limitType = sbILocalDatabaseSmartMediaList.LIMIT_TYPE_USECS;
472  list.limit = count.value * USECS_PER_MINUTE;
473  break;
474  case "hours":
475  list.limitType = sbILocalDatabaseSmartMediaList.LIMIT_TYPE_USECS;
476  list.limit = count.value * USECS_PER_HOUR;
477  break;
478  case "MB":
479  list.limitType = sbILocalDatabaseSmartMediaList.LIMIT_TYPE_BYTES;
480  list.limit = count.value * BYTES_PER_MB;
481  break;
482  case "GB":
483  list.limitType = sbILocalDatabaseSmartMediaList.LIMIT_TYPE_BYTES;
484  list.limit = count.value * BYTES_PER_GB;
485  break;
486  }
487  }
488  else {
489  list.limitType = sbILocalDatabaseSmartMediaList.LIMIT_TYPE_NONE;
490  list.limit = 0;
491  }
492 
493  // Save select by
494  var selectBy = document.getElementById("smart_selected_list");
495  if (selectBy.value == "random") {
496  list.randomSelection = true;
497  list.selectPropertyID = "";
498  }
499  else {
500  list.randomSelection = false;
501  setSelectBy(list, selectBy.value);
502  }
503 
504  var autoUpdate = document.getElementById("smart_autoupdate_check");
505  list.autoUpdate = autoUpdate.checked;
506 
507  SmartMediaListColumnSpecUpdater.update(list);
508 
509  list.rebuild();
510 
511  return true;
512 }
513 
514 function doCancel() {
515  return true;
516 }
517 
518 function getValueForSelectBy(list) {
519 
520  var value;
521  selectByList.forEach(function(e) {
522  if (e.property == list.selectPropertyID &&
523  e.direction == list.selectDirection) {
524  value = e.value;
525  }
526  });
527 
528  if (!value) value = "random";
529 
530  return value;
531 }
532 
533 function setSelectBy(list, value) {
534 
535  selectByList.forEach(function(e) {
536  if (e.value == value) {
537  list.selectPropertyID = e.property;
538  list.selectDirection = e.direction;
539  }
540  });
541 
542 }
543 
544 // we're not using radio controls because both checkboxes can be checked
545 // at the same time, but we have to keep at least one of them checked at
546 // all times
547 
548 function onCheckMatch(evt) {
549  updateOkButton();
550 }
551 
552 function onCheckLimit(evt) {
553  var check_limit = document.getElementById("smart_songs_check");
554  if (check_limit.checked) {
556  }
557  updateOkButton();
558 }
559 
560 function onSelectSelectedBy(evt) {
562 }
563 
565  var selectBy = document.getElementById("smart_selected_list");
566  var limit = document.getElementById("smart_songs_check");
567  var autoUpdate = document.getElementById("smart_autoupdate_check");
568  if (limit.checked &&
569  selectBy.value == "random") {
570  autoUpdate.checked = false;
571  autoUpdate.disabled = true;
572  } else {
573  autoUpdate.removeAttribute("disabled");
574  }
575 }
576 
577 function testAdditionalRestrictions(aConditions, aConditionsDrawer) {
578  var pm = Components.classes["@songbirdnest.com/Songbird/Properties/PropertyManager;1"]
579  .getService(Ci.sbIPropertyManager);
580  var firstFailure = -1;
581  for (var i=0; i<aConditions.length; i++) {
582  var condition = aConditions[i];
583  var info = pm.getPropertyInfo(condition.metadata)
584  if (condition.condition == info.OPERATOR_CONTAINS ||
585  condition.condition == info.OPERATOR_NOTCONTAINS ||
586  condition.condition == info.OPERATOR_BEGINSWITH ||
587  condition.condition == info.OPERATOR_NOTBEGINSWITH ||
588  condition.condition == info.OPERATOR_ENDSWITH ||
589  condition.condition == info.OPERATOR_NOTENDSWITH) {
590  if (!condition.value) {
591  aConditionsDrawer.makeInvalid(i);
592  if (firstFailure == -1)
593  firstFailure = i;
594  }
595  }
596  }
597  if (firstFailure != -1) {
598  aConditionsDrawer.focusInput(firstFailure);
599  return false;
600  }
601  return true;
602 }
603 
604 function onLimitBlur(evt) {
605  checkLimit();
606 }
607 
608 function checkLimit() {
609  var limit = document.getElementById("smart_songs_count");
610  // Constrain the value to avoid conversion problems between huge numbers and
611  // 64bits integers. Keep the value between 0 and 99,999,999,999,999, the
612  // largest integer made of only '9's that fits in 64 bits. We could constrain
613  // to 2^64 instead but that number (18446744073709551615) looks arbitrary to
614  // the user and does not suggest to him that he's reached the maximum value.
615 
616  // For each limit type, this means the maximums are:
617 
618  // - By minutes: 190,258,752 years
619  // - By hours: 11,415,525,114 years
620  // - By megabytes: 95,367,432 petabytes
621  // - By gigabytes: 97,656,250,000 petabytes
622  // - By items: 99,999,999,999,999 items
623 
624  // This "ought to be enough for anybody".
625 
626  limit.value = Math.min(limit.value, 99999999999999);
627  limit.value = Math.max(limit.value, 0);
628 }
function onCheckLimit(evt)
function updateMatchControls()
var BYTES_PER_MB
function doUnLoad()
var USECS_PER_HOUR
var SB_PROPERTY_UILIMITTYPE
var BYTES_PER_GB
var USECS_PER_MINUTE
function configureList(list)
populateBox limit
Definition: tuner2.js:877
function getValueForSelectBy(list)
function onRemoveCondition()
function doLoad()
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
function onUserInput()
let window
function doCancel()
function onSelectSelectedBy(evt)
var count
Definition: test_bug7406.js:32
var dialog
Definition: openLocation.js:43
function isConfigurationValid()
aWindow setTimeout(function(){_this.restoreHistory(aWindow, aTabs, aTabData, aIdMap);}, 0)
function onAddCondition()
return null
Definition: FeedWriter.js:1143
function check(ch, cx)
function updateOkButton()
function onCheckMatch(evt)
countRef value
Definition: FeedWriter.js:1423
var selectByList
function testAdditionalRestrictions(aConditions, aConditionsDrawer)
const Ci
function checkLimit()
function checkIfCanAutoUpdate()
function onLimitBlur(evt)
_getSelectedPageStyle s i
function loadConditions()
var SB_NS
function doOK()
function setSelectBy(list, value)