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
305
gaia/apps/music/elements/music-view-stack.js
Executable file
305
gaia/apps/music/elements/music-view-stack.js
Executable file
|
|
@ -0,0 +1,305 @@
|
|||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
var proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
|
||||
proto.createdCallback = function() {
|
||||
var style = document.createElement('style');
|
||||
style.innerHTML =
|
||||
`music-view-stack {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
music-view-stack > iframe {
|
||||
border: none;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
will-change: transform;
|
||||
}
|
||||
music-view-stack > iframe.active,
|
||||
music-view-stack > iframe.fade,
|
||||
music-view-stack > iframe.pop,
|
||||
music-view-stack > iframe.push {
|
||||
animation-duration: 0.2s;
|
||||
animation-timing-function: linear;
|
||||
visibility: visible;
|
||||
}
|
||||
music-view-stack > iframe.active {
|
||||
pointer-events: auto;
|
||||
}
|
||||
music-view-stack > iframe.fade {
|
||||
animation-duration: 0s;
|
||||
}
|
||||
music-view-stack > iframe.fade.in {
|
||||
animation-name: fade-in;
|
||||
}
|
||||
music-view-stack > iframe.fade.out {
|
||||
animation-name: fade-out;
|
||||
}
|
||||
music-view-stack > iframe.pop.in {
|
||||
animation-name: pop-in;
|
||||
}
|
||||
music-view-stack > iframe.pop.out {
|
||||
animation-name: pop-out;
|
||||
}
|
||||
music-view-stack > iframe.push.in {
|
||||
animation-name: push-in;
|
||||
}
|
||||
music-view-stack > iframe.push.out {
|
||||
animation-name: push-out;
|
||||
}
|
||||
[dir="rtl"] music-view-stack > iframe.pop.in {
|
||||
animation-name: push-in;
|
||||
}
|
||||
[dir="rtl"] music-view-stack > iframe.pop.out {
|
||||
animation-name: push-out;
|
||||
}
|
||||
[dir="rtl"] music-view-stack > iframe.push.in {
|
||||
animation-name: pop-in;
|
||||
}
|
||||
[dir="rtl"] music-view-stack > iframe.push.out {
|
||||
animation-name: pop-out;
|
||||
}
|
||||
@keyframes fade-in {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
@keyframes fade-out {
|
||||
0% { opacity: 1; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
@keyframes pop-in {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(0); }
|
||||
}
|
||||
@keyframes pop-out {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
@keyframes push-in {
|
||||
0% { transform: translateX(100%); }
|
||||
100% { transform: translateX(0); }
|
||||
}
|
||||
@keyframes push-out {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-100%); }
|
||||
}`;
|
||||
|
||||
if (this.firstChild) {
|
||||
this.insertBefore(style, this.firstChild);
|
||||
} else {
|
||||
this.appendChild(style);
|
||||
}
|
||||
|
||||
this.cachedViews = {};
|
||||
|
||||
[].forEach.call(this.querySelectorAll('iframe'), (frame) => {
|
||||
var location = new URL(frame.src);
|
||||
|
||||
var view = {
|
||||
url: location.pathname + location.search,
|
||||
frame: frame,
|
||||
title: frame.contentDocument.title
|
||||
};
|
||||
|
||||
this.initializeView(view).then((view) => {
|
||||
if (this.activeView === view) {
|
||||
this.dispatchEvent(new CustomEvent('titlechange', {
|
||||
detail: view.title
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
this.cachedViews[view.url] = view;
|
||||
});
|
||||
|
||||
this.addEventListener('animationend', (evt) => {
|
||||
var classList = evt.target.classList;
|
||||
if (classList.contains('destroy')) {
|
||||
this.destroyView(evt.target.src);
|
||||
}
|
||||
|
||||
classList.remove('fade', 'pop', 'push', 'in', 'out');
|
||||
});
|
||||
|
||||
this.views = [];
|
||||
this.activeView = null;
|
||||
};
|
||||
|
||||
proto.loadView = function(url) {
|
||||
var view = this.cachedViews[url];
|
||||
if (view) {
|
||||
return Promise.resolve(view);
|
||||
}
|
||||
|
||||
var frame = document.createElement('iframe');
|
||||
this.appendChild(frame);
|
||||
frame.src = url;
|
||||
|
||||
view = {
|
||||
url: url,
|
||||
frame: frame,
|
||||
title: ''
|
||||
};
|
||||
|
||||
return this.initializeView(view);
|
||||
};
|
||||
|
||||
proto.initializeView = function(view) {
|
||||
view.frame.addEventListener('rendered', () => {
|
||||
this.dispatchEvent(new CustomEvent('rendered'));
|
||||
});
|
||||
|
||||
view.frame.addEventListener('titlechange', (evt) => {
|
||||
view.title = evt.detail;
|
||||
|
||||
if (this.activeView === view) {
|
||||
this.dispatchEvent(new CustomEvent('titlechange', {
|
||||
detail: evt.detail
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const onL10nReady = () => {
|
||||
view.title = view.frame.contentDocument.title;
|
||||
this.cachedViews[view.url] = view;
|
||||
this.dispatchEvent(new CustomEvent('loaded'));
|
||||
return view;
|
||||
};
|
||||
|
||||
return loaded(view.frame)
|
||||
.then(() => view.frame.contentDocument.l10n.ready)
|
||||
.then(onL10nReady);
|
||||
};
|
||||
|
||||
proto.destroyView = function(url) {
|
||||
for (var key in this.cachedViews) {
|
||||
var view = this.cachedViews[key];
|
||||
if (key === url || view.frame.src === url) {
|
||||
view.frame.contentWindow.dispatchEvent(new CustomEvent('viewdestroy'));
|
||||
delete this.cachedViews[key];
|
||||
|
||||
setTimeout(() => this.removeChild(view.frame), 5000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
proto.setRootView = function(url) {
|
||||
return this.loadView(url).then((view) => {
|
||||
var oldActiveView = this.activeView;
|
||||
if (oldActiveView) {
|
||||
oldActiveView.frame.classList.add('fade', 'out');
|
||||
oldActiveView.frame.classList.remove('active');
|
||||
oldActiveView.frame.contentWindow.dispatchEvent(
|
||||
new CustomEvent('viewhidden')
|
||||
);
|
||||
}
|
||||
|
||||
this.views = [view];
|
||||
this.activeView = view;
|
||||
|
||||
var newActiveView = this.activeView;
|
||||
newActiveView.frame.classList.add('fade', 'in');
|
||||
newActiveView.frame.classList.add('active');
|
||||
newActiveView.frame.contentWindow.dispatchEvent(
|
||||
new CustomEvent('viewvisible')
|
||||
);
|
||||
|
||||
this.dispatchEvent(new CustomEvent('change', { detail: newActiveView }));
|
||||
});
|
||||
};
|
||||
|
||||
proto.pushView = function(url) {
|
||||
if (!this.activeView) {
|
||||
return this.setRootView(url);
|
||||
}
|
||||
|
||||
return this.loadView(url).then((view) => {
|
||||
requestAnimationFrame(() => {
|
||||
var oldActiveView = this.activeView;
|
||||
oldActiveView.frame.classList.add('push', 'out');
|
||||
oldActiveView.frame.classList.remove('active');
|
||||
oldActiveView.frame.contentWindow.dispatchEvent(
|
||||
new CustomEvent('viewhidden')
|
||||
);
|
||||
|
||||
this.views.push(view);
|
||||
this.activeView = view;
|
||||
|
||||
var newActiveView = this.activeView;
|
||||
newActiveView.frame.classList.add('push', 'in', 'active');
|
||||
newActiveView.frame.contentWindow.dispatchEvent(
|
||||
new CustomEvent('viewvisible')
|
||||
);
|
||||
|
||||
this.dispatchEvent(new CustomEvent('change', { detail: newActiveView }));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
proto.popView = function(destroy) {
|
||||
if (this.views.length < 2) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
var oldActiveView = this.activeView;
|
||||
|
||||
this.views.pop();
|
||||
this.activeView = this.views[this.views.length - 1];
|
||||
|
||||
var newActiveView = this.activeView;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
requestAnimationFrame(() => {
|
||||
oldActiveView.frame.classList.add('pop', 'out');
|
||||
oldActiveView.frame.classList.remove('active');
|
||||
oldActiveView.frame.contentWindow.dispatchEvent(
|
||||
new CustomEvent('viewhidden')
|
||||
);
|
||||
|
||||
if (destroy) {
|
||||
oldActiveView.frame.classList.add('destroy');
|
||||
}
|
||||
|
||||
newActiveView.frame.classList.add('pop', 'in', 'active');
|
||||
newActiveView.frame.contentWindow.dispatchEvent(
|
||||
new CustomEvent('viewvisible')
|
||||
);
|
||||
|
||||
this.dispatchEvent(new CustomEvent('change', { detail: newActiveView }));
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
window.MusicViewStack = document.registerElement('music-view-stack', {
|
||||
prototype: proto
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.name !== 'NotSupportedError') {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function loaded(frame) {
|
||||
return new Promise((resolve) => {
|
||||
frame.addEventListener('load', function onLoad() {
|
||||
frame.removeEventListener('load', onLoad);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
})(window);
|
||||
Loading…
Reference in a new issue