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.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
4
assets/webapps/video.gaiamobile.org/js/captions.js
Normal file
4
assets/webapps/video.gaiamobile.org/js/captions.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
;(function(exports){'use strict';function Captions(player){this.player=player;}
|
||||
Captions.prototype.remove=function(){for(var track of this.player.querySelectorAll('track')){if(track.src&&track.src.startsWith('blob:')){URL.revokeObjectURL(track.src);}
|
||||
track.remove();}};Captions.prototype.findAndDisplay=function(filepath){if(!filepath){return;}
|
||||
var basename=filepath.substring(0,filepath.lastIndexOf('.'));var captionsFilename=basename+'.vtt';var sdcard=navigator.getDeviceStorage('sdcard');if(!sdcard)return;var request=sdcard.get(captionsFilename);request.onsuccess=function(){var track=document.createElement('track');track.src=URL.createObjectURL(request.result);track.default=true;this.player.appendChild(track);track.track.mode='showing';}.bind(this);};exports.Captions=Captions;}(window));
|
||||
13
assets/webapps/video.gaiamobile.org/js/db.js
Normal file
13
assets/webapps/video.gaiamobile.org/js/db.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
;'use strict';function initDB(){videodb=new MediaDB('videos',null,{excludeFilter:/DCIM\/\d{3}MZLLA\/\.VID_\d{4}\.3gp$/});videodb.onupgrading=function(evt){storageState=MediaDB.UPGRADING;updateDialog();};videodb.onunavailable=function(event){storageState=event.detail;if(playerShowing){hidePlayer(true);}
|
||||
updateDialog();};videodb.oncardremoved=function(){if(playerShowing){hidePlayer(true);}};videodb.onscanend=function(){if(!firstScanEnded){firstScanEnded=true;window.performance.mark('fullyLoaded');}
|
||||
updateDialog();updateLoadingSpinner();};videodb.oncreated=function(event){event.detail.forEach(videoCreated);};videodb.ondeleted=function(event){event.detail.forEach(videoDeleted);};videodb.onenumerable=function(){storageState=false;updateDialog();enumerateDB();};videodb.onready=function(){storageState=false;updateDialog();startParsingMetadata();};}
|
||||
var enumerated=false;function enumerateDB(){if(enumerated){return;}
|
||||
enumerated=true;var firstBatchDisplayed=false;var batch=[];var batchSize=4;videodb.enumerate('date',null,'prev',function(videoinfo){if(videoinfo===null){flush();return;}
|
||||
var isVideo=videoinfo.metadata.isVideo;if(isVideo===false){return;}
|
||||
if(isVideo===undefined){addToMetadataQueue(videoinfo);return;}
|
||||
if(isVideo===true){batch.push(videoinfo);if(batch.length>=batchSize){flush();batchSize*=2;}}});function flush(){batch.forEach(addVideo);batch.length=0;if(!firstBatchDisplayed){firstBatchDisplayed=true;window.performance.mark('visuallyLoaded');window.performance.mark('contentInteractive');setTimeout(function(){var headers=document.querySelectorAll('gaia-header');for(var i=0;i<headers.length;++i){headers[i].removeAttribute('no-font-fit');}});}}}
|
||||
function addVideo(videodata){if(!videodata||!videodata.metadata.isVideo){return;}
|
||||
var view=thumbnailList.addItem(videodata);view.addTapListener(thumbnailClickHandler);view.updateTitleText();if(thumbnailList.count===1){updateDialog();}}
|
||||
function videoCreated(videoinfo){addToMetadataQueue(videoinfo);}
|
||||
function videoDeleted(filename){if(currentVideo&&filename===currentVideo.name){resetCurrentVideo();}
|
||||
thumbnailList.removeItem(filename);if(thumbnailList.count===0){updateDialog();hideSelectView();}}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
;'use strict';var ForwardRewindController=(function(){var isLongPressing=false;var intervalId=null;var forwardButton;var backwardButton;var videoToolbar;var player;var seekHelper;function init(video,forward,backward,seeker){forwardButton=forward;backwardButton=backward;player=video;seekHelper=seeker;videoToolbar=forward.parentElement;videoToolbar.addEventListener('click',handleButtonClick);videoToolbar.addEventListener('contextmenu',handlePlayerLongPress);videoToolbar.addEventListener('touchend',stopFastSeeking);}
|
||||
function uninit(video,forward,backward){videoToolbar.removeEventListener('click',handleButtonClick);videoToolbar.removeEventListener('contextmenu',handlePlayerLongPress);videoToolbar.removeEventListener('touchend',stopFastSeeking);forwardButton=null;backwardButton=null;player=null;}
|
||||
function handleButtonClick(event){if(event.target===forwardButton){startFastSeeking(1);}else if(event.target===backwardButton){startFastSeeking(-1);}else{return;}}
|
||||
function handlePlayerLongPress(event){if(event.target===forwardButton){isLongPressing=true;startFastSeeking(1);}else if(event.target===backwardButton){isLongPressing=true;startFastSeeking(-1);}else{return;}}
|
||||
function startFastSeeking(direction){var offset=direction*10;if(isLongPressing){intervalId=window.setInterval(function(){seekVideo(player.currentTime+offset);},1000);}else{seekVideo(player.currentTime+offset);}}
|
||||
function stopFastSeeking(){if(intervalId){window.clearInterval(intervalId);intervalId=null;isLongPressing=false;}}
|
||||
function seekVideo(seekTime){if(seekTime>=player.duration||seekTime<0){if(isLongPressing){stopFastSeeking();}
|
||||
if(seekTime>=player.duration){seekTime=player.duration;pause();}
|
||||
else{seekTime=0;}}
|
||||
if(seekHelper){seekHelper.seekTo(seekTime);}
|
||||
else{player.fastSeek(seekTime);}}
|
||||
return{init:init,uninit:uninit,stopFastSeeking:stopFastSeeking};}());
|
||||
30
assets/webapps/video.gaiamobile.org/js/metadata.js
Normal file
30
assets/webapps/video.gaiamobile.org/js/metadata.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
;'use strict';var metadataQueue=[];var processingQueue=false;var stopParsingMetadataCallback=null;var noMoreWorkCallback=null;function addToMetadataQueue(fileinfo){metadataQueue.push(fileinfo);startParsingMetadata();}
|
||||
function startParsingMetadata(){if(processingQueue){return;}
|
||||
if(metadataQueue.length===0){if(noMoreWorkCallback){noMoreWorkCallback();noMoreWorkCallback=null;}
|
||||
return;}
|
||||
if(document.hidden||playerShowing){return;}
|
||||
if(videodb.state!==MediaDB.READY){return;}
|
||||
processingQueue=true;showThrobber();processFirstQueuedItem();}
|
||||
function stopParsingMetadata(callback){if(!processingQueue){if(callback){callback();}
|
||||
return;}
|
||||
stopParsingMetadataCallback=callback||true;}
|
||||
function processFirstQueuedItem(){if(stopParsingMetadataCallback){var callback=stopParsingMetadataCallback;stopParsingMetadataCallback=null;processingQueue=false;hideThrobber();if(callback!==true){callback();}
|
||||
return;}
|
||||
if(metadataQueue.length===0){processingQueue=false;hideThrobber();updateDialog();if(typeof(noMoreWorkCallback)==='function'){noMoreWorkCallback();}
|
||||
return;}
|
||||
var fileinfo=metadataQueue.shift();videodb.getFile(fileinfo.name,function(file){getMetadata(file,function(metadata){fileinfo.metadata=metadata;videodb.updateMetadata(fileinfo.name,metadata,function(){if(metadata.isVideo){videodb.getFileInfo(fileinfo.name,function(dbfileinfo){addVideo(dbfileinfo);});}});setTimeout(processFirstQueuedItem);});},function(err){console.error('getFile error: ',fileinfo.name,err);processFirstQueuedItem();});}
|
||||
function getMetadata(videofile,callback){var offscreenVideo=document.createElement('video');var metadata={};if(!offscreenVideo.canPlayType(videofile.type)){metadata.isVideo=false;callback(metadata);return;}
|
||||
var url=URL.createObjectURL(videofile);offscreenVideo.preload='metadata';offscreenVideo.src=url;const METADATA_TIMEOUT=3000;var timeoutId=setTimeout(function(){console.error('No loadedmetadata or error events seen yet.','Assuming video file is corrupt:',videofile.name);offscreenVideo.onerror();},METADATA_TIMEOUT);offscreenVideo.onerror=function(e){console.error('Can\'t play video',videofile.name,e);metadata.isVideo=false;clearTimeout(timeoutId);unload();callback(metadata);};offscreenVideo.onloadedmetadata=function(){clearTimeout(timeoutId);if(!offscreenVideo.videoWidth){metadata.isVideo=false;unload();callback(metadata);return;}
|
||||
metadata.isVideo=true;metadata.title=readFromMetadata('title')||fileNameToVideoName(videofile.name);metadata.duration=offscreenVideo.duration;metadata.width=offscreenVideo.videoWidth;metadata.height=offscreenVideo.videoHeight;if(/.3gp$/.test(videofile.name)){getVideoRotation(videofile,function(rotation){if(typeof rotation==='number'){metadata.rotation=rotation;}
|
||||
else if(typeof rotation==='string'){console.warn('Video rotation:',rotation);}
|
||||
createThumbnail();});}else{metadata.rotation=0;createThumbnail();}};function readFromMetadata(lowerCaseKey){var tags=offscreenVideo.mozGetMetadata();for(var key in tags){if(key.toLowerCase()===lowerCaseKey){return tags[key];}}
|
||||
return;}
|
||||
function createThumbnail(){var t=Math.min(5,offscreenVideo.duration/10);offscreenVideo.fastSeek(t);var failed=false;var timeout=setTimeout(fail,10000);offscreenVideo.onerror=fail;function fail(){console.warn('Seek failed while creating thumbnail for',videofile.name,'. Ignoring corrupt file.');failed=true;clearTimeout(timeout);offscreenVideo.onerror=null;metadata.isVideo=false;unload();callback(metadata);}
|
||||
offscreenVideo.onseeked=function(){if(failed){return;}
|
||||
clearTimeout(timeout);captureFrame(offscreenVideo,metadata,function(poster){metadata.poster=poster;unload();callback(metadata);});};}
|
||||
function unload(){URL.revokeObjectURL(offscreenVideo.src);offscreenVideo.removeAttribute('src');offscreenVideo.load();}
|
||||
function fileNameToVideoName(filename){filename=filename.split('/').pop().replace(/\.(webm|ogv|ogg|mp4|3gp)$/,'').replace(/[_\.]/g,' ');return filename.charAt(0).toUpperCase()+filename.slice(1);}}
|
||||
function captureFrame(player,metadata,callback){try{var canvas=document.createElement('canvas');canvas.width=THUMBNAIL_WIDTH;canvas.height=THUMBNAIL_HEIGHT;var ctx=canvas.getContext('2d',{willReadFrequently:true});var vw=player.videoWidth,vh=player.videoHeight;var tw,th;switch(metadata.rotation){case 90:ctx.translate(THUMBNAIL_WIDTH,0);ctx.rotate(Math.PI/2);tw=THUMBNAIL_HEIGHT;th=THUMBNAIL_WIDTH;break;case 180:ctx.translate(THUMBNAIL_WIDTH,THUMBNAIL_HEIGHT);ctx.rotate(Math.PI);tw=THUMBNAIL_WIDTH;th=THUMBNAIL_HEIGHT;break;case 270:ctx.translate(0,THUMBNAIL_HEIGHT);ctx.rotate(-Math.PI/2);tw=THUMBNAIL_HEIGHT;th=THUMBNAIL_WIDTH;break;default:tw=THUMBNAIL_WIDTH;th=THUMBNAIL_HEIGHT;break;}
|
||||
var scale=Math.min(tw/vw,th/vh),w=scale*vw,h=scale*vh,x=(tw-w)/2/scale,y=(th-h)/2/scale;ctx.scale(scale,scale);ctx.drawImage(player,x,y);canvas.toBlob(done,'image/jpeg');}
|
||||
catch(e){console.error('Exception in captureFrame:',e,e.stack);done(null);}
|
||||
function done(blob){canvas.width=0;ctx=canvas=null;callback(blob);}}
|
||||
5
assets/webapps/video.gaiamobile.org/js/seeker.js
Normal file
5
assets/webapps/video.gaiamobile.org/js/seeker.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
;(function(exports){'use strict';function Seeker(player){this.player=player;this.seekStartedAt=null;this.currentSeekTime=null;this.nextSeekTime=null;this.player.addEventListener('seeked',this);}
|
||||
Seeker.SEEK_INTERVAL=200;Seeker.SEEK_DISTANCE=10;Seeker.prototype.seekTo=function(time){if(this.currentSeekTime===time){return;}
|
||||
var now=performance.now();if(this.currentSeekTime===null||time===0||Math.abs(this.currentSeekTime-time)>Seeker.SEEK_DISTANCE||now-this.seekStartedAt>Seeker.SEEK_INTERVAL)
|
||||
{this.player.fastSeek(time);this.seekStartedAt=now;this.currentSeekTime=time;this.nextSeekTime=null;}
|
||||
else{this.nextSeekTime=time;}};Seeker.prototype.handleEvent=function(){this.currentSeekTime=null;this.seekStartedAt=null;if(this.nextSeekTime!==null){this.seekTo(this.nextSeekTime);}};exports.Seeker=Seeker;}(window));
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
;'use strict';function ThumbnailDateGroup(item){if(!item){throw new Error('item should not be null or undefined.');}
|
||||
this.thumbnails=[];this.groupID=ThumbnailDateGroup.getGroupID(item);this.date=item.date;var wrappedHtmlText=ThumbnailDateGroup.view();var dummyDiv=document.createElement('DIV');dummyDiv.innerHTML=Sanitizer.unwrapSafeHTML(wrappedHtmlText);var domNode=dummyDiv.firstElementChild;if(!domNode){throw new Error('the template is empty');}
|
||||
this.htmlNode=domNode;this.container=domNode.querySelector('.thumbnail-group-container');this.header=domNode.querySelector('.thumbnail-group-header');this.localize();}
|
||||
ThumbnailDateGroup.view=function(){return Sanitizer.createSafeHTML`<li role="presentation">
|
||||
<div class="thumbnail-group-header" role="heading"
|
||||
aria-level="1"></div>
|
||||
<ul class="thumbnail-group-container" role="listbox"
|
||||
data-l10n-id="videos-list" aria-multiselectable="true"></ul>
|
||||
</li>`;};ThumbnailDateGroup.getGroupID=function(item){var dateObj=new Date(item.date);var month=dateObj.getMonth()+1;return'group_'+dateObj.getFullYear()+'-'+
|
||||
(month<10?'0'+month:month);};ThumbnailDateGroup.compareGroupID=function(id1,id2){return id1>id2?1:(id1<id2?-1:0);};ThumbnailDateGroup.prototype.addItem=function(item){if(!item){return;}
|
||||
var _this=this;function getInsertPosition(thumbnail){if(_this.thumbnails.length===0||thumbnail.data.date>_this.thumbnails[0].data.date){return 0;}
|
||||
else if(thumbnail.data.date<_this.thumbnails[_this.thumbnails.length-1].data.date){return _this.thumbnails.length;}
|
||||
else{return MediaUtils.binarySearch(_this.thumbnails,thumbnail,function(a,b){return b.data.date-a.data.date;});}}
|
||||
var thumbnail=new ThumbnailItem(item);var insertPosition=getInsertPosition(thumbnail);this.container.insertBefore(thumbnail.htmlNode,this.container.children[insertPosition]);this.thumbnails.splice(insertPosition,0,thumbnail);return thumbnail;};ThumbnailDateGroup.prototype.getCount=function(){return this.thumbnails.length;};ThumbnailDateGroup.prototype.removeItem=function(thumbnail){var idx=this.thumbnails.indexOf(thumbnail);if(idx<0){return;}
|
||||
this.thumbnails.splice(idx,1);this.container.removeChild(thumbnail.htmlNode);};ThumbnailDateGroup.prototype.localize=function(){var date=new Date(this.date);var formatter=IntlHelper.get('date-group');this.header.textContent=formatter.format(date);this.thumbnails.forEach(function(thumbnail){thumbnail.localize();});};
|
||||
27
assets/webapps/video.gaiamobile.org/js/thumbnail_item.js
Normal file
27
assets/webapps/video.gaiamobile.org/js/thumbnail_item.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
;'use strict';function ThumbnailItem(videoData){if(!videoData){throw new Error('videoData should not be null or undefined.');}
|
||||
this.posterNode=null;this.unwatchedNode=null;this.detailNode=null;this.titleNode=null;this.htmlNode=null;this.tapListeners=[];this.data=videoData;var _this=this;render();function convertToDOM(wrappedHTML){var dummyDiv=document.createElement('div');dummyDiv.innerHTML=Sanitizer.unwrapSafeHTML(wrappedHTML);var domNode=dummyDiv.firstElementChild;if(!domNode){throw new Error('the template does not contain any element');}
|
||||
_this.htmlNode=domNode;_this.posterNode=domNode.querySelector('.img');_this.detailNode=domNode.querySelector('.details');_this.titleNode=domNode.querySelector('.title');_this.unwatchedNode=domNode.querySelector('.unwatched');_this.sizeNode=domNode.querySelector('.size-text');}
|
||||
function render(){var duration='';if(isFinite(_this.data.metadata.duration)){duration=MediaUtils.formatDuration(_this.data.metadata.duration);}
|
||||
var videoType='';if(_this.data.type){var pos=_this.data.type.indexOf('/');videoType=(pos>-1?_this.data.type.slice(pos+1):_this.data.type);}
|
||||
var wrappedHtmlText=ThumbnailItem.view({'title':_this.data.metadata.title,'durationText':duration,'typeText':videoType});convertToDOM(wrappedHtmlText);_this.updatePoster(_this.data.metadata.bookmark||_this.data.metadata.poster);_this.setWatched(_this.data.metadata.watched);if(_this.detailNode){_this.detailNode.dataset.title=_this.data.metadata.title;}
|
||||
_this.htmlNode.addEventListener('click',dispatchClick);_this.localize();}
|
||||
function dispatchClick(){_this.tapListeners.forEach(function(listener){if(listener.handleEvent){listener.handleEvent(_this.data);}else if((typeof listener)==='function'){listener(_this.data);}});}}
|
||||
ThumbnailItem.titleMaxLines=2;ThumbnailItem.view=function({title,durationText,typeText}){return Sanitizer.createSafeHTML`<li class="thumbnail" role="option">
|
||||
<div class="inner">
|
||||
<div class="unwatched"></div>
|
||||
<img class="img" role="presentation"></img>
|
||||
<div class="details">
|
||||
<span class="title">${title}</span>
|
||||
<span class="duration-text after line-break">${durationText}</span>
|
||||
<span class="size-type-group">
|
||||
<span class="size-text after"></span>
|
||||
<span class="type-text after">${typeText}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>`;};ThumbnailItem.prototype.addTapListener=function(listener){if(!listener){return;}
|
||||
this.tapListeners[this.tapListeners.length]=listener;};ThumbnailItem.prototype.removeTapListener=function(listener){if(!listener){return;}
|
||||
var idx=this.tapListeners.indexOf(listener);if(idx>-1){this.tapListeners.splice(idx,1);}};ThumbnailItem.prototype.setWatched=function(watched){if(!this.unwatchedNode){return;}
|
||||
this.unwatchedNode.hidden=watched;};ThumbnailItem.prototype.updatePoster=function(imageblob){if(!this.posterNode){return;}
|
||||
if(this.posterNode.dataset.uri){URL.revokeObjectURL(this.posterNode.dataset.uri);}
|
||||
if(imageblob){this.posterNode.classList.remove('default');var imageUri=URL.createObjectURL(imageblob);this.posterNode.dataset.uri=imageUri;this.posterNode.src=imageUri;}else{this.posterNode.classList.add('default');this.posterNode.dataset.uri='';this.posterNode.src='style/images/default_thumbnail.png';}};ThumbnailItem.prototype.updateTitleText=function(){this.titleNode.textContent=VideoUtils.getTruncated(this.data.metadata.title,{node:this.titleNode,maxLine:ThumbnailItem.titleMaxLines});};ThumbnailItem.prototype.localize=function(){if(this.sizeNode&&isFinite(this.data.size)){MediaUtils.getLocalizedSizeTokens(this.data.size).then((args)=>{document.l10n.setAttributes(this.sizeNode,'fileSize',args);});}};
|
||||
16
assets/webapps/video.gaiamobile.org/js/thumbnail_list.js
Normal file
16
assets/webapps/video.gaiamobile.org/js/thumbnail_list.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
;'use strict';function ThumbnailList(groupClass,container){if(!groupClass||!container){throw new Error('group class or container cannot be null or undefined');}
|
||||
this.thumbnailMap={};this.groupMap={};this.itemGroups=[];this.count=0;this.groupClass=groupClass;this.container=container;document.addEventListener('DOMRetranslated',this.localize.bind(this));}
|
||||
ThumbnailList.prototype.addItem=function(item){if(!item){return null;}
|
||||
if(this.thumbnailMap[item.name]){return this.thumbnailMap[item.name];}
|
||||
var _this=this;function createItemGroup(item,before){var group=new _this.groupClass(item);_this.container.insertBefore(group.htmlNode,before?before.htmlNode:null);return group;}
|
||||
function getItemGroup(item){var groupID=_this.groupClass.getGroupID(item);var i;for(i=0;i<_this.itemGroups.length;i++){if(_this.itemGroups[i].groupID===groupID){return _this.itemGroups[i];}else if(_this.groupClass.compareGroupID(_this.itemGroups[i].groupID,groupID)<0){break;}}
|
||||
var createdGroup=createItemGroup(item,_this.itemGroups[i]);_this.itemGroups.splice(i,0,createdGroup);return createdGroup;}
|
||||
var group=getItemGroup(item);var thumbnail=group.addItem(item);this.groupMap[item.name]=group;this.thumbnailMap[item.name]=thumbnail;this.count++;return thumbnail;};ThumbnailList.prototype.removeItem=function(filename){if(!this.thumbnailMap[filename]){return;}
|
||||
var group=this.groupMap[filename];group.removeItem(this.thumbnailMap[filename]);if(!group.getCount()){this.container.removeChild(group.htmlNode);this.itemGroups.splice(this.itemGroups.indexOf(group),1);}
|
||||
this.count--;delete this.groupMap[filename];delete this.thumbnailMap[filename];};ThumbnailList.prototype.reset=function(){for(var name in this.thumbnailMap){this.groupMap[name].removeItem(this.thumbnailMap[name]);}
|
||||
this.container.innerHTML='';this.thumbnailMap={};this.itemGroups=[];this.groupMap={};this.count=0;};ThumbnailList.prototype.isPickMode=function(){return this.container.classList.contains('pick');};ThumbnailList.prototype.setPickMode=function(pick){if(pick){this.container.classList.add('pick');}else{this.container.classList.remove('pick');}};ThumbnailList.prototype.isSelectMode=function(){return this.container.classList.contains('select');};ThumbnailList.prototype.setSelectMode=function(select){if(select){this.container.classList.add('select');}else{this.container.classList.remove('select');}};ThumbnailList.prototype.updateAllThumbnailTitles=function(){var self=this;if(this.pendingTitleTextUpdate&&this.pendingTitleTextUpdate.length){this.pendingTitleTextUpdate.length=0;}
|
||||
var thumbnailKeys=Object.keys(this.thumbnailMap);this.pendingTitleTextUpdate=thumbnailKeys;setTimeout(updateNextThumbnailBatch);function updateNextThumbnailBatch(){for(var i=0;i<10;i++){if(thumbnailKeys.length===0){return;}
|
||||
var nextKey=thumbnailKeys.shift();self.thumbnailMap[nextKey].updateTitleText();}
|
||||
setTimeout(updateNextThumbnailBatch);}};ThumbnailList.prototype.findNextThumbnail=function(filename){var currentGroup=this.groupMap[filename];if(!currentGroup){return this.itemGroups.length?this.itemGroups[0].thumbnails[0]:null;}
|
||||
var currentThumbnail=this.thumbnailMap[filename];var idx;if(currentGroup.thumbnails.length===1){idx=this.itemGroups.indexOf(currentGroup);if(idx===0){if(this.itemGroups.length>1){currentThumbnail=this.itemGroups[1].thumbnails[0];}else{currentThumbnail=null;}}else{var thumbnails=this.itemGroups[idx-1].thumbnails;currentThumbnail=thumbnails[thumbnails.length-1];}}else{idx=currentGroup.thumbnails.indexOf(currentThumbnail);if(idx===0){currentThumbnail=currentGroup.thumbnails[1];}else{currentThumbnail=currentGroup.thumbnails[idx-1];}}
|
||||
return currentThumbnail;};ThumbnailList.prototype.localize=function(){this.itemGroups.forEach(function(group){group.localize();});};
|
||||
131
assets/webapps/video.gaiamobile.org/js/video.js
Normal file
131
assets/webapps/video.gaiamobile.org/js/video.js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
;'use strict';const LAYOUT_MODE={list:'layout-list',selection:'layout-selection',fullscreenPlayer:'layout-fullscreen-player'};var dom={};var ids=['thumbnail-list-title','thumbnails','thumbnails-video-button','thumbnails-select-button','thumbnails-delete-button','thumbnails-share-button','thumbnails-select-top','thumbnails-number-selected','player-view','fullscreen-button','spinner-overlay','thumbnails-single-delete-button','thumbnails-single-share-button','thumbnails-single-info-button','info-view','info-close-button','player','overlay','overlay-title','overlay-text','overlay-menu','overlay-action-button','player-header','video-container','videoBar','videoControlBar','close','play','playHead','timeSlider','elapsedTime','video-title','duration-text','elapsed-text','bufferedTime','slider-wrapper','throbber','picker-close','picker-title','picker-header','picker-done','options','options-view','seek-backward','seek-forward','in-use-overlay','in-use-overlay-title','in-use-overlay-text'];ids.forEach(function createElementRef(name){dom[toCamelCase(name)]=document.getElementById(name);});dom.player.mozAudioChannelType='content';function $(id){return document.getElementById(id);}
|
||||
var seeker=new Seeker(dom.player);var captions=new Captions(dom.player);var playing=false;var playerShowing=false;var controlShowing=false;var controlFadeTimeout=null;var selectedFileNames=[];var selectedFileNamesToBlobs={};var videodb;var currentVideo;var currentVideoBlob;var firstScanEnded=false;var THUMBNAIL_WIDTH;var THUMBNAIL_HEIGHT;var HAVE_NOTHING=0;var storageState;var currentOverlay;var dragging=false;var touchStartID=null;var sliderRect;var thumbnailList;var pendingPick;var videoHardwareReleased=false;var restoreTime=null;var isPhone;var isPortrait;var currentLayoutMode;var pendingUpdateTitleText=false;var FROMCAMERA=/DCIM\/\d{3}MZLLA\/VID_\d{4}\.3gp$/;var loadingChecker=new VideoLoadingChecker(dom.player,dom.inUseOverlay,dom.inUseOverlayTitle,dom.inUseOverlayText);var lastPlayTime=0;init();dom.player.addEventListener('play',function(){lastPlayTime=window.performance.now();});dom.player.addEventListener('mozinterruptbegin',function(){if((window.performance.now()-lastPlayTime)>50){pause();lastPlayTime=0;}});document.addEventListener('visibilitychange',function visibilityChange(){if(document.hidden){stopParsingMetadata();if(playing){pause();}}
|
||||
else{if(playerShowing){if(videoHardwareReleased){restoreVideo();}
|
||||
setControlsVisibility(true);}else{startParsingMetadata();}}});var acm=navigator.mozAudioChannelManager;if(acm){acm.addEventListener('headphoneschange',function onheadphoneschange(){if(!acm.headphones&&playing){setVideoPlaying(false);}});}
|
||||
navigator.mozSetMessageHandler('activity',handleActivityEvents);function init(){window.performance.mark('navigationLoaded');IntlHelper.define('date-group','datetime',{month:'long',year:'numeric',});thumbnailList=new ThumbnailList(ThumbnailDateGroup,dom.thumbnails);ThumbnailItem.titleMaxLines=isPhone?2:(isPortrait?4:2);initDB();addUIEventListeners();initLayout();initThumbnailSize();function addUIEventListeners(){if(!navigator.mozHasPendingMessage('activity')){initOptionsButtons();}
|
||||
document.l10n.ready.then(function(){initPlayerControls();});ForwardRewindController.init(dom.player,dom.seekForward,dom.seekBackward,seeker);dom.overlayActionButton.addEventListener('click',function(){if(pendingPick){cancelPick();}else if(currentOverlay==='empty'){launchCameraApp();}});window.performance.mark('navigationInteractive');}}
|
||||
function initThumbnailSize(){if(isPhone){THUMBNAIL_WIDTH=210*window.devicePixelRatio;THUMBNAIL_HEIGHT=120*window.devicePixelRatio;}else{var shortEdge=Math.min(window.innerWidth,window.innerHeight);THUMBNAIL_WIDTH=424*window.devicePixelRatio*shortEdge/800;THUMBNAIL_HEIGHT=Math.round(THUMBNAIL_WIDTH*4/7);}}
|
||||
function initLayout(){ScreenLayout.watch('portrait','(orientation: portrait)');isPhone=ScreenLayout.getCurrentLayout('tiny');isPortrait=ScreenLayout.getCurrentLayout('portrait');if(isPhone||isPortrait){dom.spinnerOverlay.classList.add('hidden');setDisabled(dom.playerView,false);}else{dom.spinnerOverlay.classList.remove('hidden');setDisabled(dom.playerView,true);}
|
||||
window.addEventListener('screenlayoutchange',handleScreenLayoutChange);switchLayout(LAYOUT_MODE.list);}
|
||||
function initPlayerControls(){dom.sliderWrapper.addEventListener('touchstart',handleSliderTouchStart);dom.sliderWrapper.addEventListener('touchmove',handleSliderTouchMove);dom.sliderWrapper.addEventListener('touchend',handleSliderTouchEnd);dom.player.addEventListener('timeupdate',timeUpdated);dom.player.addEventListener('seeked',updateVideoControlSlider);dom.player.addEventListener('ended',playerEnded);dom.play.addEventListener('click',handlePlayButtonClick);dom.playerHeader.addEventListener('action',handleCloseButtonClick);dom.pickerDone.addEventListener('click',postPickResult);dom.options.addEventListener('click',showOptionsView);dom.videoContainer.addEventListener('click',toggleVideoControls);dom.timeSlider.addEventListener('keypress',handleSliderKeypress);}
|
||||
function initOptionsButtons(){dom.thumbnailsVideoButton.addEventListener('click',launchCameraApp);dom.thumbnailsSelectButton.addEventListener('click',showSelectView);dom.thumbnailsSelectTop.addEventListener('action',hideSelectView);dom.thumbnailsDeleteButton.addEventListener('click',deleteSelectedItems);dom.thumbnailsShareButton.addEventListener('click',shareSelectedItems);dom.infoCloseButton.addEventListener('click',hideInfoView);dom.optionsView.addEventListener('gaiamenu-cancel',hideOptionsView);dom.fullscreenButton.addEventListener('click',toggleFullscreenPlayer);addEventListeners('.single-delete-button','click',deleteCurrentVideo);addEventListeners('.single-share-button','click',shareCurrentVideo);addEventListeners('.single-info-button','click',showInfoView);}
|
||||
function addEventListeners(selector,type,listener){var elements=document.body.querySelectorAll(selector);for(var i=0;i<elements.length;i++){elements[i].addEventListener(type,listener);}}
|
||||
function toggleFullscreenPlayer(){if(currentLayoutMode===LAYOUT_MODE.list){switchLayout(LAYOUT_MODE.fullscreenPlayer);scheduleVideoControlsAutoHiding();}else{switchLayout(LAYOUT_MODE.list);}
|
||||
VideoUtils.fitContainer(dom.videoContainer,dom.player,currentVideo.metadata.rotation||0);}
|
||||
function toggleVideoControls(e){if(controlFadeTimeout){clearTimeout(controlFadeTimeout);controlFadeTimeout=null;}
|
||||
if(!pendingPick){e.cancelBubble=!controlShowing;setControlsVisibility(!controlShowing);}}
|
||||
function handleScreenLayoutChange(){isPortrait=ScreenLayout.getCurrentLayout('portrait');if(!isPhone){if(!isPortrait&&(!firstScanEnded||processingQueue)){dom.spinnerOverlay.classList.remove('hidden');setDisabled(dom.playerView,true);}else{dom.spinnerOverlay.classList.add('hidden');setDisabled(dom.playerView,false);}
|
||||
if(currentLayoutMode===LAYOUT_MODE.list){if(isPortrait){hidePlayer(true);}else{showPlayer(currentVideo,false,false,true);}}
|
||||
ThumbnailItem.titleMaxLines=isPortrait?4:2;}
|
||||
if(currentLayoutMode!==LAYOUT_MODE.fullscreenPlayer){if(!thumbnailList){return;}
|
||||
thumbnailList.updateAllThumbnailTitles();}else{pendingUpdateTitleText=true;}
|
||||
if(dom.player.readyState!==HAVE_NOTHING){VideoUtils.fitContainer(dom.videoContainer,dom.player,currentVideo.metadata.rotation||0);}}
|
||||
function switchLayout(mode){var oldMode=currentLayoutMode;if(oldMode){document.body.classList.remove(currentLayoutMode);}
|
||||
currentLayoutMode=mode;document.body.classList.add(currentLayoutMode);if(oldMode===LAYOUT_MODE.fullscreenPlayer&&pendingUpdateTitleText){pendingUpdateTitleText=false;thumbnailList.updateAllThumbnailTitles();}}
|
||||
function handleActivityEvents(a){var activityName=a.source.name;if(activityName==='pick'){pendingPick=a;showPickView();}}
|
||||
function showInfoView(){hideOptionsView();var length=isFinite(currentVideo.metadata.duration)?MediaUtils.formatDuration(currentVideo.metadata.duration):'';var type=currentVideo.type;if(type){var index=currentVideo.type.indexOf('/');type=index>-1?currentVideo.type.slice(index+1):currentVideo.type;}
|
||||
var resolution=(currentVideo.metadata.width&¤tVideo.metadata.height)?currentVideo.metadata.width+'x'+
|
||||
currentVideo.metadata.height:'';return MediaUtils.getLocalizedSizeTokens(currentVideo.size).then((args)=>{var data={'info-name':{raw:currentVideo.metadata.title},'info-length':{raw:length},'info-size':{id:'fileSize',args:args},'info-type':{raw:type},'info-date':{raw:MediaUtils.formatDate(currentVideo.date)},'info-resolution':{raw:resolution}};MediaUtils.populateMediaInfo(data);setNFCSharing(false);dom.infoView.classList.remove('hidden');document.body.classList.add('info-view');});}
|
||||
function hideInfoView(){setNFCSharing(true);dom.infoView.classList.add('hidden');document.body.classList.remove('info-view');}
|
||||
function showSelectView(){hidePlayer(true,function(){switchLayout(LAYOUT_MODE.selection);thumbnailList.setSelectMode(true);clearSelection();});}
|
||||
function hideSelectView(){clearSelection();thumbnailList.setSelectMode(false);switchLayout(LAYOUT_MODE.list);if(!isPhone&&!isPortrait&¤tVideo){showPlayer(currentVideo,false,false,true);}}
|
||||
function showOptionsView(){if(playing){pause();}
|
||||
dom.optionsView.removeAttribute('hidden');document.body.classList.add('options-view');}
|
||||
function hideOptionsView(){dom.optionsView.setAttribute('hidden',true);document.body.classList.remove('options-view');}
|
||||
function setDisabled(element,disabled){element.classList.toggle('disabled',disabled);element.setAttribute('aria-disabled',disabled);}
|
||||
function setSelected(element,selected){element.classList.toggle('selected',selected);element.setAttribute('aria-selected',selected);}
|
||||
function clearSelection(){Array.forEach(selectedFileNames,function(name){setSelected(thumbnailList.thumbnailMap[name].htmlNode,false);});selectedFileNames=[];selectedFileNamesToBlobs={};setDisabled(dom.thumbnailsDeleteButton,true);setDisabled(dom.thumbnailsShareButton,true);document.l10n.setAttributes(dom.thumbnailsNumberSelected,'number-selected2',{n:0});}
|
||||
function updateSelection(videodata){var thumbnail=thumbnailList.thumbnailMap[videodata.name];var selected=!thumbnail.htmlNode.classList.contains('selected');setSelected(thumbnail.htmlNode,selected);var filename=videodata.name;if(selected){selectedFileNames.push(filename);videodb.getFile(filename,function(blob){selectedFileNamesToBlobs[filename]=blob;});}
|
||||
else{delete selectedFileNamesToBlobs[filename];var i=selectedFileNames.indexOf(filename);if(i!==-1){selectedFileNames.splice(i,1);}}
|
||||
var numSelected=selectedFileNames.length;document.l10n.setAttributes(dom.thumbnailsNumberSelected,'number-selected2',{n:numSelected});var noneSelected=numSelected===0;setDisabled(dom.thumbnailsDeleteButton,noneSelected);setDisabled(dom.thumbnailsShareButton,noneSelected);}
|
||||
function launchCameraApp(){var a=new MozActivity({name:'record',data:{type:'videos'}});a.onerror=function(){if(a.error.name==='NO_PROVIDER'){document.l10n.formatValue('share-noprovider').then((msg)=>{alert(msg);});}else{console.warn('share activity error:',a.error.name);}};}
|
||||
function resetCurrentVideo(){if(!currentVideo){return;}
|
||||
var currentThumbnail=thumbnailList.thumbnailMap[currentVideo.name];currentThumbnail.htmlNode.classList.remove('focused');var nextThumbnail=thumbnailList.findNextThumbnail(currentVideo.name);if(nextThumbnail){currentVideo=nextThumbnail.data;nextThumbnail.htmlNode.classList.add('focused');}else{currentVideo=null;}}
|
||||
function deleteSelectedItems(){if(selectedFileNames.length===0){return;}
|
||||
LazyLoader.load('shared/style/confirm.css',function(){document.body.classList.add('confirm-dialog');Dialogs.confirm({messageId:'delete-n-items?',messageArgs:{n:selectedFileNames.length},cancelId:'cancel',confirmId:'delete',danger:true},function(){for(var i=0;i<selectedFileNames.length;i++){deleteFile(selectedFileNames[i]);}
|
||||
clearSelection();document.body.classList.remove('confirm-dialog');},function(){document.body.classList.remove('confirm-dialog');});});}
|
||||
function deleteFile(filename){if(FROMCAMERA.test(filename)){var postername=filename.replace('.3gp','.jpg');navigator.getDeviceStorage('pictures').delete(postername);}
|
||||
videodb.deleteFile(filename);}
|
||||
function shareSelectedItems(){var blobs=selectedFileNames.map(function(name){return selectedFileNamesToBlobs[name];});share(blobs);}
|
||||
function share(blobs){if(blobs.length===0){return;}
|
||||
var names=[],fullpaths=[];blobs.forEach(function(blob){var name=blob.name;fullpaths.push(name);name=name.substring(name.lastIndexOf('/')+1);names.push(name);});if(playerShowing){releaseVideo();}
|
||||
var a=new MozActivity({name:'share',data:{type:'video/*',number:blobs.length,blobs:blobs,filenames:names,filepaths:fullpaths}});a.onsuccess=restoreVideo;a.onerror=function(){if(a.error.name==='NO_PROVIDER'){document.l10n.formatValue('share-noprovider').then((msg)=>{alert(msg);});}else{console.warn('share activity error:',a.error.name);}
|
||||
restoreVideo();};}
|
||||
function updateDialog(){if(thumbnailList.count!==0&&(!storageState||playerShowing)){showOverlay(null);}else if(storageState===MediaDB.UPGRADING){showOverlay('upgrade');}else if(storageState===MediaDB.NOCARD){showOverlay('nocard');}else if(storageState===MediaDB.UNMOUNTED){showOverlay('pluggedin');}else if(firstScanEnded&&thumbnailList.count===0&&metadataQueue.length===0){showOverlay('empty');}}
|
||||
function updateLoadingSpinner(){if(processingQueue){noMoreWorkCallback=updateLoadingSpinner;}else{window.performance.mark('scanEnd');dom.spinnerOverlay.classList.add('hidden');setDisabled(dom.playerView,false);if(thumbnailList.count){currentVideo=currentVideo||thumbnailList.itemGroups[0].thumbnails[0].data;if(!isPhone&&!isPortrait){showPlayer(currentVideo,false,false,true);}}}}
|
||||
function thumbnailClickHandler(videodata){if(!isPhone&&!isPortrait){if(!firstScanEnded||processingQueue){return;}}
|
||||
if(currentLayoutMode===LAYOUT_MODE.list){hidePlayer(true,function(){stopParsingMetadata(function(){var fullscreen=pendingPick||isPhone||isPortrait;showPlayer(videodata,!pendingPick,fullscreen,pendingPick);});});}
|
||||
else if(currentLayoutMode===LAYOUT_MODE.selection){updateSelection(videodata);}}
|
||||
function showOverlay(id){LazyLoader.load('shared/style/confirm.css',function(){currentOverlay=id;if(id===null){document.body.classList.remove('overlay');dom.overlay.classList.add('hidden');return;}
|
||||
var text,title;if(pendingPick||id==='empty'){dom.overlayMenu.classList.remove('hidden');dom.overlayActionButton.classList.remove('hidden');dom.overlayActionButton.setAttribute('data-l10n-id',pendingPick?'overlay-cancel-button':'overlay-camera-button');}else{dom.overlayMenu.classList.add('hidden');dom.overlayActionButton.classList.add('hidden');}
|
||||
if(id==='nocard'){title='nocard2-title';text='nocard3-text';}else{title=id+'-title';text=id+'-text';}
|
||||
dom.overlayTitle.setAttribute('data-l10n-id',title);dom.overlayText.setAttribute('data-l10n-id',text);dom.overlay.classList.remove('hidden');document.body.classList.add('overlay');});}
|
||||
function setControlsVisibility(visible){if(isPhone||isPortrait||currentLayoutMode!==LAYOUT_MODE.list){dom.playerView.classList[visible?'remove':'add']('video-controls-hidden');controlShowing=visible;}else{controlShowing=true;}
|
||||
dom.videoContainer.setAttribute('data-l10n-id',controlShowing?'hide-controls-button':'show-controls-button');if(controlShowing){updateVideoControlSlider();}}
|
||||
function movePlayHead(percent){if(document.documentElement.dir==='ltr'){dom.playHead.style.left=percent;}
|
||||
else{dom.playHead.style.right=percent;}}
|
||||
function updateVideoControlSlider(){var percent=(dom.player.currentTime/dom.player.duration)*100;if(isNaN(percent)){return;}
|
||||
percent+='%';dom.elapsedText.textContent=MediaUtils.formatDuration(dom.player.currentTime);dom.elapsedTime.style.width=percent;if(!dragging){movePlayHead(percent);}}
|
||||
function setVideoPlaying(playing){if(playing){play();}else{pause();}}
|
||||
function deleteCurrentVideo(){hideOptionsView();setNFCSharing(false);LazyLoader.load('shared/style/confirm.css',function(){document.body.classList.add('confirm-dialog');Dialogs.confirm({messageId:'delete-video?',cancelId:'cancel',confirmId:'delete',danger:true},function _onSuccess(){deleteFile(currentVideo.name);if(!isPhone&&!isPortrait){if(currentVideo){showPlayer(currentVideo,false,true,true);}}else{hidePlayer(false);}
|
||||
document.body.classList.remove('confirm-dialog');},function _onError(){setNFCSharing(true);document.body.classList.remove('confirm-dialog');});});}
|
||||
function handlePlayButtonClick(){setVideoPlaying(dom.player.paused);}
|
||||
function handleCloseButtonClick(){if(isPhone||isPortrait){hidePlayer(true);}else{toggleFullscreenPlayer();}}
|
||||
function postPickResult(){pendingPick.postResult({type:currentVideoBlob.type,blob:currentVideoBlob});cleanupPick();}
|
||||
function shareCurrentVideo(){hideOptionsView();videodb.getFile(currentVideo.name,function(blob){share([blob]);});}
|
||||
function handleSliderTouchStart(event){if(null!=touchStartID){return;}
|
||||
touchStartID=event.changedTouches[0].identifier;dragging=true;sliderRect=dom.sliderWrapper.getBoundingClientRect();if(dom.player.duration===Infinity){return;}
|
||||
handleSliderTouchMove(event);}
|
||||
function setVideoUrl(player,video,callback){function handleLoadedMetadata(){dom.player.onloadedmetadata=null;callback();}
|
||||
function loadVideo(url){loadingChecker.ensureVideoLoads(handleLoadedMetadata);player.src=url;}
|
||||
captions.remove();if('name'in video){videodb.getFile(video.name,function(file){captions.findAndDisplay(file.name);var url=URL.createObjectURL(file);loadVideo(url);if(pendingPick){currentVideoBlob=file;}});}else if('url'in video){loadVideo(video.url);}}
|
||||
function scheduleVideoControlsAutoHiding(){controlFadeTimeout=setTimeout(function(){setControlsVisibility(false);},250);}
|
||||
function setNFCSharing(enable){if(!window.navigator.mozNfc){return;}
|
||||
if(enable){window.navigator.mozNfc.onpeerready=function(event){videodb.getFile(currentVideo.name,function(file){event.peer.sendFile(file);});};}else{window.navigator.mozNfc.onpeerready=null;}}
|
||||
function showPlayer(video,autoPlay,enterFullscreen,keepControls){if(currentVideo){var old=thumbnailList.thumbnailMap[currentVideo.name];old.htmlNode.classList.remove('focused');}
|
||||
currentVideo=video;var thumbnail=thumbnailList.thumbnailMap[currentVideo.name];thumbnail.htmlNode.classList.add('focused');updateDialog();dom.player.preload='metadata';function doneSeeking(){dom.player.onseeked=null;setControlsVisibility(true);if(!keepControls){scheduleVideoControlsAutoHiding();}
|
||||
if(autoPlay){play();}else{pause();}
|
||||
dom.player.hidden=false;}
|
||||
dom.player.hidden=true;setVideoUrl(dom.player,currentVideo,function(){if(enterFullscreen){switchLayout(LAYOUT_MODE.fullscreenPlayer);}
|
||||
var formattedDuration=MediaUtils.formatDuration(dom.player.duration);dom.durationText.textContent=formattedDuration;timeUpdated();setButtonPaused(false);playerShowing=true;var rotation;if('metadata'in currentVideo){if(currentVideo.metadata.currentTime===dom.player.duration){currentVideo.metadata.currentTime=0;}
|
||||
dom.videoTitle.textContent=currentVideo.metadata.title;dom.player.currentTime=currentVideo.metadata.currentTime||0;rotation=currentVideo.metadata.rotation;}else{dom.videoTitle.textContent=currentVideo.title||'';dom.player.currentTime=0;rotation=0;}
|
||||
document.l10n.setAttributes(dom.timeSlider,'seek-bar',{duration:formattedDuration});dom.timeSlider.setAttribute('aria-valuemin',0);dom.timeSlider.setAttribute('aria-valuemax',dom.player.duration);dom.timeSlider.setAttribute('aria-valuenow',dom.player.currentTime);dom.timeSlider.setAttribute('aria-valuetext',MediaUtils.formatDuration(dom.player.currentTime));VideoUtils.fitContainer(dom.videoContainer,dom.player,rotation||0);if(dom.player.seeking){dom.player.onseeked=doneSeeking;}else{doneSeeking();}
|
||||
setNFCSharing(true);});}
|
||||
function hidePlayer(updateVideoMetadata,callback){if(!playerShowing){if(callback){callback();}
|
||||
return;}
|
||||
dom.player.pause();setNFCSharing(false);function completeHidingPlayer(){switchLayout(LAYOUT_MODE.list);setButtonPaused(false);playerShowing=false;updateDialog();captions.remove();dom.player.removeAttribute('src');dom.player.load();startParsingMetadata();if(callback){callback();}}
|
||||
if(!('metadata'in currentVideo)||!updateVideoMetadata||pendingPick){completeHidingPlayer();return;}
|
||||
var video=currentVideo;var thumbnail=thumbnailList.thumbnailMap[video.name];if(dom.player.currentTime===0){video.metadata.bookmark=null;updateMetadata();}
|
||||
else{captureFrame(dom.player,video.metadata,function(bookmark){video.metadata.bookmark=bookmark;updateMetadata();});}
|
||||
function updateMetadata(){thumbnail.updatePoster(video.metadata.bookmark||video.metadata.poster);if(!video.metadata.watched){video.metadata.watched=true;thumbnail.setWatched(true);}
|
||||
video.metadata.currentTime=dom.player.currentTime;videodb.updateMetadata(video.name,video.metadata);completeHidingPlayer();}}
|
||||
function playerEnded(){if(dragging){return;}
|
||||
if(playing){dom.player.currentTime=0;pause();}}
|
||||
function setButtonPaused(paused){dom.play.classList.toggle('paused',paused);dom.play.setAttribute('data-l10n-id',paused?'play-button':'pause-button');}
|
||||
function play(){loadingChecker.ensureVideoPlays();setButtonPaused(false);VideoStats.start(dom.player);dom.player.play();playing=true;}
|
||||
function pause(){loadingChecker.cancelEnsureVideoPlays();setButtonPaused(true);if(dragging){dragging=false;dom.playHead.classList.remove('active');}
|
||||
dom.player.pause();playing=false;VideoStats.stop();VideoStats.dump();}
|
||||
function timeUpdated(){if(controlShowing){if(dom.player.duration===Infinity||dom.player.duration===0){return;}
|
||||
updateVideoControlSlider();}
|
||||
dom.timeSlider.setAttribute('aria-valuenow',dom.player.currentTime);dom.timeSlider.setAttribute('aria-valuetext',MediaUtils.formatDuration(dom.player.currentTime));}
|
||||
function handleSliderTouchEnd(event){if(!event.changedTouches.identifiedTouch(touchStartID)){return;}
|
||||
touchStartID=null;if(!dragging){return;}
|
||||
dragging=false;dom.playHead.classList.remove('active');updateVideoControlSlider();if(dom.player.currentTime===dom.player.duration){pause();}}
|
||||
function handleSliderTouchMove(event){if(!dragging){return;}
|
||||
var touch=event.changedTouches.identifiedTouch(touchStartID);if(!touch){return;}
|
||||
function getTouchPos(){return(document.documentElement.dir==='ltr')?(touch.clientX-sliderRect.left):(sliderRect.right-touch.clientX);}
|
||||
var touchPos=getTouchPos();var pos=touchPos/sliderRect.width;pos=Math.max(pos,0);pos=Math.min(pos,1);var percent=pos*100+'%';dom.playHead.classList.add('active');movePlayHead(percent);dom.elapsedTime.style.width=percent;seeker.seekTo(dom.player.duration*pos);}
|
||||
function handleSliderKeypress(event){var step=Math.max(dom.player.duration/20,2);if(event.keyCode===event.DOM_VK_DOWN){seeker.seekTo(dom.player.currentTime-step);}else if(event.keyCode===event.DOM_VK_UP){seeker.seekTo(dom.player.currentTime+step);}}
|
||||
function toCamelCase(str){return str.replace(/\-(.)/g,function replacer(str,p1){return p1.toUpperCase();});}
|
||||
function showPickView(){thumbnailList.setPickMode(true);document.body.classList.add('pick-activity');dom.pickerHeader.addEventListener('action',cancelPick);if(!isPhone&&!isPortrait){thumbnailList.updateAllThumbnailTitles();}}
|
||||
function cancelPick(){pendingPick.postError('pick cancelled');cleanupPick();}
|
||||
function cleanupPick(){pendingPick=null;currentVideoBlob=null;hidePlayer(false);}
|
||||
function showThrobber(){dom.throbber.classList.remove('hidden');dom.throbber.classList.add('throb');}
|
||||
function hideThrobber(){dom.throbber.classList.remove('throb');setTimeout(function(){dom.throbber.classList.add('hidden');},100);}
|
||||
function releaseVideo(){if(videoHardwareReleased){return;}
|
||||
videoHardwareReleased=true;if(dom.player.readyState>0){restoreTime=dom.player.currentTime;}
|
||||
else{restoreTime=0;}
|
||||
captions.remove();dom.player.removeAttribute('src');dom.player.load();}
|
||||
function restoreVideo(){if(!videoHardwareReleased){return;}
|
||||
videoHardwareReleased=false;setVideoUrl(dom.player,currentVideo,function(){VideoUtils.fitContainer(dom.videoContainer,dom.player,currentVideo.metadata.rotation||0);dom.player.currentTime=restoreTime;});}
|
||||
window.addEventListener('storage',function(e){if(e.key==='view-activity-wants-to-use-hardware'&&e.newValue&&!document.hidden&&playerShowing&&!videoHardwareReleased){console.log('The video app view activity needs to play a video.');console.log('Pausing the video and returning to the thumbnails.');console.log('See bug 1088456.');handleCloseButtonClick();}});
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
;(function(exports){'use strict';exports.VideoLoadingChecker=VideoLoadingChecker;function VideoLoadingChecker(player,overlay,overlayTitle,overlayText){this.player=player;this.overlay=overlay;this.overlayTitle=overlayTitle;this.overlayText=overlayText;}
|
||||
VideoLoadingChecker.prototype={ensureVideoLoads:function vpc_ensureVideoLoads(callback){var self=this;var videoLoadedTimeoutId=setTimeout(showOverlay.bind(null,this),1500);this.player.addEventListener('loadedmetadata',handleLoadedMetadata);function handleLoadedMetadata(){clearTimeout(videoLoadedTimeoutId);videoLoadedTimeoutId=null;self.player.removeEventListener('loadedmetadata',handleLoadedMetadata);hideOverlay(self);if(callback){callback();}}},ensureVideoPlays:function(){this.playingTimeout=setTimeout(showOverlay.bind(null,this),1500);this.playingListener=this.cancelEnsureVideoPlays.bind(this);this.player.addEventListener('timeupdate',this.playingListener);},cancelEnsureVideoPlays:function(){hideOverlay(this);if(this.playingTimeout){clearTimeout(this.playingTimeout);this.playingTimeout=null;}
|
||||
if(this.playingListener){this.player.removeEventListener('timeupdate',this.playingListener);this.playingListener=null;}}};function showOverlay(checker){checker.overlayTitle.setAttribute('data-l10n-id','video-hardware-in-use-title');checker.overlayText.setAttribute('data-l10n-id','video-hardware-in-use-text');checker.overlay.classList.remove('hidden');document.body.classList.add('in-use-overlay');}
|
||||
function hideOverlay(checker){document.body.classList.remove('in-use-overlay');checker.overlay.classList.add('hidden');}}(window));
|
||||
13
assets/webapps/video.gaiamobile.org/js/video_utils.js
Normal file
13
assets/webapps/video.gaiamobile.org/js/video_utils.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
;'use strict';var VideoUtils={fitContainer:function(container,player,videoRotation){var containerWidth=container.clientWidth;var containerHeight=container.clientHeight;if(!player.videoWidth||!player.videoHeight){return;}
|
||||
var width,height;var rotation=videoRotation||0;switch(rotation){case 0:case 180:width=player.videoWidth;height=player.videoHeight;break;case 90:case 270:width=player.videoHeight;height=player.videoWidth;}
|
||||
var xscale=containerWidth/width;var yscale=containerHeight/height;var scale=Math.min(xscale,yscale);width*=scale;height*=scale;var left=((containerWidth-width)/2);var top=((containerHeight-height)/2);var transform;switch(rotation){case 0:transform='translate('+left.toFixed(4)+'px,'+
|
||||
top.toFixed(4)+'px)';break;case 90:transform='translate('+(left+width).toFixed(4)+'px,'+
|
||||
top.toFixed(4)+'px) '+'rotate(90deg)';break;case 180:transform='translate('+(left+width).toFixed(4)+'px,'+
|
||||
(top+height).toFixed(4)+'px) '+'rotate(180deg)';break;case 270:transform='translate('+left.toFixed(4)+'px,'+
|
||||
(top+height).toFixed(4)+'px) '+'rotate(270deg)';break;}
|
||||
transform+=' scale('+scale.toFixed(4)+')';player.style.transform=transform;},getTruncated:function(oldName,options){var maxLine=options.maxLine||2;var node=options.node;var ellipsisCharacter=options.ellipsisCharacter||'...';if(node===null){return oldName;}
|
||||
var realVisibility=node.style.visibility;var realWordBreak=node.style.wordBreak;node.style.visibility='hidden';node.style.wordBreak='break-all';node.textContent='.';var baseHeight=node.clientHeight;var computedStyle=window.getComputedStyle(node,null);var paddingTop=parseInt(computedStyle.getPropertyValue('padding-top'),10);var paddingBottom=parseInt(computedStyle.getPropertyValue('padding-bottom'),10);var padding=paddingTop+paddingBottom;var lineHeight=baseHeight-padding;node.textContent=oldName;var originalTitleHeight=node.clientHeight-padding;var linesForOriginalTitle=(originalTitleHeight/lineHeight);if(linesForOriginalTitle<=maxLine){node.style.visibility=realVisibility;node.style.wordBreak=realWordBreak;return oldName;}
|
||||
function titleTooLong(pos){node.textContent=oldName.slice(0,pos)+ellipsisCharacter;var tempTitleHeight=node.clientHeight-padding;return(tempTitleHeight/lineHeight)>maxLine;}
|
||||
var low=0;var high=oldName.length-1;var mid;var ellipsisIndex=0;do{mid=Math.floor((low+high)/2);if(titleTooLong(mid)){high=mid-1;}
|
||||
else{ellipsisIndex=mid;low=mid+1;}}
|
||||
while(low<=high);var newName=oldName.slice(0,ellipsisIndex)+ellipsisCharacter;node.style.visibility=realVisibility;node.style.wordBreak=realWordBreak;return newName;}};
|
||||
595
assets/webapps/video.gaiamobile.org/js/view.js
Normal file
595
assets/webapps/video.gaiamobile.org/js/view.js
Normal file
|
|
@ -0,0 +1,595 @@
|
|||
'use strict';
|
||||
/* global
|
||||
getStorageIfAvailable,
|
||||
getVideoRotation,
|
||||
VideoUtils,
|
||||
ForwardRewindController,
|
||||
MimeMapper,
|
||||
getUnusedFilename,
|
||||
MediaUtils,
|
||||
VideoLoadingChecker,
|
||||
MediaError,
|
||||
Seeker
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a stripped down version of video.js that handles the view activity
|
||||
* and displays streaming videos specified by url.
|
||||
*
|
||||
* Unfortunately, there is a fair bit of code duplication between this
|
||||
* file and video.js, but we are too close to the v1 deadline to refactor
|
||||
* the shared code into a single shared file. If the video player UI changes
|
||||
* those changes will have to be made in both video.js and view.js
|
||||
*/
|
||||
(function init() {
|
||||
var dom = {}; // document elements
|
||||
var seeker;
|
||||
var playing = false;
|
||||
var controlShowing = false;
|
||||
var controlFadeTimeout = null;
|
||||
var dragging = false;
|
||||
var storage; // A device storage object used by the save button
|
||||
var saved = false; // Did we save it?
|
||||
var videoRotation = 0;
|
||||
var activity; // The activity object we're handling
|
||||
var data; // The data sent by the initiating app
|
||||
var blob; // The blob we'll be displaying and maybe saving
|
||||
var url; // URL string representing the blob object
|
||||
var title; // What we call the video in the titlebar and banner
|
||||
// touch start id is the identifier of touch event. we only need to process
|
||||
// events related to this id.
|
||||
var touchStartID = null;
|
||||
var sliderRect;
|
||||
window.pause = pause;
|
||||
|
||||
//
|
||||
// Bug 1088456: when the view activity is launched by the bluetooth transfer
|
||||
// app (when the user taps on a downloaded file in the notification tray)
|
||||
// this code starts running while the regular video app is still running as
|
||||
// the foreground app. Since the video app does not get sent to the
|
||||
// background in this case, the currently playing video (if there is one) is
|
||||
// not paused. And so, in the case of videos that require decoder hardware,
|
||||
// the view activity cannot play the video. For this workaround, we have set
|
||||
// a localStorage property here. The video.js file should receive an event
|
||||
// when we do that and will use that as a signal to unload its video. We use
|
||||
// Date.now() as the value of the property so we get a different value and
|
||||
// generate an event each time we run.
|
||||
//
|
||||
localStorage.setItem('view-activity-wants-to-use-hardware', Date.now());
|
||||
|
||||
// Register a handler to receive the Activity object
|
||||
navigator.mozSetMessageHandler('activity', handleViewActivity);
|
||||
|
||||
function handleViewActivity(request) {
|
||||
activity = request;
|
||||
data = activity.source.data;
|
||||
blob = data.blob;
|
||||
url = data.url;
|
||||
title = data.title || '';
|
||||
|
||||
// Set up the UI, if it is not already set up
|
||||
if (!dom.player) {
|
||||
initUI();
|
||||
}
|
||||
|
||||
// Hide save button
|
||||
dom.save.hidden = true;
|
||||
|
||||
// If blob exists, video should be launched by open activity
|
||||
if (blob) {
|
||||
// The title we display for this video may be explicitly specified,
|
||||
// or it might be the specified filename to save to or it might be
|
||||
// the filename of the blob.
|
||||
title = data.title || baseName(data.filename) || baseName(blob.name);
|
||||
url = URL.createObjectURL(blob);
|
||||
|
||||
// If the app that initiated this activity wants us to allow the
|
||||
// user to save this blob as a file, and if device storage is available
|
||||
// and if there is enough free space, then display a save button.
|
||||
if (data.allowSave && data.filename && checkFilename()) {
|
||||
getStorageIfAvailable('videos', blob.size, function(ds) {
|
||||
dom.save.hidden = false;
|
||||
|
||||
// HACK: Cause gaia-header to re-run font-fit logic
|
||||
// now that the 'save' button is visible.
|
||||
dom.videoTitle.textContent = dom.videoTitle.textContent;
|
||||
|
||||
storage = ds;
|
||||
});
|
||||
}
|
||||
|
||||
// to hide player because it shows in the wrong rotation.
|
||||
dom.player.classList.add('hidden');
|
||||
// video rotation is not parsed, parse it.
|
||||
getVideoRotation(blob, function(rotation) {
|
||||
// when error found, fallback to 0
|
||||
if (typeof rotation === 'string') {
|
||||
console.error('get video rotation error: ' + rotation);
|
||||
videoRotation = 0;
|
||||
} else {
|
||||
videoRotation = rotation;
|
||||
}
|
||||
// show player when player size and rotation are correct.
|
||||
dom.player.classList.remove('hidden');
|
||||
// start to play the video that showPlayer also calls fitContainer.
|
||||
showPlayer(url, title);
|
||||
});
|
||||
} else {
|
||||
// In the url case, we don't need to calculate the rotation.
|
||||
showPlayer(url, title);
|
||||
}
|
||||
}
|
||||
|
||||
// Terminate video playback when visibility is changed.
|
||||
window.addEventListener('visibilitychange',
|
||||
function onVisibilityChanged() {
|
||||
if (document.hidden) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// We get headphoneschange event when the headphones is plugged or unplugged
|
||||
var acm = navigator.mozAudioChannelManager;
|
||||
if (acm) {
|
||||
acm.addEventListener('headphoneschange', function onheadphoneschange() {
|
||||
if (!acm.headphones && playing) {
|
||||
setVideoPlaying(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setVideoPlaying(playing) {
|
||||
if (playing) {
|
||||
play();
|
||||
} else {
|
||||
pause();
|
||||
}
|
||||
}
|
||||
|
||||
function initUI() {
|
||||
// Fullscreen mode and inline activities don't seem to play well together
|
||||
// so we'll play the video without going into fullscreen mode.
|
||||
|
||||
// Get all the elements we use by their id
|
||||
var ids = ['player', 'player-view',
|
||||
'player-header', 'play', 'playHead', 'video-container',
|
||||
'elapsedTime', 'video-title', 'duration-text', 'elapsed-text',
|
||||
'slider-wrapper', 'spinner-overlay', 'timeSlider',
|
||||
'save', 'banner', 'message', 'seek-forward',
|
||||
'seek-backward', 'videoControlBar', 'in-use-overlay',
|
||||
'in-use-overlay-title', 'in-use-overlay-text'];
|
||||
|
||||
ids.forEach(function createElementRef(name) {
|
||||
dom[toCamelCase(name)] = document.getElementById(name);
|
||||
});
|
||||
|
||||
function toCamelCase(str) {
|
||||
return str.replace(/\-(.)/g, function replacer(str, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
seeker = new Seeker(dom.player);
|
||||
|
||||
dom.player.mozAudioChannelType = 'content';
|
||||
|
||||
// handling the dragging of slider
|
||||
dom.sliderWrapper.addEventListener('touchstart', handleSliderTouchStart);
|
||||
dom.sliderWrapper.addEventListener('touchmove', handleSliderTouchMove);
|
||||
dom.sliderWrapper.addEventListener('touchend', handleSliderTouchEnd);
|
||||
|
||||
// Rescale when window size changes. This should get called when
|
||||
// orientation changes.
|
||||
window.addEventListener('resize', function() {
|
||||
if (dom.player.readyState !== dom.player.HAVE_NOTHING) {
|
||||
VideoUtils.fitContainer(dom.videoContainer, dom.player,
|
||||
videoRotation || 0);
|
||||
}
|
||||
});
|
||||
|
||||
dom.player.addEventListener('timeupdate', timeUpdated);
|
||||
dom.player.addEventListener('seeked', updateSlider);
|
||||
|
||||
// showing + hiding the loading spinner
|
||||
dom.player.addEventListener('waiting', showSpinner);
|
||||
dom.player.addEventListener('playing', hideSpinner);
|
||||
dom.player.addEventListener('play', hideSpinner);
|
||||
dom.player.addEventListener('pause', hideSpinner);
|
||||
dom.player.addEventListener('ended', playerEnded);
|
||||
dom.player.addEventListener('canplaythrough', hideSpinner);
|
||||
|
||||
// option buttons
|
||||
dom.play.addEventListener('click', handlePlayButtonClick);
|
||||
dom.playerHeader.addEventListener('action', done);
|
||||
dom.save.addEventListener('click', save);
|
||||
// show/hide controls
|
||||
dom.videoContainer.addEventListener('click', toggleVideoControls);
|
||||
|
||||
// handle slider keypress, emitted by the screen reader
|
||||
dom.timeSlider.addEventListener('keypress', handleSliderKeypress);
|
||||
|
||||
ForwardRewindController.init(dom.player, dom.seekForward, dom.seekBackward,
|
||||
seeker);
|
||||
}
|
||||
|
||||
function checkFilename() {
|
||||
var dotIdx = data.filename.lastIndexOf('.');
|
||||
if (dotIdx > -1) {
|
||||
var ext = data.filename.substr(dotIdx + 1);
|
||||
return MimeMapper.guessTypeFromExtension(ext) === blob.type;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function setControlsVisibility(visible) {
|
||||
dom.playerView.classList[visible ? 'remove' : 'add'](
|
||||
'video-controls-hidden');
|
||||
controlShowing = visible;
|
||||
// Set the proper accessibility label for the video container based on
|
||||
// controls showing.
|
||||
dom.videoContainer.setAttribute('data-l10n-id', controlShowing ?
|
||||
'hide-controls-button' : 'show-controls-button');
|
||||
if (visible) {
|
||||
// update elapsed time and slider while showing.
|
||||
updateSlider();
|
||||
}
|
||||
}
|
||||
|
||||
function handlePlayButtonClick() {
|
||||
setVideoPlaying(dom.player.paused);
|
||||
}
|
||||
|
||||
function toggleVideoControls(e) {
|
||||
// When we change the visibility state of video controls, we need to check
|
||||
// the timeout of auto hiding.
|
||||
if (controlFadeTimeout) {
|
||||
clearTimeout(controlFadeTimeout);
|
||||
controlFadeTimeout = null;
|
||||
}
|
||||
// We cannot change the visibility state of video contorls when we are in
|
||||
// picking mode.
|
||||
e.cancelBubble = !controlShowing;
|
||||
setControlsVisibility(!controlShowing);
|
||||
}
|
||||
|
||||
function handleSliderTouchStart(event) {
|
||||
// If we have a touch start event, we don't need others.
|
||||
if (null != touchStartID) {
|
||||
return;
|
||||
}
|
||||
touchStartID = event.changedTouches[0].identifier;
|
||||
|
||||
dragging = true;
|
||||
// calculate the slider wrapper size for slider dragging.
|
||||
sliderRect = dom.sliderWrapper.getBoundingClientRect();
|
||||
|
||||
// We can't do anything if we don't know our duration
|
||||
if (dom.player.duration === Infinity) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleSliderTouchMove(event);
|
||||
}
|
||||
|
||||
function done() {
|
||||
pause();
|
||||
|
||||
// Release any video resources
|
||||
dom.player.removeAttribute('src');
|
||||
dom.player.load();
|
||||
|
||||
// End the activity
|
||||
activity.postResult({saved: saved});
|
||||
|
||||
// Undo the bug 1088456 workaround hack.
|
||||
localStorage.removeItem('view-activity-wants-to-use-hardware');
|
||||
}
|
||||
|
||||
function save() {
|
||||
// Hide the menu that holds the save button: we can only save once
|
||||
dom.save.hidden = true;
|
||||
// XXX work around bug 870619
|
||||
dom.videoTitle.textContent = dom.videoTitle.textContent;
|
||||
|
||||
getUnusedFilename(storage, data.filename, function(filename) {
|
||||
var savereq = storage.addNamed(blob, filename);
|
||||
savereq.onsuccess = function() {
|
||||
// Remember that it has been saved so we can pass this back
|
||||
// to the invoking app
|
||||
saved = filename;
|
||||
// And tell the user
|
||||
showBanner({
|
||||
id: 'saved',
|
||||
args: { title: title }
|
||||
});
|
||||
};
|
||||
savereq.onerror = function(e) {
|
||||
// XXX we don't report this to the user because it is hard to
|
||||
// localize.
|
||||
console.error('Error saving', filename, e);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// show video player
|
||||
function showPlayer(url, title) {
|
||||
function handleLoadedMetadata() {
|
||||
var formattedDuration = MediaUtils.formatDuration(dom.player.duration);
|
||||
dom.durationText.textContent = formattedDuration;
|
||||
timeUpdated();
|
||||
|
||||
setButtonPaused(false);
|
||||
VideoUtils.fitContainer(dom.videoContainer, dom.player,
|
||||
videoRotation || 0);
|
||||
|
||||
dom.player.currentTime = 0;
|
||||
|
||||
// Show the controls briefly then fade out
|
||||
controlFadeTimeout = setTimeout(function() {
|
||||
setControlsVisibility(false);
|
||||
}, 2000);
|
||||
|
||||
document.l10n.setAttributes(dom.timeSlider, 'seek-bar',
|
||||
{ duration: formattedDuration });
|
||||
dom.timeSlider.setAttribute('aria-valuemin', 0);
|
||||
dom.timeSlider.setAttribute('aria-valuemax', dom.player.duration);
|
||||
dom.timeSlider.setAttribute('aria-valuenow', dom.player.currentTime);
|
||||
dom.timeSlider.setAttribute('aria-valuetext',
|
||||
MediaUtils.formatDuration(dom.player.currentTime));
|
||||
|
||||
play();
|
||||
}
|
||||
|
||||
dom.videoTitle.textContent = title || '';
|
||||
setControlsVisibility(true);
|
||||
|
||||
var loadingChecker =
|
||||
new VideoLoadingChecker(dom.player, dom.inUseOverlay,
|
||||
dom.inUseOverlayTitle,
|
||||
dom.inUseOverlayText);
|
||||
loadingChecker.ensureVideoLoads(handleLoadedMetadata);
|
||||
|
||||
dom.player.src = url;
|
||||
|
||||
dom.player.onloadeddata = function(evt) { URL.revokeObjectURL(url); };
|
||||
dom.player.onerror = function(evt) {
|
||||
var errorid = '';
|
||||
|
||||
switch (evt.target.error.code) {
|
||||
case MediaError.MEDIA_ERR_ABORTED:
|
||||
// This aborted error should be triggered by the user
|
||||
// so we don't have to show any error messages
|
||||
return;
|
||||
case MediaError.MEDIA_ERR_NETWORK:
|
||||
errorid = 'error-network';
|
||||
break;
|
||||
case MediaError.MEDIA_ERR_DECODE:
|
||||
case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
|
||||
// If users tap some video link in an offline page
|
||||
// the error code will be MEDIA_ERR_SRC_NOT_SUPPORTED
|
||||
// we also prompt the unsupported error message for it
|
||||
errorid = 'error-unsupported';
|
||||
break;
|
||||
// Is it possible to be unknown errors?
|
||||
default:
|
||||
errorid = 'error-unknown';
|
||||
break;
|
||||
}
|
||||
|
||||
handleError(errorid);
|
||||
};
|
||||
}
|
||||
|
||||
function handleError(l10nId) {
|
||||
document.l10n.formatValue(l10nId).then((msg) => {
|
||||
alert(msg);
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
function play() {
|
||||
// Switch the button icon
|
||||
setButtonPaused(false);
|
||||
|
||||
// Start playing
|
||||
dom.player.play();
|
||||
playing = true;
|
||||
}
|
||||
|
||||
function pause() {
|
||||
// Switch the button icon
|
||||
setButtonPaused(true);
|
||||
|
||||
// Stop playing the video
|
||||
dom.player.pause();
|
||||
playing = false;
|
||||
}
|
||||
|
||||
// Update the progress bar and play head as the video plays
|
||||
function timeUpdated() {
|
||||
if (controlShowing) {
|
||||
// We can't update a progress bar if we don't know how long
|
||||
// the video is. It is kind of a bug that the <video> element
|
||||
// can't figure this out for ogv videos.
|
||||
if (dom.player.duration === Infinity || dom.player.duration === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateSlider();
|
||||
}
|
||||
|
||||
dom.timeSlider.setAttribute('aria-valuenow', dom.player.currentTime);
|
||||
dom.timeSlider.setAttribute('aria-valuetext',
|
||||
MediaUtils.formatDuration(dom.player.currentTime));
|
||||
}
|
||||
|
||||
function playerEnded() {
|
||||
if (dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are still playing when this 'ended' event arrives, then the
|
||||
// user played the video all the way to the end, and we skip to the
|
||||
// beginning and pause so it is easy for the user to restart. If we
|
||||
// reach the end because the user fast forwarded or dragged the slider
|
||||
// to the end, then we will have paused the video before we get this
|
||||
// event and in that case we will remain paused at the end of the video.
|
||||
if (playing) {
|
||||
dom.player.currentTime = 0;
|
||||
pause();
|
||||
}
|
||||
}
|
||||
|
||||
function setButtonPaused(paused) {
|
||||
dom.play.classList.toggle('paused', paused);
|
||||
dom.play.setAttribute('data-l10n-id',
|
||||
paused ? 'play-button' : 'pause-button');
|
||||
}
|
||||
|
||||
function movePlayHead(percent) {
|
||||
if (document.documentElement.dir === 'ltr') {
|
||||
dom.playHead.style.left = percent;
|
||||
}
|
||||
else {
|
||||
dom.playHead.style.right = percent;
|
||||
}
|
||||
}
|
||||
|
||||
function updateSlider() {
|
||||
// We update the slider when we get a 'seeked' event.
|
||||
// Don't do updates while we're seeking because the position we fastSeek()
|
||||
// to probably isn't exactly where we requested and we don't want jerky
|
||||
// updates
|
||||
if (dom.player.seeking) {
|
||||
return;
|
||||
}
|
||||
|
||||
var percent = (dom.player.currentTime / dom.player.duration) * 100;
|
||||
if (isNaN(percent)) { // this happens when we end the activity
|
||||
return;
|
||||
}
|
||||
percent += '%';
|
||||
|
||||
dom.elapsedText.textContent = MediaUtils.formatDuration(
|
||||
dom.player.currentTime);
|
||||
dom.elapsedTime.style.width = percent;
|
||||
// Don't move the play head if the user is dragging it.
|
||||
if (!dragging) {
|
||||
movePlayHead(percent);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSliderTouchEnd(event) {
|
||||
// We don't care the event not related to touchStartID
|
||||
if (!event.changedTouches.identifiedTouch(touchStartID)) {
|
||||
return;
|
||||
}
|
||||
touchStartID = null;
|
||||
|
||||
if (!dragging) {
|
||||
// We don't need to do anything without dragging.
|
||||
return;
|
||||
}
|
||||
|
||||
dragging = false;
|
||||
|
||||
dom.playHead.classList.remove('active');
|
||||
|
||||
// We didn't move the play head while the user's finger was on it
|
||||
// so now it may be in the wrong place.
|
||||
updateSlider();
|
||||
|
||||
if (dom.player.currentTime === dom.player.duration) {
|
||||
pause();
|
||||
}
|
||||
}
|
||||
|
||||
function handleSliderTouchMove(event) {
|
||||
if (!dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
var touch = event.changedTouches.identifiedTouch(touchStartID);
|
||||
// We don't care the event not related to touchStartID
|
||||
if (!touch) {
|
||||
return;
|
||||
}
|
||||
|
||||
function getTouchPos() {
|
||||
return (document.documentElement.dir === 'ltr') ?
|
||||
(touch.clientX - sliderRect.left) :
|
||||
(sliderRect.right - touch.clientX);
|
||||
}
|
||||
|
||||
var touchPos = getTouchPos();
|
||||
|
||||
var pos = touchPos / sliderRect.width;
|
||||
pos = Math.max(pos, 0);
|
||||
pos = Math.min(pos, 1);
|
||||
|
||||
// Update the slider to match the position of the user's finger.
|
||||
// Note, however, that we don't update the displayed time until
|
||||
// we actually get a 'seeked' event.
|
||||
var percent = pos * 100 + '%';
|
||||
dom.playHead.classList.add('active');
|
||||
movePlayHead(percent);
|
||||
dom.elapsedTime.style.width = percent;
|
||||
seeker.seekTo(dom.player.duration * pos);
|
||||
}
|
||||
|
||||
function handleSliderKeypress(event) {
|
||||
// The standard accessible control for sliders is arrow up/down keys.
|
||||
// Our screenreader synthesizes those events on swipe up/down gestures.
|
||||
// Currently, we only allow screen reader users to adjust sliders with a
|
||||
// constant step size (there is no page up/down equivalent). In the case
|
||||
// of videos, we make sure that the maximum amount of steps for the entire
|
||||
// duration is 20, or 2 second increments if the duration is less then 40
|
||||
// seconds.
|
||||
var step = Math.max(dom.player.duration / 20, 2);
|
||||
if (event.keyCode === event.DOM_VK_DOWN) {
|
||||
seeker.seekTo(dom.player.currentTime - step);
|
||||
} else if (event.keyCode === event.DOM_VK_UP) {
|
||||
seeker.seekTo(dom.player.currentTime + step);
|
||||
}
|
||||
}
|
||||
|
||||
function showBanner(l10n) {
|
||||
document.l10n.setAttributes(
|
||||
dom.message,
|
||||
l10n.id,
|
||||
l10n.args);
|
||||
dom.banner.hidden = false;
|
||||
setTimeout(function() {
|
||||
dom.banner.hidden = true;
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Strip directories and just return the base filename
|
||||
function baseName(filename) {
|
||||
if (!filename) {
|
||||
return '';
|
||||
}
|
||||
return filename.substring(filename.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
function setDisabled(element, disabled) {
|
||||
element.classList.toggle('disabled', disabled);
|
||||
|
||||
// Set ARIA disabled attribute to maintain semantic meaning for the
|
||||
// assistive technologies like screen reader.
|
||||
element.setAttribute('aria-disabled', disabled);
|
||||
}
|
||||
|
||||
function showSpinner() {
|
||||
if (!blob) {
|
||||
dom.spinnerOverlay.classList.remove('hidden');
|
||||
setDisabled(dom.playerView, true);
|
||||
}
|
||||
}
|
||||
|
||||
function hideSpinner() {
|
||||
dom.spinnerOverlay.classList.add('hidden');
|
||||
setDisabled(dom.playerView, false);
|
||||
}
|
||||
})();
|
||||
Loading…
Reference in a new issue