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
54
gaia/apps/bookmark/js/activity_handler.js
Executable file
54
gaia/apps/bookmark/js/activity_handler.js
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
'use strict';
|
||||
|
||||
/* global utils, BookmarkEditor, BookmarkRemover */
|
||||
var ActivityHandler = {
|
||||
'save-bookmark': function ah_save(activity) {
|
||||
BookmarkEditor.init({
|
||||
data: activity.source.data,
|
||||
onsaved: function onsaved(saved) {
|
||||
window.addEventListener('status-hidden', function hidden() {
|
||||
window.removeEventListener('status-hidden', hidden);
|
||||
activity.postResult(saved ? 'saved' : 'updated');
|
||||
});
|
||||
|
||||
var msg = saved ?
|
||||
'added-to-home-screen-message' : 'updated-pinned-site';
|
||||
navigator.mozL10n.formatValue(msg)
|
||||
.then(msg => utils.status.show(msg));
|
||||
},
|
||||
oncancelled: function oncancelled() {
|
||||
activity.postError('cancelled');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
'remove-bookmark': function ah_remove(activity) {
|
||||
BookmarkRemover.init({
|
||||
id: activity.source.data.url,
|
||||
onremoved: function onremoved() {
|
||||
activity.postResult('removed');
|
||||
},
|
||||
oncancelled: function oncancelled(e) {
|
||||
activity.postError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
navigator.mozSetMessageHandler('activity', function onActivity(activity) {
|
||||
var name = activity.source.name;
|
||||
switch (name) {
|
||||
case 'save-bookmark':
|
||||
case 'remove-bookmark':
|
||||
if (activity.source.data.type === 'url') {
|
||||
ActivityHandler[name](activity);
|
||||
} else {
|
||||
activity.postError('type not supported');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
activity.postError('name not supported');
|
||||
}
|
||||
});
|
||||
175
gaia/apps/bookmark/js/bookmark_editor.js
Executable file
175
gaia/apps/bookmark/js/bookmark_editor.js
Executable file
|
|
@ -0,0 +1,175 @@
|
|||
'use strict';
|
||||
|
||||
/* global UrlHelper, BookmarksDatabase, Icon */
|
||||
/* exported BookmarkEditor */
|
||||
|
||||
var BookmarkEditor = {
|
||||
BOOKMARK_ICON_SIZE: 60,
|
||||
APP_ICON_SIZE: 60,
|
||||
|
||||
init: function bookmarkEditor_show(options) {
|
||||
this.data = options.data;
|
||||
this.onsaved = options.onsaved;
|
||||
this.oncancelled = options.oncancelled;
|
||||
var mode = 'add';
|
||||
BookmarksDatabase.get(this.data.url).then((function got(bookmark) {
|
||||
if (bookmark) {
|
||||
this.data = bookmark;
|
||||
mode = 'put';
|
||||
}
|
||||
this._init(mode);
|
||||
}).bind(this), this._init.bind(this, mode));
|
||||
},
|
||||
|
||||
_init: function bookmarkEditor_init(mode) {
|
||||
this.mode = document.body.dataset.mode = mode;
|
||||
this.bookmarkTitle = document.getElementById('bookmark-title');
|
||||
this.bookmarkURL = document.getElementById('bookmark-url');
|
||||
this.bookmarkIcon = document.getElementById('bookmark-icon');
|
||||
this.header = document.getElementById('header');
|
||||
this.saveButton = document.getElementById('done-button');
|
||||
this.appInstallationSection = document.getElementById('app-installation');
|
||||
this.appIcon = document.getElementById('app-icon');
|
||||
this.appIconPlaceholder = document.getElementById('app-icon-placeholder');
|
||||
this.appNameText = document.getElementById('app-name');
|
||||
this.installAppButton = document.getElementById('install-app-button');
|
||||
|
||||
this.header.addEventListener('action', this.close.bind(this));
|
||||
this.saveListener = this.save.bind(this);
|
||||
this.saveButton.addEventListener('click', this.saveListener);
|
||||
|
||||
this.bookmarkTitle.value = this.data.name || '';
|
||||
|
||||
this.bookmarkURL.textContent = this.data.url;
|
||||
|
||||
this._renderIcon();
|
||||
|
||||
if (this.data.manifestURL) {
|
||||
this.manifestURL = this.data.manifestURL;
|
||||
this._fetchManifest(this.manifestURL);
|
||||
}
|
||||
|
||||
this._checkDoneButton();
|
||||
this.form = document.getElementById('bookmark-form');
|
||||
this.form.addEventListener('input', this._checkDoneButton.bind(this));
|
||||
this.form.addEventListener('submit', this._submit.bind(this));
|
||||
var touchstart = 'ontouchstart' in window ? 'touchstart' : 'mousedown';
|
||||
this.clearButton = document.getElementById('bookmark-title-clear');
|
||||
this.clearButton.addEventListener(touchstart, this._clearTitle.bind(this));
|
||||
if (mode === 'put') {
|
||||
this._onEditMode();
|
||||
this.saveButton.setAttribute('data-l10n-id', 'done-action');
|
||||
} else {
|
||||
this.saveButton.setAttribute('data-l10n-id', 'add-action');
|
||||
}
|
||||
|
||||
// We're appending new elements to DOM so to make sure headers are
|
||||
// properly resized and centered, we emmit a lazyload event.
|
||||
// This will be removed when the gaia-header web component lands.
|
||||
window.dispatchEvent(new CustomEvent('lazyload', {
|
||||
detail: document.body
|
||||
}));
|
||||
},
|
||||
|
||||
_renderIcon: function renderIcon() {
|
||||
var icon = new Icon(this.bookmarkIcon, this.data.icon);
|
||||
icon.render({'size': this.BOOKMARK_ICON_SIZE});
|
||||
},
|
||||
|
||||
_renderAppIcon: function renderAppIcon(manifest, size) {
|
||||
// Parse icon URL from app manifest
|
||||
var iconURL = window.IconsHelper.getBestIconFromWebManifest(manifest, size);
|
||||
if (!iconURL) {
|
||||
return;
|
||||
}
|
||||
// Switch out placeholder for real icon when it loads
|
||||
this.appIconListener = this._handleAppIconLoad.bind(this);
|
||||
this.appIcon.addEventListener('load', this.appIconListener);
|
||||
this.appIcon.setAttribute('src', iconURL);
|
||||
},
|
||||
|
||||
_handleAppIconLoad: function _handleAppIconLoad() {
|
||||
this.appIconPlaceholder.classList.add('hidden');
|
||||
this.appIcon.classList.remove('hidden');
|
||||
this.appIcon.removeEventListener('load', this.appIconListener);
|
||||
},
|
||||
|
||||
_fetchManifest: function bookmarkEditor_fetchManifest(manifestURL) {
|
||||
var manifestPromise = window.WebManifestHelper.getManifest(manifestURL);
|
||||
|
||||
manifestPromise.then((function(manifestData) {
|
||||
if (manifestData) {
|
||||
this.installAppButtonListener = this._installApp.bind(this);
|
||||
this.installAppButton.addEventListener('click',
|
||||
this.installAppButtonListener);
|
||||
this.appNameText.textContent = manifestData.short_name ||
|
||||
manifestData.name;
|
||||
this.appInstallationSection.classList.remove('hidden');
|
||||
this._renderAppIcon(manifestData, manifestURL, this.APP_ICON_SIZE);
|
||||
}
|
||||
}).bind(this)).catch(function(error) {
|
||||
console.error('Unable to get web manifest: ' + error);
|
||||
});
|
||||
|
||||
return manifestPromise;
|
||||
},
|
||||
|
||||
_onEditMode: function bookmarkEditor_onEditMode() {
|
||||
// Done button will be disabled on edit mode once it is displayed
|
||||
this.saveButton.disabled = true;
|
||||
},
|
||||
|
||||
close: function bookmarkEditor_close() {
|
||||
this.oncancelled();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the submit case for the form when the user presses the enter key.
|
||||
* @param {Event} event The form submit event.
|
||||
*/
|
||||
_submit: function(event) {
|
||||
event.preventDefault();
|
||||
if (!this.saveButton.disabled) {
|
||||
this.save();
|
||||
}
|
||||
},
|
||||
|
||||
_clearTitle: function bookmarkEditor_clearTitle(event) {
|
||||
event.preventDefault();
|
||||
this.bookmarkTitle.value = '';
|
||||
this._checkDoneButton();
|
||||
},
|
||||
|
||||
_checkDoneButton: function bookmarkEditor_checkDoneButton() {
|
||||
// If one of the fields is blank, the “Done” button should be dimmed and
|
||||
// inactive
|
||||
var title = this.bookmarkTitle.value.trim();
|
||||
this.saveButton.disabled = title === '';
|
||||
},
|
||||
|
||||
_installApp: function bookmarkEditor_installApp() {
|
||||
window.navigator.mozApps.install(this.manifestURL);
|
||||
},
|
||||
|
||||
save: function bookmarkEditor_save(evt) {
|
||||
this.saveButton.removeEventListener('click', this.saveListener);
|
||||
if (this.installAppButtonListener) {
|
||||
this.installAppButton.removeEventListener('click',
|
||||
this.installAppButtonListener);
|
||||
}
|
||||
|
||||
// Only allow urls to be bookmarked.
|
||||
// This is defensive check - callers should filter out non-URLs.
|
||||
var url = this.data.url.trim();
|
||||
if (UrlHelper.isNotURL(url)) {
|
||||
this.oncancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
this.data.name = this.bookmarkTitle.value;
|
||||
this.data.url = url;
|
||||
|
||||
BookmarksDatabase[this.mode](this.data).then(this.onsaved.bind(this),
|
||||
this.close.bind(this));
|
||||
}
|
||||
};
|
||||
39
gaia/apps/bookmark/js/bookmark_remover.js
Executable file
39
gaia/apps/bookmark/js/bookmark_remover.js
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
'use strict';
|
||||
|
||||
/* global BookmarksDatabase */
|
||||
/* exported BookmarkRemover */
|
||||
var BookmarkRemover = {
|
||||
init: function bookmarkRemover_init(data) {
|
||||
var id = data.id;
|
||||
BookmarksDatabase.get(id).then(function got(bookmark) {
|
||||
if (!bookmark) {
|
||||
data.oncancelled('bookmark does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
// Title and message
|
||||
var ttl = document.getElementById('title');
|
||||
var msg = document.getElementById('message');
|
||||
ttl.setAttribute('data-l10n-id', 'remove-title');
|
||||
ttl.setAttribute('data-l10n-args',
|
||||
JSON.stringify({ name: bookmark.name }));
|
||||
msg.setAttribute('data-l10n-id', 'remove-message');
|
||||
msg.setAttribute('data-l10n-args',
|
||||
JSON.stringify({ name: bookmark.name }));
|
||||
|
||||
// Action handlers
|
||||
document.getElementById('cancel-action').addEventListener('click',
|
||||
function onCancel() {
|
||||
data.oncancelled('cancelled');
|
||||
}
|
||||
);
|
||||
|
||||
document.getElementById('remove-action').addEventListener('click',
|
||||
function onConfirm(evt) {
|
||||
evt.target.removeEventListener('click', onConfirm);
|
||||
BookmarksDatabase.remove(id).then(data.onremoved, data.oncancelled);
|
||||
}
|
||||
);
|
||||
}, data.oncancelled);
|
||||
}
|
||||
};
|
||||
32
gaia/apps/bookmark/js/components/status-behavior.css
Executable file
32
gaia/apps/bookmark/js/components/status-behavior.css
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
section[role="status"] {
|
||||
animation-name: hide;
|
||||
animation-duration: .3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
section[role="status"].onviewport {
|
||||
animation-name: show;
|
||||
}
|
||||
|
||||
section[role="status"].hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes show {
|
||||
from {
|
||||
transform: translateY(100%)
|
||||
}
|
||||
to {
|
||||
transform: translateY(0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes hide {
|
||||
from {
|
||||
transform: translateY(0)
|
||||
}
|
||||
to {
|
||||
transform: translateY(100%)
|
||||
}
|
||||
}
|
||||
177
gaia/apps/bookmark/js/components/status.js
Executable file
177
gaia/apps/bookmark/js/components/status.js
Executable file
|
|
@ -0,0 +1,177 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
window.utils = window.utils || {};
|
||||
|
||||
window.utils.status = (function() {
|
||||
|
||||
// How many milliseconds is displayed the status component by default
|
||||
var DISPLAYED_TIME = 1500;
|
||||
|
||||
// References to the DOMElement(s) that renders the status UI component
|
||||
var section, content;
|
||||
|
||||
// The numerical ID of the timeout in order to hide UI component
|
||||
var timeoutID;
|
||||
|
||||
/*
|
||||
* Clears the callback in charge of hiding the component after timeout
|
||||
*/
|
||||
function clearHideTimeout() {
|
||||
if (timeoutID === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.clearTimeout(timeoutID);
|
||||
timeoutID = null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shows the status component
|
||||
*
|
||||
* @param{Object} Message. It could be a string or a DOMFragment that
|
||||
* represents the normal and strong strings
|
||||
*
|
||||
* @param{int} It defines the time that the status is displayed in ms. This
|
||||
* parameter is optional
|
||||
*
|
||||
*/
|
||||
function show(message, duration) {
|
||||
clearHideTimeout();
|
||||
content.innerHTML = '';
|
||||
|
||||
if (typeof message === 'string') {
|
||||
content.textContent = message;
|
||||
} else {
|
||||
try {
|
||||
// Here we should have a DOMFragment
|
||||
content.appendChild(message);
|
||||
} catch (ex) {
|
||||
console.error('DOMException: ' + ex.message);
|
||||
}
|
||||
}
|
||||
|
||||
section.classList.remove('hidden');
|
||||
section.classList.add('onviewport');
|
||||
timeoutID = window.setTimeout(hide, duration || DISPLAYED_TIME);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is invoked when some animation is ended
|
||||
*/
|
||||
function animationEnd(evt) {
|
||||
var eventName = 'status-showed';
|
||||
|
||||
if (evt.animationName === 'hide') {
|
||||
clearHideTimeout();
|
||||
section.classList.add('hidden');
|
||||
eventName = 'status-hidden';
|
||||
}
|
||||
|
||||
window.dispatchEvent(new CustomEvent(eventName));
|
||||
}
|
||||
|
||||
/*
|
||||
* Hides the status component
|
||||
*/
|
||||
function hide() {
|
||||
section.classList.remove('onviewport');
|
||||
}
|
||||
|
||||
/*
|
||||
* Releases memory
|
||||
*/
|
||||
function destroy() {
|
||||
section.removeEventListener('animationend', animationEnd);
|
||||
document.body.removeChild(section);
|
||||
clearHideTimeout();
|
||||
section = content = null;
|
||||
}
|
||||
|
||||
function getPath() {
|
||||
return '/js/components/';
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes the library. Basically it creates the markup:
|
||||
*
|
||||
* <section role="status">
|
||||
* <p>xxx</p>
|
||||
* </section>
|
||||
*/
|
||||
function initialize() {
|
||||
if (section) {
|
||||
return;
|
||||
}
|
||||
|
||||
section = document.createElement('section');
|
||||
|
||||
var link = document.createElement('link');
|
||||
link.type = 'text/css';
|
||||
link.rel = 'stylesheet';
|
||||
link.href = getPath() + 'status-behavior.css';
|
||||
document.head.appendChild(link);
|
||||
|
||||
section.setAttribute('role', 'status');
|
||||
section.classList.add('hidden');
|
||||
|
||||
content = document.createElement('p');
|
||||
|
||||
section.appendChild(content);
|
||||
section.addEventListener('animationend', animationEnd);
|
||||
|
||||
// to prevent flickering
|
||||
setTimeout(function append() {
|
||||
document.body.appendChild(section);
|
||||
});
|
||||
}
|
||||
|
||||
// Initializing the library
|
||||
if (document.readyState === 'complete') {
|
||||
initialize();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', function loaded() {
|
||||
document.removeEventListener('DOMContentLoaded', loaded);
|
||||
initialize();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
/*
|
||||
* The library is auto-initialized but it is for unit testing purposes
|
||||
*/
|
||||
init: initialize,
|
||||
|
||||
/*
|
||||
* Shows the status component
|
||||
*
|
||||
* @param{Object} Message. It could be a string or a DOMFragment that
|
||||
* represents the normal and strong strings
|
||||
*
|
||||
* @param{int} It defines the time that the status is displayed in ms
|
||||
*
|
||||
*/
|
||||
show: show,
|
||||
|
||||
/*
|
||||
* Hides the status component
|
||||
*/
|
||||
hide: hide,
|
||||
|
||||
/*
|
||||
* Releases memory
|
||||
*/
|
||||
destroy: destroy,
|
||||
|
||||
/*
|
||||
* Sets up the duration in milliseconds that a status is displayed
|
||||
*
|
||||
* @param{int} The time in milliseconds
|
||||
*
|
||||
*/
|
||||
setDuration: function setDuration(time) {
|
||||
DISPLAYED_TIME = time || DISPLAYED_TIME;
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
Loading…
Reference in a new issue