Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
377 lines
9 KiB
JavaScript
Executable file
377 lines
9 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
/* global AppWindowManager */
|
|
|
|
var StackManager = {
|
|
init: function sm_init() {
|
|
window.addEventListener('appcreated', this);
|
|
window.addEventListener('launchapp', this);
|
|
window.addEventListener('appopening', this);
|
|
window.addEventListener('appterminated', this);
|
|
window.addEventListener('home', this);
|
|
window.addEventListener('cardviewclosed', this);
|
|
},
|
|
|
|
getCurrent: function sm_getCurrent() {
|
|
if (this.position < 0) {
|
|
return undefined;
|
|
}
|
|
|
|
var app = this._currentFromStack();
|
|
|
|
// XXX: This code will be removed when bug 967405 lands.
|
|
// Until then we can get into edge cases where the app currently
|
|
// displayed is not part of the stack and we don't want to break.
|
|
if (!app) {
|
|
app = AppWindowManager.getActiveApp();
|
|
}
|
|
|
|
return app;
|
|
},
|
|
|
|
_currentFromStack: function sm_currentInStack() {
|
|
return this._stack[this.position].getActiveWindow();
|
|
},
|
|
outOfStack: function sm_outOfStack() {
|
|
return (this._currentFromStack() !== this.getCurrent());
|
|
},
|
|
|
|
getPrev: function sm_getPrev() {
|
|
if (this.outOfStack()) {
|
|
return undefined;
|
|
}
|
|
|
|
var inGroupPrev = this._currentFromStack().getPrev();
|
|
if (inGroupPrev) {
|
|
return inGroupPrev;
|
|
}
|
|
var previousGroup = this._stack[this.position - 1];
|
|
if (previousGroup) {
|
|
return previousGroup.getLeafWindow();
|
|
}
|
|
|
|
return undefined;
|
|
},
|
|
|
|
getNext: function sm_getNext() {
|
|
if (this.outOfStack()) {
|
|
return undefined;
|
|
}
|
|
|
|
var inGroupNext = this._currentFromStack().getNext();
|
|
if (inGroupNext) {
|
|
return inGroupNext;
|
|
}
|
|
var nextGroup = this._stack[this.position + 1];
|
|
if (nextGroup) {
|
|
return nextGroup.getRootWindow();
|
|
}
|
|
|
|
return undefined;
|
|
},
|
|
|
|
goPrev: function sm_goPrev() {
|
|
var oldApp = this.getCurrent();
|
|
var newApp = this.getPrev();
|
|
if (!oldApp || !newApp) {
|
|
return;
|
|
}
|
|
|
|
this._queueBroadcast(newApp, oldApp);
|
|
|
|
if (newApp.groupID !== oldApp.groupID) {
|
|
this.position--;
|
|
}
|
|
},
|
|
|
|
goNext: function sm_goNext() {
|
|
var oldApp = this.getCurrent();
|
|
var newApp = this.getNext();
|
|
if (!oldApp || !newApp) {
|
|
return;
|
|
}
|
|
|
|
this._queueBroadcast(newApp, oldApp);
|
|
|
|
if (newApp.groupID !== oldApp.groupID) {
|
|
this.position++;
|
|
}
|
|
},
|
|
|
|
commit: function sm_commit() {
|
|
// We're back to the same place, let's close up the gesture without
|
|
// queueing.
|
|
if (this._didntMove) {
|
|
window.dispatchEvent(new CustomEvent('sheets-gesture-end'));
|
|
}
|
|
if (!this._broadcastTimeout) {
|
|
this._broadcast();
|
|
}
|
|
},
|
|
|
|
commitClose: function sm_commitClose() {
|
|
// TODO: make this transition pretty but at least we're
|
|
// fixing the race condition
|
|
clearTimeout(this._broadcastTimeout);
|
|
this._broadcastTimeout = null;
|
|
|
|
if (this._appIn) {
|
|
this._appIn.broadcast('closed');
|
|
}
|
|
|
|
if (this._appOut) {
|
|
this._appOut.transitionController.clearTransitionClasses();
|
|
}
|
|
|
|
this._cleanUp();
|
|
},
|
|
|
|
snapshot: function sm_snapshot() {
|
|
return this._stack.slice(0);
|
|
},
|
|
|
|
get length() {
|
|
return this._stack.length;
|
|
},
|
|
|
|
get position() {
|
|
return this._current;
|
|
},
|
|
|
|
get _didntMove() {
|
|
return !!this._appIn && this._appIn === this._appOut;
|
|
},
|
|
|
|
set position(position) {
|
|
var _position = parseInt(position);
|
|
if (_position < -1 || _position >= this._stack.length) {
|
|
console.warn('bad stack position. requested position = ',
|
|
_position, '; stack length = ', this._stack.length);
|
|
return;
|
|
}
|
|
|
|
this._current = _position;
|
|
},
|
|
|
|
_stack: [],
|
|
_current: -1,
|
|
|
|
handleEvent: function sm_handleEvent(e) {
|
|
switch (e.type) {
|
|
case 'appcreated':
|
|
var app = e.detail;
|
|
// Multiple apps use role=system to opt out of being part of
|
|
// the the card view.
|
|
// XXX: This code will be removed when bug 967405 lands.
|
|
if (app.manifest && app.manifest.role == 'system') {
|
|
return;
|
|
}
|
|
|
|
// The FTU application should never show up in the stack.
|
|
// XXX: This code will be removed when bug 967405 lands.
|
|
if (app.name == 'FTU') {
|
|
return;
|
|
}
|
|
|
|
// If the app is a child window of other window, do not insert it.
|
|
if (app.previousWindow) {
|
|
return;
|
|
}
|
|
|
|
if (app.stayBackground) {
|
|
this._insertBelow(app);
|
|
} else {
|
|
this._moveToTop(this.position);
|
|
this._insertOnTop(app);
|
|
}
|
|
break;
|
|
case 'launchapp':
|
|
var config = e.detail;
|
|
if (!config.stayBackground) {
|
|
this._moveToTop(this.position);
|
|
|
|
var idx = this._indexOfURL(config.url);
|
|
if (idx !== undefined) {
|
|
this._moveToTop(idx);
|
|
}
|
|
}
|
|
break;
|
|
case 'appopening':
|
|
var app = e.detail; // jshint ignore: line
|
|
var root = app.getRootWindow();
|
|
|
|
var id = this._indexOfInstanceID(root.instanceID);
|
|
if (id !== undefined && id !== this._current) {
|
|
this._current = id;
|
|
}
|
|
break;
|
|
case 'home':
|
|
this._moveToTop(this.position);
|
|
this.position = -1;
|
|
this.commitClose();
|
|
break;
|
|
case 'appterminated':
|
|
var instanceID = e.detail.instanceID;
|
|
this._remove(instanceID);
|
|
break;
|
|
case 'cardviewclosed':
|
|
if (e.detail && e.detail.newStackPosition) {
|
|
this.position = e.detail.newStackPosition;
|
|
}
|
|
break;
|
|
}
|
|
this._stackChanged();
|
|
},
|
|
|
|
_insertBelow: function sm_insertBelow(app) {
|
|
this._stack.splice(0, 0, app);
|
|
if (this._stack.length > 1) {
|
|
this.position++;
|
|
} else {
|
|
this.position = 0;
|
|
}
|
|
},
|
|
|
|
_insertOnTop: function sm_insertOnTop(app) {
|
|
this.position = this._stack.push(app) - 1;
|
|
},
|
|
|
|
_moveToTop: function sm_moveToTop(index) {
|
|
if (index === -1 || index >= this._stack.length) {
|
|
return;
|
|
}
|
|
|
|
var sheet = this._stack.splice(index, 1)[0];
|
|
this.position = this._stack.push(sheet) - 1;
|
|
},
|
|
|
|
_indexOfURL: function sm_indexOfURL(url) {
|
|
var result;
|
|
this._stack.some(function(app, idx) {
|
|
if (app.url == url) {
|
|
result = idx;
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
return result;
|
|
},
|
|
|
|
_indexOfInstanceID: function sm_indexOfIntanceID(instanceID) {
|
|
var result;
|
|
this._stack.some(function(app, idx) {
|
|
if (app.instanceID == instanceID) {
|
|
result = idx;
|
|
return true;
|
|
} false;
|
|
});
|
|
|
|
return result;
|
|
},
|
|
|
|
_remove: function sm_remove(instanceID) {
|
|
for (var i = (this._stack.length - 1); i >= 0; i--) {
|
|
var sConfig = this._stack[i];
|
|
|
|
if (sConfig.instanceID == instanceID) {
|
|
this._stack.splice(i, 1);
|
|
|
|
if (i <= this.position && this.position >= 0) {
|
|
this.position--;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
},
|
|
|
|
_stackChanged: function sm_stackChanged() {
|
|
var details = {
|
|
position: this.position,
|
|
sheets: this._stack
|
|
};
|
|
|
|
var evt = new CustomEvent('stackchanged', { detail: details });
|
|
|
|
window.dispatchEvent(evt);
|
|
},
|
|
|
|
_broadcastTimeout: null,
|
|
_appIn: null,
|
|
_appOut: null,
|
|
_queueBroadcast: function sm_queueBroadcast(appIn, appOut) {
|
|
if (this._appIn) {
|
|
this._appIn.cancelQueuedShow();
|
|
}
|
|
this._appIn = appIn;
|
|
appIn.queueShow();
|
|
|
|
if (!this._appOut) {
|
|
this._appOut = appOut;
|
|
appOut.queueHide();
|
|
}
|
|
|
|
if (this._broadcastTimeout === null) {
|
|
AppWindowManager.sendStopRecordingRequest();
|
|
}
|
|
|
|
clearTimeout(this._broadcastTimeout);
|
|
this._broadcastTimeout = setTimeout(this._broadcast.bind(this), 800);
|
|
},
|
|
|
|
_broadcast: function sm_broadcast(close) {
|
|
clearTimeout(this._broadcastTimeout);
|
|
this._broadcastTimeout = null;
|
|
|
|
// We're back to the same place
|
|
if (this._didntMove) {
|
|
this._appIn.transitionController.clearTransitionClasses();
|
|
this._cleanUp();
|
|
return;
|
|
}
|
|
|
|
// We're done swiping around, let's close up the gesture. Note that
|
|
// sheets-gesture-start is detected and sent in SheetsTransition!!!
|
|
window.dispatchEvent(new CustomEvent('sheets-gesture-end'));
|
|
|
|
if (this._appIn) {
|
|
this._appIn.broadcast('swipein');
|
|
}
|
|
if (this._appOut) {
|
|
this._appOut.broadcast('swipeout');
|
|
}
|
|
|
|
this._cleanUp();
|
|
|
|
this._stackChanged();
|
|
},
|
|
|
|
|
|
_cleanUp: function sm_cleanUp() {
|
|
this._appIn = null;
|
|
this._appOut = null;
|
|
},
|
|
|
|
/* Debug */
|
|
_dump: function sm_dump() {
|
|
var prefix = 'StackManager';
|
|
for (var i = 0; i < this._stack.length; i++) {
|
|
var separator = (i == this.position) ? ' * ' : ' - ';
|
|
console.log(prefix + separator + i + ' -> ' + this._stack[i].name +
|
|
'/' + this._stack[i].instanceID);
|
|
var child = this._stack[i].nextWindow;
|
|
while (child) {
|
|
var separator2 = (child.isActive()) ? ' @ ' : ' = ';
|
|
console.log(prefix + separator2 + i + ' ---> ' + this._stack[i].name +
|
|
'/' + this._stack[i].instanceID);
|
|
child = child.nextWindow;
|
|
}
|
|
}
|
|
},
|
|
__clearAll: function sm_clearAll() {
|
|
this._stack = [];
|
|
this.position = 0;
|
|
}
|
|
};
|
|
|
|
StackManager.init();
|