VulpesOS/gaia/apps/ringtones/js/tone_list.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

195 lines
5.9 KiB
JavaScript
Executable file

/* exported ToneList */
/* global Templates */
'use strict';
var ToneList = (function() {
// Make sure to keep this in sync with the value in null_ringtone.js!
// (We copy the value here so that you don't need to load null_ringtone.js
// if you don't need it.)
var NONE_ID = 'none:none';
/**
* Compare two tone objects, sorting first on their title, and then their
* subtitle. Note: The empty ringtone is always the "smallest".
*
* @param {Tone} a The first tone.
* @param {Tone} b The second tone.
* @param {Number} <0 if a < b, 0 if a == b, and >0 if a > b.
*/
function toneCompare(a, b) {
// Make sure the empty ringtone is always first.
if (a.id === NONE_ID && b.id === NONE_ID) {
return 0;
} else if (a.id === NONE_ID) {
return -1;
} else if (b.id === NONE_ID) {
return 1;
}
var aName = a.name.toLocaleLowerCase();
var bName = b.name.toLocaleLowerCase();
var cmp = aName.localeCompare(bName);
if (cmp) {
return cmp;
}
var aSubtitle = (a.subtitle || '').toLocaleLowerCase();
var bSubtitle = (b.subtitle || '').toLocaleLowerCase();
return aSubtitle.localeCompare(bSubtitle);
}
/**
* Convert a string of HTML to a DOM node and translate any l10n IDs as
* appropriate. Assumes that mozL10n has already been fully initialized.
*
* @param {String} htmlText The HTML text to turn into a proper DOM node.
* @return {Node} The converted DOM node.
*/
function domify(htmlText) {
var dummyDiv = document.createElement('div');
dummyDiv.innerHTML = htmlText;
var element = dummyDiv.firstElementChild;
return element;
}
/**
* Create a new list of tones. Assumes that mozL10n has already been fully
* initialized. This class is meant to be subclassed by consumers; feel free
* to extend makeItem() to do whatever you need!
*
* @param {String} titleID The l10n ID of the list's header
* @param {Node} parent (optional) The parent node to append this list to.
*/
function ToneList(titleID, parent) {
this.element = domify(Templates.soundList({l10nID: titleID}));
if (parent) {
parent.appendChild(this.element);
}
this._ul = this.element.querySelector('ul');
// This maps tone IDs to an object containing the tone object and the DOM
// node for it. We have to keep the tone objects around anyway, so we're
// not wasting (much) memory with this.
this._toneMap = {};
}
ToneList.prototype = {
/**
* Create and return a list item for a tone.
*
* @param {Object} tone The tone to use for the list item.
* @return {Node} The list item.
*/
makeItem: function(tone) {
var templateArgs = {};
if (tone.l10nID) {
templateArgs.l10nID = tone.l10nID;
} else {
templateArgs.name = tone.name;
}
var item = domify(Templates.soundItem(templateArgs));
item.dataset.id = tone.id;
var detailNode = item.querySelector('details');
if (detailNode && tone.subtitle) {
detailNode.textContent = tone.subtitle;
} else if (detailNode) {
detailNode.parentNode.hideDetails();
} else if (tone.subtitle) {
var subtitle = document.createElement('p');
subtitle.classList.add('subtitle');
var bdi = document.createElement('bdi');
bdi.textContent = tone.subtitle;
subtitle.appendChild(bdi);
item.querySelector('.name').parentNode.appendChild(subtitle);
}
return item;
},
/**
* Insert a tone or array of tones in sorted order into the list.
*
* @param {Object} tones A single tone object or an array thereof.
*/
add: function(tones) {
// If we have no children yet, we can be speedy and sort our new tones
// first, and then append them to the end of the list.
if (this._ul.children.length === 0) {
if (Array.isArray(tones)) {
tones.sort(toneCompare);
tones.forEach(this._append.bind(this));
} else {
this._append(tones); // tones is really just a single tone here!
}
} else {
if (Array.isArray(tones)) {
tones.forEach(this._insertSorted.bind(this));
} else {
this._insertSorted(tones); // tones is really just a single tone here!
}
}
},
/**
* Remove an item from the list.
*
* @param {Tone} tone The tone to remove.
*/
remove: function(tone) {
this._ul.removeChild(this._toneMap[tone.id].element);
if (!this._ul.firstChild) {
this.element.hidden = true;
}
},
/**
* Append a tone object to the end of the list. Useful when we know it
* belongs at the end.
*
* @param {Tone} tone The tone to append.
*/
_append: function(tone) {
if (tone.id in this._toneMap) {
throw new Error('A tone with this ID is already in the list: ' +
tone.id);
}
var newItem = this.makeItem(tone);
this._toneMap[tone.id] = {tone: tone, element: newItem};
this.element.hidden = false;
this._ul.appendChild(newItem);
},
/**
* Insert a tone object into the list based on its sort order.
*
* @param {Tone} tone The tone to insert.
*/
_insertSorted: function(tone) {
if (tone.id in this._toneMap) {
throw new Error('A tone with this ID is already in the list: ' +
tone.id);
}
var newItem = this.makeItem(tone);
this._toneMap[tone.id] = {tone: tone, element: newItem};
this.element.hidden = false;
var items = this._ul.querySelectorAll('li');
for (var i = 0; i < items.length; i++) {
var currTone = this._toneMap[items[i].dataset.id].tone;
if (toneCompare(tone, currTone) < 0) {
this._ul.insertBefore(newItem, items[i]);
return;
}
}
this._ul.appendChild(newItem);
}
};
return ToneList;
})();