VulpesOS/assets/webapps/fm.gaiamobile.org/js/fm.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

60 lines
No EOL
16 KiB
JavaScript

;'use strict';function $(id){return document.getElementById(id);}
function $$(expr){return document.querySelectorAll(expr);}
var mozFMRadio=navigator.mozFM||navigator.mozFMRadio||{speakerEnabled:false,frequency:null,enabled:false,antennaAvailable:true,signalStrength:1,frequencyLowerBound:87.5,frequencyUpperBound:108,channelWidth:0.1,onsignalstrengthchange:function emptyFunction(){},onfrequencychange:function emptyFunction(){},onenabled:function emptyFunction(){},ondisabled:function emptyFunction(){},onantennaavailablechange:function emptyFunction(){},disable:function fm_disable(){if(!this.enabled){return;}
this.enabled=false;var self=this;window.setTimeout(function(){self.ondisabled();},500);return{};},enable:function fm_enable(frequency){if(this.enabled){return;}
this.enabled=true;var self=this;window.setTimeout(function(){self.onenabled();self.setFrequency(frequency);},500);return{};},setFrequency:function fm_setFrequency(freq){freq=parseFloat(freq.toFixed(1));var previousValue=this.frequency;this.frequency=freq;if(previousValue!=freq){this.onfrequencychange();}
return{};},seekUp:function fm_seekUp(){var self=this;if(this._seekRequest){return;}
this._seekRequest={};this._seekTimeout=window.setTimeout(function su_timeout(){self.setFrequency(self.frequency+0.5);if(self._seekRequest.onsuccess){self._seekRequest.onsuccess();}
self._clearSeekRequest();},1000);return this._seekRequest;},seekDown:function fm_seekDown(){var self=this;if(this._seekRequest){return;}
this._seekRequest={};this._seekTimeout=window.setTimeout(function sd_timeout(){self.setFrequency(self.frequency-0.5);if(self._seekRequest.onsuccess){self._seekRequest.onsuccess();}
self._clearSeekRequest();},1000);return this._seekRequest;},cancelSeek:function fm_cancelSeek(){this._clearSeekRequest();var request={};window.setTimeout(function(){if(request.onsuccess){request.onsuccess();}},0);return request;},_clearSeekRequest:function fm_clearSeek(){if(this._seekTimeout){window.clearTimeout(this._seekTimeout);this._seekTimeout=null;}
if(this._seekRequest&&this._seekRequest.onerror){this._seekRequest.onerror();this._seekRequest=null;}}};(function(aGlobal){aGlobal.SpeakerManager=aGlobal.SpeakerManager||aGlobal.MozSpeakerManager;if(aGlobal.SpeakerManager){return;}
function SpeakerManager(){this.speakerforced=false;}
SpeakerManager.prototype={set forcespeaker(enable){if(this.speakerforced!=enable){this.speakerforced=enable;if(this.onspeakerforcedchange){this.onspeakerforcedchange();}}}};aGlobal.SpeakerManager=SpeakerManager;})(window);var enabling=false;function updatePowerUI(){var enabled=mozFMRadio.enabled;var powerSwitch=$('power-switch');if(enabled){window.performance.mark('fmRadioEnabled');powerSwitch.setAttribute('data-l10n-id','power-switch-off');}else{powerSwitch.setAttribute('data-l10n-id','power-switch-on');}
console.log('Power status: '+(enabled?'on':'off'));powerSwitch.dataset.enabled=enabled;powerSwitch.dataset.enabling=enabling;}
var airplaneModeEnabled=false;function updateWarningModeUI(){$('airplane-mode-warning').hidden=!airplaneModeEnabled;$('antenna-warning').hidden=mozFMRadio.antennaAvailable||airplaneModeEnabled;$('container').classList.toggle('hidden-block',airplaneModeEnabled||!mozFMRadio.antennaAvailable);}
function updateFrequencyBarUI(){var frequencyBar=$('frequency-bar');if(enabling){frequencyBar.classList.add('dim');}else{frequencyBar.classList.remove('dim');}}
function updateEnablingState(enablingState){enabling=enablingState;updatePowerUI();updateFrequencyBarUI();}
function enableFMRadio(frequency){if(airplaneModeEnabled){return;}
var request=mozFMRadio.enable(frequency);request.onerror=function onerror_enableFMRadio(event){updateEnablingState(false);};updateEnablingState(true);}
function cancelSeekAndSetFreq(frequency){function setFreq(){mozFMRadio.setFrequency(frequency);}
var seeking=!!$('power-switch').getAttribute('data-seeking');if(!seeking){setFreq();}else{var request=mozFMRadio.cancelSeek();request.onsuccess=setFreq;request.onerror=setFreq;}}
var frequencyDialer={unit:2,_bandUpperBound:0,_bandLowerBound:0,_minFrequency:0,_maxFrequency:0,_currentFreqency:0,_translateX:0,init:function(){updateWarningModeUI();this._initUI();this.setFrequency(mozFMRadio.frequency);this._addEventListeners();},_addEventListeners:function(){function _removeEventListeners(){document.body.removeEventListener('touchend',fd_body_touchend,false);document.body.removeEventListener('touchmove',fd_body_touchmove,false);}
function cloneEvent(evt){if('touches'in evt){evt=evt.touches[0];}
return{x:evt.clientX,y:evt.clientX,timestamp:evt.timeStamp};}
var self=this;var SPEED_THRESHOLD=0.1;var currentEvent,startEvent,currentSpeed;var tunedFrequency=0;function toFixed(frequency){return parseFloat(frequency.toFixed(1));}
function _calcSpeed(){var movingSpace=startEvent.x-currentEvent.x;var deltaTime=currentEvent.timestamp-startEvent.timestamp;var speed=movingSpace/deltaTime;currentSpeed=parseFloat(speed.toFixed(2));}
function _calcTargetFrequency(){return tunedFrequency-getMovingSpace()/self._space;}
function getMovingSpace(){var movingSpace=currentEvent.x-startEvent.x;return movingSpace;}
function fd_body_touchmove(event){event.stopPropagation();currentEvent=cloneEvent(event);_calcSpeed();var dialer=$('frequency-dialer');var translateX=self._translateX+getMovingSpace();self._translateX=translateX;var count=dialer.childNodes.length;for(var i=0;i<count;i++){var child=dialer.childNodes[i];child.style.MozTransform='translateX('+translateX+'px)';}
tunedFrequency=_calcTargetFrequency();var roundedFrequency=Math.round(tunedFrequency*10)/10;if(roundedFrequency!=self._currentFreqency){self.setFrequency(toFixed(roundedFrequency),true);}
startEvent=currentEvent;}
function fd_body_touchend(event){event.stopPropagation();_removeEventListeners();$('frequency-dialer').classList.add('animation-on');if(Math.abs(currentSpeed)>SPEED_THRESHOLD){var direction=currentSpeed>0?1:-1;tunedFrequency+=Math.min(Math.abs(currentSpeed)*3,3)*direction;}
tunedFrequency=self.setFrequency(toFixed(tunedFrequency));cancelSeekAndSetFreq(tunedFrequency);currentEvent=null;startEvent=null;currentSpeed=0;}
function fd_touchstart(event){event.stopPropagation();$('frequency-dialer').classList.remove('animation-on');startEvent=currentEvent=cloneEvent(event);tunedFrequency=self._currentFreqency;_removeEventListeners();document.body.addEventListener('touchmove',fd_body_touchmove,false);document.body.addEventListener('touchend',fd_body_touchend,false);}
function fd_key(event){if(event.keyCode===event.DOM_VK_UP){tunedFrequency=self._currentFreqency+0.1;}else if(event.keyCode===event.DOM_VK_DOWN){tunedFrequency=self._currentFreqency-0.1;}else{return;}
tunedFrequency=self.setFrequency(toFixed(tunedFrequency));cancelSeekAndSetFreq(tunedFrequency);}
var dialerContainer=$('dialer-container');dialerContainer.addEventListener('touchstart',fd_touchstart,false);dialerContainer.addEventListener('keypress',fd_key,false);},_initUI:function(){$('frequency-dialer').innerHTML='';var lower=this._bandLowerBound=mozFMRadio.frequencyLowerBound;var upper=this._bandUpperBound=mozFMRadio.frequencyUpperBound;var unit=this.unit;this._minFrequency=lower-lower%unit;this._maxFrequency=upper+unit-upper%unit;var unitCount=(this._maxFrequency-this._minFrequency)/unit;for(var i=0;i<unitCount;++i){var start=this._minFrequency+i*unit;start=start<lower?lower:start;var end=this._maxFrequency+i*unit+unit;end=upper<end?upper:end;this._addDialerUnit(start,end);}
var _dialerUnits=$$('#frequency-dialer .dialer-unit');var _dialerUnitWidth=_dialerUnits[0].clientWidth;this._dialerWidth=_dialerUnitWidth*_dialerUnits.length;this._space=this._dialerWidth/(this._maxFrequency-this._minFrequency);for(i=0;i<_dialerUnits.length;i++){_dialerUnits[i].style.left=i*_dialerUnitWidth+'px';}},_addDialerUnit:function(start,end){var markStart=start-start%this.unit;var startMaskWidth=0;var endMaskWidth=0;var unitWidth=16;var total=this.unit*10;for(var i=0;i<total;i++){var dialValue=markStart+i*0.1;if(dialValue<start){startMaskWidth+=unitWidth;}else if(dialValue>end){endMaskWidth+=unitWidth;}}
var container=document.createElement('div');container.classList.add('dialer-unit-mark-box');if(startMaskWidth>0){var markEl=document.createElement('div');markEl.classList.add('dialer-unit-mark-mask-start');markEl.style.width=startMaskWidth+'px';container.appendChild(markEl);}
if(endMaskWidth>0){var markEnd=document.createElement('div');markEnd.classList.add('dialer-unit-mark-mask-end');markEnd.style.width=endMaskWidth+'px';container.appendChild(markEnd);}
var width=(100/this.unit)+'%';for(var j=0;j<this.unit;j++){var frequency=Math.floor(markStart)+j;var showFloor=frequency>=start&&frequency<=end;var unit=document.createElement('div');unit.classList.add('dialer-unit-floor');if(!showFloor){unit.classList.add('hidden-block');}
unit.style.width=width;unit.appendChild(document.createTextNode(frequency));container.appendChild(unit);}
var dialerUnit=document.createElement('div');dialerUnit.className='dialer-unit';dialerUnit.appendChild(container);$('frequency-dialer').appendChild(dialerUnit);},_updateUI:function(frequency,ignoreDialer){document.l10n.setAttributes($('frequency'),'frequency-MHz',{value:frequency.toFixed(1)});if(true!==ignoreDialer){this._translateX=(this._minFrequency-frequency)*this._space;var dialer=$('frequency-dialer');var count=dialer.childNodes.length;for(var i=0;i<count;i++){dialer.childNodes[i].style.MozTransform='translateX('+this._translateX+'px)';}
$('dialer-container').setAttribute('aria-valuenow',frequency);}},setFrequency:function(frequency,ignoreDialer){if(frequency<this._bandLowerBound){frequency=this._bandLowerBound;}
if(frequency>this._bandUpperBound){frequency=this._bandUpperBound;}
this._currentFreqency=frequency;this._updateUI(frequency,ignoreDialer);return frequency;},getFrequency:function(){return this._currentFreqency;}};var historyList={_historyList:[],KEYNAME:'historylist',SIZE:1,init:function hl_init(callback){var self=this;window.asyncStorage.getItem(this.KEYNAME,function storage_getItem(value){self._historyList=value||[];if(typeof callback=='function'){callback();}});},_save:function hl_save(){window.asyncStorage.setItem(this.KEYNAME,this._historyList);},add:function hl_add(freq){if(freq==null){return;}
var self=this;self._historyList.push({name:freq+'',frequency:freq});if(self._historyList.length>self.SIZE){self._historyList.shift();}
self._save();},last:function hl_last(){if(this._historyList.length===0){return null;}
else{return this._historyList[this._historyList.length-1];}}};var favoritesList={_favList:null,KEYNAME:'favlist',init:function(callback){var self=this;window.asyncStorage.getItem(this.KEYNAME,function storage_getItem(value){self._favList=value||{};self._showListUI();if(typeof callback=='function'){callback();}});var _container=$('fav-list-container');_container.addEventListener('click',function _onclick(event){var frequency=self._getElemFreq(event.target);if(!frequency){return;}
if(event.target.classList.contains('fav-list-remove-button')){self.remove(frequency);updateFreqUI();}else{if(mozFMRadio.enabled){cancelSeekAndSetFreq(frequency);}else{enableFMRadio(frequency);}}});},_save:function(){window.asyncStorage.setItem(this.KEYNAME,this._favList);},_showListUI:function(){var self=this;this.forEach(function(f){self._addItemToListUI(f);});},_addItemToListUI:function(item){var container=$('fav-list-container');var elem=document.createElement('div');elem.id=this._getUIElemId(item);elem.className='fav-list-item';elem.setAttribute('role','option');var subElem=document.createElement('div');subElem.className='fav-list-frequency';document.l10n.setAttributes(subElem,'fav-frequency-MHz',{value:item.frequency.toFixed(1)});elem.appendChild(subElem);subElem=document.createElement('div');subElem.className='fav-list-remove-button';elem.appendChild(subElem);if(container.childNodes.length===0){container.appendChild(elem);}else{var childNodes=container.childNodes;for(var i=0;i<childNodes.length;i++){var child=childNodes[i];var elemFreq=this._getElemFreq(child);if(item.frequency<elemFreq){container.insertBefore(elem,child);break;}else if(i==childNodes.length-1){container.appendChild(elem);break;}}}
return elem;},_removeItemFromListUI:function(freq){if(!this.contains(freq)){return;}
var itemElem=$(this._getUIElemId(this._favList[freq]));if(itemElem){itemElem.parentNode.removeChild(itemElem);}},_getUIElemId:function(item){return'frequency-'+item.frequency;},_getElemFreq:function(elem){elem=elem.closest('.fav-list-item');return parseFloat(elem.id.substring(elem.id.indexOf('-')+1));},forEach:function(callback){for(var freq in this._favList){callback(this._favList[freq]);}},contains:function(freq){if(!this._favList){return false;}
return typeof this._favList[freq]!='undefined';},add:function(freq){if(!this.contains(freq)){this._favList[freq]={name:freq+'',frequency:freq};this._save();var elem=this._addItemToListUI(this._favList[freq]);window.scrollTo(0,elem.offsetTop);}},remove:function(freq){var exists=this.contains(freq);this._removeItemFromListUI(freq);delete this._favList[freq];this._save();return exists;},select:function(freq){var items=$$('#fav-list-container div.fav-list-item');for(var i=0;i<items.length;i++){var item=items[i];if(this._getElemFreq(item)==freq){item.classList.add('selected');item.setAttribute('aria-selected',true);}else{item.classList.remove('selected');item.setAttribute('aria-selected',false);}}}};function updateFreqUI(){historyList.add(mozFMRadio.frequency);frequencyDialer.setFrequency(mozFMRadio.frequency);var frequency=frequencyDialer.getFrequency();favoritesList.select(frequency);var bookmarkButton=$('bookmark-button');bookmarkButton.dataset.bookmarked=favoritesList.contains(frequency);bookmarkButton.setAttribute('aria-pressed',favoritesList.contains(frequency));}
function init(){frequencyDialer.init();function onclick_seekbutton(event){var seekButton=this;var powerSwitch=$('power-switch');var seeking=!!powerSwitch.getAttribute('data-seeking');var up=seekButton.id=='frequency-op-seekup';function seek(){powerSwitch.dataset.seeking=true;var request=up?mozFMRadio.seekUp():mozFMRadio.seekDown();request.onsuccess=function seek_onsuccess(){powerSwitch.removeAttribute('data-seeking');};request.onerror=function seek_onerror(){powerSwitch.removeAttribute('data-seeking');};}
if(seeking){var request=mozFMRadio.cancelSeek();request.onsuccess=seek;request.onerror=seek;}else{seek();}}
$('frequency-op-seekdown').addEventListener('click',onclick_seekbutton,false);$('frequency-op-seekup').addEventListener('click',onclick_seekbutton,false);$('power-switch').addEventListener('click',function toggle_fm(){if(mozFMRadio.enabled){mozFMRadio.disable();}else{enableFMRadio(frequencyDialer.getFrequency());}},false);$('bookmark-button').addEventListener('click',function toggle_bookmark(){var frequency=frequencyDialer.getFrequency();if(favoritesList.contains(frequency)){favoritesList.remove(frequency);}else{favoritesList.add(frequency);}
updateFreqUI();},false);var speakerManager=new window.SpeakerManager();$('speaker-switch').addEventListener('click',function toggle_speaker(){speakerManager.forcespeaker=!speakerManager.speakerforced;},false);speakerManager.onspeakerforcedchange=function onspeakerforcedchange(){var speakerSwitch=$('speaker-switch');speakerSwitch.dataset.speakerOn=speakerManager.speakerforced;speakerSwitch.setAttribute('aria-pressed',speakerManager.speakerforced);};mozFMRadio.onfrequencychange=updateFreqUI;mozFMRadio.onenabled=function(){updateEnablingState(false);};mozFMRadio.ondisabled=function(){updateEnablingState(false);};mozFMRadio.onantennaavailablechange=function onAntennaChange(){updateWarningModeUI();if(mozFMRadio.antennaAvailable){if(!!window._previousFMRadioState||!!window._previousEnablingState){enableFMRadio(frequencyDialer.getFrequency());}}else{window._previousFMRadioState=mozFMRadio.enabled;window._previousEnablingState=enabling;mozFMRadio.disable();}};updateWarningModeUI();AirplaneModeHelper.addEventListener('statechange',function(status){airplaneModeEnabled=status==='enabled';updateWarningModeUI();});historyList.init(function hl_ready(){if(mozFMRadio.antennaAvailable){if(historyList.last()&&historyList.last().frequency){enableFMRadio(historyList.last().frequency);}else{enableFMRadio(mozFMRadio.frequencyLowerBound);}
favoritesList.init(updateFreqUI);}else{window._previousFMRadioState=true;updateWarningModeUI();favoritesList.init();}
updatePowerUI();window.performance.mark('fullyLoaded');});}
document.l10n.ready.then(function(){window.performance.mark('navigationLoaded');AirplaneModeHelper.ready(function(){airplaneModeEnabled=AirplaneModeHelper.getStatus()=='enabled';init();window.performance.mark('navigationInteractive');window.performance.mark('visuallyLoaded');window.performance.mark('contentInteractive');});});window.addEventListener('unload',function(e){mozFMRadio.disable();},false);