Release Preview 20 shared desktop, Android and Sargo updates
Restore Gaia settings and media workflows, extend native network adapters, and align the APK payload. Generate French/English changelogs and website entries from the simplified JSON source. Validation: shared JS and Tundra unit tests; desktop workflows and persistence; signed APK payload parity; Sargo RAM and installed startup with boot readback. Android runtime qualification is recorded separately.
This commit is contained in:
parent
be2cdce823
commit
1311bed5e9
170 changed files with 19698 additions and 2495 deletions
101
overrides/camera.gaiamobile.org/js/lib/dcf.js
Normal file
101
overrides/camera.gaiamobile.org/js/lib/dcf.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
define(function(require, exports, module) {
|
||||
'use strict';
|
||||
|
||||
// This handles the logic pertaining to the naming of files according
|
||||
// to the Design rule for Camera File System
|
||||
// * http://en.wikipedia.org/wiki/Design_rule_for_Camera_File_system
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
*/
|
||||
|
||||
var asyncStorage = require('asyncStorage');
|
||||
var debug = require('debug')('dcf');
|
||||
var format = require('format');
|
||||
|
||||
/**
|
||||
* Locals
|
||||
*/
|
||||
|
||||
var dcfConfigLoaded = false;
|
||||
var deferredArgs = null;
|
||||
var defaultSeq = {file: 1, dir: 100};
|
||||
var dcfConfig = {
|
||||
key: 'dcf_key',
|
||||
seq: null,
|
||||
postFix: 'MZLLA',
|
||||
prefix: {video: 'VID_', image: 'IMG_'},
|
||||
ext: {video: window.VulpesVideoExtension || '3gp', image: 'jpg'}
|
||||
};
|
||||
|
||||
exports.init = function() {
|
||||
debug('initializing');
|
||||
exports.checkFileCounter(function() {
|
||||
// We have a previous call to createDCFFilename that is waiting for
|
||||
// a response, fire it again
|
||||
if (deferredArgs) {
|
||||
var args = deferredArgs;
|
||||
exports.createDCFFilename(args.storage, args.type, args.callback);
|
||||
deferredArgs = null;
|
||||
}
|
||||
|
||||
debug('initialized');
|
||||
});
|
||||
};
|
||||
|
||||
exports.checkFileCounter = function(callback) {
|
||||
asyncStorage.getItem(dcfConfig.key, function(value) {
|
||||
dcfConfigLoaded = true;
|
||||
dcfConfig.seq = value ? value : defaultSeq;
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.createDCFFilename = function(storage, type, callback) {
|
||||
|
||||
// We havent loaded the current counters from indexedDB yet, defer
|
||||
// the call
|
||||
if (!dcfConfigLoaded) {
|
||||
deferredArgs = {storage: storage, type: type, callback: callback};
|
||||
return;
|
||||
}
|
||||
|
||||
var dir = 'DCIM/' + dcfConfig.seq.dir + dcfConfig.postFix + '/';
|
||||
var filename = dcfConfig.prefix[type] +
|
||||
format.padLeft(dcfConfig.seq.file, 4, '0') + '.' +
|
||||
dcfConfig.ext[type];
|
||||
var filepath = dir + filename;
|
||||
|
||||
// A file with this name may have been written by the user or
|
||||
// our indexeddb sequence tracker was cleared, check we wont overwrite
|
||||
// anything
|
||||
var req = storage.get(filepath);
|
||||
|
||||
// A file existed, we bump the directory then try to generate a
|
||||
// new filename
|
||||
req.onsuccess = function() {
|
||||
dcfConfig.seq.file = 1;
|
||||
dcfConfig.seq.dir += 1;
|
||||
asyncStorage.setItem(dcfConfig.key, dcfConfig.seq, function() {
|
||||
exports.createDCFFilename(storage, type, callback);
|
||||
});
|
||||
};
|
||||
|
||||
// No file existed, we are good to go
|
||||
req.onerror = function() {
|
||||
if (dcfConfig.seq.file < 9999) {
|
||||
dcfConfig.seq.file += 1;
|
||||
} else {
|
||||
dcfConfig.seq.file = 1;
|
||||
dcfConfig.seq.dir += 1;
|
||||
}
|
||||
asyncStorage.setItem(dcfConfig.key, dcfConfig.seq, function() {
|
||||
callback(filepath, filename, dir);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
});
|
||||
File diff suppressed because it is too large
Load diff
713
overrides/gallery.gaiamobile.org/js/MetadataParser.js
Normal file
713
overrides/gallery.gaiamobile.org/js/MetadataParser.js
Normal file
|
|
@ -0,0 +1,713 @@
|
|||
'use strict';
|
||||
/* global
|
||||
CONFIG_MAX_IMAGE_PIXEL_SIZE,
|
||||
CONFIG_REQUIRED_EXIF_PREVIEW_HEIGHT,
|
||||
CONFIG_REQUIRED_EXIF_PREVIEW_WIDTH,
|
||||
cropResizeRotate,
|
||||
Downsample,
|
||||
getImageSize,
|
||||
getVideoRotation,
|
||||
isPhone,
|
||||
parseJPEGMetadata,
|
||||
videostorage
|
||||
*/
|
||||
/* exported
|
||||
metadataParser
|
||||
*/
|
||||
|
||||
//
|
||||
// This file defines a single metadataParsers object. The two
|
||||
// properties of this object are metadata parsing functions for image
|
||||
// files and video files, intended for use with the MediaDB class.
|
||||
//
|
||||
// This file depends on JPEGMetadataParser.js and blobview.js
|
||||
//
|
||||
var metadataParser = (function() {
|
||||
// If we generate our own thumbnails, aim for this size.
|
||||
// Calculate needed size from longer side of the screen.
|
||||
var THUMBNAIL_WIDTH = computeThumbnailWidth();
|
||||
var THUMBNAIL_HEIGHT = THUMBNAIL_WIDTH;
|
||||
|
||||
function computeThumbnailWidth() {
|
||||
// Make sure this works regardless of current device orientation
|
||||
var width = window.innerWidth || screen.width;
|
||||
var height = window.innerHeight || screen.height;
|
||||
var portraitWidth = Math.max(1, Math.min(width, height));
|
||||
var landscapeWidth = Math.max(1, Math.max(width, height));
|
||||
var thumbnailsPerRowPortrait = isPhone ? 3 : 4;
|
||||
var thumbnailsPerRowLandscape = isPhone ? 4 : 6;
|
||||
return Math.round(window.devicePixelRatio *
|
||||
Math.max(portraitWidth / thumbnailsPerRowPortrait,
|
||||
landscapeWidth / thumbnailsPerRowLandscape));
|
||||
}
|
||||
|
||||
var thumbnailSize = {
|
||||
width: THUMBNAIL_WIDTH,
|
||||
height: THUMBNAIL_HEIGHT
|
||||
};
|
||||
|
||||
// Don't try to decode image files of unknown type if bigger than this
|
||||
var MAX_UNKNOWN_IMAGE_FILE_SIZE = 0.5 * 1024 * 1024; // half a megabyte
|
||||
|
||||
// If a jpeg does not have a suitable EXIF preview and has more than
|
||||
// this number of pixels, then we create an external preview for it
|
||||
var MAX_PREVIEWLESS_JPEG_SIZE = 5 * 1024 * 1024; // 5 megapixels
|
||||
|
||||
// An <img> element for loading images
|
||||
var offscreenImage = new Image();
|
||||
|
||||
// The screen size. Preview images must be at least this big
|
||||
// Note that we use screen.width instead of window.innerWidth because
|
||||
// the window size is different when we are invoked for a pick activity
|
||||
// (non-fullscreen) and when we are invoked normally (fullscreen).
|
||||
var sw = screen.width * window.devicePixelRatio;
|
||||
var sh = screen.height * window.devicePixelRatio;
|
||||
|
||||
// Create a thumbnail size canvas, copy the <img> or <video> into it
|
||||
// cropping the edges as needed to make it fit, and then extract the
|
||||
// thumbnail image as a blob and pass it to the callback.
|
||||
// This utility function is used by both the image and video metadata parsers
|
||||
function createThumbnailFromElement(elt, video, rotation,
|
||||
mirrored, callback, error) {
|
||||
try {
|
||||
// Create a thumbnail image
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = THUMBNAIL_WIDTH;
|
||||
canvas.height = THUMBNAIL_HEIGHT;
|
||||
var context = canvas.getContext('2d', { willReadFrequently: true });
|
||||
var eltwidth = elt.width;
|
||||
var eltheight = elt.height;
|
||||
var scalex = canvas.width / eltwidth;
|
||||
var scaley = canvas.height / eltheight;
|
||||
|
||||
// Take the larger of the two scales: we crop the image to the thumbnail
|
||||
var scale = Math.max(scalex, scaley);
|
||||
|
||||
// Calculate the region of the image that will be copied to the
|
||||
// canvas to create the thumbnail
|
||||
var w = Math.round(THUMBNAIL_WIDTH / scale);
|
||||
var h = Math.round(THUMBNAIL_HEIGHT / scale);
|
||||
var x = Math.round((eltwidth - w) / 2);
|
||||
var y = Math.round((eltheight - h) / 2);
|
||||
|
||||
var centerX = Math.floor(THUMBNAIL_WIDTH / 2);
|
||||
var centerY = Math.floor(THUMBNAIL_HEIGHT / 2);
|
||||
|
||||
// If a orientation is specified, rotate/mirroring the canvas context.
|
||||
if (rotation || mirrored) {
|
||||
context.save();
|
||||
// All transformation are applied to the center of the thumbnail.
|
||||
context.translate(centerX, centerY);
|
||||
}
|
||||
if (mirrored) {
|
||||
context.scale(-1, 1);
|
||||
}
|
||||
if (rotation) {
|
||||
switch (rotation) {
|
||||
case 90:
|
||||
context.rotate(Math.PI / 2);
|
||||
break;
|
||||
case 180:
|
||||
context.rotate(Math.PI);
|
||||
break;
|
||||
case 270:
|
||||
context.rotate(-Math.PI / 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rotation || mirrored) {
|
||||
context.translate(-centerX, -centerY);
|
||||
}
|
||||
|
||||
// Draw that region of the image into the canvas, scaling it down
|
||||
context.drawImage(elt, x, y, w, h,
|
||||
0, 0, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
|
||||
|
||||
// Restore the default rotation so the play arrow comes out correctly
|
||||
if (rotation || mirrored) {
|
||||
context.restore();
|
||||
}
|
||||
|
||||
// If this is a video, superimpose a translucent play button over
|
||||
// the captured video frame to distinguish it from a still photo thumbnail
|
||||
if (video) {
|
||||
// First draw a transparent gray circle
|
||||
context.fillStyle = 'rgba(0, 0, 0, .2)';
|
||||
context.beginPath();
|
||||
context.arc(THUMBNAIL_WIDTH / 2, THUMBNAIL_HEIGHT / 2,
|
||||
THUMBNAIL_HEIGHT / 5, 0, 2 * Math.PI, false);
|
||||
context.fill();
|
||||
|
||||
// Now outline the circle in white
|
||||
context.strokeStyle = 'rgba(255,255,255,.6)';
|
||||
context.lineWidth = 2;
|
||||
context.stroke();
|
||||
|
||||
// And add a white play arrow.
|
||||
context.beginPath();
|
||||
context.fillStyle = 'rgba(255,255,255,.6)';
|
||||
// The height of an equilateral triangle is sqrt(3)/2 times the side
|
||||
var side = THUMBNAIL_HEIGHT / 5;
|
||||
var triangle_height = side * Math.sqrt(3) / 2;
|
||||
context.moveTo(THUMBNAIL_WIDTH / 2 + triangle_height * 2 / 3,
|
||||
THUMBNAIL_HEIGHT / 2);
|
||||
context.lineTo(THUMBNAIL_WIDTH / 2 - triangle_height / 3,
|
||||
THUMBNAIL_HEIGHT / 2 - side / 2);
|
||||
context.lineTo(THUMBNAIL_WIDTH / 2 - triangle_height / 3,
|
||||
THUMBNAIL_HEIGHT / 2 + side / 2);
|
||||
context.closePath();
|
||||
context.fill();
|
||||
}
|
||||
|
||||
canvas.toBlob(function(blob) {
|
||||
context = null;
|
||||
canvas.width = canvas.height = 0;
|
||||
canvas = null;
|
||||
callback(blob);
|
||||
}, 'image/jpeg');
|
||||
} catch (ex) {
|
||||
// An error may be thrown when the drawImage decodes a broken/trancated
|
||||
// image.
|
||||
// The elt may be a offscreen image. So, the image metadata is parsed, and
|
||||
// the image data is loaded but not decoded. The drawImage triggers the
|
||||
// image decoder to decode the image data. And an error may be thrown.
|
||||
error('createThumbnailFromElement:' + ex.message);
|
||||
}
|
||||
}
|
||||
|
||||
var VIDEOFILE = /DCIM\/\d{3}MZLLA\/VID_\d{4}\.jpg/;
|
||||
|
||||
function metadataParser(file, metadataCallback, metadataError, bigFile) {
|
||||
// If the file is a poster image for a video file, then we've want
|
||||
// video metadata, not image metadata
|
||||
if (VIDEOFILE.test(file.name)) {
|
||||
videoMetadataParser(file, metadataCallback, metadataError);
|
||||
return;
|
||||
}
|
||||
|
||||
// Figure out how big the image is if we can. For JPEG files this
|
||||
// calls the JPEG parser and returns the EXIF preview if there is one.
|
||||
getImageSize(file, gotImageSize, gotImageSizeError);
|
||||
|
||||
function gotImageSizeError(errmsg) {
|
||||
// The image is not a JPEG, PNG, GIF or BMP file. We may still be
|
||||
// able to decode and display it but we don't know the image
|
||||
// size, so we won't even try if the file is too big.
|
||||
if (file.size > MAX_UNKNOWN_IMAGE_FILE_SIZE) {
|
||||
metadataError('Ignoring large file ' + file.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the file is too small to be an image, ignore it
|
||||
if (file.size < 32) {
|
||||
metadataError('Ignoring small file ' + file.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the error message is anything other than unknown image type
|
||||
// it means we've got a corrupt image file, or the image metdata parser
|
||||
// can't handle the file for some reason. Log a warning but keep going
|
||||
// in case the image is good and the metadata parser is buggy.
|
||||
if (errmsg !== 'unknown image type') {
|
||||
console.warn('getImageSize', errmsg, file.name);
|
||||
}
|
||||
|
||||
// If it is not too big create a preview and thumbnail.
|
||||
createThumbnailAndPreview(file,
|
||||
metadataCallback,
|
||||
metadataError,
|
||||
false,
|
||||
bigFile,
|
||||
{});
|
||||
}
|
||||
|
||||
function gotImageSize(metadata) {
|
||||
//
|
||||
// If the image is too big, reject it now so we don't have
|
||||
// memory trouble later.
|
||||
//
|
||||
// CONFIG_MAX_IMAGE_PIXEL_SIZE is maximum image resolution we
|
||||
// can handle. It's from config.js which is generated at build
|
||||
// time (see build/application-data.js).
|
||||
//
|
||||
// For jpeg images we can downsample while decoding which means that
|
||||
// we can handle images that are quite a bit larger.
|
||||
//
|
||||
// getImageSize() distinguishes pjpeg files from jpeg, because
|
||||
// progressive jpeg files are quite different than regular jpegs.
|
||||
// Not only do pjpegs not benefit from using #-moz-samplesize, they
|
||||
// actually require more memory to decode than other images of the same
|
||||
// resolution, so we actually need to reduce the image size limit for
|
||||
// this image type.
|
||||
//
|
||||
var imagesizelimit = CONFIG_MAX_IMAGE_PIXEL_SIZE;
|
||||
if (metadata.type === 'jpeg') {
|
||||
imagesizelimit *= Downsample.MAX_AREA_REDUCTION;
|
||||
}
|
||||
else if (metadata.type === 'pjpeg') {
|
||||
// As a semi-educated guess, we'll say that we can handle pjpegs
|
||||
// 2/3rds the size of the largest PNG we can handle.
|
||||
imagesizelimit *= 2 / 3;
|
||||
}
|
||||
|
||||
//
|
||||
// Even if we can downsample an image while decoding it, we still
|
||||
// have to read the entire image file. If the file is particularly
|
||||
// large we might also have memory problems. (See bug 1008834: a 20mb
|
||||
// 80mp jpeg file will cause an OOM on Tarako even though we can
|
||||
// decode it at < 2mp). Rather than adding another build-time config
|
||||
// variable to specify the maximum file size, however, we'll just
|
||||
// base the file size limit on CONFIG_MAX_IMAGE_PIXEL_SIZE.
|
||||
// So if that variable is set to 2M, then we might use up to 12Mb of
|
||||
// memory. 2 * 2M bytes for the image file and 4 bytes times 2M pixels
|
||||
// for the decoded image. A 4mb file size limit should accomodate
|
||||
// most JPEG files up to 12 or 16 megapixels.
|
||||
//
|
||||
// Note that this size limit is just a guess. If we continue to see
|
||||
// OOMs with large files then we should change the number 2 below
|
||||
// to something smaller.
|
||||
//
|
||||
var filesizelimit = 2 * CONFIG_MAX_IMAGE_PIXEL_SIZE;
|
||||
|
||||
if (metadata.width * metadata.height > imagesizelimit ||
|
||||
file.size > filesizelimit) {
|
||||
metadataError('Ignoring high-resolution image ' + file.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the file included a preview image, see if it is big enough
|
||||
if (metadata.preview) {
|
||||
// Create a blob that is just the preview image
|
||||
var previewblob = file.slice(metadata.preview.start,
|
||||
metadata.preview.end,
|
||||
'image/jpeg');
|
||||
|
||||
// Check to see if the preview is big enough to use in MediaFrame
|
||||
parseJPEGMetadata(previewblob, previewsuccess, previewerror);
|
||||
}
|
||||
else {
|
||||
// If there wasn't a preview image, then generate a preview and
|
||||
// thumbnail from the full size image.
|
||||
useFullsizeImage();
|
||||
}
|
||||
|
||||
function previewerror(msg) {
|
||||
// The preview isn't a valid jpeg file, so use the full image to
|
||||
// create a preview and a thumbnail
|
||||
console.error(msg);
|
||||
useFullsizeImage();
|
||||
}
|
||||
|
||||
// If we can't find a valid embedded EXIF preview image then we come here
|
||||
function useFullsizeImage() {
|
||||
// If the full size image is a JPEG then we'll just skip the
|
||||
// preview generation because we can downsample and decode it
|
||||
// at preview size when needed. So in this case, we just need
|
||||
// to create a thumbnail for the image. On the other hand, if
|
||||
// this is not a JPEG image then we have to create both a
|
||||
// thumbnail and an external preview image. Note that progressive
|
||||
// jpegs will have a type 'pjpeg' and will be handled below
|
||||
// like png images.
|
||||
|
||||
// For jpeg images with image sizes more than 5MP, we need
|
||||
// to create preview on sdcard to avoid flash while opening image.
|
||||
// See bug 1181290.
|
||||
// This constraint can be removed when gecko supports downscale during
|
||||
// decode by canvas size for offscreen images. This will
|
||||
// facilitate removing #moz-sample-size and using ddd.
|
||||
// See bug 1206206.
|
||||
|
||||
if (metadata.type === 'jpeg') {
|
||||
if (metadata.width * metadata.height <= MAX_PREVIEWLESS_JPEG_SIZE) {
|
||||
// If the jpeg image isn't too big we don't need a preview
|
||||
// because we can decode it quickly enough at screen size.
|
||||
// So in this case we just need to create a thumbnail.
|
||||
cropResizeRotate(file, null, thumbnailSize, null, metadata,
|
||||
function gotThumbnail(error, thumbnailBlob) {
|
||||
if (error) {
|
||||
var msg = 'Failed to create thumbnail for' +
|
||||
file.name;
|
||||
console.error(msg);
|
||||
metadataError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
metadata.preview = null;
|
||||
metadata.thumbnail = thumbnailBlob;
|
||||
metadataCallback(metadata);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Otherwise, this is a high-resolution jpeg that does not
|
||||
// have an EXIF preview big enough for the screen. We need
|
||||
// to create both a thumbnail and a preview image but we can
|
||||
// do that with efficient downsampling using cropResizeRotate().
|
||||
createJPEGThumbnailAndPreview(file, metadata,
|
||||
metadataCallback,
|
||||
metadataError);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// In this case we have no preview and the image is not a jpeg
|
||||
// that can be downsampled with #-moz-samplesize
|
||||
createThumbnailAndPreview(file,
|
||||
metadataCallback,
|
||||
metadataError,
|
||||
false,
|
||||
bigFile,
|
||||
metadata);
|
||||
}
|
||||
}
|
||||
|
||||
function previewsuccess(previewmetadata) {
|
||||
// Size of the preview image
|
||||
var pw = previewmetadata.width;
|
||||
var ph = previewmetadata.height;
|
||||
// optional configuration specifying minimum size
|
||||
var mw = CONFIG_REQUIRED_EXIF_PREVIEW_WIDTH;
|
||||
var mh = CONFIG_REQUIRED_EXIF_PREVIEW_HEIGHT;
|
||||
|
||||
var bigenough;
|
||||
|
||||
// If config.js specifies a minimum required preview size,
|
||||
// then this preview is big enough if both dimensions are
|
||||
// larger than that configured minimum. Otherwise, the preview
|
||||
// is big enough if at least one dimension is >= the screen
|
||||
// size in both portait and landscape mode.
|
||||
if (mw && mh) {
|
||||
bigenough =
|
||||
Math.max(pw, ph) >= Math.max(mw, mh) &&
|
||||
Math.min(pw, ph) >= Math.min(mw, mh);
|
||||
}
|
||||
else {
|
||||
bigenough = (pw >= sw || ph >= sh) && (pw >= sh || ph >= sw);
|
||||
}
|
||||
|
||||
// Does the aspect ratio of the preview match the aspect ratio of
|
||||
// the full-size image? If not we have to assume it is distorted
|
||||
// and should not be used.
|
||||
var aspectRatioMatches =
|
||||
Math.abs(pw / ph - metadata.width / metadata.height) < 0.01;
|
||||
|
||||
// If the preview is good, use it to create a thumbnail.
|
||||
if (bigenough && aspectRatioMatches) {
|
||||
metadata.preview.width = pw;
|
||||
metadata.preview.height = ph;
|
||||
|
||||
// This is the most common case. We've got a JPEG image with a
|
||||
// JPEG preview. All we need is a thumbnail, and we can create that
|
||||
// most efficiently with cropResizeRotate which will use
|
||||
// #-moz-samplesize to save memory while decoding the image.
|
||||
// Note that we apply the EXIF orientation information from
|
||||
// the full size image to the preview image.
|
||||
previewmetadata.rotation = metadata.rotation;
|
||||
previewmetadata.mirrored = metadata.mirrored;
|
||||
cropResizeRotate(previewblob, null, thumbnailSize, null,
|
||||
previewmetadata,
|
||||
function gotThumbnailBlob(error, thumbnailBlob) {
|
||||
if (error) {
|
||||
previewerror(error);
|
||||
return;
|
||||
}
|
||||
metadata.thumbnail = thumbnailBlob;
|
||||
metadataCallback(metadata);
|
||||
});
|
||||
} else {
|
||||
// Preview isn't big enough so get one the hard way
|
||||
useFullsizeImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called for JPEG images that do not have a suitable
|
||||
// EXIF preview image and that have more than MAX_PREVIEWLESS_JPEG_SIZE
|
||||
// pixels. It creates and saves a preview image and a thumbnail.
|
||||
// Unlike createThumbnailAndPreview(), it uses cropResizeRotate() which
|
||||
// downsamples jpeg images while decoding them to save memory.
|
||||
function createJPEGThumbnailAndPreview(file, metadata,
|
||||
metadataCallback, metadataError)
|
||||
{
|
||||
|
||||
// Figure out the preview size.
|
||||
var scale = Math.max(Math.min(sw / metadata.width,
|
||||
sh / metadata.height, 1),
|
||||
Math.min(sh / metadata.width,
|
||||
sw / metadata.height, 1));
|
||||
var previewSize = {
|
||||
width: Math.round(metadata.width * scale),
|
||||
height: Math.round(metadata.height * scale)
|
||||
};
|
||||
|
||||
// Create a preview image.
|
||||
// Note that we can't use the full metadata object here
|
||||
// because we don't want the preview image to be rotated.
|
||||
// We want the same orientation as the original image
|
||||
cropResizeRotate(file, null, previewSize, null,
|
||||
{ width: metadata.width, height: metadata.height },
|
||||
function gotPreview(error, previewBlob) {
|
||||
if (error) {
|
||||
var msg = 'Failed to create preview for' +
|
||||
file.name;
|
||||
console.error(msg);
|
||||
metadataError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save this preview to the sdcard
|
||||
savePreview(file, previewBlob, metadataError, function(filename) {
|
||||
// Record the preview image in the metadata
|
||||
metadata.preview = {
|
||||
filename: filename,
|
||||
width: previewSize.width,
|
||||
height: previewSize.height
|
||||
};
|
||||
|
||||
// Now create the thumbnail from this smaller preview image
|
||||
// In this case we need to pass the size of the preview
|
||||
// and the rotation of the original image.
|
||||
cropResizeRotate(previewBlob, null, thumbnailSize, null,
|
||||
{
|
||||
width: previewSize.width,
|
||||
height: previewSize.height,
|
||||
rotation: metadata.rotation
|
||||
},
|
||||
function gotThumbnail(error, thumbnailBlob) {
|
||||
// If thumbnail creation failed report the error
|
||||
if (error) {
|
||||
metadataError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, remember thumbnail and we're done
|
||||
metadata.thumbnail = thumbnailBlob;
|
||||
metadataCallback(metadata);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Load an image from a file into an <img> tag, and then use that
|
||||
// to get its dimensions and create a thumbnail. Store these values in
|
||||
// a metadata object, and pass the object to the callback function.
|
||||
// If anything goes wrong, pass an error message to the error function.
|
||||
// If it is a large image, create and save a preview for it as well.
|
||||
//
|
||||
// We used to call this function for every image. Now that gecko supports
|
||||
// the #-moz-samplesize media fragment, however, we handle jpeg images
|
||||
// with cropResizeRotate() for memory efficiency, and this function is
|
||||
// used only for non-jpeg files.
|
||||
function createThumbnailAndPreview(file, callback, error, nopreview,
|
||||
bigFile, metadata) {
|
||||
var url = URL.createObjectURL(file);
|
||||
offscreenImage.src = url;
|
||||
|
||||
offscreenImage.onerror = function() {
|
||||
URL.revokeObjectURL(url);
|
||||
offscreenImage.onload = offscreenImage.onerror = null; offscreenImage.removeAttribute('src');
|
||||
error('createThumbnailAndPreview: Image failed to load');
|
||||
};
|
||||
|
||||
offscreenImage.onload = function() {
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
var iw = offscreenImage.width;
|
||||
var ih = offscreenImage.height;
|
||||
|
||||
// Don't overwrite the metadata in the case we read a previewblob.
|
||||
if (!nopreview) {
|
||||
metadata.width = iw;
|
||||
metadata.height = ih;
|
||||
}
|
||||
|
||||
// If this is a big image, then decoding it takes a lot of memory.
|
||||
// We set this flag to prevent the user from zooming in on other
|
||||
// images at the same time because that also takes a lot of memory
|
||||
// and we don't want to crash with an OOM. If we find one big image
|
||||
// we assume that there may be others, so the flag remains set until
|
||||
// the current scan is complete.
|
||||
//
|
||||
// XXX: When bug 854795 is fixed, we'll be able to create previews
|
||||
// for large images without using so much memory, and we can remove
|
||||
// this flag then.
|
||||
if (iw * ih > 2 * 1024 * 1024 && bigFile) {
|
||||
bigFile();
|
||||
}
|
||||
|
||||
// If the image was already thumbnail size, it is its own thumbnail
|
||||
// and it does not need a preview
|
||||
if (metadata.width <= THUMBNAIL_WIDTH &&
|
||||
metadata.height <= THUMBNAIL_HEIGHT) {
|
||||
offscreenImage.onload = offscreenImage.onerror = null; offscreenImage.removeAttribute('src');
|
||||
metadata.thumbnail = file;
|
||||
callback(metadata);
|
||||
}
|
||||
else {
|
||||
createThumbnailFromElement(
|
||||
offscreenImage,
|
||||
false,
|
||||
metadata.rotation || 0,
|
||||
metadata.mirrored || false,
|
||||
gotThumbnail,
|
||||
error);
|
||||
}
|
||||
|
||||
function gotThumbnail(thumbnail) {
|
||||
metadata.thumbnail = thumbnail;
|
||||
// If no preview was requested, or if if the image was less
|
||||
// than half a megapixel then it does not need a preview
|
||||
// image, and we can call the callback right away
|
||||
if (nopreview || metadata.width * metadata.height < 512 * 1024) {
|
||||
offscreenImage.onload = offscreenImage.onerror = null; offscreenImage.removeAttribute('src');
|
||||
callback(metadata);
|
||||
}
|
||||
else {
|
||||
// Otherwise, this was a big image and we need to create a
|
||||
// preview for it so we can avoid decoding the full size
|
||||
// image again when possible
|
||||
createAndSavePreview();
|
||||
}
|
||||
}
|
||||
|
||||
function createAndSavePreview() {
|
||||
// Figure out the preview size.
|
||||
// Make sure the size is big enough for both landscape and portrait
|
||||
var scale = Math.max(Math.min(sw / iw, sh / ih, 1),
|
||||
Math.min(sh / iw, sw / ih, 1));
|
||||
// preview width and height
|
||||
var pw = Math.round(iw * scale);
|
||||
var ph = Math.round(ih * scale);
|
||||
|
||||
// Create the preview in a canvas
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = pw;
|
||||
canvas.height = ph;
|
||||
var context = canvas.getContext('2d', { willReadFrequently: true });
|
||||
context.drawImage(offscreenImage, 0, 0, iw, ih, 0, 0, pw, ph);
|
||||
canvas.toBlob(function(blob) {
|
||||
offscreenImage.onload = offscreenImage.onerror = null; offscreenImage.removeAttribute('src');
|
||||
canvas.width = canvas.height = 0;
|
||||
savePreview(file, blob, error, function(filename) {
|
||||
metadata.preview = {
|
||||
filename: filename,
|
||||
width: pw,
|
||||
height: ph
|
||||
};
|
||||
callback(metadata);
|
||||
});
|
||||
}, 'image/jpeg');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Given an image file and a preview blob for it, this function saves
|
||||
// the preview to a file in .gallery/previews/ and then passes the filename
|
||||
// to the specified callback. Calls errorCallback if an error occurs.
|
||||
// This is used by createThumbnailAndPreview() and by
|
||||
// createJPEGThumbnailAndPreview().
|
||||
function savePreview(file, previewblob, errorCallback, filenameCallback) {
|
||||
var storage = navigator.getDeviceStorage('pictures');
|
||||
var filename;
|
||||
if (file.name[0] === '/') {
|
||||
// We expect file.name to be a fully qualified name (perhaps
|
||||
// something like /sdcard/DCIM/100MZLLA/IMG_0001.jpg).
|
||||
var slashIndex = file.name.indexOf('/', 1);
|
||||
if (slashIndex < 0) {
|
||||
errorCallback('savePreview: Bad filename: \'' + file.name + '\'');
|
||||
return;
|
||||
}
|
||||
filename =
|
||||
file.name.substring(0, slashIndex) + // storageName (i.e. /sdcard)
|
||||
|
||||
'/.gallery/previews' +
|
||||
file.name.substring(slashIndex); // rest of path (i,e, /DCIM/...)
|
||||
} else {
|
||||
// On non-composite storage areas (e.g. desktop), file.name will be
|
||||
// a relative path.
|
||||
filename = '.gallery/previews/' + file.name;
|
||||
}
|
||||
|
||||
// Add a '.jpeg' extension to all preview files. This is necessary
|
||||
// because all previews get saved as jpegs even if the original
|
||||
// image is not a jpeg. But DeviceStorage uses the filename extension
|
||||
// to determine the file type.
|
||||
filename += '.jpeg';
|
||||
|
||||
// Delete any existing preview by this name
|
||||
var delreq = storage.delete(filename);
|
||||
delreq.onsuccess = delreq.onerror = save;
|
||||
|
||||
function save() {
|
||||
var savereq = storage.addNamed(previewblob, filename);
|
||||
savereq.onerror = function() {
|
||||
console.error('Could not save preview image', filename);
|
||||
};
|
||||
|
||||
// Don't actually wait for the save to complete.
|
||||
filenameCallback(filename);
|
||||
}
|
||||
}
|
||||
|
||||
function videoMetadataParser(file, metadataCallback, errorCallback) {
|
||||
var metadata = {};
|
||||
var extensions = ['webm', '3gp', 'mp4'];
|
||||
function findVideo() {
|
||||
var videofilename = file.name.replace(/\.jpg$/, '.' + extensions.shift());
|
||||
var getreq = videostorage.get(videofilename);
|
||||
getreq.onerror = function() {
|
||||
if (extensions.length) findVideo();
|
||||
else errorCallback('cannot get video file: ' + videofilename);
|
||||
};
|
||||
getreq.onsuccess = function() {
|
||||
metadata.video = videofilename;
|
||||
if (/\.webm$/.test(videofilename)) {
|
||||
metadata.rotation = 0;
|
||||
getVideoThumbnailAndSize();
|
||||
return;
|
||||
}
|
||||
getVideoRotation(getreq.result, function(rotation) {
|
||||
if (typeof rotation === 'number') {
|
||||
metadata.rotation = rotation;
|
||||
getVideoThumbnailAndSize();
|
||||
} else errorCallback('Video rotation: ' + rotation);
|
||||
});
|
||||
};
|
||||
}
|
||||
findVideo();
|
||||
|
||||
function getVideoThumbnailAndSize() {
|
||||
var url = URL.createObjectURL(file);
|
||||
offscreenImage.src = url;
|
||||
|
||||
offscreenImage.onerror = function() {
|
||||
URL.revokeObjectURL(url);
|
||||
offscreenImage.onload = offscreenImage.onerror = null; offscreenImage.removeAttribute('src');
|
||||
errorCallback('getVideoThumanailAndSize: Image failed to load');
|
||||
};
|
||||
|
||||
offscreenImage.onload = function() {
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
// We store the unrotated size of the poster image. The video
|
||||
// itself might be larger or smaller than this, but it will
|
||||
// have the same orientation and aspect ratio.
|
||||
metadata.width = offscreenImage.width;
|
||||
metadata.height = offscreenImage.height;
|
||||
|
||||
createThumbnailFromElement(offscreenImage,
|
||||
true,
|
||||
metadata.rotation,
|
||||
false,
|
||||
function(thumbnail) {
|
||||
metadata.thumbnail = thumbnail;
|
||||
offscreenImage.onload = offscreenImage.onerror = null; offscreenImage.removeAttribute('src');
|
||||
metadataCallback(metadata);
|
||||
},
|
||||
errorCallback);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return metadataParser;
|
||||
}());
|
||||
1777
overrides/gallery.gaiamobile.org/js/metadata_scripts.js
Normal file
1777
overrides/gallery.gaiamobile.org/js/metadata_scripts.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -38,6 +38,26 @@ var oldValue=this._array[index];this._array[index]=value;this._emitEvent('replac
|
|||
*
|
||||
* @module PageTransitions
|
||||
*/
|
||||
/**
|
||||
* PageTransitions provides transition functions used when navigating panels.
|
||||
*
|
||||
* @module PageTransitions
|
||||
*/
|
||||
/**
|
||||
* PageTransitions provides transition functions used when navigating panels.
|
||||
*
|
||||
* @module PageTransitions
|
||||
*/
|
||||
/**
|
||||
* PageTransitions provides transition functions used when navigating panels.
|
||||
*
|
||||
* @module PageTransitions
|
||||
*/
|
||||
/**
|
||||
* PageTransitions provides transition functions used when navigating panels.
|
||||
*
|
||||
* @module PageTransitions
|
||||
*/
|
||||
define('modules/page_transitions',[],function() {
|
||||
'use strict';
|
||||
|
||||
|
|
@ -155,7 +175,6 @@ define('modules/page_transitions',[],function() {
|
|||
}
|
||||
};
|
||||
});
|
||||
|
||||
define('modules/panel',[],function(){'use strict';var _emptyFunc=function panel_emptyFunc(){};var Panel=function ctor_panel(options){var _initialized=false;options=options||{};options.onInit=options.onInit||_emptyFunc;options.onUninit=options.onUninit||_emptyFunc;options.onShow=options.onShow||_emptyFunc;options.onHide=options.onHide||_emptyFunc;options.onBeforeShow=options.onBeforeShow||_emptyFunc;options.onBeforeHide=options.onBeforeHide||_emptyFunc;return{get initialized(){return _initialized;},init:function(panel,initOptions){if(_initialized){return;}
|
||||
_initialized=true;return options.onInit(panel,initOptions);},uninit:function(){if(!_initialized){return;}
|
||||
_initialized=false;options.onUninit();},show:function(panel,showOptions){return Promise.resolve(this.init(panel,showOptions)).then(function(){return options.onShow(panel,showOptions);});},hide:function(){return options.onHide();},beforeShow:function(panel,beforeShowOptions){return Promise.resolve(this.init(panel,beforeShowOptions)).then(function(){return options.onBeforeShow(panel,beforeShowOptions);});},beforeHide:function(){return options.onBeforeHide();}};};return Panel;});define('modules/settings_cache',[],function(){'use strict';var _settings=window.navigator.mozSettings;var _settingsCache=null;var _settingsCacheRequestSent=null;var _pendingSettingsCallbacks=[];var _callbacks=[];var _onSettingsChange=function sc_onSettingsChange(event){var key=event.settingName;var value=event.settingValue;if(_settingsCache){_settingsCache[key]=value;}
|
||||
|
|
@ -214,23 +233,403 @@ return this._isOnRealDevice;},getCurrentLayout:function sl_getCurrentLayout(type
|
|||
if(typeof this.queries[type]!=='undefined'){return this.queries[type].matches;}
|
||||
return false;},watch:function sl_watch(name,media){var mediaString=media||this.queries[name].media;if(!mediaString){return;}
|
||||
this.unwatch(name);this.queries[name]=window.matchMedia(mediaString);this.boundHandleChange=this.handleChange.bind(this);this.queries[name].addListener(this.boundHandleChange);},unwatch:function sl_unwatch(name){if(this.queries[name]){this.queries[name].removeListener(this.boundHandleChange);}},handleChange:function sl_handleChange(evt){for(var key in this.queries){if(this.queries[key].media!==evt.media){continue;}
|
||||
window.dispatchEvent(new CustomEvent('screenlayoutchange',{detail:{name:key,status:evt.matches}}));}}};ScreenLayout.init();define("shared/screen_layout",(function(global){return function(){var ret,fn;return ret||global.ScreenLayout;};}(this)));define('modules/settings_service',['require','modules/page_transitions','modules/panel_cache','shared/screen_layout','shared/lazy_loader','settings'],function(require){'use strict';var PageTransitions=require('modules/page_transitions');var PanelCache=require('modules/panel_cache');var ScreenLayout=require('shared/screen_layout');var LazyLoader=require('shared/lazy_loader');var Settings=require('settings');var _rootPanelId=null;var _currentNavigation=null;var _navigating=false;var _pendingNavigationRequest=null;var _cachedNavigation=null;var _cachedNavigationOptions={};var _activityHandler=null;var _loadModulesForSubPanelsPromise=null;var _getAppNameToLink=function ss_get_app_name_to_link(panelId){var reAppName=/app:(\w+)/;var name=reAppName.exec(panelId);return name&&name[1];};var _getAppInfo=function ss_get_app_info(appName){var _supportedAppInFrame={keyboard:{},bluetooth:{}};var appInfo=_supportedAppInFrame[appName];if(!appInfo){return false;}
|
||||
var prefix='app://'+appName+'.gaiamobile.org/';var defaultSrc=prefix+'settings.html';var appMozapp=prefix+'manifest.webapp';var appSrc=appInfo.src?prefix+appInfo.src:defaultSrc;return{src:appSrc,mozapp:appMozapp};};var _isTabletAndLandscape=function ss_is_tablet_and_landscape(){return ScreenLayout.getCurrentLayout('tabletAndLandscaped');};var _retriveParentPanelId=function ss_retriveParentPanelId(panelId){var headerSelector='#'+panelId+' > gaia-header';var header=document.querySelector(headerSelector);return(header&&header.dataset.href||'').replace('#','');};var _shallCloseActivity=function ss_shallCloseActivity(panelId){if(panelId==='close'){return true;}
|
||||
if(!_currentNavigation){return false;}
|
||||
var parentPanelId=_retriveParentPanelId(_currentNavigation.panelId);return _currentNavigation.panelId===_activityHandler.targetPanelId&&panelId===parentPanelId;};var _transit=function ss_transit(oldPanel,newPanel,callback){var promise=new Promise(function(resolve){var wrappedCallback=function(){if(typeof callback==='function'){callback();}
|
||||
resolve();};if(_isTabletAndLandscape()){PageTransitions.twoColumn(oldPanel,newPanel,wrappedCallback);}else{PageTransitions.oneColumn(oldPanel,newPanel,wrappedCallback);}});return promise;};var _loadPanel=function ss_loadPanel(panelId,callback){var panelElement=document.getElementById(panelId);if(panelElement.dataset.rendered){callback();return;}
|
||||
panelElement.dataset.rendered=true;if(panelElement.dataset.requireSubPanels){var selector='section[id^="'+panelElement.id+'-"]';var subPanels=document.querySelectorAll(selector);for(var i=0,il=subPanels.length;i<il;i++){LazyLoader.load([subPanels[i]]);}
|
||||
LazyLoader.load([panelElement],callback);}else{LazyLoader.load([panelElement],callback);}};var _loadModulesForSubPanels=function ss_loadModules(panelId){if(panelId===_rootPanelId){return Promise.resolve();}else{if(!_loadModulesForSubPanelsPromise){_loadModulesForSubPanelsPromise=new Promise(function(resolve){require(['shared/async_storage'],resolve);});}
|
||||
return _loadModulesForSubPanelsPromise;}};var _onVisibilityChange=function ss_onVisibilityChange(){_handleVisibilityChange(!document.hidden);};var _handleVisibilityChange=function ss_onVisibilityChange(visible){if(!_currentNavigation){return;}
|
||||
var panel=_currentNavigation.panel;var element=_currentNavigation.panelElement;var options=_currentNavigation.options;if(!panel){return;}
|
||||
if(visible){panel.beforeShow(element,options);panel.show(element,options);}else{panel.beforeHide();panel.hide();}};var _navigate=function ss_navigate(panelId,options,callback){if(_currentNavigation&&_currentNavigation.panelId===panelId){callback();return;}
|
||||
_loadPanel(panelId,function(){PanelCache.get(panelId,function(panel){var newPanelElement=document.getElementById(panelId);var currentPanelId=_currentNavigation&&_currentNavigation.panelId;var currentPanelElement=_currentNavigation&&_currentNavigation.panelElement;var currentPanel=_currentNavigation&&_currentNavigation.panel;_cachedNavigation=_currentNavigation;_cachedNavigationOptions=options;options=options||{};_loadModulesForSubPanels(panelId).then(function(){if(currentPanel&¤tPanelId!==_rootPanelId){return currentPanel.beforeHide();}}).then(function(){return panel.beforeShow(newPanelElement,options);}).then(function(){return _transit(currentPanelElement,newPanelElement);}).then(function(){if(currentPanel&¤tPanelId!==_rootPanelId){return currentPanel.hide();}}).then(function(){return panel.show(newPanelElement,options);}).then(function(){_currentNavigation={panelId:panelId,panelElement:newPanelElement,panel:panel,options:options};Settings._currentPanel='#'+panelId;callback();});});});};return{reset:function ss_reset(){_rootPanelId=null;_currentNavigation=null;_cachedNavigation=null;_cachedNavigationOptions={};_activityHandler=null;_navigating=false;_pendingNavigationRequest=null;window.removeEventListener('visibilitychange',_onVisibilityChange);},init:function ss_init(options){if(options){_rootPanelId=options.rootPanelId||'root';_activityHandler=options.context&&options.context.activityHandler;}
|
||||
window.addEventListener('visibilitychange',_onVisibilityChange);},navigate:function ss_navigate(panelId,options,callback){if(_activityHandler&&_shallCloseActivity(panelId)){_activityHandler.postResult();return;}
|
||||
if(_navigating){_pendingNavigationRequest=arguments;return;}
|
||||
var appName=_getAppNameToLink(panelId);if(appName){var appInfo=_getAppInfo(appName);if(!appInfo){console.error('We only embed trust apps.');return;}
|
||||
panelId='frame';options=options||{};options.mozapp=appInfo.mozapp;options.src=appInfo.src;}
|
||||
_navigating=true;_navigate(panelId,options,(function(){_navigating=false;if(_pendingNavigationRequest){var args=_pendingNavigationRequest;_pendingNavigationRequest=null;this.navigate.apply(this,args);}
|
||||
if(callback){callback();}}).bind(this));},back:function ss_back(){if(_activityHandler){_activityHandler.postResult();}else if(_cachedNavigation){this.navigate(_cachedNavigation.panelId,_cachedNavigationOptions);_cachedNavigation=null;_cachedNavigationOptions={};}}};});require(['config/require'],function(){'use strict';define('boot',['require','shared/settings_listener','modules/mvvm/observable','modules/mvvm/observable_array','modules/base/event_emitter','modules/settings_service','shared/screen_layout','settings'],function(require){require('shared/settings_listener');require('modules/mvvm/observable');require('modules/mvvm/observable_array');require('modules/base/event_emitter');var SettingsService=require('modules/settings_service');var ScreenLayout=require('shared/screen_layout');var Settings=require('settings');function isInitialPanel(panel){if(Settings.isTabletAndLandscape()){return panel===Settings.initialPanelForTablet;}else{return panel===('#'+window.LaunchContext.initialPanelId);}}
|
||||
window.dispatchEvent(new CustomEvent('screenlayoutchange',{detail:{name:key,status:evt.matches}}));}}};ScreenLayout.init();define("shared/screen_layout",(function(global){return function(){var ret,fn;return ret||global.ScreenLayout;};}(this)));/**
|
||||
* SettingsService is a singleton that provides the navigation service. It
|
||||
* gets the corresponding panel module from PanelCache and call to its basic
|
||||
* functions when navigating.
|
||||
*
|
||||
* @module SettingsService
|
||||
*/
|
||||
/**
|
||||
* SettingsService is a singleton that provides the navigation service. It
|
||||
* gets the corresponding panel module from PanelCache and call to its basic
|
||||
* functions when navigating.
|
||||
*
|
||||
* @module SettingsService
|
||||
*/
|
||||
/**
|
||||
* SettingsService is a singleton that provides the navigation service. It
|
||||
* gets the corresponding panel module from PanelCache and call to its basic
|
||||
* functions when navigating.
|
||||
*
|
||||
* @module SettingsService
|
||||
*/
|
||||
/**
|
||||
* SettingsService is a singleton that provides the navigation service. It
|
||||
* gets the corresponding panel module from PanelCache and call to its basic
|
||||
* functions when navigating.
|
||||
*
|
||||
* @module SettingsService
|
||||
*/
|
||||
define('modules/settings_service',["require", "modules/page_transitions", "modules/panel_cache", "shared/screen_layout", "shared/lazy_loader", "settings"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PageTransitions = require('modules/page_transitions');
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var ScreenLayout = require('shared/screen_layout');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
var Settings = require('settings');
|
||||
|
||||
var _rootPanelId = null;
|
||||
/**
|
||||
* _currentNavigation caches information of the current panel including id,
|
||||
* element, module, and options.
|
||||
*/
|
||||
var _currentNavigation = null;
|
||||
var _navigating = false;
|
||||
var _pendingNavigationRequest = null;
|
||||
|
||||
var _cachedNavigation = null;
|
||||
var _cachedNavigationOptions = {};
|
||||
|
||||
var _activityHandler = null;
|
||||
|
||||
var _loadModulesForSubPanelsPromise = null;
|
||||
|
||||
var _getAppNameToLink = function ss_get_app_name_to_link(panelId) {
|
||||
var reAppName = /app:(\w+)/;
|
||||
var name = reAppName.exec(panelId);
|
||||
return name && name[1];
|
||||
};
|
||||
|
||||
var _getAppInfo = function ss_get_app_info(appName) {
|
||||
// We can customize the path for specific apps
|
||||
var _supportedAppInFrame = {
|
||||
keyboard: {},
|
||||
bluetooth: {}
|
||||
};
|
||||
|
||||
var appInfo = _supportedAppInFrame[appName];
|
||||
if (!appInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var prefix = 'app://' + appName + '.gaiamobile.org/';
|
||||
var defaultSrc = prefix + 'settings.html';
|
||||
var appMozapp = prefix + 'manifest.webapp';
|
||||
var appSrc = appInfo.src ? prefix + appInfo.src : defaultSrc;
|
||||
|
||||
return {
|
||||
src: appSrc,
|
||||
mozapp: appMozapp
|
||||
};
|
||||
};
|
||||
|
||||
var _isTabletAndLandscape = function ss_is_tablet_and_landscape() {
|
||||
return ScreenLayout.getCurrentLayout('tabletAndLandscaped');
|
||||
};
|
||||
|
||||
var _retriveParentPanelId = function ss_retriveParentPanelId(panelId) {
|
||||
var headerSelector = '#' + panelId + ' > gaia-header';
|
||||
var header = document.querySelector(headerSelector);
|
||||
return (header && header.dataset.href || '').replace('#', '');
|
||||
};
|
||||
|
||||
var _shallCloseActivity = function ss_shallCloseActivity(panelId) {
|
||||
// If we're handling an activity and the 'back' button is hit, close the
|
||||
// activity if the panel id to be navigated equals the parent panel id.
|
||||
|
||||
// This is for the root panel
|
||||
if (panelId === 'close') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_currentNavigation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the parent panel id of the current panel.
|
||||
var parentPanelId = _retriveParentPanelId(_currentNavigation.panelId);
|
||||
|
||||
// Close the activity if the current panel is the original target panel,
|
||||
// and the new panel is the parent panel of the current panel.
|
||||
return _currentNavigation.panelId === _activityHandler.targetPanelId &&
|
||||
panelId === parentPanelId;
|
||||
};
|
||||
|
||||
var _transit = function ss_transit(oldPanel, newPanel, callback) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var wrappedCallback = function() {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
|
||||
if (_isTabletAndLandscape()) {
|
||||
PageTransitions.twoColumn(oldPanel, newPanel, wrappedCallback);
|
||||
} else {
|
||||
PageTransitions.oneColumn(oldPanel, newPanel, wrappedCallback);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
};
|
||||
|
||||
var _loadPanel = function ss_loadPanel(panelId, callback) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], callback);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], callback);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var _loadModulesForSubPanels = function ss_loadModules(panelId) {
|
||||
if (panelId === _rootPanelId) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
if (!_loadModulesForSubPanelsPromise) {
|
||||
_loadModulesForSubPanelsPromise = new Promise(function(resolve) {
|
||||
require(['shared/async_storage'], resolve);
|
||||
});
|
||||
}
|
||||
return _loadModulesForSubPanelsPromise;
|
||||
}
|
||||
};
|
||||
|
||||
var _onVisibilityChange = function ss_onVisibilityChange() {
|
||||
_handleVisibilityChange(!document.hidden);
|
||||
};
|
||||
|
||||
/**
|
||||
* When the app becomes invisible, we should call to beforeHide and hide
|
||||
* functions of the current panel. When the app becomes visible, we should
|
||||
* call to beforeShow and show functions of the current panel with the
|
||||
* cached options.
|
||||
*/
|
||||
var _handleVisibilityChange = function ss_onVisibilityChange(visible) {
|
||||
if (!_currentNavigation) {
|
||||
return;
|
||||
}
|
||||
|
||||
var panel = _currentNavigation.panel;
|
||||
var element = _currentNavigation.panelElement;
|
||||
var options = _currentNavigation.options;
|
||||
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
panel.beforeShow(element, options);
|
||||
panel.show(element, options);
|
||||
} else {
|
||||
panel.beforeHide();
|
||||
panel.hide();
|
||||
}
|
||||
};
|
||||
|
||||
var _navigate = function ss_navigate(panelId, options, callback) {
|
||||
// Early return if the panel to be navigated is the same as the
|
||||
// current one.
|
||||
if (_currentNavigation && _currentNavigation.panelId === panelId) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
_loadPanel(panelId, function() {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
var newPanelElement = document.getElementById(panelId);
|
||||
var currentPanelId =
|
||||
_currentNavigation && _currentNavigation.panelId;
|
||||
var currentPanelElement =
|
||||
_currentNavigation && _currentNavigation.panelElement;
|
||||
var currentPanel = _currentNavigation && _currentNavigation.panel;
|
||||
|
||||
// Keep these to make sure we can use when going back
|
||||
_cachedNavigation = _currentNavigation;
|
||||
_cachedNavigationOptions = options;
|
||||
|
||||
// Prepare options and calls to the panel object's before
|
||||
// show function.
|
||||
options = options || {};
|
||||
|
||||
// 0. start the chain
|
||||
_loadModulesForSubPanels(panelId)
|
||||
// 1. beforeHide previous panel
|
||||
.then(function() {
|
||||
// We don't deactivate the root panel.
|
||||
if (currentPanel && currentPanelId !== _rootPanelId) {
|
||||
return currentPanel.beforeHide();
|
||||
}
|
||||
})
|
||||
// 2. beforeShow next panel
|
||||
.then(function() {
|
||||
return panel.beforeShow(newPanelElement, options);
|
||||
})
|
||||
// 3. do the transition
|
||||
.then(function() {
|
||||
return _transit(currentPanelElement, newPanelElement);
|
||||
})
|
||||
// 4. hide previous panel
|
||||
.then(function() {
|
||||
// We don't deactivate the root panel.
|
||||
if (currentPanel && currentPanelId !== _rootPanelId) {
|
||||
return currentPanel.hide();
|
||||
}
|
||||
})
|
||||
// 5. show next panel
|
||||
.then(function() {
|
||||
return panel.show(newPanelElement, options);
|
||||
})
|
||||
// 6. keep information
|
||||
.then(function() {
|
||||
// Update the current navigation object
|
||||
_currentNavigation = {
|
||||
panelId: panelId,
|
||||
panelElement: newPanelElement,
|
||||
panel: panel,
|
||||
options: options
|
||||
};
|
||||
|
||||
// XXX we need to remove this line in the future
|
||||
// to make sure we won't manipulate Settings
|
||||
// directly
|
||||
Settings._currentPanel = '#' + panelId;
|
||||
callback();
|
||||
}).catch(function(error) {
|
||||
console.error('Settings panel ' + panelId + ':', error);
|
||||
newPanelElement.classList.remove('current');
|
||||
if (currentPanelElement) currentPanelElement.classList.add('current');
|
||||
// Release the navigation lock even when a hardware query fails.
|
||||
callback(error);
|
||||
if (window.VulpesGaiaUI) {
|
||||
window.VulpesGaiaUI.notice(document.documentElement.lang.indexOf('fr') === 0 ?
|
||||
'Ce panneau n’a pas pu être chargé. Vous pouvez revenir aux autres paramètres.' :
|
||||
'This panel could not be loaded. You can return to the other settings.');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
reset: function ss_reset() {
|
||||
_rootPanelId = null;
|
||||
_currentNavigation = null;
|
||||
_cachedNavigation = null;
|
||||
_cachedNavigationOptions = {};
|
||||
_activityHandler = null;
|
||||
_navigating = false;
|
||||
_pendingNavigationRequest = null;
|
||||
window.removeEventListener('visibilitychange', _onVisibilityChange);
|
||||
},
|
||||
|
||||
/**
|
||||
* Init SettingsService.
|
||||
*
|
||||
* @alias module:SettingsService#init
|
||||
* @param {Object} options
|
||||
* @param {String} options.rootPanelId
|
||||
* Panel with the specified id is assumed to be be kept on
|
||||
* on the screen always. We don't call to its hide and
|
||||
* beforeHide functions.
|
||||
* @param {Object} options.context
|
||||
* The launch context specifying the default panel and the
|
||||
* activity handler if the app is invoked by web
|
||||
* activities.
|
||||
* @param {String} options.context.initialPanelId
|
||||
* @param {ActivityHandler} options.context.activityHandler
|
||||
*/
|
||||
init: function ss_init(options) {
|
||||
if (options) {
|
||||
_rootPanelId = options.rootPanelId || 'root';
|
||||
_activityHandler = options.context && options.context.activityHandler;
|
||||
}
|
||||
|
||||
window.addEventListener('visibilitychange', _onVisibilityChange);
|
||||
},
|
||||
|
||||
/**
|
||||
* Navigate to a panel with options. The navigation transition is
|
||||
* determined based on the current screen size and orientation.
|
||||
*
|
||||
* @alias module:SettingsService#navigate
|
||||
* @param {String} panelId
|
||||
* @param {Object} options
|
||||
* @param {Function} callback
|
||||
*/
|
||||
navigate: function ss_navigate(panelId, options, callback) {
|
||||
// Check if the app is invoked by web activity and shall post result.
|
||||
if (_activityHandler && _shallCloseActivity(panelId)) {
|
||||
_activityHandler.postResult();
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the navigation request if it is navigating.
|
||||
if (_navigating) {
|
||||
_pendingNavigationRequest = arguments;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we find out the link includes information about app's name,
|
||||
// it means that we are going to embed the app into our app.
|
||||
//
|
||||
// In this way, we have to navigate to `frame` panel and embed it.
|
||||
var appName = _getAppNameToLink(panelId);
|
||||
if (appName) {
|
||||
var appInfo = _getAppInfo(appName);
|
||||
|
||||
if (!appInfo) {
|
||||
console.error('We only embed trust apps.');
|
||||
return;
|
||||
}
|
||||
|
||||
panelId = 'frame';
|
||||
options = options || {};
|
||||
options.mozapp = appInfo.mozapp;
|
||||
options.src = appInfo.src;
|
||||
}
|
||||
|
||||
_navigating = true;
|
||||
_navigate(panelId, options, (function() {
|
||||
_navigating = false;
|
||||
|
||||
// Navigate to the pending navigation if any.
|
||||
if (_pendingNavigationRequest) {
|
||||
var args = _pendingNavigationRequest;
|
||||
_pendingNavigationRequest = null;
|
||||
this.navigate.apply(this, args);
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Go back to previous panel
|
||||
*
|
||||
* @alias module:SettingsService#back
|
||||
*/
|
||||
back: function ss_back() {
|
||||
if (_activityHandler) {
|
||||
_activityHandler.postResult();
|
||||
} else if (_cachedNavigation) {
|
||||
this.navigate(_cachedNavigation.panelId, _cachedNavigationOptions);
|
||||
_cachedNavigation = null;
|
||||
_cachedNavigationOptions = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
require(['config/require'],function(){'use strict';define('boot',['require','shared/settings_listener','modules/mvvm/observable','modules/mvvm/observable_array','modules/base/event_emitter','modules/settings_service','shared/screen_layout','settings'],function(require){require('shared/settings_listener');require('modules/mvvm/observable');require('modules/mvvm/observable_array');require('modules/base/event_emitter');var SettingsService=require('modules/settings_service');var ScreenLayout=require('shared/screen_layout');var Settings=require('settings');function isInitialPanel(panel){if(Settings.isTabletAndLandscape()){return panel===Settings.initialPanelForTablet;}else{return panel===('#'+window.LaunchContext.initialPanelId);}}
|
||||
window.addEventListener('panelready',function onPanelReady(e){if(!isInitialPanel(e.detail.current)){return;}
|
||||
var initialPanelHandler=window.LaunchContext.initialPanelHandler;if(initialPanelHandler){initialPanelHandler.release();var pendingTargetPanel=initialPanelHandler.pendingTargetPanel;switch(pendingTargetPanel){case'call':var mozMobileConnections=navigator.mozMobileConnections;if(mozMobileConnections&&mozMobileConnections.length>1){pendingTargetPanel='call-iccs';}
|
||||
SettingsService.navigate(pendingTargetPanel);break;default:if(pendingTargetPanel){SettingsService.navigate(pendingTargetPanel);}
|
||||
|
|
|
|||
419
overrides/settings.gaiamobile.org/js/modules/date_time.js
Normal file
419
overrides/settings.gaiamobile.org/js/modules/date_time.js
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
/**
|
||||
* DateTime is a singleton that caches date, time, timezone values for
|
||||
* Date & Time panel fast access
|
||||
*
|
||||
* @module DateTime
|
||||
*/
|
||||
/**
|
||||
* DateTime is a singleton that caches date, time, timezone values for
|
||||
* Date & Time panel fast access
|
||||
*
|
||||
* @module DateTime
|
||||
*/
|
||||
/**
|
||||
* DateTime is a singleton that caches date, time, timezone values for
|
||||
* Date & Time panel fast access
|
||||
*
|
||||
* @module DateTime
|
||||
*/
|
||||
/**
|
||||
* DateTime is a singleton that caches date, time, timezone values for
|
||||
* Date & Time panel fast access
|
||||
*
|
||||
* @module DateTime
|
||||
*/
|
||||
/**
|
||||
* DateTime is a singleton that caches date, time, timezone values for
|
||||
* Date & Time panel fast access
|
||||
*
|
||||
* @module DateTime
|
||||
*/
|
||||
/**
|
||||
* DateTime is a singleton that caches date, time, timezone values for
|
||||
* Date & Time panel fast access
|
||||
*
|
||||
* @module DateTime
|
||||
*/
|
||||
/**
|
||||
* DateTime is a singleton that caches date, time, timezone values for
|
||||
* Date & Time panel fast access
|
||||
*
|
||||
* @module DateTime
|
||||
*/
|
||||
/**
|
||||
* DateTime is a singleton that caches date, time, timezone values for
|
||||
* Date & Time panel fast access
|
||||
*
|
||||
* @module DateTime
|
||||
*/
|
||||
define('modules/date_time',["require", "modules/settings_cache", "modules/base/module", "modules/mvvm/observable"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var Module = require('modules/base/module');
|
||||
var Observable = require('modules/mvvm/observable');
|
||||
var settings = window.navigator.mozSettings;
|
||||
|
||||
// keys
|
||||
var _kClockAutoEnabled = 'time.clock.automatic-update.enabled';
|
||||
var _kTimezoneAutoEnabled = 'time.timezone.automatic-update.enabled';
|
||||
var _kClockAutoAvailable = 'time.clock.automatic-update.available';
|
||||
var _kTimezoneAutoAvailable = 'time.timezone.automatic-update.available';
|
||||
var _kTimezone = 'time.timezone';
|
||||
var _kUserSelected = 'time.timezone.user-selected';
|
||||
var _kLocaleTime = 'locale.hour12';
|
||||
|
||||
// handler
|
||||
var _updateDateTimeout = null;
|
||||
var _updateTimeTimeout = null;
|
||||
|
||||
var DateTime = Module.create(function DateTime() {
|
||||
this.super(Observable).call(this);
|
||||
this._init();
|
||||
}).extend(Observable);
|
||||
|
||||
|
||||
// public observers
|
||||
/**
|
||||
* Auto setting Date & Time.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
* @type {Boolean}
|
||||
*/
|
||||
Observable.defineObservableProperty(DateTime.prototype, 'clockAutoEnabled', {
|
||||
readonly: true,
|
||||
value: true
|
||||
});
|
||||
|
||||
/**
|
||||
* SIM provide Auto clock function.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
* @type {Boolean}
|
||||
*/
|
||||
Observable.defineObservableProperty(DateTime.prototype,
|
||||
'clockAutoAvailable', {
|
||||
readonly: true,
|
||||
value: true
|
||||
});
|
||||
|
||||
/**
|
||||
* SIM provide Auto timezone function.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
* @type {Boolean}
|
||||
*/
|
||||
Observable.defineObservableProperty(DateTime.prototype,
|
||||
'timezoneAutoAvailable', {
|
||||
readonly: true,
|
||||
value: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Current Timezone.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
* @type {String}
|
||||
*/
|
||||
Observable.defineObservableProperty(DateTime.prototype, 'timezone', {
|
||||
readonly: true,
|
||||
value: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* User Selected Timezone.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
* @type {String}
|
||||
*/
|
||||
Observable.defineObservableProperty(DateTime.prototype,
|
||||
'userSelectedTimezone', {
|
||||
readonly: true,
|
||||
value: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* Current Date string.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
* @type {String}
|
||||
*/
|
||||
Observable.defineObservableProperty(DateTime.prototype, 'date', {
|
||||
readonly: true,
|
||||
value: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* Localized current time string.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
* @type {String}
|
||||
*/
|
||||
Observable.defineObservableProperty(DateTime.prototype, 'time', {
|
||||
readonly: true,
|
||||
value: ''
|
||||
});
|
||||
|
||||
/**
|
||||
* Current AM/PM or 24 state.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
* @type {Boolean}
|
||||
*/
|
||||
Observable.defineObservableProperty(DateTime.prototype, 'currentHour12', {
|
||||
readonly: true,
|
||||
value: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Init DateTime module.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DateTime
|
||||
*/
|
||||
DateTime.prototype._init = function() {
|
||||
this._mozTime = window.navigator.mozTime;
|
||||
|
||||
this._boundSetTimeAutoEnabled = function(event) {
|
||||
this._clockAutoEnabled = event.settingValue;
|
||||
// also set timezoneAutoEnabled
|
||||
this._setTimezoneAutoEnabled(event.settingValue);
|
||||
}.bind(this);
|
||||
this._boundSetClockAutoAvailable = function(event) {
|
||||
this._clockAutoAvailable = event.settingValue;
|
||||
}.bind(this);
|
||||
this._boundSetTimezoneAutoAvailable = function(event) {
|
||||
this._timezoneAutoAvailable = event.settingValue;
|
||||
}.bind(this);
|
||||
this._boundSetTimezone = function(event) {
|
||||
this._timezone = event.settingValue;
|
||||
}.bind(this);
|
||||
this._boundUserSelectedTimezone = function(event) {
|
||||
this._userSelectedTimezone = event.settingValue;
|
||||
}.bind(this);
|
||||
this._boundCurrentHour12 = function(event) {
|
||||
this._currentHour12 = event.settingValue;
|
||||
|
||||
this._autoUpdateDateTime();
|
||||
}.bind(this);
|
||||
|
||||
this._getDefaults();
|
||||
this._attachListeners();
|
||||
};
|
||||
|
||||
DateTime.prototype._attachListeners = function() {
|
||||
window.navigator.mozSettings.addObserver(_kClockAutoEnabled,
|
||||
this._boundSetTimeAutoEnabled);
|
||||
window.navigator.mozSettings.addObserver(_kClockAutoAvailable,
|
||||
this._boundSetClockAutoAvailable);
|
||||
window.navigator.mozSettings.addObserver(_kTimezoneAutoAvailable,
|
||||
this._boundSetTimezoneAutoAvailable);
|
||||
window.navigator.mozSettings.addObserver(_kTimezone,
|
||||
this._boundSetTimezone);
|
||||
window.navigator.mozSettings.addObserver(_kUserSelected,
|
||||
this._boundUserSelectedTimezone);
|
||||
window.navigator.mozSettings.addObserver(_kLocaleTime,
|
||||
this._boundCurrentHour12);
|
||||
// Listen to 'DOMRetranslated' to get the latest l10n resource.
|
||||
document.addEventListener('DOMRetranslated', this);
|
||||
// Listen to 'moztimechange' to update clock
|
||||
window.addEventListener('moztimechange', this);
|
||||
};
|
||||
|
||||
DateTime.prototype.handleEvent = function(evt) {
|
||||
switch (evt.type) {
|
||||
case 'moztimechange':
|
||||
this._autoUpdateDateTime();
|
||||
break;
|
||||
case 'DOMRetranslated':
|
||||
var d = new Date();
|
||||
this._date = this._formatDate(d);
|
||||
this._time = this._formatTime(d);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* fill in default values to public observers
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DateTime
|
||||
*/
|
||||
DateTime.prototype._getDefaults = function() {
|
||||
SettingsCache.getSettings(function(results) {
|
||||
this._clockAutoEnabled = results[_kClockAutoEnabled];
|
||||
this._clockAutoAvailable = results[_kClockAutoAvailable];
|
||||
this._timezoneAutoAvailable = results[_kTimezoneAutoAvailable];
|
||||
this._timezone = results[_kTimezone];
|
||||
this._currentHour12 = results[_kLocaleTime];
|
||||
// render date/time after get proper format
|
||||
this._autoUpdateDateTime();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Auto update date and time
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DateTime
|
||||
*/
|
||||
DateTime.prototype._autoUpdateDateTime = function() {
|
||||
window.clearTimeout(_updateDateTimeout);
|
||||
window.clearTimeout(_updateTimeTimeout);
|
||||
this._autoUpdateDate();
|
||||
this._autoUpdateTime();
|
||||
};
|
||||
|
||||
/**
|
||||
* Update Date periodically.
|
||||
*
|
||||
* DONT call this function directly,
|
||||
* call _autoUpdateDateTime instead.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DateTime
|
||||
*/
|
||||
DateTime.prototype._autoUpdateDate = function() {
|
||||
var d = new Date();
|
||||
this._date = this._formatDate(d);
|
||||
|
||||
var remainMillisecond = (24 - d.getHours()) * 3600 * 1000 -
|
||||
d.getMinutes() * 60 * 1000 - d.getMilliseconds();
|
||||
_updateDateTimeout = window.setTimeout(
|
||||
function updateDateTimeout() {
|
||||
this._autoUpdateDate();
|
||||
}.bind(this), remainMillisecond);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update Time periodically.
|
||||
*
|
||||
* DONT call this function directly,
|
||||
* call _autoUpdateDateTime instead.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DateTime
|
||||
*/
|
||||
DateTime.prototype._autoUpdateTime = function() {
|
||||
var d = new Date();
|
||||
this._time = this._formatTime(d);
|
||||
|
||||
var remainMillisecond = (60 - d.getSeconds()) * 1000;
|
||||
_updateTimeTimeout = window.setTimeout(
|
||||
function updateTimeTimeout() {
|
||||
this._autoUpdateTime();
|
||||
}.bind(this), remainMillisecond);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return preffered date format.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DateTime
|
||||
* @param {Date} input as Date
|
||||
* @returns {String}
|
||||
*/
|
||||
DateTime.prototype._formatDate = function(d) {
|
||||
return d.toLocaleString(navigator.languages, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return preffered time format.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DateTime
|
||||
* @param {Date} input as Date
|
||||
* @returns {String}
|
||||
*/
|
||||
DateTime.prototype._formatTime = function(d) {
|
||||
return d.toLocaleString(navigator.languages, {
|
||||
hour12: navigator.mozHour12,
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Change system time
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
*/
|
||||
DateTime.prototype.setTime = function(type, value) {
|
||||
var newDate = new Date();
|
||||
var tokens;
|
||||
|
||||
switch (type) {
|
||||
case 'date':
|
||||
tokens = value.split('-');
|
||||
newDate.setFullYear(Number(tokens[0]), Number(tokens[1]) - 1, Number(tokens[2]));
|
||||
break;
|
||||
|
||||
case 'time':
|
||||
tokens = value.split(':');
|
||||
newDate.setHours(parseInt(tokens[0]));
|
||||
newDate.setMinutes(parseInt(tokens[1]));
|
||||
break;
|
||||
}
|
||||
if (!this._mozTime) return;
|
||||
return this._mozTime.set(newDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Change TimezoneAutoEnabled settings.
|
||||
*
|
||||
* @access private
|
||||
* @memberOf DateTime
|
||||
*/
|
||||
DateTime.prototype._setTimezoneAutoEnabled = function(enabled) {
|
||||
var cset = {};
|
||||
cset[_kTimezoneAutoEnabled] = enabled;
|
||||
settings.createLock().set(cset);
|
||||
},
|
||||
|
||||
/**
|
||||
* Change UserSelectedTimezone settings.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
*/
|
||||
DateTime.prototype.setUserSelectedTimezone = function(selected) {
|
||||
var cset = {};
|
||||
cset[_kTimezone] = selected;
|
||||
settings.createLock().set(cset);
|
||||
};
|
||||
|
||||
/**
|
||||
* Change hour12 settings.
|
||||
* this valie is used to determine 12 or 24 time format.
|
||||
*
|
||||
* @access public
|
||||
* @memberOf DateTime
|
||||
*/
|
||||
DateTime.prototype.setCurrentHour12 = function(selected) {
|
||||
var cset = {};
|
||||
cset[_kLocaleTime] = selected;
|
||||
settings.createLock().set(cset);
|
||||
};
|
||||
|
||||
DateTime.prototype.getHour12ForCurrentLocale = function() {
|
||||
var format = new Intl.DateTimeFormat(navigator.languages, {
|
||||
hour: 'numeric'
|
||||
});
|
||||
return format.resolvedOptions().hour12;
|
||||
};
|
||||
|
||||
return DateTime();
|
||||
});
|
||||
483
overrides/settings.gaiamobile.org/js/modules/dialog_service.js
Normal file
483
overrides/settings.gaiamobile.org/js/modules/dialog_service.js
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
|
||||
define('modules/dialog_manager',["require", "modules/panel_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var DialogManager = function() {
|
||||
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
||||
|
||||
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
||||
};
|
||||
|
||||
DialogManager.prototype = {
|
||||
/**
|
||||
* load panel based on passed in panelId
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_loadPanel: function dm__loadPanel(panelId) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* promised version of PanelCache.get()
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_getPanel: function dm__getPanel(panelId) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
resolve(panel);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is used to control visibility of overlayDOM
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {Boolean} show
|
||||
*/
|
||||
_showOverlay: function dm__showOverlay(show) {
|
||||
this._overlayDOM.hidden = !show;
|
||||
},
|
||||
|
||||
/**
|
||||
* It is used to control the timing of transitions so that we can make sure
|
||||
* whether animation is done or not.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_transit: function dm__transit(method, dialog, options) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var panel = dialog.panel;
|
||||
var completed = false;
|
||||
var fallback;
|
||||
function finish(event) {
|
||||
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
clearTimeout(fallback);
|
||||
panel.removeEventListener('transitionend', finish);
|
||||
panel.removeEventListener('transitioncancel', finish);
|
||||
if (method === 'close') panel.hidden = true;
|
||||
resolve();
|
||||
}
|
||||
if (method === 'open') panel.hidden = false;
|
||||
setTimeout(function() {
|
||||
panel.addEventListener('transitionend', finish);
|
||||
panel.addEventListener('transitioncancel', finish);
|
||||
panel.classList.toggle('current', method === 'open');
|
||||
var style = window.getComputedStyle(panel);
|
||||
function milliseconds(value) {
|
||||
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
||||
}
|
||||
var durations = style.transitionDuration.split(',').map(milliseconds);
|
||||
var delays = style.transitionDelay.split(',').map(milliseconds);
|
||||
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
||||
return value + delays[index % delays.length];
|
||||
}));
|
||||
// Reduced motion, display changes or interrupted transitions may
|
||||
// omit the visibility event. Always release the dialog queue.
|
||||
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
||||
}, 150);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to open panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_open: function dm__open(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: load panel
|
||||
return self._loadPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function() {
|
||||
// 2: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 3: call beforeShow
|
||||
foundPanel = panel;
|
||||
return foundPanel.beforeShow(dialog.panel, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 4. UI stuffs + transition
|
||||
dialog.init();
|
||||
dialog.initUI();
|
||||
dialog.bindEvents();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(true);
|
||||
}
|
||||
|
||||
return self._transit('open', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. show that panel as a dialog
|
||||
return foundPanel.show(dialog.panel, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to close panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_close: function dm__close(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
var cachedResult;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 2: Let's validate to see whether we can close this dialog or not.
|
||||
foundPanel = panel;
|
||||
|
||||
var promise;
|
||||
// custom dialog - onSubmit
|
||||
if (foundPanel.onSubmit && options._type === 'submit') {
|
||||
promise = foundPanel.onSubmit();
|
||||
// custom dialog - onCancel
|
||||
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
||||
promise = foundPanel.onCancel();
|
||||
// if no onSubmit & onCancel, pass directly
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function(result) {
|
||||
cachedResult = result;
|
||||
|
||||
// 3: call beforeHide
|
||||
return foundPanel.beforeHide();
|
||||
})
|
||||
.then(function() {
|
||||
// 4. transition
|
||||
return self._transit('close', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. call hide
|
||||
return foundPanel.hide();
|
||||
})
|
||||
.then(function() {
|
||||
// 6. Get result and cleanup dialog
|
||||
var result;
|
||||
|
||||
// for prompt dialog, we have to get its own result from input text.
|
||||
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
||||
result = dialog.getResult();
|
||||
} else if (cachedResult) {
|
||||
result = cachedResult;
|
||||
}
|
||||
|
||||
dialog.cleanup();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* It is a bridge to call open or close function.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_navigate: function dm__navigate(method, dialog, options) {
|
||||
method = (method === 'open') ? '_open' : '_close';
|
||||
return this[method](dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to open dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
open: function dm_open(dialog, options) {
|
||||
return this._navigate('open', dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to close dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
close: function dm_close(dialog, type, options) {
|
||||
options._type = type;
|
||||
return this._navigate('close', dialog, options);
|
||||
}
|
||||
};
|
||||
|
||||
var dialogManager = new DialogManager();
|
||||
return dialogManager;
|
||||
});
|
||||
define('modules/dialog/base_dialog',['require'],function(require){'use strict';var BaseDialog=function(panelDOM,options){this.panel=panelDOM;this._options=options||{};};BaseDialog.prototype.DIALOG_CLASS='dialog';BaseDialog.prototype.TRANSITION_CLASS='fade';BaseDialog.prototype.SUBMIT_BUTTON_SELECTOR='[type="submit"]';BaseDialog.prototype.CANCEL_BUTTON_SELECTOR='[type="reset"]';BaseDialog.prototype.MESSAGE_SELECTOR='.settings-dialog-message';BaseDialog.prototype.TITLE_SELECTOR='.settings-dialog-title';BaseDialog.prototype.init=function bd_init(){this.TRANSITION_CLASS=this._options.transition||this.TRANSITION_CLASS;this.panel.classList.add(this.DIALOG_CLASS);this.panel.classList.add(this.TRANSITION_CLASS);};BaseDialog.prototype.initUI=function bd_initUI(){var message=this._options.message;var title=this._options.title;var submitButton=this._options.submitButton;var cancelButton=this._options.cancelButton;this._updateMessage(message);this._updateTitle(title);this._updateSubmitButton(submitButton);this._updateCancelButton(cancelButton);};BaseDialog.prototype.bindEvents=function bd_bindEvent(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};BaseDialog.prototype._updateMessage=function bd__updateMessage(message){var messageDOM=this.panel.querySelector(this.MESSAGE_SELECTOR);if(messageDOM&&message){message=this._getWrapL10nObject(message);document.l10n.setAttributes(messageDOM,message.id,message.args);}};BaseDialog.prototype._updateTitle=function bd__updateTitle(title){var titleDOM=this.panel.querySelector(this.TITLE_SELECTOR);if(titleDOM&&title){title=this._getWrapL10nObject(title);document.l10n.setAttributes(titleDOM,title.id,title.args);}};BaseDialog.prototype._updateSubmitButton=function bd__update(options){var buttonDOM=this.getSubmitButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'recommend';}};BaseDialog.prototype._updateCancelButton=function bd__updateText(options){var buttonDOM=this.getCancelButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'';}};BaseDialog.prototype._getWrapL10nObject=function bd__getWrapL10nObject(input){if(typeof input==='string'){return{id:input,args:null};}else if(typeof input==='object'){if(typeof input.id==='undefined'){throw new Error('You forgot to put l10nId - '+
|
||||
JSON.stringify(input));}else{return{id:input.id,args:input.args||null,style:input.style};}}else{throw new Error('You are using the wrong L10nObject, '+'please check its format again');}};BaseDialog.prototype.getDOM=function bd_getDOM(){return this.panel;};BaseDialog.prototype.getSubmitButton=function bd_getSubmitButton(){return this.panel.querySelector(this.SUBMIT_BUTTON_SELECTOR);};BaseDialog.prototype.getCancelButton=function bd_getCancelButton(){return this.panel.querySelector(this.CANCEL_BUTTON_SELECTOR);};BaseDialog.prototype.cleanup=function bd_cleanup(){if(this.DIALOG_CLASS!=='panel-dialog'){this._updateTitle('settings-'+this.DIALOG_CLASS+'-header');this._updateSubmitButton('ok');this._updateCancelButton('cancel');}
|
||||
this.panel.classList.remove(this.DIALOG_CLASS);this.panel.classList.remove(this.TRANSITION_CLASS);};return BaseDialog;});define('modules/dialog/panel_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PanelDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PanelDialog.prototype=Object.create(BaseDialog.prototype);PanelDialog.prototype.constructor=PanelDialog;PanelDialog.prototype.DIALOG_CLASS='panel-dialog';PanelDialog.prototype.TRANSITION_CLASS='fade';return function ctor_PanelDialog(panelDOM,options){return new PanelDialog(panelDOM,options);};});define('modules/dialog/alert_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var AlertDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};AlertDialog.prototype=Object.create(BaseDialog.prototype);AlertDialog.prototype.constructor=AlertDialog;AlertDialog.prototype.DIALOG_CLASS='alert-dialog';AlertDialog.prototype.TRANSITION_CLASS='fade';AlertDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};};return function ctor_alertDialog(panelDOM,options){return new AlertDialog(panelDOM,options);};});define('modules/dialog/confirm_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var ConfirmDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};ConfirmDialog.prototype=Object.create(BaseDialog.prototype);ConfirmDialog.prototype.constructor=ConfirmDialog;ConfirmDialog.prototype.DIALOG_CLASS='confirm-dialog';ConfirmDialog.prototype.TRANSITION_CLASS='fade';ConfirmDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};return function ctor_confirmDialog(panelDOM,options){return new ConfirmDialog(panelDOM,options);};});define('modules/dialog/prompt_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PromptDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PromptDialog.prototype=Object.create(BaseDialog.prototype);PromptDialog.prototype.constructor=PromptDialog;PromptDialog.prototype.DIALOG_CLASS='prompt-dialog';PromptDialog.prototype.TRANSITION_CLASS='fade';PromptDialog.prototype.INPUT_SELECTOR='.settings-dialog-input';PromptDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};PromptDialog.prototype.initUI=function(){BaseDialog.prototype.initUI.call(this);this.getInput().value=this._options.defaultValue||'';};PromptDialog.prototype.getInput=function(){return this.panel.querySelector(this.INPUT_SELECTOR);};PromptDialog.prototype.getResult=function(){return this.getInput().value;};return function ctor_promptDialog(panelDOM,options){return new PromptDialog(panelDOM,options);};});define('modules/dialog_service',["require", "settings", "modules/defer", "modules/dialog_manager", "modules/dialog/panel_dialog", "modules/dialog/alert_dialog", "modules/dialog/confirm_dialog", "modules/dialog/prompt_dialog"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var Settings = require('settings');
|
||||
var Defer = require('modules/defer');
|
||||
var DialogManager = require('modules/dialog_manager');
|
||||
|
||||
var PanelDialog = require('modules/dialog/panel_dialog');
|
||||
var AlertDialog = require('modules/dialog/alert_dialog');
|
||||
var ConfirmDialog = require('modules/dialog/confirm_dialog');
|
||||
var PromptDialog = require('modules/dialog/prompt_dialog');
|
||||
|
||||
var DialogService = function() {
|
||||
this._navigating = false;
|
||||
this._pendingRequests = [];
|
||||
this._settingsAlertDialogId = 'settings-alert-dialog';
|
||||
this._settingsBaseDialogId = 'settings-base-dialog';
|
||||
this._settingsConfirmDialogId = 'settings-confirm-dialog';
|
||||
this._settingsPromptDialogId = 'settings-prompt-dialog';
|
||||
};
|
||||
|
||||
DialogService.prototype = {
|
||||
/**
|
||||
* Alert dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
alert: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsAlertDialogId, {
|
||||
type: 'alert',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsConfirmDialogId, {
|
||||
type: 'confirm',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
prompt: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsPromptDialogId, {
|
||||
type: 'prompt',
|
||||
message: message,
|
||||
title: options.title,
|
||||
defaultValue: options.defaultValue,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel dialog. If you are going to show any panel as a dialog,
|
||||
* you have to use this method to show them.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} panelId
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
show: function dm_show(panelId, userOptions, _pendingDefer) {
|
||||
var self = this;
|
||||
var defer;
|
||||
var dialog;
|
||||
var dialogDOM = document.getElementById(panelId);
|
||||
var currentPanel = Settings.currentPanel;
|
||||
var options = userOptions || {};
|
||||
|
||||
if (_pendingDefer) {
|
||||
defer = _pendingDefer;
|
||||
} else {
|
||||
defer = Defer();
|
||||
}
|
||||
|
||||
if (this._navigating) {
|
||||
this._pendingRequests.push({
|
||||
defer: defer,
|
||||
panelId: panelId,
|
||||
userOptions: userOptions
|
||||
});
|
||||
} else {
|
||||
if ('#' + panelId === currentPanel) {
|
||||
defer.reject('You are showing the same panel #' + panelId);
|
||||
} else {
|
||||
var opening;
|
||||
var closing = false;
|
||||
function close(type) {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
// A tap during the opening animation must not run hide/show
|
||||
// concurrently, or a queued dialog can inherit stale form values.
|
||||
Promise.resolve(opening).then(function() {
|
||||
return DialogManager.close(dialog, type, options);
|
||||
}).then(function(result) {
|
||||
self._navigating = false;
|
||||
defer.resolve({type: type, value: result});
|
||||
self._execPendingRequest();
|
||||
}, function(error) {
|
||||
// Panel validation rejects to keep the form open. Allow a
|
||||
// correction or cancellation without rejecting the show promise.
|
||||
closing = false;
|
||||
dialogDOM.dispatchEvent(new CustomEvent('dialogvalidationfailed', {
|
||||
detail: error
|
||||
}));
|
||||
});
|
||||
}
|
||||
options.onWrapSubmit = function() { close('submit'); };
|
||||
options.onWrapCancel = function() { close('cancel'); };
|
||||
|
||||
switch (options.type) {
|
||||
case 'alert':
|
||||
dialog = AlertDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'confirm':
|
||||
dialog = ConfirmDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'prompt':
|
||||
dialog = PromptDialog(dialogDOM, options);
|
||||
break;
|
||||
default:
|
||||
dialog = PanelDialog(dialogDOM, options);
|
||||
break;
|
||||
}
|
||||
this._navigating = true;
|
||||
opening = DialogManager.open(dialog, options);
|
||||
opening.catch(function(error) {
|
||||
// A missing host API must not lock every later Settings dialog.
|
||||
dialogDOM.classList.remove('current');
|
||||
dialogDOM.hidden = true;
|
||||
var overlay = document.querySelector('.settings-dialog-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
self._navigating = false;
|
||||
defer.reject(error);
|
||||
self._execPendingRequest();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method can help us pop up any pending request and would try to
|
||||
* show it after previous request was done.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access private
|
||||
*/
|
||||
_execPendingRequest: function() {
|
||||
var request = this._pendingRequests.pop();
|
||||
if (request) {
|
||||
this.show(request.panelId, request.userOptions, request.defer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dialogService = new DialogService();
|
||||
return dialogService;
|
||||
});
|
||||
|
|
@ -1,4 +1,52 @@
|
|||
|
||||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
*
|
||||
* @module SliderHandler
|
||||
*/
|
||||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
*
|
||||
* @module SliderHandler
|
||||
*/
|
||||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
*
|
||||
* @module SliderHandler
|
||||
*/
|
||||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
*
|
||||
* @module SliderHandler
|
||||
*/
|
||||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
*
|
||||
* @module SliderHandler
|
||||
*/
|
||||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
*
|
||||
* @module SliderHandler
|
||||
*/
|
||||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
*
|
||||
* @module SliderHandler
|
||||
*/
|
||||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
*
|
||||
* @module SliderHandler
|
||||
*/
|
||||
/**
|
||||
* Update setting value based on slider position.
|
||||
* Base on sound panel's implementation.
|
||||
|
|
@ -128,6 +176,5 @@ define('panels/display/slider_handler',["require"],function(require) {
|
|||
return new SliderHandler();
|
||||
};
|
||||
});
|
||||
|
||||
define('panels/display/display',['require','shared/settings_listener','panels/display/slider_handler'],function(require){'use strict';var SettingsListener=require('shared/settings_listener');var SliderHandler=require('panels/display/slider_handler');const AUTO_BRIGHTNESS_SETTING='screen.automatic-brightness';const SCREEN_BRIGHTNESS='screen.brightness';var Display=function(){this.elements=null;};Display.prototype={init:function d_init(elements,data){this.elements=elements;this.initBrightnessItems(data);SliderHandler().init(elements.brightnessManualInput,SCREEN_BRIGHTNESS);},initBrightnessItems:function d_init_brightness_items(data){var brightnessAuto=this.elements.brightnessAuto;var brightnessAutoCheckbox=this.elements.brightnessAutoCheckbox;var brightnessManual=this.elements.brightnessManual;var brightnessManualInput=this.elements.brightnessManualInput;if(data.ambientLight){brightnessAuto.hidden=false;SettingsListener.observe(AUTO_BRIGHTNESS_SETTING,false,function(value){brightnessAutoCheckbox.checked=value;brightnessManual.hidden=value;}.bind(this));}else{brightnessAuto.hidden=true;brightnessManual.hidden=false;var cset={};cset[AUTO_BRIGHTNESS_SETTING]=false;SettingsListener.getSettingsLock().set(cset);}
|
||||
SettingsListener.observe(SCREEN_BRIGHTNESS,0.5,function(value){brightnessManualInput.value=value;}.bind(this));}};return function ctor_display(){return new Display();};});define('panels/display/panel',['require','modules/settings_panel','panels/display/display','shared/lazy_loader'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var DisplayModule=require('panels/display/display');var LazyLoader=require('shared/lazy_loader');var displayElements={};return function ctor_display_panel(){var display=DisplayModule();return SettingsPanel({onInit:function dp_onInit(panel){displayElements={brightnessManual:panel.querySelector('.brightness-manual'),brightnessManualInput:panel.querySelector('.brightness-manual input'),brightnessAuto:panel.querySelector('.brightness-auto'),brightnessAutoCheckbox:panel.querySelector('.brightness-auto gaia-checkbox')};LazyLoader.getJSON('/resources/device-features.json').then(function(data){display.init(displayElements,data);});}});};});
|
||||
|
|
@ -0,0 +1,492 @@
|
|||
|
||||
define('modules/dialog_manager',["require", "modules/panel_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var DialogManager = function() {
|
||||
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
||||
|
||||
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
||||
};
|
||||
|
||||
DialogManager.prototype = {
|
||||
/**
|
||||
* load panel based on passed in panelId
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_loadPanel: function dm__loadPanel(panelId) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* promised version of PanelCache.get()
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_getPanel: function dm__getPanel(panelId) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
resolve(panel);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is used to control visibility of overlayDOM
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {Boolean} show
|
||||
*/
|
||||
_showOverlay: function dm__showOverlay(show) {
|
||||
this._overlayDOM.hidden = !show;
|
||||
},
|
||||
|
||||
/**
|
||||
* It is used to control the timing of transitions so that we can make sure
|
||||
* whether animation is done or not.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_transit: function dm__transit(method, dialog, options) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var panel = dialog.panel;
|
||||
var completed = false;
|
||||
var fallback;
|
||||
function finish(event) {
|
||||
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
clearTimeout(fallback);
|
||||
panel.removeEventListener('transitionend', finish);
|
||||
panel.removeEventListener('transitioncancel', finish);
|
||||
if (method === 'close') panel.hidden = true;
|
||||
resolve();
|
||||
}
|
||||
if (method === 'open') panel.hidden = false;
|
||||
setTimeout(function() {
|
||||
panel.addEventListener('transitionend', finish);
|
||||
panel.addEventListener('transitioncancel', finish);
|
||||
panel.classList.toggle('current', method === 'open');
|
||||
var style = window.getComputedStyle(panel);
|
||||
function milliseconds(value) {
|
||||
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
||||
}
|
||||
var durations = style.transitionDuration.split(',').map(milliseconds);
|
||||
var delays = style.transitionDelay.split(',').map(milliseconds);
|
||||
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
||||
return value + delays[index % delays.length];
|
||||
}));
|
||||
// Reduced motion, display changes or interrupted transitions may
|
||||
// omit the visibility event. Always release the dialog queue.
|
||||
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
||||
}, 150);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to open panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_open: function dm__open(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: load panel
|
||||
return self._loadPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function() {
|
||||
// 2: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 3: call beforeShow
|
||||
foundPanel = panel;
|
||||
return foundPanel.beforeShow(dialog.panel, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 4. UI stuffs + transition
|
||||
dialog.init();
|
||||
dialog.initUI();
|
||||
dialog.bindEvents();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(true);
|
||||
}
|
||||
|
||||
return self._transit('open', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. show that panel as a dialog
|
||||
return foundPanel.show(dialog.panel, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to close panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_close: function dm__close(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
var cachedResult;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 2: Let's validate to see whether we can close this dialog or not.
|
||||
foundPanel = panel;
|
||||
|
||||
var promise;
|
||||
// custom dialog - onSubmit
|
||||
if (foundPanel.onSubmit && options._type === 'submit') {
|
||||
promise = foundPanel.onSubmit();
|
||||
// custom dialog - onCancel
|
||||
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
||||
promise = foundPanel.onCancel();
|
||||
// if no onSubmit & onCancel, pass directly
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function(result) {
|
||||
cachedResult = result;
|
||||
|
||||
// 3: call beforeHide
|
||||
return foundPanel.beforeHide();
|
||||
})
|
||||
.then(function() {
|
||||
// 4. transition
|
||||
return self._transit('close', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. call hide
|
||||
return foundPanel.hide();
|
||||
})
|
||||
.then(function() {
|
||||
// 6. Get result and cleanup dialog
|
||||
var result;
|
||||
|
||||
// for prompt dialog, we have to get its own result from input text.
|
||||
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
||||
result = dialog.getResult();
|
||||
} else if (cachedResult) {
|
||||
result = cachedResult;
|
||||
}
|
||||
|
||||
dialog.cleanup();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* It is a bridge to call open or close function.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_navigate: function dm__navigate(method, dialog, options) {
|
||||
method = (method === 'open') ? '_open' : '_close';
|
||||
return this[method](dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to open dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
open: function dm_open(dialog, options) {
|
||||
return this._navigate('open', dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to close dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
close: function dm_close(dialog, type, options) {
|
||||
options._type = type;
|
||||
return this._navigate('close', dialog, options);
|
||||
}
|
||||
};
|
||||
|
||||
var dialogManager = new DialogManager();
|
||||
return dialogManager;
|
||||
});
|
||||
define('modules/dialog/base_dialog',['require'],function(require){'use strict';var BaseDialog=function(panelDOM,options){this.panel=panelDOM;this._options=options||{};};BaseDialog.prototype.DIALOG_CLASS='dialog';BaseDialog.prototype.TRANSITION_CLASS='fade';BaseDialog.prototype.SUBMIT_BUTTON_SELECTOR='[type="submit"]';BaseDialog.prototype.CANCEL_BUTTON_SELECTOR='[type="reset"]';BaseDialog.prototype.MESSAGE_SELECTOR='.settings-dialog-message';BaseDialog.prototype.TITLE_SELECTOR='.settings-dialog-title';BaseDialog.prototype.init=function bd_init(){this.TRANSITION_CLASS=this._options.transition||this.TRANSITION_CLASS;this.panel.classList.add(this.DIALOG_CLASS);this.panel.classList.add(this.TRANSITION_CLASS);};BaseDialog.prototype.initUI=function bd_initUI(){var message=this._options.message;var title=this._options.title;var submitButton=this._options.submitButton;var cancelButton=this._options.cancelButton;this._updateMessage(message);this._updateTitle(title);this._updateSubmitButton(submitButton);this._updateCancelButton(cancelButton);};BaseDialog.prototype.bindEvents=function bd_bindEvent(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};BaseDialog.prototype._updateMessage=function bd__updateMessage(message){var messageDOM=this.panel.querySelector(this.MESSAGE_SELECTOR);if(messageDOM&&message){message=this._getWrapL10nObject(message);document.l10n.setAttributes(messageDOM,message.id,message.args);}};BaseDialog.prototype._updateTitle=function bd__updateTitle(title){var titleDOM=this.panel.querySelector(this.TITLE_SELECTOR);if(titleDOM&&title){title=this._getWrapL10nObject(title);document.l10n.setAttributes(titleDOM,title.id,title.args);}};BaseDialog.prototype._updateSubmitButton=function bd__update(options){var buttonDOM=this.getSubmitButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'recommend';}};BaseDialog.prototype._updateCancelButton=function bd__updateText(options){var buttonDOM=this.getCancelButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'';}};BaseDialog.prototype._getWrapL10nObject=function bd__getWrapL10nObject(input){if(typeof input==='string'){return{id:input,args:null};}else if(typeof input==='object'){if(typeof input.id==='undefined'){throw new Error('You forgot to put l10nId - '+
|
||||
JSON.stringify(input));}else{return{id:input.id,args:input.args||null,style:input.style};}}else{throw new Error('You are using the wrong L10nObject, '+'please check its format again');}};BaseDialog.prototype.getDOM=function bd_getDOM(){return this.panel;};BaseDialog.prototype.getSubmitButton=function bd_getSubmitButton(){return this.panel.querySelector(this.SUBMIT_BUTTON_SELECTOR);};BaseDialog.prototype.getCancelButton=function bd_getCancelButton(){return this.panel.querySelector(this.CANCEL_BUTTON_SELECTOR);};BaseDialog.prototype.cleanup=function bd_cleanup(){if(this.DIALOG_CLASS!=='panel-dialog'){this._updateTitle('settings-'+this.DIALOG_CLASS+'-header');this._updateSubmitButton('ok');this._updateCancelButton('cancel');}
|
||||
this.panel.classList.remove(this.DIALOG_CLASS);this.panel.classList.remove(this.TRANSITION_CLASS);};return BaseDialog;});define('modules/dialog/panel_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PanelDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PanelDialog.prototype=Object.create(BaseDialog.prototype);PanelDialog.prototype.constructor=PanelDialog;PanelDialog.prototype.DIALOG_CLASS='panel-dialog';PanelDialog.prototype.TRANSITION_CLASS='fade';return function ctor_PanelDialog(panelDOM,options){return new PanelDialog(panelDOM,options);};});define('modules/dialog/alert_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var AlertDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};AlertDialog.prototype=Object.create(BaseDialog.prototype);AlertDialog.prototype.constructor=AlertDialog;AlertDialog.prototype.DIALOG_CLASS='alert-dialog';AlertDialog.prototype.TRANSITION_CLASS='fade';AlertDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};};return function ctor_alertDialog(panelDOM,options){return new AlertDialog(panelDOM,options);};});define('modules/dialog/confirm_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var ConfirmDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};ConfirmDialog.prototype=Object.create(BaseDialog.prototype);ConfirmDialog.prototype.constructor=ConfirmDialog;ConfirmDialog.prototype.DIALOG_CLASS='confirm-dialog';ConfirmDialog.prototype.TRANSITION_CLASS='fade';ConfirmDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};return function ctor_confirmDialog(panelDOM,options){return new ConfirmDialog(panelDOM,options);};});define('modules/dialog/prompt_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PromptDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PromptDialog.prototype=Object.create(BaseDialog.prototype);PromptDialog.prototype.constructor=PromptDialog;PromptDialog.prototype.DIALOG_CLASS='prompt-dialog';PromptDialog.prototype.TRANSITION_CLASS='fade';PromptDialog.prototype.INPUT_SELECTOR='.settings-dialog-input';PromptDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};PromptDialog.prototype.initUI=function(){BaseDialog.prototype.initUI.call(this);this.getInput().value=this._options.defaultValue||'';};PromptDialog.prototype.getInput=function(){return this.panel.querySelector(this.INPUT_SELECTOR);};PromptDialog.prototype.getResult=function(){return this.getInput().value;};return function ctor_promptDialog(panelDOM,options){return new PromptDialog(panelDOM,options);};});define('modules/dialog_service',["require", "settings", "modules/defer", "modules/dialog_manager", "modules/dialog/panel_dialog", "modules/dialog/alert_dialog", "modules/dialog/confirm_dialog", "modules/dialog/prompt_dialog"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var Settings = require('settings');
|
||||
var Defer = require('modules/defer');
|
||||
var DialogManager = require('modules/dialog_manager');
|
||||
|
||||
var PanelDialog = require('modules/dialog/panel_dialog');
|
||||
var AlertDialog = require('modules/dialog/alert_dialog');
|
||||
var ConfirmDialog = require('modules/dialog/confirm_dialog');
|
||||
var PromptDialog = require('modules/dialog/prompt_dialog');
|
||||
|
||||
var DialogService = function() {
|
||||
this._navigating = false;
|
||||
this._pendingRequests = [];
|
||||
this._settingsAlertDialogId = 'settings-alert-dialog';
|
||||
this._settingsBaseDialogId = 'settings-base-dialog';
|
||||
this._settingsConfirmDialogId = 'settings-confirm-dialog';
|
||||
this._settingsPromptDialogId = 'settings-prompt-dialog';
|
||||
};
|
||||
|
||||
DialogService.prototype = {
|
||||
/**
|
||||
* Alert dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
alert: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsAlertDialogId, {
|
||||
type: 'alert',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsConfirmDialogId, {
|
||||
type: 'confirm',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
prompt: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsPromptDialogId, {
|
||||
type: 'prompt',
|
||||
message: message,
|
||||
title: options.title,
|
||||
defaultValue: options.defaultValue,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel dialog. If you are going to show any panel as a dialog,
|
||||
* you have to use this method to show them.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} panelId
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
show: function dm_show(panelId, userOptions, _pendingDefer) {
|
||||
var self = this;
|
||||
var defer;
|
||||
var dialog;
|
||||
var dialogDOM = document.getElementById(panelId);
|
||||
var currentPanel = Settings.currentPanel;
|
||||
var options = userOptions || {};
|
||||
|
||||
if (_pendingDefer) {
|
||||
defer = _pendingDefer;
|
||||
} else {
|
||||
defer = Defer();
|
||||
}
|
||||
|
||||
if (this._navigating) {
|
||||
this._pendingRequests.push({
|
||||
defer: defer,
|
||||
panelId: panelId,
|
||||
userOptions: userOptions
|
||||
});
|
||||
} else {
|
||||
if ('#' + panelId === currentPanel) {
|
||||
defer.reject('You are showing the same panel #' + panelId);
|
||||
} else {
|
||||
var opening;
|
||||
var closing = false;
|
||||
function close(type) {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
// A tap during the opening animation must not run hide/show
|
||||
// concurrently, or a queued dialog can inherit stale form values.
|
||||
Promise.resolve(opening).then(function() {
|
||||
return DialogManager.close(dialog, type, options);
|
||||
}).then(function(result) {
|
||||
self._navigating = false;
|
||||
defer.resolve({type: type, value: result});
|
||||
self._execPendingRequest();
|
||||
}, function(error) {
|
||||
// Panel validation rejects to keep the form open. Allow a
|
||||
// correction or cancellation without rejecting the show promise.
|
||||
closing = false;
|
||||
dialogDOM.dispatchEvent(new CustomEvent('dialogvalidationfailed', {
|
||||
detail: error
|
||||
}));
|
||||
});
|
||||
}
|
||||
options.onWrapSubmit = function() { close('submit'); };
|
||||
options.onWrapCancel = function() { close('cancel'); };
|
||||
|
||||
switch (options.type) {
|
||||
case 'alert':
|
||||
dialog = AlertDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'confirm':
|
||||
dialog = ConfirmDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'prompt':
|
||||
dialog = PromptDialog(dialogDOM, options);
|
||||
break;
|
||||
default:
|
||||
dialog = PanelDialog(dialogDOM, options);
|
||||
break;
|
||||
}
|
||||
this._navigating = true;
|
||||
opening = DialogManager.open(dialog, options);
|
||||
opening.catch(function(error) {
|
||||
// A missing host API must not lock every later Settings dialog.
|
||||
dialogDOM.classList.remove('current');
|
||||
dialogDOM.hidden = true;
|
||||
var overlay = document.querySelector('.settings-dialog-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
self._navigating = false;
|
||||
defer.reject(error);
|
||||
self._execPendingRequest();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method can help us pop up any pending request and would try to
|
||||
* show it after previous request was done.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access private
|
||||
*/
|
||||
_execPendingRequest: function() {
|
||||
var request = this._pendingRequests.pop();
|
||||
if (request) {
|
||||
this.show(request.panelId, request.userOptions, request.defer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dialogService = new DialogService();
|
||||
return dialogService;
|
||||
});
|
||||
define('panels/feedback_send/feedback_send',['require','modules/dialog_service','modules/settings_service','modules/settings_cache','shared/async_storage'],function(require){'use strict';var DialogService=require('modules/dialog_service');var SettingsService=require('modules/settings_service');var SettingsCache=require('modules/settings_cache');require('shared/async_storage');var SendFeedback=function(){};SendFeedback.prototype={_SettingsCache:SettingsCache,_SettingsService:SettingsService,init:function(elements){this.elements=elements;this.options={};this._sendData={product:'Firefox OS',platform:'Firefox OS'};this._showEmail=false;},_isHappy:function(){var happy=this.options&&this.options.feel;return happy==='feedback-happy';},updateTitle:function(){var id='feedback_whyfeel_'+(this._isHappy()?'happy':'sad');this.elements.title.setAttribute('data-l10n-id',id);},getPreviousInputs:function(){window.asyncStorage.getItem('feedback',function(value){this._inputData=value||{};}.bind(this));},keepAllInputs:function(){window.asyncStorage.setItem('feedback',this._inputData);},get _inputData(){return{description:this.elements.description.value,email:this.elements.emailInput.value,emailEnable:this._showEmail};},set _inputData(data){this.elements.description.value=data.description||'';this.elements.emailInput.value=data.email||'';this._showEmail=!data.emailEnable;this.enableEmail();},send:function(){this.elements.sendBtn.disabled=true;if(!navigator.onLine){this._messageHandler('connect-error');return;}
|
||||
var emailBar=this.elements.emailColumn;var emailInput=this.elements.emailInput;var contextInput=this.elements.description;if(contextInput.textLength===0){this._messageHandler('empty-comment');return;}else{this._sendData.description=contextInput.value;}
|
||||
if(!emailBar.hidden){this._sendData.email=emailInput.value;}else{delete this._sendData.email;}
|
||||
if(!emailBar.hidden&&(!emailInput.value.length||!emailInput.validity.valid)){this._messageHandler('wrong-email');return;}
|
||||
var currentSetting=this._SettingsCache.cache;var feedbackUrl=currentSetting['feedback.url'];this._sendData.version=currentSetting['deviceinfo.os'];this._sendData.device=currentSetting['deviceinfo.hardware'];this._sendData.locale=currentSetting['language.current'];this._sendData.happy=this._isHappy();this._xhr=new XMLHttpRequest({mozSystem:true});this._xhr.open('POST',feedbackUrl,true);this._xhr.setRequestHeader('Content-type','application/json');this._xhr.timeout=5000;this._xhr.onload=this._responseHandler.bind(this);this._xhr.ontimeout=function(){this._messageHandler('timeout');}.bind(this);this._xhr.send(JSON.stringify(this._sendData));},enableEmail:function(){var original=this._showEmail;this._showEmail=!original;this.elements.emailEnable.checked=!original;this.elements.emailColumn.hidden=original;},back:function(){this.keepAllInputs();this._SettingsService.navigate('improveBrowserOS-chooseFeedback');},_responseHandler:function(){switch(this._xhr.status){case 201:this._messageHandler('success');break;case 429:this._messageHandler('success');break;case 404:this._messageHandler('server-off');break;case 400:this._messageHandler('unknown-error');break;default:this._messageHandler('unknown-error');break;}},_messageHandler:function(type){if(type==='success'){this._openDoneDialog();}else{this.keepAllInputs();this._openAlertDialog(type);}
|
||||
this.elements.sendBtn.disabled=false;},_openAlertDialog:function(type){DialogService.alert({id:'feedback-errormessage-'+type},{submitButton:{id:'ok',style:'full'}});},_openDoneDialog:function(){DialogService.alert({id:'feedback-complete-msg'},{title:'sendFeedbackTitle',submitButton:{id:'done',style:'full'}}).then(function(){this._SettingsService.navigate('improveBrowserOS');}.bind(this));}};return function ctor_send_feedback(){return new SendFeedback();};});define('panels/feedback_send/panel',['require','modules/settings_panel','panels/feedback_send/feedback_send'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var SendFeedback=require('panels/feedback_send/feedback_send');return function ctor_sendFeedbackPanel(){var elements={};var sendFeedback=SendFeedback();var eventMapping=[{elementName:'sendBtn',eventType:'click',methodName:'send'},{elementName:'emailEnable',eventType:'change',methodName:'enableEmail'},{elementName:'header',eventType:'action',methodName:'back'}];function bindEvents(elements){eventMapping.forEach(function(map){map.method=sendFeedback[map.methodName].bind(sendFeedback);elements[map.elementName].addEventListener(map.eventType,map.method);});}
|
||||
function unbindEvents(elements){eventMapping.forEach(function(map){if(!map.method){return;}
|
||||
elements[map.elementName].removeEventListener(map.eventType,map.method);});}
|
||||
return SettingsPanel({onInit:function(panel){elements={title:panel.querySelector('#feedback-title'),description:panel.querySelector('#feedback-description'),emailInput:panel.querySelector('#feedback-email'),emailColumn:panel.querySelector('#feedback-emailbar'),emailEnable:panel.querySelector('#email-enable'),sendBtn:panel.querySelector('#feedback-send-btn'),header:panel.querySelector('#feedback-header')};sendFeedback.init(elements);},onBeforeShow:function(panel,options){bindEvents(elements);sendFeedback.options=options;sendFeedback.updateTitle();sendFeedback.getPreviousInputs();},onBeforeHide:function(){unbindEvents(elements);if(document.hidden){sendFeedback.keepAllInputs();}}});};});
|
||||
|
|
@ -0,0 +1,508 @@
|
|||
|
||||
var Normalizer={initAsciiNormalizer:function normalizer_init(){var equivalentChars={'a':'áăǎâäȧạȁàảȃāąåḁⱥãǽǣæ','A':'ÁĂǍÂÄȦẠȀÀẢȂĀĄÅḀȺÃǼǢÆ','b':'ḃḅɓḇƀƃ','B':'ḂḄƁḆɃƂ','c':'ćčçĉċƈȼ','C':'ĆČÇĈĊƇȻ','d':'ďḑḓḋḍɗḏđƌð','D':'ĎḐḒḊḌƊḎĐƋ','e':'éĕěȩêḙëėẹȅèẻȇēę','E':'ÉĔĚȨÊḘËĖẸȄÈẺȆĒĘ','f':'ḟƒ','F':'ḞƑ','g':'ǵğǧģĝġɠḡǥ','G':'ǴĞǦĢĜĠƓḠǤ','h':'ḫȟḩĥⱨḧḣḥħ','H':'ḪȞḨĤⱧḦḢḤĦ','i':'íĭǐîïịȉìỉȋīįɨĩḭı','I':'ÍĬǏÎÏỊȈÌỈȊĪĮƗĨḬ','j':'ĵɉ','J':'ĴɈ','k':'ḱǩķⱪꝃḳƙḵꝁ','K':'ḰǨĶⱩꝂḲƘḴꝀ','l':'ĺƚľļḽḷⱡꝉḻŀɫł','L':'ĹȽĽĻḼḶⱠꝈḺĿⱢŁ','m':'ḿṁṃɱ','M':'ḾṀṂⱮ','n':'ńňņṋṅṇǹɲṉƞñ','N':'ŃŇŅṊṄṆǸƝṈȠÑ','o':'óŏǒôöȯọőȍòỏơȏꝋꝍōǫøõœ','O':'ÓŎǑÔÖȮỌŐȌÒỎƠȎꝊꝌŌǪØÕŒ','p':'ṕṗꝓƥᵽꝑ','P':'ṔṖꝒƤⱣꝐ','q':'ꝗ','Q':'Ꝗ','r':'ŕřŗṙṛȑȓṟɍɽ','R':'ŔŘŖṘṚȐȒṞɌⱤ','s':'śšşŝșṡṣß','S':'ŚŠŞŜȘṠṢ','t':'ťţṱțⱦṫṭƭṯʈŧ','T':'ŤŢṰȚȾṪṬƬṮƮŦ','u':'úŭǔûṷüṳụűȕùủưȗūųůũṵ','U':'ÚŬǓÛṶÜṲỤŰȔÙỦƯȖŪŲŮŨṴ','v':'ṿʋṽ','V':'ṾƲṼ','w':'ẃŵẅẇẉẁⱳ','W':'ẂŴẄẆẈẀⱲ','x':'ẍẋ','X':'ẌẊ','y':'ýŷÿẏỵỳƴỷỿȳɏỹ','Y':'ÝŶŸẎỴỲƳỶỾȲɎỸ','z':'źžẑⱬżẓȥẕƶ','Z':'ŹŽẐⱫŻẒȤẔƵ'};this._toAsciiForm={};for(var letter in equivalentChars){var accentedForms=equivalentChars[letter];for(var i=accentedForms.length-1;i>=0;i--){this._toAsciiForm[accentedForms[i]]=letter;}}},toAscii:function normalizer_toAscii(str){if(!str||typeof str!='string'){return'';}
|
||||
if(!this._toAsciiForm){Normalizer.initAsciiNormalizer();}
|
||||
var result='';for(var i=0,len=str.length;i<len;i++){result+=this._toAsciiForm[str.charAt(i)]||str.charAt(i);}
|
||||
return result;},escapeHTML:function normalizer_escapeHTML(str,escapeQuotes){if(Array.isArray(str)){return Normalizer.escapeHTML(str.join(' '),escapeQuotes);}
|
||||
if(!str||typeof str!='string'){return'';}
|
||||
var escaped=str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');if(escapeQuotes){return escaped.replace(/"/g,'"').replace(/'/g,''');}
|
||||
return escaped;},unescapeHTML:function normalizer_unescapeHTML(text){return text.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,'\'');},escapeRegExp:function normalizer_escapeRegExp(str){return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,'\\$&');}};define("shared/text_normalizer",(function(global){return function(){var ret,fn;return ret||global.Normalizer;};}(this)));var FxAccountsIACHelper=function FxAccountsIACHelper(){var DEFAULT_CONNECTION_STRING='fxa-mgmt';var default_rules={'manifestURLs':['app://system.gaiamobile.org/manifest.webapp','app://smart-system.gaiamobile.org/manifest.webapp']};var CONNECTION_STRING=DEFAULT_CONNECTION_STRING;var rules=default_rules;var port;var callbacks={};var eventListeners={};var init=function init(options){if(!options){return;}
|
||||
if(options.keyword&&typeof(options.keyword)=='string'&&options.keyword.length>0){CONNECTION_STRING=options.keyword;}
|
||||
if(options.rules&&typeof(options.rules)=='object'){rules=options.rules;}};var addEventListener=function addEventListener(eventName,listener){if(!eventListeners[eventName]){eventListeners[eventName]=[];}
|
||||
eventListeners[eventName].push(listener);};var removeEventListener=function removeEventListener(eventName,listener){var listeners=eventListeners[eventName];if(!listeners){return;}
|
||||
var index=listeners.indexOf(listener);if(index===-1){return;}
|
||||
listeners.splice(index,1);};var reset=function reset(){CONNECTION_STRING=DEFAULT_CONNECTION_STRING;rules=default_rules;eventListeners={};callbacks={};port=null;};var onMessage=function onMessage(evt){if(evt&&evt.data){var message=evt.data;if(message.eventName){var listeners=eventListeners[message.eventName];if(!listeners){return;}
|
||||
for(var listener in listeners){if(listeners[listener]&&typeof listeners[listener]==='function'){listeners[listener]();}}
|
||||
return;}
|
||||
var cbs,cb;if(message.methodName){cbs=callbacks[message.methodName];if(!cbs||!cbs.length){console.warn('No callbacks for method '+message.methodName);return;}
|
||||
while(cbs.length){cb=cbs.shift();if(!message.error){cb.successCb(message.data);}else{cb.errorCb(message.error);}}}}else{console.error('Unknown');}};var getSelf=function getSelf(cb,error){var request=navigator.mozApps.getSelf();request.onsuccess=function onSuccess(evt){cb(evt.target.result);};request.onerror=function onError(){if(error&&typeof(error)==='function'){error();}};};var requestQueue=[];var isConnecting=false;var connect=function connect(cb){if(isConnecting){return requestQueue.push(cb);}
|
||||
isConnecting=true;_connect(function onConnect(err){var next;isConnecting=false;if(typeof cb==='function'){cb(err);}
|
||||
while(!!(next=requestQueue.shift())){next&&next(err);}});};var _connect=function _connect(callback){getSelf(function onApp(app){if(!app){return;}
|
||||
app.connect(CONNECTION_STRING,rules).then(function(ports){if(!ports||ports.length!==1){return;}
|
||||
port=ports[0];callback&&callback();});});};var sendMessage=function sendMessage(message,successCb,errorCb){if(port){_sendMessage(message,successCb,errorCb);return;}
|
||||
connect(function(){_sendMessage(message,successCb,errorCb);});};var _sendMessage=function _sendMessage(message,successCb,errorCb){var name=message.name;if(!name){return;}
|
||||
if(!callbacks[name]){callbacks[name]=[];}
|
||||
callbacks[name].push({successCb:successCb,errorCb:errorCb});port.onmessage=onMessage;port.postMessage(message);};var getAccount=function getAccount(successCb,errorCb){sendMessage({'name':'getAccount'},successCb,errorCb);};var openFlow=function openFlow(successCb,errorCb){sendMessage({'name':'openFlow'},successCb,errorCb);};var logout=function logout(successCb,errorCb){sendMessage({'name':'logout'},successCb,errorCb);};var refreshAuthentication=function refreshAuthentication(email,successCb,errorCb){sendMessage({'name':'refreshAuthentication','email':email},successCb,errorCb);};var resendVerificationEmail=function resendVerificationEmail(email,successCb,errorCb){sendMessage({'name':'resendVerificationEmail','email':email},successCb,errorCb);};connect();return{'addEventListener':addEventListener,'getAccount':getAccount,'init':init,'logout':logout,'openFlow':openFlow,'refreshAuthentication':refreshAuthentication,'removeEventListener':removeEventListener,'resendVerificationEmail':resendVerificationEmail,'reset':reset};}();define("shared/fxa_iac_client",(function(global){return function(){var ret,fn;return ret||global.FxAccountsIACHelper;};}(this)));define('modules/dialog_manager',["require", "modules/panel_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var DialogManager = function() {
|
||||
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
||||
|
||||
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
||||
};
|
||||
|
||||
DialogManager.prototype = {
|
||||
/**
|
||||
* load panel based on passed in panelId
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_loadPanel: function dm__loadPanel(panelId) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* promised version of PanelCache.get()
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_getPanel: function dm__getPanel(panelId) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
resolve(panel);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is used to control visibility of overlayDOM
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {Boolean} show
|
||||
*/
|
||||
_showOverlay: function dm__showOverlay(show) {
|
||||
this._overlayDOM.hidden = !show;
|
||||
},
|
||||
|
||||
/**
|
||||
* It is used to control the timing of transitions so that we can make sure
|
||||
* whether animation is done or not.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_transit: function dm__transit(method, dialog, options) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var panel = dialog.panel;
|
||||
var completed = false;
|
||||
var fallback;
|
||||
function finish(event) {
|
||||
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
clearTimeout(fallback);
|
||||
panel.removeEventListener('transitionend', finish);
|
||||
panel.removeEventListener('transitioncancel', finish);
|
||||
if (method === 'close') panel.hidden = true;
|
||||
resolve();
|
||||
}
|
||||
if (method === 'open') panel.hidden = false;
|
||||
setTimeout(function() {
|
||||
panel.addEventListener('transitionend', finish);
|
||||
panel.addEventListener('transitioncancel', finish);
|
||||
panel.classList.toggle('current', method === 'open');
|
||||
var style = window.getComputedStyle(panel);
|
||||
function milliseconds(value) {
|
||||
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
||||
}
|
||||
var durations = style.transitionDuration.split(',').map(milliseconds);
|
||||
var delays = style.transitionDelay.split(',').map(milliseconds);
|
||||
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
||||
return value + delays[index % delays.length];
|
||||
}));
|
||||
// Reduced motion, display changes or interrupted transitions may
|
||||
// omit the visibility event. Always release the dialog queue.
|
||||
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
||||
}, 150);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to open panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_open: function dm__open(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: load panel
|
||||
return self._loadPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function() {
|
||||
// 2: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 3: call beforeShow
|
||||
foundPanel = panel;
|
||||
return foundPanel.beforeShow(dialog.panel, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 4. UI stuffs + transition
|
||||
dialog.init();
|
||||
dialog.initUI();
|
||||
dialog.bindEvents();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(true);
|
||||
}
|
||||
|
||||
return self._transit('open', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. show that panel as a dialog
|
||||
return foundPanel.show(dialog.panel, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to close panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_close: function dm__close(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
var cachedResult;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 2: Let's validate to see whether we can close this dialog or not.
|
||||
foundPanel = panel;
|
||||
|
||||
var promise;
|
||||
// custom dialog - onSubmit
|
||||
if (foundPanel.onSubmit && options._type === 'submit') {
|
||||
promise = foundPanel.onSubmit();
|
||||
// custom dialog - onCancel
|
||||
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
||||
promise = foundPanel.onCancel();
|
||||
// if no onSubmit & onCancel, pass directly
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function(result) {
|
||||
cachedResult = result;
|
||||
|
||||
// 3: call beforeHide
|
||||
return foundPanel.beforeHide();
|
||||
})
|
||||
.then(function() {
|
||||
// 4. transition
|
||||
return self._transit('close', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. call hide
|
||||
return foundPanel.hide();
|
||||
})
|
||||
.then(function() {
|
||||
// 6. Get result and cleanup dialog
|
||||
var result;
|
||||
|
||||
// for prompt dialog, we have to get its own result from input text.
|
||||
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
||||
result = dialog.getResult();
|
||||
} else if (cachedResult) {
|
||||
result = cachedResult;
|
||||
}
|
||||
|
||||
dialog.cleanup();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* It is a bridge to call open or close function.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_navigate: function dm__navigate(method, dialog, options) {
|
||||
method = (method === 'open') ? '_open' : '_close';
|
||||
return this[method](dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to open dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
open: function dm_open(dialog, options) {
|
||||
return this._navigate('open', dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to close dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
close: function dm_close(dialog, type, options) {
|
||||
options._type = type;
|
||||
return this._navigate('close', dialog, options);
|
||||
}
|
||||
};
|
||||
|
||||
var dialogManager = new DialogManager();
|
||||
return dialogManager;
|
||||
});
|
||||
define('modules/dialog/base_dialog',['require'],function(require){'use strict';var BaseDialog=function(panelDOM,options){this.panel=panelDOM;this._options=options||{};};BaseDialog.prototype.DIALOG_CLASS='dialog';BaseDialog.prototype.TRANSITION_CLASS='fade';BaseDialog.prototype.SUBMIT_BUTTON_SELECTOR='[type="submit"]';BaseDialog.prototype.CANCEL_BUTTON_SELECTOR='[type="reset"]';BaseDialog.prototype.MESSAGE_SELECTOR='.settings-dialog-message';BaseDialog.prototype.TITLE_SELECTOR='.settings-dialog-title';BaseDialog.prototype.init=function bd_init(){this.TRANSITION_CLASS=this._options.transition||this.TRANSITION_CLASS;this.panel.classList.add(this.DIALOG_CLASS);this.panel.classList.add(this.TRANSITION_CLASS);};BaseDialog.prototype.initUI=function bd_initUI(){var message=this._options.message;var title=this._options.title;var submitButton=this._options.submitButton;var cancelButton=this._options.cancelButton;this._updateMessage(message);this._updateTitle(title);this._updateSubmitButton(submitButton);this._updateCancelButton(cancelButton);};BaseDialog.prototype.bindEvents=function bd_bindEvent(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};BaseDialog.prototype._updateMessage=function bd__updateMessage(message){var messageDOM=this.panel.querySelector(this.MESSAGE_SELECTOR);if(messageDOM&&message){message=this._getWrapL10nObject(message);document.l10n.setAttributes(messageDOM,message.id,message.args);}};BaseDialog.prototype._updateTitle=function bd__updateTitle(title){var titleDOM=this.panel.querySelector(this.TITLE_SELECTOR);if(titleDOM&&title){title=this._getWrapL10nObject(title);document.l10n.setAttributes(titleDOM,title.id,title.args);}};BaseDialog.prototype._updateSubmitButton=function bd__update(options){var buttonDOM=this.getSubmitButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'recommend';}};BaseDialog.prototype._updateCancelButton=function bd__updateText(options){var buttonDOM=this.getCancelButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'';}};BaseDialog.prototype._getWrapL10nObject=function bd__getWrapL10nObject(input){if(typeof input==='string'){return{id:input,args:null};}else if(typeof input==='object'){if(typeof input.id==='undefined'){throw new Error('You forgot to put l10nId - '+
|
||||
JSON.stringify(input));}else{return{id:input.id,args:input.args||null,style:input.style};}}else{throw new Error('You are using the wrong L10nObject, '+'please check its format again');}};BaseDialog.prototype.getDOM=function bd_getDOM(){return this.panel;};BaseDialog.prototype.getSubmitButton=function bd_getSubmitButton(){return this.panel.querySelector(this.SUBMIT_BUTTON_SELECTOR);};BaseDialog.prototype.getCancelButton=function bd_getCancelButton(){return this.panel.querySelector(this.CANCEL_BUTTON_SELECTOR);};BaseDialog.prototype.cleanup=function bd_cleanup(){if(this.DIALOG_CLASS!=='panel-dialog'){this._updateTitle('settings-'+this.DIALOG_CLASS+'-header');this._updateSubmitButton('ok');this._updateCancelButton('cancel');}
|
||||
this.panel.classList.remove(this.DIALOG_CLASS);this.panel.classList.remove(this.TRANSITION_CLASS);};return BaseDialog;});define('modules/dialog/panel_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PanelDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PanelDialog.prototype=Object.create(BaseDialog.prototype);PanelDialog.prototype.constructor=PanelDialog;PanelDialog.prototype.DIALOG_CLASS='panel-dialog';PanelDialog.prototype.TRANSITION_CLASS='fade';return function ctor_PanelDialog(panelDOM,options){return new PanelDialog(panelDOM,options);};});define('modules/dialog/alert_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var AlertDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};AlertDialog.prototype=Object.create(BaseDialog.prototype);AlertDialog.prototype.constructor=AlertDialog;AlertDialog.prototype.DIALOG_CLASS='alert-dialog';AlertDialog.prototype.TRANSITION_CLASS='fade';AlertDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};};return function ctor_alertDialog(panelDOM,options){return new AlertDialog(panelDOM,options);};});define('modules/dialog/confirm_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var ConfirmDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};ConfirmDialog.prototype=Object.create(BaseDialog.prototype);ConfirmDialog.prototype.constructor=ConfirmDialog;ConfirmDialog.prototype.DIALOG_CLASS='confirm-dialog';ConfirmDialog.prototype.TRANSITION_CLASS='fade';ConfirmDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};return function ctor_confirmDialog(panelDOM,options){return new ConfirmDialog(panelDOM,options);};});define('modules/dialog/prompt_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PromptDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PromptDialog.prototype=Object.create(BaseDialog.prototype);PromptDialog.prototype.constructor=PromptDialog;PromptDialog.prototype.DIALOG_CLASS='prompt-dialog';PromptDialog.prototype.TRANSITION_CLASS='fade';PromptDialog.prototype.INPUT_SELECTOR='.settings-dialog-input';PromptDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};PromptDialog.prototype.initUI=function(){BaseDialog.prototype.initUI.call(this);this.getInput().value=this._options.defaultValue||'';};PromptDialog.prototype.getInput=function(){return this.panel.querySelector(this.INPUT_SELECTOR);};PromptDialog.prototype.getResult=function(){return this.getInput().value;};return function ctor_promptDialog(panelDOM,options){return new PromptDialog(panelDOM,options);};});define('modules/dialog_service',["require", "settings", "modules/defer", "modules/dialog_manager", "modules/dialog/panel_dialog", "modules/dialog/alert_dialog", "modules/dialog/confirm_dialog", "modules/dialog/prompt_dialog"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var Settings = require('settings');
|
||||
var Defer = require('modules/defer');
|
||||
var DialogManager = require('modules/dialog_manager');
|
||||
|
||||
var PanelDialog = require('modules/dialog/panel_dialog');
|
||||
var AlertDialog = require('modules/dialog/alert_dialog');
|
||||
var ConfirmDialog = require('modules/dialog/confirm_dialog');
|
||||
var PromptDialog = require('modules/dialog/prompt_dialog');
|
||||
|
||||
var DialogService = function() {
|
||||
this._navigating = false;
|
||||
this._pendingRequests = [];
|
||||
this._settingsAlertDialogId = 'settings-alert-dialog';
|
||||
this._settingsBaseDialogId = 'settings-base-dialog';
|
||||
this._settingsConfirmDialogId = 'settings-confirm-dialog';
|
||||
this._settingsPromptDialogId = 'settings-prompt-dialog';
|
||||
};
|
||||
|
||||
DialogService.prototype = {
|
||||
/**
|
||||
* Alert dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
alert: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsAlertDialogId, {
|
||||
type: 'alert',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsConfirmDialogId, {
|
||||
type: 'confirm',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
prompt: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsPromptDialogId, {
|
||||
type: 'prompt',
|
||||
message: message,
|
||||
title: options.title,
|
||||
defaultValue: options.defaultValue,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel dialog. If you are going to show any panel as a dialog,
|
||||
* you have to use this method to show them.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} panelId
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
show: function dm_show(panelId, userOptions, _pendingDefer) {
|
||||
var self = this;
|
||||
var defer;
|
||||
var dialog;
|
||||
var dialogDOM = document.getElementById(panelId);
|
||||
var currentPanel = Settings.currentPanel;
|
||||
var options = userOptions || {};
|
||||
|
||||
if (_pendingDefer) {
|
||||
defer = _pendingDefer;
|
||||
} else {
|
||||
defer = Defer();
|
||||
}
|
||||
|
||||
if (this._navigating) {
|
||||
this._pendingRequests.push({
|
||||
defer: defer,
|
||||
panelId: panelId,
|
||||
userOptions: userOptions
|
||||
});
|
||||
} else {
|
||||
if ('#' + panelId === currentPanel) {
|
||||
defer.reject('You are showing the same panel #' + panelId);
|
||||
} else {
|
||||
var opening;
|
||||
var closing = false;
|
||||
function close(type) {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
// A tap during the opening animation must not run hide/show
|
||||
// concurrently, or a queued dialog can inherit stale form values.
|
||||
Promise.resolve(opening).then(function() {
|
||||
return DialogManager.close(dialog, type, options);
|
||||
}).then(function(result) {
|
||||
self._navigating = false;
|
||||
defer.resolve({type: type, value: result});
|
||||
self._execPendingRequest();
|
||||
}, function(error) {
|
||||
// Panel validation rejects to keep the form open. Allow a
|
||||
// correction or cancellation without rejecting the show promise.
|
||||
closing = false;
|
||||
dialogDOM.dispatchEvent(new CustomEvent('dialogvalidationfailed', {
|
||||
detail: error
|
||||
}));
|
||||
});
|
||||
}
|
||||
options.onWrapSubmit = function() { close('submit'); };
|
||||
options.onWrapCancel = function() { close('cancel'); };
|
||||
|
||||
switch (options.type) {
|
||||
case 'alert':
|
||||
dialog = AlertDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'confirm':
|
||||
dialog = ConfirmDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'prompt':
|
||||
dialog = PromptDialog(dialogDOM, options);
|
||||
break;
|
||||
default:
|
||||
dialog = PanelDialog(dialogDOM, options);
|
||||
break;
|
||||
}
|
||||
this._navigating = true;
|
||||
opening = DialogManager.open(dialog, options);
|
||||
opening.catch(function(error) {
|
||||
// A missing host API must not lock every later Settings dialog.
|
||||
dialogDOM.classList.remove('current');
|
||||
dialogDOM.hidden = true;
|
||||
var overlay = document.querySelector('.settings-dialog-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
self._navigating = false;
|
||||
defer.reject(error);
|
||||
self._execPendingRequest();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method can help us pop up any pending request and would try to
|
||||
* show it after previous request was done.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access private
|
||||
*/
|
||||
_execPendingRequest: function() {
|
||||
var request = this._pendingRequests.pop();
|
||||
if (request) {
|
||||
this.show(request.panelId, request.userOptions, request.defer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dialogService = new DialogService();
|
||||
return dialogService;
|
||||
});
|
||||
define('panels/firefox_accounts/firefox_accounts',['require','shared/text_normalizer','shared/fxa_iac_client','modules/dialog_service'],function(require){var normalizer=require('shared/text_normalizer');var fxaHelper=require('shared/fxa_iac_client');var dialogService=require('modules/dialog_service');var FirefoxAccounts=function(){this._boundRefreshStatus=this._boundRefreshStatus||this.refreshStatus.bind(this);};FirefoxAccounts.prototype={onInit:function fxa_init(elements){this._elements=elements;},onBeforeShow:function fxa_beforeShow(){fxaHelper.addEventListener('onlogin',this._boundRefreshStatus);fxaHelper.addEventListener('onverified',this._boundRefreshStatus);fxaHelper.addEventListener('onlogout',this._boundRefreshStatus);this._boundRefreshStatus();},onBeforeHide:function fxa_beforeHide(){fxaHelper.removeEventListener('onlogin',this._boundRefreshStatus);fxaHelper.removeEventListener('onverified',this._boundRefreshStatus);fxaHelper.removeEventListener('onlogout',this._boundRefreshStatus);},refreshStatus:function fxa_refreshStatus(){fxaHelper.getAccount(this._onFxAccountStateChange.bind(this),this._onFxAccountError.bind(this));},_onFxAccountStateChange:function fxa_onFxAccountStateChange(e){var email=e?normalizer.escapeHTML(e.email):'';if(!e){this._hideLoggedInPanel();this._hideUnverifiedPanel();this._showLoggedOutPanel();}else if(e.verified){this._hideLoggedOutPanel();this._hideUnverifiedPanel();this._showLoggedInPanel(email);}else{this._hideLoggedOutPanel();this._hideLoggedInPanel();this._showUnverifiedPanel(email);}},_onFxAccountError:function fxa_onFxAccountError(err){console.error('FxaPanel: Error getting Firefox Account: '+err.error);},_hideLoggedOutPanel:function fxa_hideLoggedOutPanel(){this._elements.loginBtn.onclick=null;this._elements.loggedOutPanel.hidden=true;},_showLoggedOutPanel:function fxa_showLoggedOutPanel(){this._elements.loginBtn.onclick=this._onLoginClick.bind(this);this._elements.loggedOutPanel.hidden=false;},_hideLoggedInPanel:function fxa_hideLoggedInPanel(){this._elements.loggedInPanel.hidden=true;this._elements.loggedInEmail.textContent='';this._elements.logoutBtn.onclick=null;},_showLoggedInPanel:function fxa_showLoggedInPanel(email){document.l10n.setAttributes(this._elements.loggedInEmail,'fxa-logged-in-text',{email:email});this._elements.loggedInPanel.hidden=false;this._elements.logoutBtn.onclick=this._onLogoutClick.bind(this);},_hideUnverifiedPanel:function fxa_hideUnverifiedPanel(){this._elements.unverifiedPanel.hidden=true;this._elements.unverifiedEmail.textContent='';this._elements.cancelBtn.onclick=null;if(this._elements.resendLink){this._elements.resendLink.onclick=null;}},_showUnverifiedPanel:function fxa_showUnverifiedPanel(email){this._elements.unverifiedPanel.hidden=false;this._elements.cancelBtn.onclick=this._onLogoutClick.bind(this);document.l10n.setAttributes(this._elements.unverifiedEmail,'fxa-verification-email-sent-msg',{email:email});this._elements.resendLink.onclick=this._onResendClick.bind(this);},_onLogoutClick:function fxa_onLogoutClick(e){e.stopPropagation();e.preventDefault();fxaHelper.logout(this._onFxAccountStateChange.bind(this),this._onFxAccountError.bind(this));},_onResendClick:function fxa__onResendClick(e){e.stopPropagation();e.preventDefault();if(e.target.classList.contains('disabled')){return;}
|
||||
fxaHelper.getAccount((accts)=>{var email=accts&&accts.email;if(!email){return this._onFxAccountStateChange(accts);}
|
||||
fxaHelper.resendVerificationEmail(email,this._onResend.bind(this,email),this._onFxAccountError.bind(this));},this._onFxAccountError.bind(this));},_onResend:function fxa__onResend(email){dialogService.alert({id:'fxa-resend-alert',args:{email:email}});this._elements.resendLink.classList.add('disabled');setTimeout(()=>{this._elements.resendLink.classList.remove('disabled');},60000);},_onLoginClick:function fxa_onLoginClick(e){e.stopPropagation();e.preventDefault();fxaHelper.openFlow(this._onFxAccountStateChange.bind(this),this._onFxAccountError.bind(this));}};return function ctor_firefox_accounts(){return new FirefoxAccounts();};});define('panels/firefox_accounts/panel',['require','modules/settings_panel','panels/firefox_accounts/firefox_accounts'],function(require){var SettingsPanel=require('modules/settings_panel');var FirefoxAccounts=require('panels/firefox_accounts/firefox_accounts');return function ctor_firefox_account_panel(){var elements;var firefoxAccounts=FirefoxAccounts();return SettingsPanel({onInit:function(panel){elements={fxaContainer:document.getElementById('fxa'),loggedOutPanel:document.getElementById('fxa-logged-out'),loggedInPanel:document.getElementById('fxa-logged-in'),unverifiedPanel:document.getElementById('fxa-unverified'),resendEmail:document.getElementById('fxa-resend-email'),cancelBtn:document.getElementById('fxa-cancel-confirmation'),loginBtn:document.getElementById('fxa-login'),logoutBtn:document.getElementById('fxa-logout'),loggedInEmail:document.getElementById('fxa-logged-in-text'),unverifiedEmail:document.getElementById('fxa-unverified-text'),resendLink:document.getElementById('fxa-resend')};firefoxAccounts.onInit(elements);},onBeforeShow:function(){firefoxAccounts.onBeforeShow();},onBeforeHide:function(){firefoxAccounts.onBeforeHide();}});};});
|
||||
|
|
@ -0,0 +1,505 @@
|
|||
|
||||
define('modules/sync_manager_bridge',['require'],function(require){'use strict';var SyncManagerBridge={_port:null,_requests:new Map(),set onsyncchange(callback){this._onsyncchange=callback;},connect:function(){if(this._port){return Promise.resolve(this._port);}
|
||||
return new Promise((resolve,reject)=>{navigator.mozApps.getSelf().onsuccess=event=>{var app=event.target.result;app.connect('gaia-sync-management').then(ports=>{if(!ports||!ports.length){return reject();}
|
||||
this._port=ports[0];this._port.onmessage=this.onmessage.bind(this);resolve(this._port);}).catch(reject);};});},iacRequest:function(request){return new Promise(resolve=>{this.connect().then(port=>{if(request.id){this._requests.set(request.id,resolve);}
|
||||
port.postMessage(request);});});},onmessage:function(event){var message=event.data;if(!message){return;}
|
||||
if(message.id&&this._requests.has(message.id)){this._requests.get(message.id)(message);this._requests.delete(message.id);return;}
|
||||
if(message.name!=='onsyncchange'){return;}
|
||||
if(this._onsyncchange){this._onsyncchange(message);}},getInfo:function(){return this.iacRequest({name:'getInfo',id:Date.now()});},};['enable','disable','sync'].forEach(requestName=>{SyncManagerBridge[requestName]=()=>{return SyncManagerBridge.iacRequest({name:requestName});};});return SyncManagerBridge;});define('modules/dialog_manager',["require", "modules/panel_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var DialogManager = function() {
|
||||
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
||||
|
||||
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
||||
};
|
||||
|
||||
DialogManager.prototype = {
|
||||
/**
|
||||
* load panel based on passed in panelId
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_loadPanel: function dm__loadPanel(panelId) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* promised version of PanelCache.get()
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_getPanel: function dm__getPanel(panelId) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
resolve(panel);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is used to control visibility of overlayDOM
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {Boolean} show
|
||||
*/
|
||||
_showOverlay: function dm__showOverlay(show) {
|
||||
this._overlayDOM.hidden = !show;
|
||||
},
|
||||
|
||||
/**
|
||||
* It is used to control the timing of transitions so that we can make sure
|
||||
* whether animation is done or not.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_transit: function dm__transit(method, dialog, options) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var panel = dialog.panel;
|
||||
var completed = false;
|
||||
var fallback;
|
||||
function finish(event) {
|
||||
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
clearTimeout(fallback);
|
||||
panel.removeEventListener('transitionend', finish);
|
||||
panel.removeEventListener('transitioncancel', finish);
|
||||
if (method === 'close') panel.hidden = true;
|
||||
resolve();
|
||||
}
|
||||
if (method === 'open') panel.hidden = false;
|
||||
setTimeout(function() {
|
||||
panel.addEventListener('transitionend', finish);
|
||||
panel.addEventListener('transitioncancel', finish);
|
||||
panel.classList.toggle('current', method === 'open');
|
||||
var style = window.getComputedStyle(panel);
|
||||
function milliseconds(value) {
|
||||
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
||||
}
|
||||
var durations = style.transitionDuration.split(',').map(milliseconds);
|
||||
var delays = style.transitionDelay.split(',').map(milliseconds);
|
||||
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
||||
return value + delays[index % delays.length];
|
||||
}));
|
||||
// Reduced motion, display changes or interrupted transitions may
|
||||
// omit the visibility event. Always release the dialog queue.
|
||||
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
||||
}, 150);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to open panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_open: function dm__open(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: load panel
|
||||
return self._loadPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function() {
|
||||
// 2: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 3: call beforeShow
|
||||
foundPanel = panel;
|
||||
return foundPanel.beforeShow(dialog.panel, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 4. UI stuffs + transition
|
||||
dialog.init();
|
||||
dialog.initUI();
|
||||
dialog.bindEvents();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(true);
|
||||
}
|
||||
|
||||
return self._transit('open', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. show that panel as a dialog
|
||||
return foundPanel.show(dialog.panel, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to close panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_close: function dm__close(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
var cachedResult;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 2: Let's validate to see whether we can close this dialog or not.
|
||||
foundPanel = panel;
|
||||
|
||||
var promise;
|
||||
// custom dialog - onSubmit
|
||||
if (foundPanel.onSubmit && options._type === 'submit') {
|
||||
promise = foundPanel.onSubmit();
|
||||
// custom dialog - onCancel
|
||||
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
||||
promise = foundPanel.onCancel();
|
||||
// if no onSubmit & onCancel, pass directly
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function(result) {
|
||||
cachedResult = result;
|
||||
|
||||
// 3: call beforeHide
|
||||
return foundPanel.beforeHide();
|
||||
})
|
||||
.then(function() {
|
||||
// 4. transition
|
||||
return self._transit('close', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. call hide
|
||||
return foundPanel.hide();
|
||||
})
|
||||
.then(function() {
|
||||
// 6. Get result and cleanup dialog
|
||||
var result;
|
||||
|
||||
// for prompt dialog, we have to get its own result from input text.
|
||||
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
||||
result = dialog.getResult();
|
||||
} else if (cachedResult) {
|
||||
result = cachedResult;
|
||||
}
|
||||
|
||||
dialog.cleanup();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* It is a bridge to call open or close function.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_navigate: function dm__navigate(method, dialog, options) {
|
||||
method = (method === 'open') ? '_open' : '_close';
|
||||
return this[method](dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to open dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
open: function dm_open(dialog, options) {
|
||||
return this._navigate('open', dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to close dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
close: function dm_close(dialog, type, options) {
|
||||
options._type = type;
|
||||
return this._navigate('close', dialog, options);
|
||||
}
|
||||
};
|
||||
|
||||
var dialogManager = new DialogManager();
|
||||
return dialogManager;
|
||||
});
|
||||
define('modules/dialog/base_dialog',['require'],function(require){'use strict';var BaseDialog=function(panelDOM,options){this.panel=panelDOM;this._options=options||{};};BaseDialog.prototype.DIALOG_CLASS='dialog';BaseDialog.prototype.TRANSITION_CLASS='fade';BaseDialog.prototype.SUBMIT_BUTTON_SELECTOR='[type="submit"]';BaseDialog.prototype.CANCEL_BUTTON_SELECTOR='[type="reset"]';BaseDialog.prototype.MESSAGE_SELECTOR='.settings-dialog-message';BaseDialog.prototype.TITLE_SELECTOR='.settings-dialog-title';BaseDialog.prototype.init=function bd_init(){this.TRANSITION_CLASS=this._options.transition||this.TRANSITION_CLASS;this.panel.classList.add(this.DIALOG_CLASS);this.panel.classList.add(this.TRANSITION_CLASS);};BaseDialog.prototype.initUI=function bd_initUI(){var message=this._options.message;var title=this._options.title;var submitButton=this._options.submitButton;var cancelButton=this._options.cancelButton;this._updateMessage(message);this._updateTitle(title);this._updateSubmitButton(submitButton);this._updateCancelButton(cancelButton);};BaseDialog.prototype.bindEvents=function bd_bindEvent(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};BaseDialog.prototype._updateMessage=function bd__updateMessage(message){var messageDOM=this.panel.querySelector(this.MESSAGE_SELECTOR);if(messageDOM&&message){message=this._getWrapL10nObject(message);document.l10n.setAttributes(messageDOM,message.id,message.args);}};BaseDialog.prototype._updateTitle=function bd__updateTitle(title){var titleDOM=this.panel.querySelector(this.TITLE_SELECTOR);if(titleDOM&&title){title=this._getWrapL10nObject(title);document.l10n.setAttributes(titleDOM,title.id,title.args);}};BaseDialog.prototype._updateSubmitButton=function bd__update(options){var buttonDOM=this.getSubmitButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'recommend';}};BaseDialog.prototype._updateCancelButton=function bd__updateText(options){var buttonDOM=this.getCancelButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'';}};BaseDialog.prototype._getWrapL10nObject=function bd__getWrapL10nObject(input){if(typeof input==='string'){return{id:input,args:null};}else if(typeof input==='object'){if(typeof input.id==='undefined'){throw new Error('You forgot to put l10nId - '+
|
||||
JSON.stringify(input));}else{return{id:input.id,args:input.args||null,style:input.style};}}else{throw new Error('You are using the wrong L10nObject, '+'please check its format again');}};BaseDialog.prototype.getDOM=function bd_getDOM(){return this.panel;};BaseDialog.prototype.getSubmitButton=function bd_getSubmitButton(){return this.panel.querySelector(this.SUBMIT_BUTTON_SELECTOR);};BaseDialog.prototype.getCancelButton=function bd_getCancelButton(){return this.panel.querySelector(this.CANCEL_BUTTON_SELECTOR);};BaseDialog.prototype.cleanup=function bd_cleanup(){if(this.DIALOG_CLASS!=='panel-dialog'){this._updateTitle('settings-'+this.DIALOG_CLASS+'-header');this._updateSubmitButton('ok');this._updateCancelButton('cancel');}
|
||||
this.panel.classList.remove(this.DIALOG_CLASS);this.panel.classList.remove(this.TRANSITION_CLASS);};return BaseDialog;});define('modules/dialog/panel_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PanelDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PanelDialog.prototype=Object.create(BaseDialog.prototype);PanelDialog.prototype.constructor=PanelDialog;PanelDialog.prototype.DIALOG_CLASS='panel-dialog';PanelDialog.prototype.TRANSITION_CLASS='fade';return function ctor_PanelDialog(panelDOM,options){return new PanelDialog(panelDOM,options);};});define('modules/dialog/alert_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var AlertDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};AlertDialog.prototype=Object.create(BaseDialog.prototype);AlertDialog.prototype.constructor=AlertDialog;AlertDialog.prototype.DIALOG_CLASS='alert-dialog';AlertDialog.prototype.TRANSITION_CLASS='fade';AlertDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};};return function ctor_alertDialog(panelDOM,options){return new AlertDialog(panelDOM,options);};});define('modules/dialog/confirm_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var ConfirmDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};ConfirmDialog.prototype=Object.create(BaseDialog.prototype);ConfirmDialog.prototype.constructor=ConfirmDialog;ConfirmDialog.prototype.DIALOG_CLASS='confirm-dialog';ConfirmDialog.prototype.TRANSITION_CLASS='fade';ConfirmDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};return function ctor_confirmDialog(panelDOM,options){return new ConfirmDialog(panelDOM,options);};});define('modules/dialog/prompt_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PromptDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PromptDialog.prototype=Object.create(BaseDialog.prototype);PromptDialog.prototype.constructor=PromptDialog;PromptDialog.prototype.DIALOG_CLASS='prompt-dialog';PromptDialog.prototype.TRANSITION_CLASS='fade';PromptDialog.prototype.INPUT_SELECTOR='.settings-dialog-input';PromptDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};PromptDialog.prototype.initUI=function(){BaseDialog.prototype.initUI.call(this);this.getInput().value=this._options.defaultValue||'';};PromptDialog.prototype.getInput=function(){return this.panel.querySelector(this.INPUT_SELECTOR);};PromptDialog.prototype.getResult=function(){return this.getInput().value;};return function ctor_promptDialog(panelDOM,options){return new PromptDialog(panelDOM,options);};});define('modules/dialog_service',["require", "settings", "modules/defer", "modules/dialog_manager", "modules/dialog/panel_dialog", "modules/dialog/alert_dialog", "modules/dialog/confirm_dialog", "modules/dialog/prompt_dialog"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var Settings = require('settings');
|
||||
var Defer = require('modules/defer');
|
||||
var DialogManager = require('modules/dialog_manager');
|
||||
|
||||
var PanelDialog = require('modules/dialog/panel_dialog');
|
||||
var AlertDialog = require('modules/dialog/alert_dialog');
|
||||
var ConfirmDialog = require('modules/dialog/confirm_dialog');
|
||||
var PromptDialog = require('modules/dialog/prompt_dialog');
|
||||
|
||||
var DialogService = function() {
|
||||
this._navigating = false;
|
||||
this._pendingRequests = [];
|
||||
this._settingsAlertDialogId = 'settings-alert-dialog';
|
||||
this._settingsBaseDialogId = 'settings-base-dialog';
|
||||
this._settingsConfirmDialogId = 'settings-confirm-dialog';
|
||||
this._settingsPromptDialogId = 'settings-prompt-dialog';
|
||||
};
|
||||
|
||||
DialogService.prototype = {
|
||||
/**
|
||||
* Alert dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
alert: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsAlertDialogId, {
|
||||
type: 'alert',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsConfirmDialogId, {
|
||||
type: 'confirm',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
prompt: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsPromptDialogId, {
|
||||
type: 'prompt',
|
||||
message: message,
|
||||
title: options.title,
|
||||
defaultValue: options.defaultValue,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel dialog. If you are going to show any panel as a dialog,
|
||||
* you have to use this method to show them.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} panelId
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
show: function dm_show(panelId, userOptions, _pendingDefer) {
|
||||
var self = this;
|
||||
var defer;
|
||||
var dialog;
|
||||
var dialogDOM = document.getElementById(panelId);
|
||||
var currentPanel = Settings.currentPanel;
|
||||
var options = userOptions || {};
|
||||
|
||||
if (_pendingDefer) {
|
||||
defer = _pendingDefer;
|
||||
} else {
|
||||
defer = Defer();
|
||||
}
|
||||
|
||||
if (this._navigating) {
|
||||
this._pendingRequests.push({
|
||||
defer: defer,
|
||||
panelId: panelId,
|
||||
userOptions: userOptions
|
||||
});
|
||||
} else {
|
||||
if ('#' + panelId === currentPanel) {
|
||||
defer.reject('You are showing the same panel #' + panelId);
|
||||
} else {
|
||||
var opening;
|
||||
var closing = false;
|
||||
function close(type) {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
// A tap during the opening animation must not run hide/show
|
||||
// concurrently, or a queued dialog can inherit stale form values.
|
||||
Promise.resolve(opening).then(function() {
|
||||
return DialogManager.close(dialog, type, options);
|
||||
}).then(function(result) {
|
||||
self._navigating = false;
|
||||
defer.resolve({type: type, value: result});
|
||||
self._execPendingRequest();
|
||||
}, function(error) {
|
||||
// Panel validation rejects to keep the form open. Allow a
|
||||
// correction or cancellation without rejecting the show promise.
|
||||
closing = false;
|
||||
dialogDOM.dispatchEvent(new CustomEvent('dialogvalidationfailed', {
|
||||
detail: error
|
||||
}));
|
||||
});
|
||||
}
|
||||
options.onWrapSubmit = function() { close('submit'); };
|
||||
options.onWrapCancel = function() { close('cancel'); };
|
||||
|
||||
switch (options.type) {
|
||||
case 'alert':
|
||||
dialog = AlertDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'confirm':
|
||||
dialog = ConfirmDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'prompt':
|
||||
dialog = PromptDialog(dialogDOM, options);
|
||||
break;
|
||||
default:
|
||||
dialog = PanelDialog(dialogDOM, options);
|
||||
break;
|
||||
}
|
||||
this._navigating = true;
|
||||
opening = DialogManager.open(dialog, options);
|
||||
opening.catch(function(error) {
|
||||
// A missing host API must not lock every later Settings dialog.
|
||||
dialogDOM.classList.remove('current');
|
||||
dialogDOM.hidden = true;
|
||||
var overlay = document.querySelector('.settings-dialog-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
self._navigating = false;
|
||||
defer.reject(error);
|
||||
self._execPendingRequest();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method can help us pop up any pending request and would try to
|
||||
* show it after previous request was done.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access private
|
||||
*/
|
||||
_execPendingRequest: function() {
|
||||
var request = this._pendingRequests.pop();
|
||||
if (request) {
|
||||
this.show(request.panelId, request.userOptions, request.defer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dialogService = new DialogService();
|
||||
return dialogService;
|
||||
});
|
||||
define('panels/firefox_sync/firefox_sync',['require','shared/settings_listener','modules/sync_manager_bridge','modules/dialog_service'],function(require){'use strict';var SettingsListener=require('shared/settings_listener');var SyncManagerBridge=require('modules/sync_manager_bridge');var DialogService=require('modules/dialog_service');const LOGGED_OUT_SCREEN='loggedout';const LOGGED_IN_SCREEN='loggedin';const ELEMENTS=[{screen:LOGGED_OUT_SCREEN,selector:'.fxsync-login',event:'click',listener:'enable'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-logout',event:'click',listener:'disable'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-user'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-sync-now',event:'click',listener:'sync'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-tos',event:'click',listener:'openTos'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-privacy',event:'click',listener:'openPrivacy'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-collections-bookmarks'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-collections-history'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-collections-passwords'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-last-sync'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-unverified'},{screen:LOGGED_IN_SCREEN,selector:'.fxsync-empty-account'}];function getElementName(str){str=str.toLowerCase().replace('.fxsync-','');return str.replace(/-([a-z])/g,g=>{return g[1].toUpperCase();});}
|
||||
function FirefoxSyncPanel(panel,screens){this.panel=panel;this.screens=screens;this.elements={};this.currentScreen=null;this.collections=new Map();['sync.collections.bookmarks.enabled','sync.collections.history.enabled','sync.collections.passwords.enabled'].forEach(setting=>{SettingsListener.observe(setting,true,enabled=>{enabled?this.collections.set(setting,enabled):this.collections.delete(setting);this.maybeEnableSyncNow();});});SyncManagerBridge.onsyncchange=this.onsyncchange.bind(this);}
|
||||
FirefoxSyncPanel.prototype={refresh(){SyncManagerBridge.getInfo().then(this.onsyncchange.bind(this));},clean(){window.clearTimeout(this.timeout);this.timeout=undefined;window.removeEventListener('offline',this.onlineListener);window.removeEventListener('online',this.onlineListener);},onsyncchange(message){if(!message||!message.state){throw new Error('Missing sync state');}
|
||||
switch(message.state){case'disabled':this.showScreen(LOGGED_OUT_SCREEN);this.hideEnabling();this.clean();break;case'enabling':this.showScreen(LOGGED_OUT_SCREEN);this.showEnabling();break;case'enabled':this.hideEnabling();this.showScreen(LOGGED_IN_SCREEN);this.showSyncNow();this.showUser(message.user);this.showLastSync(message.lastSync);if(message.error){LazyLoader.load('shared/js/sync/errors.js',()=>{if(message.error==ERROR_INVALID_SYNC_ACCOUNT){this.showEmptyAccount(message.user);}});}else{this.hideEmptyAccount();}
|
||||
break;case'syncing':this.showScreen(LOGGED_IN_SCREEN);this.showUser(message.user);this.showSyncing();break;case'errored':LazyLoader.load('shared/js/sync/errors.js',()=>{if(message.error==ERROR_UNVERIFIED_ACCOUNT){this.showScreen(LOGGED_IN_SCREEN);this.showUnverified(message.user);return;}
|
||||
if(message.error==ERROR_INVALID_SYNC_ACCOUNT){this.showScreen(LOGGED_IN_SCREEN);this.showEmptyAccount(message.user);return;}
|
||||
const IGNORED_ERRORS=[ERROR_DIALOG_CLOSED_BY_USER,ERROR_SYNC_APP_KILLED];if(IGNORED_ERRORS.indexOf(message.error)>-1){return;}
|
||||
var errorMsg=ERROR_UNKNOWN;var title;const KNOWN_ERRORS=[ERROR_OFFLINE,ERROR_NO_KEY_FETCH_TOKEN];if(KNOWN_ERRORS.indexOf(message.error)>-1){title=message.error;errorMsg=message.error+'-explanation';}
|
||||
DialogService.alert(errorMsg,{title:title}).then(()=>{this.showScreen(LOGGED_OUT_SCREEN);this.clean();});});break;}},addListener(element,event,listener){if(!element||!event||!listener){return;}
|
||||
element.addEventListener(event,this[listener]);},removeListener(element,event,listener){if(!element||!event||!listener){return;}
|
||||
element.removeEventListener(event,this[listener]);},loadElements(screen){ELEMENTS.forEach(element=>{var name=getElementName(element.selector);if(element.screen==screen){this.elements[name]=this.panel.querySelector(element.selector);this.addListener(this.elements[name],element.event,element.listener);return;}
|
||||
this.removeListener(this.elements[name],element.event,element.listener);this.elements[name]=null;});},showScreen(screen){if(this.currentScreen==screen){return;}
|
||||
this.currentScreen=screen;this.screens.loggedIn.hidden=(screen!=LOGGED_IN_SCREEN);this.screens.loggedOut.hidden=(screen!=LOGGED_OUT_SCREEN);this.loadElements(screen);},showEnabling(){this.elements.login.disabled=true;this.elements.login.dataset.l10nId='fxsync-signing';},hideEnabling(){if(!this.elements.login){return;}
|
||||
this.elements.login.disabled=false;this.elements.login.dataset.l10nId='fxsync-get-started';},showSyncing(){this.elements.syncNow.dataset.l10nId='fxsync-syncing';this.disableSyncNowAndCollections(true);},maybeEnableSyncNow(){if(!this.elements.syncNow){return;}
|
||||
this.elements.syncNow.disabled=(this.collections.size<=0)||!navigator.onLine;},showSyncNow(){this.elements.syncNow.dataset.l10nId='fxsync-sync-now';this.disableSyncNowAndCollections(false);this.elements.unverified.hidden=true;this.maybeEnableSyncNow();this.onlineListener=this.maybeEnableSyncNow.bind(this);window.addEventListener('offline',this.onlineListener);window.addEventListener('online',this.onlineListener);},showUser(email){this.elements.user.textContent=email;},showUnverified(email){this.showUser(email);this.showLastSync();this.disableSyncNowAndCollections(true);this.elements.unverified.hidden=false;},showEmptyAccount(email){this.showUser(email);this.showLastSync();this.disableSyncNowAndCollections(false);this.elements.emptyAccount.hidden=false;},hideEmptyAccount(){this.elements.emptyAccount.hidden=true;},showLastSync(time){if(!time){this.elements.lastSync.classList.add('hidden');return;}
|
||||
var formatter=mozIntl._gaia.RelativeDate(navigator.languages,{style:'short'});formatter.format(time).then(relDate=>{var selector='span[data-l10n-id=fxsync-last-synced]';var lastSync=this.elements.lastSync.querySelector(selector);document.l10n.setAttributes(lastSync,'fxsync-last-synced',{when:relDate});this.elements.lastSync.classList.remove('hidden');});this.timeout=window.setTimeout(()=>{this.showLastSync(time);},60*1000);},disableSyncNowAndCollections(disabled){['syncNow','collectionsBookmarks','collectionsHistory','collectionsPasswords'].forEach(name=>{this.elements[name].disabled=disabled;});},enable(){SyncManagerBridge.enable();},disable(){SyncManagerBridge.disable();},sync(){SyncManagerBridge.sync();},openTos(){window.open('https://accounts.firefox.com/legal/terms');},openPrivacy(){window.open('https://accounts.firefox.com/legal/privacy');}};return(panel,screens)=>{return new FirefoxSyncPanel(panel,screens);};});define('panels/firefox_sync/panel',['require','modules/settings_panel','modules/settings_utils','panels/firefox_sync/firefox_sync'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var SettingsUtils=require('modules/settings_utils');var FirefoxSyncPanel=require('panels/firefox_sync/firefox_sync');return()=>{return SettingsPanel({onInit:function fxsync_onInit(panel){var screens={loggedIn:panel.querySelector('.fxsync-logged-in'),loggedOut:panel.querySelector('.fxsync-logged-out')};this.syncPanel=FirefoxSyncPanel(panel,screens);var header=panel.querySelector('gaia-header');SettingsUtils.runHeaderFontFit(header);},onShow:function fxsync_onShow(){this.syncPanel.refresh();},onHide:function fxsync_onHide(){this.syncPanel.clean();}});};});
|
||||
171
overrides/settings.gaiamobile.org/js/panels/help/panel.js
Normal file
171
overrides/settings.gaiamobile.org/js/panels/help/panel.js
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
|
||||
/**
|
||||
* Handle support panel functionality with SIM and without SIM
|
||||
*/
|
||||
/**
|
||||
* Handle support panel functionality with SIM and without SIM
|
||||
*/
|
||||
/**
|
||||
* Handle support panel functionality with SIM and without SIM
|
||||
*/
|
||||
/**
|
||||
* Handle support panel functionality with SIM and without SIM
|
||||
*/
|
||||
/**
|
||||
* Handle support panel functionality with SIM and without SIM
|
||||
*/
|
||||
/**
|
||||
* Handle support panel functionality with SIM and without SIM
|
||||
*/
|
||||
define('panels/help/support',["require", "modules/settings_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
var SettingsCache = require('modules/settings_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var Support = function() {};
|
||||
|
||||
Support.prototype = {
|
||||
/**
|
||||
* initialization
|
||||
*/
|
||||
init: function support_init(elements) {
|
||||
this._elements = elements;
|
||||
/**
|
||||
* We'll stash the support info in here when reading from the
|
||||
* Settings.
|
||||
* If no values are found in Settings for support info We'll
|
||||
* refer to the JSON File Data.
|
||||
*/
|
||||
this._callSupportInfo = null;
|
||||
var url = 'https://vulpes-os.org/';
|
||||
this._elements.userGuide.addEventListener('click', () => {
|
||||
this._elements.userGuide.blur();
|
||||
var activity = new window.MozActivity({
|
||||
name: 'view',
|
||||
data: { type: 'url', url: url }
|
||||
});
|
||||
// For workaround jshint.
|
||||
activity.onsuccess = function() {};
|
||||
});
|
||||
|
||||
// parse support information from data
|
||||
this._getSupportInfo(this._displaySupportInfo.bind(this));
|
||||
},
|
||||
/**
|
||||
* Clean states for test
|
||||
*/
|
||||
uninit: function() {
|
||||
this._elements = null;
|
||||
this._supportInfo = null;
|
||||
this._callSupportInfo = null;
|
||||
},
|
||||
/**
|
||||
* get support data from cache or from resource file
|
||||
*/
|
||||
_getSupportInfo: function support_getSupportInfo(callback) {
|
||||
if (this._supportInfo) {
|
||||
callback(this._supportInfo);
|
||||
return;
|
||||
}
|
||||
LazyLoader.getJSON('/resources/support.json')
|
||||
.then(function loadSupportInfo(data) {
|
||||
this._supportInfo = data;
|
||||
callback(this._supportInfo);
|
||||
}.bind(this)).catch(function(error) {
|
||||
console.warn('Support information:', error);
|
||||
callback({onlinesupport: {title: 'Vulpes OS', href: 'https://vulpes-os.org/'}, callsupport: []});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* create support url items
|
||||
*/
|
||||
_createLinkNode: function support_createLinkNode(data, type) {
|
||||
var link = document.createElement('a');
|
||||
link.setAttribute('href', data.href);
|
||||
link.classList.add('link-text');
|
||||
if (type == 'tel') {
|
||||
link.textContent = data.title + ' (' + data.href + ')';
|
||||
} else {
|
||||
link.textContent = data.title;
|
||||
}
|
||||
return link;
|
||||
},
|
||||
/**
|
||||
* Indicate to our panel that there is support info present.
|
||||
*/
|
||||
_enableSupportInfo: function support_enableSupportInfo() {
|
||||
this._elements.help.dataset.hasSupportInfo = true;
|
||||
},
|
||||
/**
|
||||
* Local helper function to set the online support information
|
||||
* once it's retrieved or we've determined that we'll use
|
||||
* what's in the build JSON file.
|
||||
*/
|
||||
_setOnlineSupportInfo:
|
||||
function support_setOnlineSupportInfo(onlineSupportInfo) {
|
||||
this._enableSupportInfo();
|
||||
var nodes = this._createLinkNode(onlineSupportInfo);
|
||||
this._elements.supportText.appendChild(nodes);
|
||||
},
|
||||
/**
|
||||
* Local helper function to set the information once we've
|
||||
* retrieved it.
|
||||
*/
|
||||
_setCallSupportInfo:
|
||||
function support_setCallSupportInfo(supportInfo) {
|
||||
this._enableSupportInfo();
|
||||
for (var id in supportInfo) {
|
||||
this._elements.supportNumber
|
||||
.appendChild(this._createLinkNode(supportInfo[id], 'tel'));
|
||||
}
|
||||
},
|
||||
_renderSupportInfo: function support_renderSupportInfo(result) {
|
||||
var onlineSupportInfo = null;
|
||||
var onlineSupportTitle = result['support.onlinesupport.title'];
|
||||
if (typeof onlineSupportTitle === 'string' && onlineSupportTitle !== '') {
|
||||
onlineSupportInfo = { title: onlineSupportTitle };
|
||||
onlineSupportInfo.href = result['support.onlinesupport.href'];
|
||||
this._setOnlineSupportInfo(onlineSupportInfo);
|
||||
} else if (this._supportInfo) {
|
||||
this._setOnlineSupportInfo(this._supportInfo.onlinesupport);
|
||||
}
|
||||
|
||||
// Check to see if we have a title for the first support number.
|
||||
var callSupport1Title = result['support.callsupport1.title'];
|
||||
// If we have a title we'll go ahead and load the href for it too.
|
||||
if (typeof callSupport1Title === 'string' && callSupport1Title !== '') {
|
||||
this._callSupportInfo = [
|
||||
{
|
||||
'title': callSupport1Title,
|
||||
'href': result['support.callsupport1.href']
|
||||
}
|
||||
];
|
||||
|
||||
// Now check to see if we have a title for the second
|
||||
// support number. If we do, we'll load it's href as well.
|
||||
var callSupport2Title = result['support.callsupport2.title'];
|
||||
if (typeof callSupport2Title === 'string' && callSupport2Title !== '') {
|
||||
this._callSupportInfo.push({
|
||||
'title': callSupport2Title,
|
||||
'href': result['support.callsupport2.href']
|
||||
});
|
||||
// Finally set the support info retreived from Settings.
|
||||
this._setCallSupportInfo(this._callSupportInfo);
|
||||
}
|
||||
} else if (this._supportInfo) {
|
||||
// No customized values, use what's in the JSON file.
|
||||
this._setCallSupportInfo(this._supportInfo.callsupport);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* display Support information
|
||||
*/
|
||||
_displaySupportInfo: function support_displaySupportInfo(supportInfo) {
|
||||
SettingsCache.getSettings(this._renderSupportInfo.bind(this));
|
||||
}
|
||||
};
|
||||
return function ctor_support() {
|
||||
return new Support();
|
||||
};
|
||||
});
|
||||
define('panels/help/panel',['require','modules/settings_panel','panels/help/support'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var Support=require('panels/help/support');return function ctor_support_panel(){var help=Support();return SettingsPanel({onInit:function(panel){var elements={userGuide:panel.querySelector('#user-guide'),help:panel,supportText:panel.querySelector('#help-online-support-text'),supportNumber:panel.querySelector('#help-call-support-numbers')};help.init(elements);}});};});
|
||||
|
|
@ -0,0 +1,490 @@
|
|||
|
||||
define('panels/keyboard_add_layouts/nested_template_factory',['require'],function(require){'use strict';return function ctor_nestedTemplate(parentTemplate,childTemplate){var listViews=[];var template=parentTemplate.bind({listViews:listViews,childTemplate:childTemplate});Object.defineProperty(template,'listViews',{configurable:false,get:function(){return listViews;}});return template;};});define('panels/keyboard_add_layouts/keyboard_template',['require','modules/mvvm/list_view'],function(require){'use strict';var ListView=require('modules/mvvm/list_view');return function kal_keyboardTemplate(keyboard,recycled,helper){var layoutTemplate=this.childTemplate;var listViews=this.listViews;var container,header,h2,ul,listView;if(recycled){container=recycled;h2=container.querySelector('h2');ul=container.querySelector('ul');}else{container=document.createElement('div');header=document.createElement('header');h2=document.createElement('h2');ul=document.createElement('ul');header.appendChild(h2);container.appendChild(header);container.appendChild(ul);}
|
||||
listView=listViews.some(function eachListView(list){if(list.element===ul){list.set(keyboard.layouts);list.enabled=true;return true;}});if(!listView){listView=ListView(ul,keyboard.layouts,layoutTemplate);listView.enabled=true;listViews.push(listView);}
|
||||
helper.observeAndCall(keyboard,{name:function refreshName(){h2.textContent=keyboard.name;}});return container;};});define('panels/keyboard_add_layouts/layout_template',['require'],function(require){'use strict';return function kal_layoutTemplate(layout,recycled,helper){var container=null;var nameBdi,checkbox;if(recycled){container=recycled;checkbox=container.querySelector('gaia-checkbox');nameBdi=container.querySelector('bdi');}else{container=document.createElement('li');checkbox=document.createElement('gaia-checkbox');nameBdi=document.createElement('bdi');checkbox.appendChild(document.createElement('label'));checkbox.lastChild.appendChild(nameBdi);container.appendChild(checkbox);}
|
||||
if(layout.nameL10nId){nameBdi.dataset.l10nId=layout.nameL10nId;}
|
||||
checkbox.addEventListener('change',function(e){layout.enabled=e.target.checked;});helper.observeAndCall(layout,{name:function refreshName(){nameBdi.textContent=layout.name;},enabled:function(){checkbox.checked=layout.enabled;}});return container;};});define('modules/dialog_manager',["require", "modules/panel_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var DialogManager = function() {
|
||||
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
||||
|
||||
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
||||
};
|
||||
|
||||
DialogManager.prototype = {
|
||||
/**
|
||||
* load panel based on passed in panelId
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_loadPanel: function dm__loadPanel(panelId) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* promised version of PanelCache.get()
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_getPanel: function dm__getPanel(panelId) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
resolve(panel);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is used to control visibility of overlayDOM
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {Boolean} show
|
||||
*/
|
||||
_showOverlay: function dm__showOverlay(show) {
|
||||
this._overlayDOM.hidden = !show;
|
||||
},
|
||||
|
||||
/**
|
||||
* It is used to control the timing of transitions so that we can make sure
|
||||
* whether animation is done or not.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_transit: function dm__transit(method, dialog, options) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var panel = dialog.panel;
|
||||
var completed = false;
|
||||
var fallback;
|
||||
function finish(event) {
|
||||
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
clearTimeout(fallback);
|
||||
panel.removeEventListener('transitionend', finish);
|
||||
panel.removeEventListener('transitioncancel', finish);
|
||||
if (method === 'close') panel.hidden = true;
|
||||
resolve();
|
||||
}
|
||||
if (method === 'open') panel.hidden = false;
|
||||
setTimeout(function() {
|
||||
panel.addEventListener('transitionend', finish);
|
||||
panel.addEventListener('transitioncancel', finish);
|
||||
panel.classList.toggle('current', method === 'open');
|
||||
var style = window.getComputedStyle(panel);
|
||||
function milliseconds(value) {
|
||||
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
||||
}
|
||||
var durations = style.transitionDuration.split(',').map(milliseconds);
|
||||
var delays = style.transitionDelay.split(',').map(milliseconds);
|
||||
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
||||
return value + delays[index % delays.length];
|
||||
}));
|
||||
// Reduced motion, display changes or interrupted transitions may
|
||||
// omit the visibility event. Always release the dialog queue.
|
||||
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
||||
}, 150);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to open panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_open: function dm__open(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: load panel
|
||||
return self._loadPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function() {
|
||||
// 2: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 3: call beforeShow
|
||||
foundPanel = panel;
|
||||
return foundPanel.beforeShow(dialog.panel, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 4. UI stuffs + transition
|
||||
dialog.init();
|
||||
dialog.initUI();
|
||||
dialog.bindEvents();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(true);
|
||||
}
|
||||
|
||||
return self._transit('open', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. show that panel as a dialog
|
||||
return foundPanel.show(dialog.panel, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to close panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_close: function dm__close(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
var cachedResult;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 2: Let's validate to see whether we can close this dialog or not.
|
||||
foundPanel = panel;
|
||||
|
||||
var promise;
|
||||
// custom dialog - onSubmit
|
||||
if (foundPanel.onSubmit && options._type === 'submit') {
|
||||
promise = foundPanel.onSubmit();
|
||||
// custom dialog - onCancel
|
||||
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
||||
promise = foundPanel.onCancel();
|
||||
// if no onSubmit & onCancel, pass directly
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function(result) {
|
||||
cachedResult = result;
|
||||
|
||||
// 3: call beforeHide
|
||||
return foundPanel.beforeHide();
|
||||
})
|
||||
.then(function() {
|
||||
// 4. transition
|
||||
return self._transit('close', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. call hide
|
||||
return foundPanel.hide();
|
||||
})
|
||||
.then(function() {
|
||||
// 6. Get result and cleanup dialog
|
||||
var result;
|
||||
|
||||
// for prompt dialog, we have to get its own result from input text.
|
||||
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
||||
result = dialog.getResult();
|
||||
} else if (cachedResult) {
|
||||
result = cachedResult;
|
||||
}
|
||||
|
||||
dialog.cleanup();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* It is a bridge to call open or close function.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_navigate: function dm__navigate(method, dialog, options) {
|
||||
method = (method === 'open') ? '_open' : '_close';
|
||||
return this[method](dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to open dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
open: function dm_open(dialog, options) {
|
||||
return this._navigate('open', dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to close dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
close: function dm_close(dialog, type, options) {
|
||||
options._type = type;
|
||||
return this._navigate('close', dialog, options);
|
||||
}
|
||||
};
|
||||
|
||||
var dialogManager = new DialogManager();
|
||||
return dialogManager;
|
||||
});
|
||||
define('modules/dialog/base_dialog',['require'],function(require){'use strict';var BaseDialog=function(panelDOM,options){this.panel=panelDOM;this._options=options||{};};BaseDialog.prototype.DIALOG_CLASS='dialog';BaseDialog.prototype.TRANSITION_CLASS='fade';BaseDialog.prototype.SUBMIT_BUTTON_SELECTOR='[type="submit"]';BaseDialog.prototype.CANCEL_BUTTON_SELECTOR='[type="reset"]';BaseDialog.prototype.MESSAGE_SELECTOR='.settings-dialog-message';BaseDialog.prototype.TITLE_SELECTOR='.settings-dialog-title';BaseDialog.prototype.init=function bd_init(){this.TRANSITION_CLASS=this._options.transition||this.TRANSITION_CLASS;this.panel.classList.add(this.DIALOG_CLASS);this.panel.classList.add(this.TRANSITION_CLASS);};BaseDialog.prototype.initUI=function bd_initUI(){var message=this._options.message;var title=this._options.title;var submitButton=this._options.submitButton;var cancelButton=this._options.cancelButton;this._updateMessage(message);this._updateTitle(title);this._updateSubmitButton(submitButton);this._updateCancelButton(cancelButton);};BaseDialog.prototype.bindEvents=function bd_bindEvent(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};BaseDialog.prototype._updateMessage=function bd__updateMessage(message){var messageDOM=this.panel.querySelector(this.MESSAGE_SELECTOR);if(messageDOM&&message){message=this._getWrapL10nObject(message);document.l10n.setAttributes(messageDOM,message.id,message.args);}};BaseDialog.prototype._updateTitle=function bd__updateTitle(title){var titleDOM=this.panel.querySelector(this.TITLE_SELECTOR);if(titleDOM&&title){title=this._getWrapL10nObject(title);document.l10n.setAttributes(titleDOM,title.id,title.args);}};BaseDialog.prototype._updateSubmitButton=function bd__update(options){var buttonDOM=this.getSubmitButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'recommend';}};BaseDialog.prototype._updateCancelButton=function bd__updateText(options){var buttonDOM=this.getCancelButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'';}};BaseDialog.prototype._getWrapL10nObject=function bd__getWrapL10nObject(input){if(typeof input==='string'){return{id:input,args:null};}else if(typeof input==='object'){if(typeof input.id==='undefined'){throw new Error('You forgot to put l10nId - '+
|
||||
JSON.stringify(input));}else{return{id:input.id,args:input.args||null,style:input.style};}}else{throw new Error('You are using the wrong L10nObject, '+'please check its format again');}};BaseDialog.prototype.getDOM=function bd_getDOM(){return this.panel;};BaseDialog.prototype.getSubmitButton=function bd_getSubmitButton(){return this.panel.querySelector(this.SUBMIT_BUTTON_SELECTOR);};BaseDialog.prototype.getCancelButton=function bd_getCancelButton(){return this.panel.querySelector(this.CANCEL_BUTTON_SELECTOR);};BaseDialog.prototype.cleanup=function bd_cleanup(){if(this.DIALOG_CLASS!=='panel-dialog'){this._updateTitle('settings-'+this.DIALOG_CLASS+'-header');this._updateSubmitButton('ok');this._updateCancelButton('cancel');}
|
||||
this.panel.classList.remove(this.DIALOG_CLASS);this.panel.classList.remove(this.TRANSITION_CLASS);};return BaseDialog;});define('modules/dialog/panel_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PanelDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PanelDialog.prototype=Object.create(BaseDialog.prototype);PanelDialog.prototype.constructor=PanelDialog;PanelDialog.prototype.DIALOG_CLASS='panel-dialog';PanelDialog.prototype.TRANSITION_CLASS='fade';return function ctor_PanelDialog(panelDOM,options){return new PanelDialog(panelDOM,options);};});define('modules/dialog/alert_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var AlertDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};AlertDialog.prototype=Object.create(BaseDialog.prototype);AlertDialog.prototype.constructor=AlertDialog;AlertDialog.prototype.DIALOG_CLASS='alert-dialog';AlertDialog.prototype.TRANSITION_CLASS='fade';AlertDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};};return function ctor_alertDialog(panelDOM,options){return new AlertDialog(panelDOM,options);};});define('modules/dialog/confirm_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var ConfirmDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};ConfirmDialog.prototype=Object.create(BaseDialog.prototype);ConfirmDialog.prototype.constructor=ConfirmDialog;ConfirmDialog.prototype.DIALOG_CLASS='confirm-dialog';ConfirmDialog.prototype.TRANSITION_CLASS='fade';ConfirmDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};return function ctor_confirmDialog(panelDOM,options){return new ConfirmDialog(panelDOM,options);};});define('modules/dialog/prompt_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PromptDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PromptDialog.prototype=Object.create(BaseDialog.prototype);PromptDialog.prototype.constructor=PromptDialog;PromptDialog.prototype.DIALOG_CLASS='prompt-dialog';PromptDialog.prototype.TRANSITION_CLASS='fade';PromptDialog.prototype.INPUT_SELECTOR='.settings-dialog-input';PromptDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};PromptDialog.prototype.initUI=function(){BaseDialog.prototype.initUI.call(this);this.getInput().value=this._options.defaultValue||'';};PromptDialog.prototype.getInput=function(){return this.panel.querySelector(this.INPUT_SELECTOR);};PromptDialog.prototype.getResult=function(){return this.getInput().value;};return function ctor_promptDialog(panelDOM,options){return new PromptDialog(panelDOM,options);};});define('modules/dialog_service',["require", "settings", "modules/defer", "modules/dialog_manager", "modules/dialog/panel_dialog", "modules/dialog/alert_dialog", "modules/dialog/confirm_dialog", "modules/dialog/prompt_dialog"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var Settings = require('settings');
|
||||
var Defer = require('modules/defer');
|
||||
var DialogManager = require('modules/dialog_manager');
|
||||
|
||||
var PanelDialog = require('modules/dialog/panel_dialog');
|
||||
var AlertDialog = require('modules/dialog/alert_dialog');
|
||||
var ConfirmDialog = require('modules/dialog/confirm_dialog');
|
||||
var PromptDialog = require('modules/dialog/prompt_dialog');
|
||||
|
||||
var DialogService = function() {
|
||||
this._navigating = false;
|
||||
this._pendingRequests = [];
|
||||
this._settingsAlertDialogId = 'settings-alert-dialog';
|
||||
this._settingsBaseDialogId = 'settings-base-dialog';
|
||||
this._settingsConfirmDialogId = 'settings-confirm-dialog';
|
||||
this._settingsPromptDialogId = 'settings-prompt-dialog';
|
||||
};
|
||||
|
||||
DialogService.prototype = {
|
||||
/**
|
||||
* Alert dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
alert: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsAlertDialogId, {
|
||||
type: 'alert',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsConfirmDialogId, {
|
||||
type: 'confirm',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
prompt: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsPromptDialogId, {
|
||||
type: 'prompt',
|
||||
message: message,
|
||||
title: options.title,
|
||||
defaultValue: options.defaultValue,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel dialog. If you are going to show any panel as a dialog,
|
||||
* you have to use this method to show them.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} panelId
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
show: function dm_show(panelId, userOptions, _pendingDefer) {
|
||||
var self = this;
|
||||
var defer;
|
||||
var dialog;
|
||||
var dialogDOM = document.getElementById(panelId);
|
||||
var currentPanel = Settings.currentPanel;
|
||||
var options = userOptions || {};
|
||||
|
||||
if (_pendingDefer) {
|
||||
defer = _pendingDefer;
|
||||
} else {
|
||||
defer = Defer();
|
||||
}
|
||||
|
||||
if (this._navigating) {
|
||||
this._pendingRequests.push({
|
||||
defer: defer,
|
||||
panelId: panelId,
|
||||
userOptions: userOptions
|
||||
});
|
||||
} else {
|
||||
if ('#' + panelId === currentPanel) {
|
||||
defer.reject('You are showing the same panel #' + panelId);
|
||||
} else {
|
||||
var opening;
|
||||
var closing = false;
|
||||
function close(type) {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
// A tap during the opening animation must not run hide/show
|
||||
// concurrently, or a queued dialog can inherit stale form values.
|
||||
Promise.resolve(opening).then(function() {
|
||||
return DialogManager.close(dialog, type, options);
|
||||
}).then(function(result) {
|
||||
self._navigating = false;
|
||||
defer.resolve({type: type, value: result});
|
||||
self._execPendingRequest();
|
||||
}, function(error) {
|
||||
// Panel validation rejects to keep the form open. Allow a
|
||||
// correction or cancellation without rejecting the show promise.
|
||||
closing = false;
|
||||
dialogDOM.dispatchEvent(new CustomEvent('dialogvalidationfailed', {
|
||||
detail: error
|
||||
}));
|
||||
});
|
||||
}
|
||||
options.onWrapSubmit = function() { close('submit'); };
|
||||
options.onWrapCancel = function() { close('cancel'); };
|
||||
|
||||
switch (options.type) {
|
||||
case 'alert':
|
||||
dialog = AlertDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'confirm':
|
||||
dialog = ConfirmDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'prompt':
|
||||
dialog = PromptDialog(dialogDOM, options);
|
||||
break;
|
||||
default:
|
||||
dialog = PanelDialog(dialogDOM, options);
|
||||
break;
|
||||
}
|
||||
this._navigating = true;
|
||||
opening = DialogManager.open(dialog, options);
|
||||
opening.catch(function(error) {
|
||||
// A missing host API must not lock every later Settings dialog.
|
||||
dialogDOM.classList.remove('current');
|
||||
dialogDOM.hidden = true;
|
||||
var overlay = document.querySelector('.settings-dialog-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
self._navigating = false;
|
||||
defer.reject(error);
|
||||
self._execPendingRequest();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method can help us pop up any pending request and would try to
|
||||
* show it after previous request was done.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access private
|
||||
*/
|
||||
_execPendingRequest: function() {
|
||||
var request = this._pendingRequests.pop();
|
||||
if (request) {
|
||||
this.show(request.panelId, request.userOptions, request.defer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dialogService = new DialogService();
|
||||
return dialogService;
|
||||
});
|
||||
define('panels/keyboard_add_layouts/core',['require','modules/mvvm/list_view'],function(require){'use strict';var ListView=require('modules/mvvm/list_view');function KeyboardAddLayoutsCore(context,template){this._enabled=false;this._listView=null;this._keyboardContext=context;this._keyboardTemplate=template;}
|
||||
KeyboardAddLayoutsCore.prototype={get enabled(){return this._enabled;},set enabled(value){this._enabled=value;if(this._listView){this._listView.enabled=this._enabled;}
|
||||
this._keyboardTemplate.listViews.forEach(function(listView){listView.enabled=this._enabled;}.bind(this));},_initInstalledLayoutListView:function kal_initListView(listViewRoot,keyboards,keyboardTemplate){this._listView=ListView(listViewRoot,keyboards,keyboardTemplate);},_showEnabledDefaultDialog:function kal_showDialog(layout,missingType){require(['modules/dialog_service'],function(DialogService){document.l10n.formatValue('keyboardType-'+missingType).then(type=>{DialogService.alert({id:'defaultKeyboardEnabled',args:{layoutName:layout.inputManifest.name,appName:layout.manifest.name}},{title:{id:'mustHaveOneKeyboard',args:{type:type}}});});});},init:function kal_onInit(elements){var that=this;this._keyboardContext.init(function(){that._keyboardContext.keyboards(function(keyboards){that._initInstalledLayoutListView(elements.listViewRoot,keyboards,that._keyboardTemplate);that.enabled=true;});});this._keyboardContext.defaultKeyboardEnabled(this._showEnabledDefaultDialog);}};return function ctor_kalCore(context,template){return new KeyboardAddLayoutsCore(context,template);};});define('panels/keyboard_add_layouts/panel',['require','modules/settings_panel','modules/keyboard_context','shared/keyboard_helper','panels/keyboard_add_layouts/nested_template_factory','panels/keyboard_add_layouts/keyboard_template','panels/keyboard_add_layouts/layout_template','panels/keyboard_add_layouts/core'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var KeyboardContext=require('modules/keyboard_context');var KeyboardHelper=require('shared/keyboard_helper');var NestedTemplateFactory=require('panels/keyboard_add_layouts/nested_template_factory');var keyboardTemplate=require('panels/keyboard_add_layouts/keyboard_template');var layoutTemplate=require('panels/keyboard_add_layouts/layout_template');var Core=require('panels/keyboard_add_layouts/core');return function ctor_addLayoutsPanel(){var nestedTemplate=NestedTemplateFactory(keyboardTemplate,layoutTemplate);var core=Core(KeyboardContext,nestedTemplate);return SettingsPanel({onInit:function kalp_onInit(rootElement){core.init({listViewRoot:rootElement.querySelector('.keyboardAppContainer')});},onBeforeShow:function kalp_onBeforeShow(){core.enabled=true;},onBeforeHide:function kalp_onBeforeHide(){KeyboardHelper.saveToSettings();},onHide:function kalp_onHide(){core.enabled=false;}});};});
|
||||
486
overrides/settings.gaiamobile.org/js/panels/screen_lock/panel.js
Normal file
486
overrides/settings.gaiamobile.org/js/panels/screen_lock/panel.js
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
|
||||
define('modules/dialog_manager',["require", "modules/panel_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var DialogManager = function() {
|
||||
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
||||
|
||||
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
||||
};
|
||||
|
||||
DialogManager.prototype = {
|
||||
/**
|
||||
* load panel based on passed in panelId
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_loadPanel: function dm__loadPanel(panelId) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* promised version of PanelCache.get()
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_getPanel: function dm__getPanel(panelId) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
resolve(panel);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is used to control visibility of overlayDOM
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {Boolean} show
|
||||
*/
|
||||
_showOverlay: function dm__showOverlay(show) {
|
||||
this._overlayDOM.hidden = !show;
|
||||
},
|
||||
|
||||
/**
|
||||
* It is used to control the timing of transitions so that we can make sure
|
||||
* whether animation is done or not.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_transit: function dm__transit(method, dialog, options) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var panel = dialog.panel;
|
||||
var completed = false;
|
||||
var fallback;
|
||||
function finish(event) {
|
||||
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
clearTimeout(fallback);
|
||||
panel.removeEventListener('transitionend', finish);
|
||||
panel.removeEventListener('transitioncancel', finish);
|
||||
if (method === 'close') panel.hidden = true;
|
||||
resolve();
|
||||
}
|
||||
if (method === 'open') panel.hidden = false;
|
||||
setTimeout(function() {
|
||||
panel.addEventListener('transitionend', finish);
|
||||
panel.addEventListener('transitioncancel', finish);
|
||||
panel.classList.toggle('current', method === 'open');
|
||||
var style = window.getComputedStyle(panel);
|
||||
function milliseconds(value) {
|
||||
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
||||
}
|
||||
var durations = style.transitionDuration.split(',').map(milliseconds);
|
||||
var delays = style.transitionDelay.split(',').map(milliseconds);
|
||||
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
||||
return value + delays[index % delays.length];
|
||||
}));
|
||||
// Reduced motion, display changes or interrupted transitions may
|
||||
// omit the visibility event. Always release the dialog queue.
|
||||
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
||||
}, 150);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to open panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_open: function dm__open(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: load panel
|
||||
return self._loadPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function() {
|
||||
// 2: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 3: call beforeShow
|
||||
foundPanel = panel;
|
||||
return foundPanel.beforeShow(dialog.panel, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 4. UI stuffs + transition
|
||||
dialog.init();
|
||||
dialog.initUI();
|
||||
dialog.bindEvents();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(true);
|
||||
}
|
||||
|
||||
return self._transit('open', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. show that panel as a dialog
|
||||
return foundPanel.show(dialog.panel, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to close panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_close: function dm__close(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
var cachedResult;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 2: Let's validate to see whether we can close this dialog or not.
|
||||
foundPanel = panel;
|
||||
|
||||
var promise;
|
||||
// custom dialog - onSubmit
|
||||
if (foundPanel.onSubmit && options._type === 'submit') {
|
||||
promise = foundPanel.onSubmit();
|
||||
// custom dialog - onCancel
|
||||
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
||||
promise = foundPanel.onCancel();
|
||||
// if no onSubmit & onCancel, pass directly
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function(result) {
|
||||
cachedResult = result;
|
||||
|
||||
// 3: call beforeHide
|
||||
return foundPanel.beforeHide();
|
||||
})
|
||||
.then(function() {
|
||||
// 4. transition
|
||||
return self._transit('close', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. call hide
|
||||
return foundPanel.hide();
|
||||
})
|
||||
.then(function() {
|
||||
// 6. Get result and cleanup dialog
|
||||
var result;
|
||||
|
||||
// for prompt dialog, we have to get its own result from input text.
|
||||
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
||||
result = dialog.getResult();
|
||||
} else if (cachedResult) {
|
||||
result = cachedResult;
|
||||
}
|
||||
|
||||
dialog.cleanup();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* It is a bridge to call open or close function.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_navigate: function dm__navigate(method, dialog, options) {
|
||||
method = (method === 'open') ? '_open' : '_close';
|
||||
return this[method](dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to open dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
open: function dm_open(dialog, options) {
|
||||
return this._navigate('open', dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to close dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
close: function dm_close(dialog, type, options) {
|
||||
options._type = type;
|
||||
return this._navigate('close', dialog, options);
|
||||
}
|
||||
};
|
||||
|
||||
var dialogManager = new DialogManager();
|
||||
return dialogManager;
|
||||
});
|
||||
define('modules/dialog/base_dialog',['require'],function(require){'use strict';var BaseDialog=function(panelDOM,options){this.panel=panelDOM;this._options=options||{};};BaseDialog.prototype.DIALOG_CLASS='dialog';BaseDialog.prototype.TRANSITION_CLASS='fade';BaseDialog.prototype.SUBMIT_BUTTON_SELECTOR='[type="submit"]';BaseDialog.prototype.CANCEL_BUTTON_SELECTOR='[type="reset"]';BaseDialog.prototype.MESSAGE_SELECTOR='.settings-dialog-message';BaseDialog.prototype.TITLE_SELECTOR='.settings-dialog-title';BaseDialog.prototype.init=function bd_init(){this.TRANSITION_CLASS=this._options.transition||this.TRANSITION_CLASS;this.panel.classList.add(this.DIALOG_CLASS);this.panel.classList.add(this.TRANSITION_CLASS);};BaseDialog.prototype.initUI=function bd_initUI(){var message=this._options.message;var title=this._options.title;var submitButton=this._options.submitButton;var cancelButton=this._options.cancelButton;this._updateMessage(message);this._updateTitle(title);this._updateSubmitButton(submitButton);this._updateCancelButton(cancelButton);};BaseDialog.prototype.bindEvents=function bd_bindEvent(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};BaseDialog.prototype._updateMessage=function bd__updateMessage(message){var messageDOM=this.panel.querySelector(this.MESSAGE_SELECTOR);if(messageDOM&&message){message=this._getWrapL10nObject(message);document.l10n.setAttributes(messageDOM,message.id,message.args);}};BaseDialog.prototype._updateTitle=function bd__updateTitle(title){var titleDOM=this.panel.querySelector(this.TITLE_SELECTOR);if(titleDOM&&title){title=this._getWrapL10nObject(title);document.l10n.setAttributes(titleDOM,title.id,title.args);}};BaseDialog.prototype._updateSubmitButton=function bd__update(options){var buttonDOM=this.getSubmitButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'recommend';}};BaseDialog.prototype._updateCancelButton=function bd__updateText(options){var buttonDOM=this.getCancelButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'';}};BaseDialog.prototype._getWrapL10nObject=function bd__getWrapL10nObject(input){if(typeof input==='string'){return{id:input,args:null};}else if(typeof input==='object'){if(typeof input.id==='undefined'){throw new Error('You forgot to put l10nId - '+
|
||||
JSON.stringify(input));}else{return{id:input.id,args:input.args||null,style:input.style};}}else{throw new Error('You are using the wrong L10nObject, '+'please check its format again');}};BaseDialog.prototype.getDOM=function bd_getDOM(){return this.panel;};BaseDialog.prototype.getSubmitButton=function bd_getSubmitButton(){return this.panel.querySelector(this.SUBMIT_BUTTON_SELECTOR);};BaseDialog.prototype.getCancelButton=function bd_getCancelButton(){return this.panel.querySelector(this.CANCEL_BUTTON_SELECTOR);};BaseDialog.prototype.cleanup=function bd_cleanup(){if(this.DIALOG_CLASS!=='panel-dialog'){this._updateTitle('settings-'+this.DIALOG_CLASS+'-header');this._updateSubmitButton('ok');this._updateCancelButton('cancel');}
|
||||
this.panel.classList.remove(this.DIALOG_CLASS);this.panel.classList.remove(this.TRANSITION_CLASS);};return BaseDialog;});define('modules/dialog/panel_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PanelDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PanelDialog.prototype=Object.create(BaseDialog.prototype);PanelDialog.prototype.constructor=PanelDialog;PanelDialog.prototype.DIALOG_CLASS='panel-dialog';PanelDialog.prototype.TRANSITION_CLASS='fade';return function ctor_PanelDialog(panelDOM,options){return new PanelDialog(panelDOM,options);};});define('modules/dialog/alert_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var AlertDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};AlertDialog.prototype=Object.create(BaseDialog.prototype);AlertDialog.prototype.constructor=AlertDialog;AlertDialog.prototype.DIALOG_CLASS='alert-dialog';AlertDialog.prototype.TRANSITION_CLASS='fade';AlertDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};};return function ctor_alertDialog(panelDOM,options){return new AlertDialog(panelDOM,options);};});define('modules/dialog/confirm_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var ConfirmDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};ConfirmDialog.prototype=Object.create(BaseDialog.prototype);ConfirmDialog.prototype.constructor=ConfirmDialog;ConfirmDialog.prototype.DIALOG_CLASS='confirm-dialog';ConfirmDialog.prototype.TRANSITION_CLASS='fade';ConfirmDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};return function ctor_confirmDialog(panelDOM,options){return new ConfirmDialog(panelDOM,options);};});define('modules/dialog/prompt_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PromptDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PromptDialog.prototype=Object.create(BaseDialog.prototype);PromptDialog.prototype.constructor=PromptDialog;PromptDialog.prototype.DIALOG_CLASS='prompt-dialog';PromptDialog.prototype.TRANSITION_CLASS='fade';PromptDialog.prototype.INPUT_SELECTOR='.settings-dialog-input';PromptDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};PromptDialog.prototype.initUI=function(){BaseDialog.prototype.initUI.call(this);this.getInput().value=this._options.defaultValue||'';};PromptDialog.prototype.getInput=function(){return this.panel.querySelector(this.INPUT_SELECTOR);};PromptDialog.prototype.getResult=function(){return this.getInput().value;};return function ctor_promptDialog(panelDOM,options){return new PromptDialog(panelDOM,options);};});define('modules/dialog_service',["require", "settings", "modules/defer", "modules/dialog_manager", "modules/dialog/panel_dialog", "modules/dialog/alert_dialog", "modules/dialog/confirm_dialog", "modules/dialog/prompt_dialog"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var Settings = require('settings');
|
||||
var Defer = require('modules/defer');
|
||||
var DialogManager = require('modules/dialog_manager');
|
||||
|
||||
var PanelDialog = require('modules/dialog/panel_dialog');
|
||||
var AlertDialog = require('modules/dialog/alert_dialog');
|
||||
var ConfirmDialog = require('modules/dialog/confirm_dialog');
|
||||
var PromptDialog = require('modules/dialog/prompt_dialog');
|
||||
|
||||
var DialogService = function() {
|
||||
this._navigating = false;
|
||||
this._pendingRequests = [];
|
||||
this._settingsAlertDialogId = 'settings-alert-dialog';
|
||||
this._settingsBaseDialogId = 'settings-base-dialog';
|
||||
this._settingsConfirmDialogId = 'settings-confirm-dialog';
|
||||
this._settingsPromptDialogId = 'settings-prompt-dialog';
|
||||
};
|
||||
|
||||
DialogService.prototype = {
|
||||
/**
|
||||
* Alert dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
alert: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsAlertDialogId, {
|
||||
type: 'alert',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsConfirmDialogId, {
|
||||
type: 'confirm',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
prompt: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsPromptDialogId, {
|
||||
type: 'prompt',
|
||||
message: message,
|
||||
title: options.title,
|
||||
defaultValue: options.defaultValue,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel dialog. If you are going to show any panel as a dialog,
|
||||
* you have to use this method to show them.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} panelId
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
show: function dm_show(panelId, userOptions, _pendingDefer) {
|
||||
var self = this;
|
||||
var defer;
|
||||
var dialog;
|
||||
var dialogDOM = document.getElementById(panelId);
|
||||
var currentPanel = Settings.currentPanel;
|
||||
var options = userOptions || {};
|
||||
|
||||
if (_pendingDefer) {
|
||||
defer = _pendingDefer;
|
||||
} else {
|
||||
defer = Defer();
|
||||
}
|
||||
|
||||
if (this._navigating) {
|
||||
this._pendingRequests.push({
|
||||
defer: defer,
|
||||
panelId: panelId,
|
||||
userOptions: userOptions
|
||||
});
|
||||
} else {
|
||||
if ('#' + panelId === currentPanel) {
|
||||
defer.reject('You are showing the same panel #' + panelId);
|
||||
} else {
|
||||
var opening;
|
||||
var closing = false;
|
||||
function close(type) {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
// A tap during the opening animation must not run hide/show
|
||||
// concurrently, or a queued dialog can inherit stale form values.
|
||||
Promise.resolve(opening).then(function() {
|
||||
return DialogManager.close(dialog, type, options);
|
||||
}).then(function(result) {
|
||||
self._navigating = false;
|
||||
defer.resolve({type: type, value: result});
|
||||
self._execPendingRequest();
|
||||
}, function(error) {
|
||||
// Panel validation rejects to keep the form open. Allow a
|
||||
// correction or cancellation without rejecting the show promise.
|
||||
closing = false;
|
||||
dialogDOM.dispatchEvent(new CustomEvent('dialogvalidationfailed', {
|
||||
detail: error
|
||||
}));
|
||||
});
|
||||
}
|
||||
options.onWrapSubmit = function() { close('submit'); };
|
||||
options.onWrapCancel = function() { close('cancel'); };
|
||||
|
||||
switch (options.type) {
|
||||
case 'alert':
|
||||
dialog = AlertDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'confirm':
|
||||
dialog = ConfirmDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'prompt':
|
||||
dialog = PromptDialog(dialogDOM, options);
|
||||
break;
|
||||
default:
|
||||
dialog = PanelDialog(dialogDOM, options);
|
||||
break;
|
||||
}
|
||||
this._navigating = true;
|
||||
opening = DialogManager.open(dialog, options);
|
||||
opening.catch(function(error) {
|
||||
// A missing host API must not lock every later Settings dialog.
|
||||
dialogDOM.classList.remove('current');
|
||||
dialogDOM.hidden = true;
|
||||
var overlay = document.querySelector('.settings-dialog-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
self._navigating = false;
|
||||
defer.reject(error);
|
||||
self._execPendingRequest();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method can help us pop up any pending request and would try to
|
||||
* show it after previous request was done.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access private
|
||||
*/
|
||||
_execPendingRequest: function() {
|
||||
var request = this._pendingRequests.pop();
|
||||
if (request) {
|
||||
this.show(request.panelId, request.userOptions, request.defer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dialogService = new DialogService();
|
||||
return dialogService;
|
||||
});
|
||||
define('panels/screen_lock/screen_lock',['require','modules/dialog_service','shared/settings_listener'],function(require){'use strict';var DialogService=require('modules/dialog_service');var SettingsListener=require('shared/settings_listener');var ScreenLock=function ctor_screenlock(){return{_settings:{passcodeEnabled:false,lockscreenEnabled:false},onInit:function sl_onInit(elements){this._elements=elements;this._elements.passcodeEnable.addEventListener('click',this);this._elements.lockscreenEnable.addEventListener('click',this);this._elements.passcodeEditButton.addEventListener('click',this);this._fetchSettings();},_fetchSettings:function sl_fetchSettings(){SettingsListener.observe('lockscreen.enabled',false,function(enabled){this._toggleLockscreen(enabled);}.bind(this));SettingsListener.observe('lockscreen.passcode-lock.enabled',false,function(enabled){this._togglePasscode(enabled);}.bind(this));},_togglePasscode:function sl_togglePasscode(enabled){this._settings.passcodeEnabled=enabled;this._elements.panel.dataset.passcodeEnabled=enabled;this._elements.passcodeEnable.checked=enabled;},_toggleLockscreen:function sl_toggleLockscreen(enabled){this._settings.lockscreenEnabled=enabled;this._elements.panel.dataset.lockscreenEnabled=enabled;this._elements.lockscreenEnable.checked=enabled;},_showDialog:function sl_showDialog(mode){DialogService.show('screenLock-passcode',{mode:mode});},handleEvent:function sl_handleEvent(evt){switch(evt.target){case this._elements.passcodeEnable:evt.preventDefault();if(this._settings.passcodeEnabled){this._showDialog('confirm');}else{this._showDialog('create');}
|
||||
break;case this._elements.lockscreenEnable:if(this._settings.lockscreenEnabled===true&&this._settings.passcodeEnabled===true){evt.preventDefault();this._showDialog('confirmLock');}
|
||||
break;case this._elements.passcodeEditButton:this._showDialog('edit');break;}}};};return ScreenLock;});define('panels/screen_lock/panel',['require','modules/settings_panel','panels/screen_lock/screen_lock'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var ScreenLock=require('panels/screen_lock/screen_lock');return function ctor_screenlock(){var screenLock=ScreenLock();return SettingsPanel({onInit:function(panel){var elements={panel:panel,lockscreenEnable:panel.querySelector('.lockscreen-enable'),passcodeEnable:panel.querySelector('.passcode-enable'),passcodeEditButton:panel.querySelector('.passcode-edit')};screenLock.onInit(elements);}});};});
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
define('panels/simcard_manager/sim_ui_model',['require'],function(require){'use strict';var SimUIModel=function(cardIndex){this._cardIndex=cardIndex;this._enabled=false;this._absent=false;this._locked=false;this._defaultName={id:'simWithIndex',args:{index:this._cardIndex+1}};this._name=this._defaultName;this._number='';this._operator=null;};SimUIModel.prototype={getInfo:function(){var keys=['enabled','absent','locked','name','number','operator'];var info={};keys.forEach(function(key){info[key]=this['_'+key];}.bind(this));return info;},setState:function(key,options){switch(key){case'nosim':this._enabled=false;this._absent=true;this._locked=false;this._number='';this._operator={id:'noSimCard'};this._name=this._defaultName;break;case'locked':this._enabled=false;this._absent=false;this._locked=true;this._number='';this._operator={id:'sim-pin-locked'};this._name=this._defaultName;break;case'blocked':this._enabled=true;this._absent=true;this._locked=false;this._number='';this._operator=null;this._name={id:'noSimCard'};break;case'normal':this._enabled=true;this._absent=false;this._locked=false;this._number=options.number;if(options.operator){this._operator={text:options.operator};}else{this._operator={id:'no-operator'};}
|
||||
this._name=this._defaultName;break;}}};return function ctor_simUIModel(cardIndex){return new SimUIModel(cardIndex);};});define('panels/simcard_manager/simcard_manager',['require','shared/sanitizer','shared/sim_settings_helper','shared/airplane_mode_helper','shared/mobile_operator','panels/simcard_manager/sim_ui_model'],function(require){'use strict';var l10n=document.l10n;var Sanitizer=require('shared/sanitizer');var SimSettingsHelper=require('shared/sim_settings_helper');var AirplaneModeHelper=require('shared/airplane_mode_helper');var MobileOperator=require('shared/mobile_operator');var SimUIModel=require('panels/simcard_manager/sim_ui_model');var SimCardManager=function(elements){this._elements=elements;this._simcards=[];this._isAirplaneMode=false;};SimCardManager.prototype={init:function scm_init(){this._elements.outgoingCallSelect.addEventListener('change',this);this._elements.outgoingMessagesSelect.addEventListener('change',this);this._addOutgoingDataSelectEvent();this._addVoiceChangeEventOnConns();this._addCardStateChangeEventOnIccs();this._addLocalizedChangeEventOnIccs();SimSettingsHelper.observe('outgoingData',this._outgoingDataChangeEvent.bind(this));this._addAirplaneModeChangeEvent();this._isAirplaneMode=AirplaneModeHelper.getStatus()==='enabled'?true:false;this._initSimCardsInfo();this._initSimCardManagerUI();},simItemView:function({simIndex}){return Sanitizer.escapeHTML`<li class="sim-card sim-card-${simIndex}">
|
||||
<div class="sim-card-icon"></div>
|
||||
<div class="information-container">
|
||||
<p class="sim-card-name"></p>
|
||||
<p class="sim-card-operator"></p>
|
||||
<bdi class="sim-card-number"></bdi>
|
||||
</div>
|
||||
</li>`;},_initSimCardsInfo:function scm__initSimCardsInfo(){var conns=window.navigator.mozMobileConnections;for(var cardIndex=0;cardIndex<conns.length;cardIndex++){var conn=conns[cardIndex];var iccId=conn.iccId;var simcard=SimUIModel(cardIndex);this._simcards.push(simcard);this._updateCardState(cardIndex,iccId);}},handleEvent:function scm_handlEvent(evt){var cardIndex=evt.target.value;if(cardIndex===SimSettingsHelper.EMPTY_OPTION_VALUE){return;}
|
||||
switch(evt.target){case this._elements.outgoingCallSelect:SimSettingsHelper.setServiceOnCard('outgoingCall',cardIndex);break;case this._elements.outgoingMessagesSelect:SimSettingsHelper.setServiceOnCard('outgoingMessages',cardIndex);break;}},_outgoingDataChangeEvent:function scm__outgoingDataChangeEvent(cardIndex){this._elements.outgoingDataSelect.value=cardIndex;},_addOutgoingDataSelectEvent:function scm__addOutgoingDataSelectEvent(){var prevCardIndex;var newCardIndex;this._elements.outgoingDataSelect.addEventListener('focus',function(){prevCardIndex=this.selectedIndex;newCardIndex=this.selectedIndex;});this._elements.outgoingDataSelect.addEventListener('blur',function(){newCardIndex=this.selectedIndex;if(prevCardIndex!==newCardIndex){l10n.formatValue('change-outgoing-data-confirm').then(value=>{var wantToChange=window.confirm(value);if(wantToChange){SimSettingsHelper.setServiceOnCard('outgoingData',newCardIndex);}else{this.selectedIndex=prevCardIndex;}});}});},_getSimCardsCount:function scm__getSimCardsCount(){return this._simcards.length;},_getSimCardInfo:function scm__getSimCardInfo(cardIndex){return this._simcards[cardIndex].getInfo();},_getSimCard:function scm__getSimCard(cardIndex){return this._simcards[cardIndex];},_updateSimCardsUI:function scm__updateSimCardsUI(){this._simcards.forEach(function(simcard,cardIndex){this._updateSimCardUI(cardIndex);}.bind(this));},_updateSimCardUI:function scm__updateSimCardUI(cardIndex){var simcardInfo=this._getSimCardInfo(cardIndex);var selectors=['name','number','operator'];var cardSelector='.sim-card-'+cardIndex;var cardDom=this._elements.simCardContainer.querySelector(cardSelector);cardDom.classList.toggle('absent',simcardInfo.absent);cardDom.classList.toggle('locked',simcardInfo.locked);cardDom.classList.toggle('enabled',simcardInfo.enabled);cardDom.classList.toggle('with-number',!!simcardInfo.number);selectors.forEach(function(selector){var targetSelector=cardSelector+' .sim-card-'+selector;var element=this._elements.simCardContainer.querySelector(targetSelector);if(selector==='number'){element.textContent=simcardInfo[selector];}else{var l10nObj=simcardInfo[selector];if(l10nObj.id){l10n.setAttributes(element,l10nObj.id,l10nObj.args);}else{element.removeAttribute('data-l10n-id');element.textContent=l10nObj.text;}}}.bind(this));},_initSimCardManagerUI:function scm__initSimCardManagerUI(){this._initSimCardsUI();this._updateSelectOptionsUI();this._updateSimCardsUI();this._updateSimSettingsUI();},_initSimCardsUI:function scm__initSimCardsUI(){var simItemHTMLs=[];this._simcards.forEach(function(simcard,index){simItemHTMLs.push(this.simItemView({simIndex:index.toString()}));}.bind(this));this._elements.simCardContainer.innerHTML=simItemHTMLs.join('');},_updateSimSettingsUI:function scm__updateSimSettingsUI(){var firstCardInfo=this._simcards[0].getInfo();var secondCardInfo=(this._simcards[1]?this._simcards[1].getInfo():{absent:true});var hidden=firstCardInfo.absent&&secondCardInfo.absent||this._isAirplaneMode;this._elements.simSettingsHeader.hidden=hidden;this._elements.simSettingsList.hidden=hidden;},_updateSelectOptionsUI:function scm__updateSelectOptionsUI(){var firstCardInfo=this._simcards[0].getInfo();var secondCardInfo=(this._simcards[1]?this._simcards[1].getInfo():{absent:true});if(!firstCardInfo.absent&&!secondCardInfo.absent){SimSettingsHelper.getCardIndexFrom('outgoingCall',function(cardIndex){this._updateSelectOptionUI('outgoingCall',cardIndex,this._elements.outgoingCallSelect);}.bind(this));SimSettingsHelper.getCardIndexFrom('outgoingMessages',function(cardIndex){this._updateSelectOptionUI('outgoingMessages',cardIndex,this._elements.outgoingMessagesSelect);}.bind(this));SimSettingsHelper.getCardIndexFrom('outgoingData',function(cardIndex){this._updateSelectOptionUI('outgoingData',cardIndex,this._elements.outgoingDataSelect);}.bind(this));}else{var selectedCardIndex;if(firstCardInfo.absent&&secondCardInfo.absent){selectedCardIndex=0;}else{selectedCardIndex=firstCardInfo.absent?1:0;}
|
||||
this._elements.outgoingCallSelect.disabled=true;this._elements.outgoingMessagesSelect.disabled=true;this._elements.outgoingDataSelect.disabled=true;this._updateSelectOptionUI('outgoingCall',selectedCardIndex,this._elements.outgoingCallSelect);this._updateSelectOptionUI('outgoingMessages',selectedCardIndex,this._elements.outgoingMessagesSelect);this._updateSelectOptionUI('outgoingData',selectedCardIndex,this._elements.outgoingDataSelect);}},_updateSelectOptionUI:function scm__updateSelectOptionUI(storageKey,selectedCardIndex,selectDOM){while(selectDOM.firstChild){selectDOM.removeChild(selectDOM.firstChild);}
|
||||
this._simcards.forEach(function(simcard,index){var simcardInfo=simcard.getInfo();var option=document.createElement('option');option.value=index;if(simcardInfo.absent){option.value=SimSettingsHelper.EMPTY_OPTION_VALUE;option.text=SimSettingsHelper.EMPTY_OPTION_TEXT;}else{if(simcardInfo.name.id){l10n.setAttributes(option,simcardInfo.name.id,simcardInfo.name.args);}else{option.text=simcardInfo.name.text;}}
|
||||
if(index==selectedCardIndex){option.selected=true;}
|
||||
selectDOM.add(option);});if(storageKey==='outgoingCall'||storageKey==='outgoingMessages'){var option=document.createElement('option');option.value=SimSettingsHelper.ALWAYS_ASK_OPTION_VALUE;option.setAttribute('data-l10n-id','sim-manager-always-ask');if(SimSettingsHelper.ALWAYS_ASK_OPTION_VALUE===selectedCardIndex){option.selected=true;}
|
||||
selectDOM.add(option);}},_isSimCardLocked:function scm__isSimCardLocked(cardState){var lockedState=['pinRequired','pukRequired','networkLocked','serviceProviderLocked','corporateLocked','network1Locked','network2Locked','hrpdNetworkLocked','ruimCorporateLocked','ruimServiceProviderLocked'];return lockedState.indexOf(cardState)!==-1;},_isSimCardBlocked:function scm__isSimCardBlocked(cardState){var uselessState=['permanentBlocked'];return uselessState.indexOf(cardState)!==-1;},_addVoiceChangeEventOnConns:function scm__addVoiceChangeEventOnConns(){var conns=window.navigator.mozMobileConnections;for(var i=0;i<conns.length;i++){var iccId=conns[i].iccId;conns[i].addEventListener('voicechange',this._updateCardStateWithUI.bind(this,i,iccId));}},_addCardStateChangeEventOnIccs:function scm__addCardStateChangeEventOnIccs(){var conns=window.navigator.mozMobileConnections;var iccManager=window.navigator.mozIccManager;for(var i=0;i<conns.length;i++){var iccId=conns[i].iccId;var icc=iccManager.getIccById(iccId);if(icc){this._addChangeEventOnIccByIccId(iccId);}}},_addLocalizedChangeEventOnIccs:function scm__addLocalizedChangeEventOnIccs(){var conns=window.navigator.mozMobileConnections;document.addEventListener('DOMRetranslated',function(){for(var i=0;i<conns.length;i++){var iccId=conns[i].iccId;this._updateCardStateWithUI(i,iccId);}}.bind(this));},_addChangeEventOnIccByIccId:function scm__addChangeEventOnIccByIccId(iccId){var self=this;var icc=window.navigator.mozIccManager.getIccById(iccId);if(icc){icc.addEventListener('cardstatechange',function(){var cardIndex=self._getCardIndexByIccId(iccId);self._updateCardStateWithUI(cardIndex,iccId);if(self._isSimCardBlocked(icc.cardState)){self._updateSelectOptionsUI();}});}},_addAirplaneModeChangeEvent:function scm__addAirplaneModeChangeEvent(){var self=this;AirplaneModeHelper.addEventListener('statechange',function(state){if(state==='enabled'||state==='disabled'){var enabled=(state==='enabled')?true:false;self._isAirplaneMode=enabled;self._updateCardsState();self._updateSimCardsUI();self._updateSimSettingsUI();}});},_updateCardsState:function scm__updateCardsState(){var conns=window.navigator.mozMobileConnections;for(var cardIndex=0;cardIndex<conns.length;cardIndex++){var iccId=conns[cardIndex].iccId;this._updateCardState(cardIndex,iccId);}},_updateCardState:function scm__updateCardState(cardIndex,iccId){var iccManager=window.navigator.mozIccManager;var conn=window.navigator.mozMobileConnections[cardIndex];var simcard=this._simcards[cardIndex];if(!iccId||this._isAirplaneMode){simcard.setState('nosim');}else{var icc=iccManager.getIccById(iccId);var iccInfo=icc.iccInfo;var cardState=icc.cardState;var operatorInfo=MobileOperator.userFacingInfo(conn);if(this._isSimCardLocked(cardState)){simcard.setState('locked');}else if(this._isSimCardBlocked(cardState)){simcard.setState('blocked');}else{simcard.setState('normal',{number:iccInfo.msisdn||iccInfo.mdn||'',operator:operatorInfo.operator});}}},_updateCardStateWithUI:function scm__updateCardStateWithUI(cardIndex,iccId){this._updateCardState(cardIndex,iccId);this._updateSimCardUI(cardIndex);this._updateSimSettingsUI();},_getCardIndexByIccId:function scm__getCardIndexByIccId(iccId){var conns=window.navigator.mozMobileConnections;var cardIndex;for(var i=0;i<conns.length;i++){if(conns[i].iccId==iccId){cardIndex=i;}}
|
||||
return cardIndex;}};return SimCardManager;});define('panels/simcard_manager/panel',['require','modules/settings_panel','panels/simcard_manager/simcard_manager'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var SimCardManager=require('panels/simcard_manager/simcard_manager');return function ctor_sim_manager_panel(){return SettingsPanel({onInit:function(panel){var simcardManager=new SimCardManager({simCardContainer:panel.querySelector('.sim-card-container'),simSettingsHeader:panel.querySelector('.sim-manager-settings-header'),simSettingsList:panel.querySelector('.sim-manager-select-list'),outgoingCallSelect:panel.querySelector('.sim-manager-outgoing-call-select'),outgoingMessagesSelect:panel.querySelector('.sim-manager-outgoing-messages-select'),outgoingDataSelect:panel.querySelector('.sim-manager-outgoing-data-select'),});simcardManager.init();}});};});
|
||||
502
overrides/settings.gaiamobile.org/js/panels/simpin/panel.js
Normal file
502
overrides/settings.gaiamobile.org/js/panels/simpin/panel.js
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
|
||||
define('modules/dialog_manager',["require", "modules/panel_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var DialogManager = function() {
|
||||
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
||||
|
||||
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
||||
};
|
||||
|
||||
DialogManager.prototype = {
|
||||
/**
|
||||
* load panel based on passed in panelId
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_loadPanel: function dm__loadPanel(panelId) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* promised version of PanelCache.get()
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_getPanel: function dm__getPanel(panelId) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
resolve(panel);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is used to control visibility of overlayDOM
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {Boolean} show
|
||||
*/
|
||||
_showOverlay: function dm__showOverlay(show) {
|
||||
this._overlayDOM.hidden = !show;
|
||||
},
|
||||
|
||||
/**
|
||||
* It is used to control the timing of transitions so that we can make sure
|
||||
* whether animation is done or not.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_transit: function dm__transit(method, dialog, options) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var panel = dialog.panel;
|
||||
var completed = false;
|
||||
var fallback;
|
||||
function finish(event) {
|
||||
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
clearTimeout(fallback);
|
||||
panel.removeEventListener('transitionend', finish);
|
||||
panel.removeEventListener('transitioncancel', finish);
|
||||
if (method === 'close') panel.hidden = true;
|
||||
resolve();
|
||||
}
|
||||
if (method === 'open') panel.hidden = false;
|
||||
setTimeout(function() {
|
||||
panel.addEventListener('transitionend', finish);
|
||||
panel.addEventListener('transitioncancel', finish);
|
||||
panel.classList.toggle('current', method === 'open');
|
||||
var style = window.getComputedStyle(panel);
|
||||
function milliseconds(value) {
|
||||
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
||||
}
|
||||
var durations = style.transitionDuration.split(',').map(milliseconds);
|
||||
var delays = style.transitionDelay.split(',').map(milliseconds);
|
||||
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
||||
return value + delays[index % delays.length];
|
||||
}));
|
||||
// Reduced motion, display changes or interrupted transitions may
|
||||
// omit the visibility event. Always release the dialog queue.
|
||||
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
||||
}, 150);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to open panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_open: function dm__open(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: load panel
|
||||
return self._loadPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function() {
|
||||
// 2: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 3: call beforeShow
|
||||
foundPanel = panel;
|
||||
return foundPanel.beforeShow(dialog.panel, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 4. UI stuffs + transition
|
||||
dialog.init();
|
||||
dialog.initUI();
|
||||
dialog.bindEvents();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(true);
|
||||
}
|
||||
|
||||
return self._transit('open', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. show that panel as a dialog
|
||||
return foundPanel.show(dialog.panel, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to close panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_close: function dm__close(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
var cachedResult;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 2: Let's validate to see whether we can close this dialog or not.
|
||||
foundPanel = panel;
|
||||
|
||||
var promise;
|
||||
// custom dialog - onSubmit
|
||||
if (foundPanel.onSubmit && options._type === 'submit') {
|
||||
promise = foundPanel.onSubmit();
|
||||
// custom dialog - onCancel
|
||||
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
||||
promise = foundPanel.onCancel();
|
||||
// if no onSubmit & onCancel, pass directly
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function(result) {
|
||||
cachedResult = result;
|
||||
|
||||
// 3: call beforeHide
|
||||
return foundPanel.beforeHide();
|
||||
})
|
||||
.then(function() {
|
||||
// 4. transition
|
||||
return self._transit('close', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. call hide
|
||||
return foundPanel.hide();
|
||||
})
|
||||
.then(function() {
|
||||
// 6. Get result and cleanup dialog
|
||||
var result;
|
||||
|
||||
// for prompt dialog, we have to get its own result from input text.
|
||||
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
||||
result = dialog.getResult();
|
||||
} else if (cachedResult) {
|
||||
result = cachedResult;
|
||||
}
|
||||
|
||||
dialog.cleanup();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* It is a bridge to call open or close function.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_navigate: function dm__navigate(method, dialog, options) {
|
||||
method = (method === 'open') ? '_open' : '_close';
|
||||
return this[method](dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to open dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
open: function dm_open(dialog, options) {
|
||||
return this._navigate('open', dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to close dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
close: function dm_close(dialog, type, options) {
|
||||
options._type = type;
|
||||
return this._navigate('close', dialog, options);
|
||||
}
|
||||
};
|
||||
|
||||
var dialogManager = new DialogManager();
|
||||
return dialogManager;
|
||||
});
|
||||
define('modules/dialog/base_dialog',['require'],function(require){'use strict';var BaseDialog=function(panelDOM,options){this.panel=panelDOM;this._options=options||{};};BaseDialog.prototype.DIALOG_CLASS='dialog';BaseDialog.prototype.TRANSITION_CLASS='fade';BaseDialog.prototype.SUBMIT_BUTTON_SELECTOR='[type="submit"]';BaseDialog.prototype.CANCEL_BUTTON_SELECTOR='[type="reset"]';BaseDialog.prototype.MESSAGE_SELECTOR='.settings-dialog-message';BaseDialog.prototype.TITLE_SELECTOR='.settings-dialog-title';BaseDialog.prototype.init=function bd_init(){this.TRANSITION_CLASS=this._options.transition||this.TRANSITION_CLASS;this.panel.classList.add(this.DIALOG_CLASS);this.panel.classList.add(this.TRANSITION_CLASS);};BaseDialog.prototype.initUI=function bd_initUI(){var message=this._options.message;var title=this._options.title;var submitButton=this._options.submitButton;var cancelButton=this._options.cancelButton;this._updateMessage(message);this._updateTitle(title);this._updateSubmitButton(submitButton);this._updateCancelButton(cancelButton);};BaseDialog.prototype.bindEvents=function bd_bindEvent(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};BaseDialog.prototype._updateMessage=function bd__updateMessage(message){var messageDOM=this.panel.querySelector(this.MESSAGE_SELECTOR);if(messageDOM&&message){message=this._getWrapL10nObject(message);document.l10n.setAttributes(messageDOM,message.id,message.args);}};BaseDialog.prototype._updateTitle=function bd__updateTitle(title){var titleDOM=this.panel.querySelector(this.TITLE_SELECTOR);if(titleDOM&&title){title=this._getWrapL10nObject(title);document.l10n.setAttributes(titleDOM,title.id,title.args);}};BaseDialog.prototype._updateSubmitButton=function bd__update(options){var buttonDOM=this.getSubmitButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'recommend';}};BaseDialog.prototype._updateCancelButton=function bd__updateText(options){var buttonDOM=this.getCancelButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'';}};BaseDialog.prototype._getWrapL10nObject=function bd__getWrapL10nObject(input){if(typeof input==='string'){return{id:input,args:null};}else if(typeof input==='object'){if(typeof input.id==='undefined'){throw new Error('You forgot to put l10nId - '+
|
||||
JSON.stringify(input));}else{return{id:input.id,args:input.args||null,style:input.style};}}else{throw new Error('You are using the wrong L10nObject, '+'please check its format again');}};BaseDialog.prototype.getDOM=function bd_getDOM(){return this.panel;};BaseDialog.prototype.getSubmitButton=function bd_getSubmitButton(){return this.panel.querySelector(this.SUBMIT_BUTTON_SELECTOR);};BaseDialog.prototype.getCancelButton=function bd_getCancelButton(){return this.panel.querySelector(this.CANCEL_BUTTON_SELECTOR);};BaseDialog.prototype.cleanup=function bd_cleanup(){if(this.DIALOG_CLASS!=='panel-dialog'){this._updateTitle('settings-'+this.DIALOG_CLASS+'-header');this._updateSubmitButton('ok');this._updateCancelButton('cancel');}
|
||||
this.panel.classList.remove(this.DIALOG_CLASS);this.panel.classList.remove(this.TRANSITION_CLASS);};return BaseDialog;});define('modules/dialog/panel_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PanelDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PanelDialog.prototype=Object.create(BaseDialog.prototype);PanelDialog.prototype.constructor=PanelDialog;PanelDialog.prototype.DIALOG_CLASS='panel-dialog';PanelDialog.prototype.TRANSITION_CLASS='fade';return function ctor_PanelDialog(panelDOM,options){return new PanelDialog(panelDOM,options);};});define('modules/dialog/alert_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var AlertDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};AlertDialog.prototype=Object.create(BaseDialog.prototype);AlertDialog.prototype.constructor=AlertDialog;AlertDialog.prototype.DIALOG_CLASS='alert-dialog';AlertDialog.prototype.TRANSITION_CLASS='fade';AlertDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};};return function ctor_alertDialog(panelDOM,options){return new AlertDialog(panelDOM,options);};});define('modules/dialog/confirm_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var ConfirmDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};ConfirmDialog.prototype=Object.create(BaseDialog.prototype);ConfirmDialog.prototype.constructor=ConfirmDialog;ConfirmDialog.prototype.DIALOG_CLASS='confirm-dialog';ConfirmDialog.prototype.TRANSITION_CLASS='fade';ConfirmDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};return function ctor_confirmDialog(panelDOM,options){return new ConfirmDialog(panelDOM,options);};});define('modules/dialog/prompt_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PromptDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PromptDialog.prototype=Object.create(BaseDialog.prototype);PromptDialog.prototype.constructor=PromptDialog;PromptDialog.prototype.DIALOG_CLASS='prompt-dialog';PromptDialog.prototype.TRANSITION_CLASS='fade';PromptDialog.prototype.INPUT_SELECTOR='.settings-dialog-input';PromptDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};PromptDialog.prototype.initUI=function(){BaseDialog.prototype.initUI.call(this);this.getInput().value=this._options.defaultValue||'';};PromptDialog.prototype.getInput=function(){return this.panel.querySelector(this.INPUT_SELECTOR);};PromptDialog.prototype.getResult=function(){return this.getInput().value;};return function ctor_promptDialog(panelDOM,options){return new PromptDialog(panelDOM,options);};});define('modules/dialog_service',["require", "settings", "modules/defer", "modules/dialog_manager", "modules/dialog/panel_dialog", "modules/dialog/alert_dialog", "modules/dialog/confirm_dialog", "modules/dialog/prompt_dialog"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var Settings = require('settings');
|
||||
var Defer = require('modules/defer');
|
||||
var DialogManager = require('modules/dialog_manager');
|
||||
|
||||
var PanelDialog = require('modules/dialog/panel_dialog');
|
||||
var AlertDialog = require('modules/dialog/alert_dialog');
|
||||
var ConfirmDialog = require('modules/dialog/confirm_dialog');
|
||||
var PromptDialog = require('modules/dialog/prompt_dialog');
|
||||
|
||||
var DialogService = function() {
|
||||
this._navigating = false;
|
||||
this._pendingRequests = [];
|
||||
this._settingsAlertDialogId = 'settings-alert-dialog';
|
||||
this._settingsBaseDialogId = 'settings-base-dialog';
|
||||
this._settingsConfirmDialogId = 'settings-confirm-dialog';
|
||||
this._settingsPromptDialogId = 'settings-prompt-dialog';
|
||||
};
|
||||
|
||||
DialogService.prototype = {
|
||||
/**
|
||||
* Alert dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
alert: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsAlertDialogId, {
|
||||
type: 'alert',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsConfirmDialogId, {
|
||||
type: 'confirm',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
prompt: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsPromptDialogId, {
|
||||
type: 'prompt',
|
||||
message: message,
|
||||
title: options.title,
|
||||
defaultValue: options.defaultValue,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel dialog. If you are going to show any panel as a dialog,
|
||||
* you have to use this method to show them.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} panelId
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
show: function dm_show(panelId, userOptions, _pendingDefer) {
|
||||
var self = this;
|
||||
var defer;
|
||||
var dialog;
|
||||
var dialogDOM = document.getElementById(panelId);
|
||||
var currentPanel = Settings.currentPanel;
|
||||
var options = userOptions || {};
|
||||
|
||||
if (_pendingDefer) {
|
||||
defer = _pendingDefer;
|
||||
} else {
|
||||
defer = Defer();
|
||||
}
|
||||
|
||||
if (this._navigating) {
|
||||
this._pendingRequests.push({
|
||||
defer: defer,
|
||||
panelId: panelId,
|
||||
userOptions: userOptions
|
||||
});
|
||||
} else {
|
||||
if ('#' + panelId === currentPanel) {
|
||||
defer.reject('You are showing the same panel #' + panelId);
|
||||
} else {
|
||||
var opening;
|
||||
var closing = false;
|
||||
function close(type) {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
// A tap during the opening animation must not run hide/show
|
||||
// concurrently, or a queued dialog can inherit stale form values.
|
||||
Promise.resolve(opening).then(function() {
|
||||
return DialogManager.close(dialog, type, options);
|
||||
}).then(function(result) {
|
||||
self._navigating = false;
|
||||
defer.resolve({type: type, value: result});
|
||||
self._execPendingRequest();
|
||||
}, function(error) {
|
||||
// Panel validation rejects to keep the form open. Allow a
|
||||
// correction or cancellation without rejecting the show promise.
|
||||
closing = false;
|
||||
dialogDOM.dispatchEvent(new CustomEvent('dialogvalidationfailed', {
|
||||
detail: error
|
||||
}));
|
||||
});
|
||||
}
|
||||
options.onWrapSubmit = function() { close('submit'); };
|
||||
options.onWrapCancel = function() { close('cancel'); };
|
||||
|
||||
switch (options.type) {
|
||||
case 'alert':
|
||||
dialog = AlertDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'confirm':
|
||||
dialog = ConfirmDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'prompt':
|
||||
dialog = PromptDialog(dialogDOM, options);
|
||||
break;
|
||||
default:
|
||||
dialog = PanelDialog(dialogDOM, options);
|
||||
break;
|
||||
}
|
||||
this._navigating = true;
|
||||
opening = DialogManager.open(dialog, options);
|
||||
opening.catch(function(error) {
|
||||
// A missing host API must not lock every later Settings dialog.
|
||||
dialogDOM.classList.remove('current');
|
||||
dialogDOM.hidden = true;
|
||||
var overlay = document.querySelector('.settings-dialog-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
self._navigating = false;
|
||||
defer.reject(error);
|
||||
self._execPendingRequest();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method can help us pop up any pending request and would try to
|
||||
* show it after previous request was done.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access private
|
||||
*/
|
||||
_execPendingRequest: function() {
|
||||
var request = this._pendingRequests.pop();
|
||||
if (request) {
|
||||
this.show(request.panelId, request.userOptions, request.defer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dialogService = new DialogService();
|
||||
return dialogService;
|
||||
});
|
||||
define('panels/simpin/simpin',['require','shared/airplane_mode_helper','shared/simslot_manager','modules/dialog_service','shared/sanitizer','shared/toaster','modules/sim_security'],function(require){'use strict';var AirplaneModeHelper=require('shared/airplane_mode_helper');var SIMSlotManager=require('shared/simslot_manager');var DialogService=require('modules/dialog_service');var Sanitizer=require('shared/sanitizer');var Toaster=require('shared/toaster');var SimSecurity=require('modules/sim_security');var SimPin=function(elements){this._elements=elements;};SimPin.prototype={init:function simpin_init(){AirplaneModeHelper.ready(()=>{this.conns=window.navigator.mozMobileConnections;this.iccManager=window.navigator.mozIccManager;this.isAirplaneMode=(AirplaneModeHelper.getStatus()==='enabled');this._elements.simPinContainer.addEventListener('click',this);this._elements.simPinContainer.addEventListener('change',this.simPinSwitched.bind(this));this.addIccDetectedEvent();this.addIccUndetectedEvent();this.addAirplaneModeChangeEvent();this.initSimPinBack();this.initSimPinsUI();this.updateSimPinsUI();this.addChangeEventOnIccs();});},simPinView:function({simIndex,simPinArgs}){return Sanitizer.escapeHTML`
|
||||
<li class="simpin-enabled simpin-enabled-${simIndex}
|
||||
simpin-${simIndex}">
|
||||
<gaia-switch data-ignore data-sim-index="${simIndex}">
|
||||
<label data-l10n-id="simPinWithIndex"
|
||||
data-l10n-args="${simPinArgs}"></label>
|
||||
</gaia-switch>
|
||||
</li>
|
||||
<li class="simpin-change simpin-change-${simIndex} simpin-${simIndex}"
|
||||
hidden>
|
||||
<a class="menu-item" href="#" data-sim-index="${simIndex}"
|
||||
data-type="changeSimPin">
|
||||
<span data-l10n-id="changeSimPin"></span>
|
||||
</a>
|
||||
</li>`;},initSimPinBack:function simpin_initSimPinBack(){this._elements.simPinHeader.dataset.href=SIMSlotManager.isMultiSIM()?'#sim-manager':'#root';},initSimPinsUI:function simpin_initSimPinsUI(){var simPinHTMLs=[];[].forEach.call(this.conns,(conn,index)=>{var simPinIndex=index+1;if(!SIMSlotManager.isMultiSIM()){simPinIndex='';}
|
||||
simPinHTMLs.push(this.simPinView({'simIndex':index.toString(),'simPinArgs':JSON.stringify({'index':simPinIndex})}));});this._elements.simPinContainer.innerHTML=simPinHTMLs.join('');},updateSimPinUI:function simpin_updateSimPinUI(cardIndex){var iccId=this.conns[cardIndex].iccId;var icc=this.iccManager.getIccById(iccId);var changeSimPinItem=this._elements.simPinContainer.querySelector('.simpin-change-'+cardIndex);var simPinCheckbox=this._elements.simPinContainer.querySelector('.simpin-enabled-'+cardIndex+' gaia-switch');var isSimAvailable=icc&&icc.cardState&&icc.cardState!=='unknown';if(!isSimAvailable||this.isAirplaneMode){simPinCheckbox.disabled=true;changeSimPinItem.hidden=true;return Promise.resolve();}
|
||||
return SimSecurity.getCardLock(cardIndex,'pin').then((result)=>{var enabled=result.enabled;simPinCheckbox.disabled=false;simPinCheckbox.checked=enabled;changeSimPinItem.hidden=!enabled;},()=>{console.log('onerror');console.log('cardIndex',cardIndex);});},updateSimPinsUI:function simpin_updateSimPinsUI(){[].forEach.call(this.conns,(simcard,cardIndex)=>{this.updateSimPinUI(cardIndex);});},handleEvent:function simpin_handleEvent(evt){var target=evt.target;var cardIndex=target.dataset&&target.dataset.simIndex;var type=target.dataset&&target.dataset.type;cardIndex=parseInt(cardIndex,10);switch(type){case'changeSimPin':this.changeSimPin(cardIndex);break;}},simPinSwitched:function simpin_simPinSwitched(evt){var target=evt.target;var cardIndex=target.dataset&&target.dataset.simIndex;cardIndex=parseInt(cardIndex,10);this.checkSimPin(target,cardIndex);},checkSimPin:function simpin_checkSimPin(checkbox,cardIndex){var enabled=checkbox.checked;var iccId=this.conns[cardIndex].iccId;var icc=this.iccManager.getIccById(iccId);switch(icc.cardState){case'pukRequired':return DialogService.show('simpin-dialog',{method:'unlock_puk',cardIndex:cardIndex}).then((result)=>{var type=result.type;if(type==='submit'){checkbox.checked=true;this.updateSimPinUI(cardIndex);}else{checkbox.checked=!enabled;this.updateSimPinUI(cardIndex);}});default:var action=enabled?'enable_lock':'disable_lock';return DialogService.show('simpin-dialog',{method:action,cardIndex:cardIndex}).then((result)=>{var type=result.type;if(type==='submit'){this.updateSimPinUI(cardIndex);}else{checkbox.checked=!enabled;this.updateSimPinUI(cardIndex);}});}},changeSimPin:function(cardIndex){return DialogService.show('simpin-dialog',{method:'change_pin',cardIndex:cardIndex}).then(function(result){var type=result.type;if(type==='submit'){var toast;if(SIMSlotManager.isMultiSIM()){toast={messageL10nId:'simPinChangedSuccessfullyWithIndex',messageL10nArgs:{'index':+(cardIndex)+1},latency:3000,useTransition:true};}else{toast={messageL10nId:'simPinChangedSuccessfully',latency:3000,useTransition:true};}
|
||||
Toaster.showToast(toast);}});},addIccDetectedEvent:function simpin_addIccDetectedEvent(){this.iccManager.addEventListener('iccdetected',(evt)=>{var iccId=evt.iccId;var icc=this.iccManager.getIccById(iccId);if(icc){var cardIndex=this.getCardIndexByIccId(iccId);this.updateSimPinUI(cardIndex);this.addChangeEventOnIccByIccId(iccId);}});},addIccUndetectedEvent:function simpin_addIccDetectedEvent(){this.iccManager.addEventListener('iccundetected',(evt)=>{this.updateSimPinsUI();});},addAirplaneModeChangeEvent:function simpin_addAirplaneModeChangeEvent(){AirplaneModeHelper.addEventListener('statechange',(status)=>{this.isAirplaneMode=(status==='enabled');this.updateSimPinsUI();});},addChangeEventOnIccs:function simpin_addChangeEventOnIccs(){for(var i=0;i<this.conns.length;i++){var iccId=this.conns[i].iccId;var icc=this.iccManager.getIccById(iccId);if(icc){this.addChangeEventOnIccByIccId(iccId);}}},addChangeEventOnIccByIccId:function simpin_addChangeEventOnIccByIccId(iccId){var icc=this.iccManager.getIccById(iccId);if(icc){icc.addEventListener('cardstatechange',()=>{var cardIndex=this.getCardIndexByIccId(iccId);this.updateSimPinUI(cardIndex);});}},getCardIndexByIccId:function simpin_getCardIndexByIccId(iccId){var cardIndex;for(var i=0;i<this.conns.length;i++){if(this.conns[i].iccId==iccId){cardIndex=i;}}
|
||||
return cardIndex;}};return function ctor_simpin(elements){return new SimPin(elements);};});define('panels/simpin/panel',['require','modules/settings_panel','panels/simpin/simpin'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var SimPin=require('panels/simpin/simpin');return function ctor_simpin_panel(){return SettingsPanel({onInit:function(panel){var elements={};elements.simPinContainer=panel.querySelector('.simpin-container');elements.simPinHeader=panel.querySelector('.simpin-header');var simpin=SimPin(elements);simpin.init();}});};});
|
||||
487
overrides/settings.gaiamobile.org/js/panels/usb_storage/panel.js
Normal file
487
overrides/settings.gaiamobile.org/js/panels/usb_storage/panel.js
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
|
||||
define('modules/dialog_manager',["require", "modules/panel_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var DialogManager = function() {
|
||||
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
||||
|
||||
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
||||
};
|
||||
|
||||
DialogManager.prototype = {
|
||||
/**
|
||||
* load panel based on passed in panelId
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_loadPanel: function dm__loadPanel(panelId) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* promised version of PanelCache.get()
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_getPanel: function dm__getPanel(panelId) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
resolve(panel);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is used to control visibility of overlayDOM
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {Boolean} show
|
||||
*/
|
||||
_showOverlay: function dm__showOverlay(show) {
|
||||
this._overlayDOM.hidden = !show;
|
||||
},
|
||||
|
||||
/**
|
||||
* It is used to control the timing of transitions so that we can make sure
|
||||
* whether animation is done or not.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_transit: function dm__transit(method, dialog, options) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var panel = dialog.panel;
|
||||
var completed = false;
|
||||
var fallback;
|
||||
function finish(event) {
|
||||
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
clearTimeout(fallback);
|
||||
panel.removeEventListener('transitionend', finish);
|
||||
panel.removeEventListener('transitioncancel', finish);
|
||||
if (method === 'close') panel.hidden = true;
|
||||
resolve();
|
||||
}
|
||||
if (method === 'open') panel.hidden = false;
|
||||
setTimeout(function() {
|
||||
panel.addEventListener('transitionend', finish);
|
||||
panel.addEventListener('transitioncancel', finish);
|
||||
panel.classList.toggle('current', method === 'open');
|
||||
var style = window.getComputedStyle(panel);
|
||||
function milliseconds(value) {
|
||||
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
||||
}
|
||||
var durations = style.transitionDuration.split(',').map(milliseconds);
|
||||
var delays = style.transitionDelay.split(',').map(milliseconds);
|
||||
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
||||
return value + delays[index % delays.length];
|
||||
}));
|
||||
// Reduced motion, display changes or interrupted transitions may
|
||||
// omit the visibility event. Always release the dialog queue.
|
||||
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
||||
}, 150);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to open panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_open: function dm__open(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: load panel
|
||||
return self._loadPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function() {
|
||||
// 2: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 3: call beforeShow
|
||||
foundPanel = panel;
|
||||
return foundPanel.beforeShow(dialog.panel, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 4. UI stuffs + transition
|
||||
dialog.init();
|
||||
dialog.initUI();
|
||||
dialog.bindEvents();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(true);
|
||||
}
|
||||
|
||||
return self._transit('open', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. show that panel as a dialog
|
||||
return foundPanel.show(dialog.panel, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to close panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_close: function dm__close(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
var cachedResult;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 2: Let's validate to see whether we can close this dialog or not.
|
||||
foundPanel = panel;
|
||||
|
||||
var promise;
|
||||
// custom dialog - onSubmit
|
||||
if (foundPanel.onSubmit && options._type === 'submit') {
|
||||
promise = foundPanel.onSubmit();
|
||||
// custom dialog - onCancel
|
||||
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
||||
promise = foundPanel.onCancel();
|
||||
// if no onSubmit & onCancel, pass directly
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function(result) {
|
||||
cachedResult = result;
|
||||
|
||||
// 3: call beforeHide
|
||||
return foundPanel.beforeHide();
|
||||
})
|
||||
.then(function() {
|
||||
// 4. transition
|
||||
return self._transit('close', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. call hide
|
||||
return foundPanel.hide();
|
||||
})
|
||||
.then(function() {
|
||||
// 6. Get result and cleanup dialog
|
||||
var result;
|
||||
|
||||
// for prompt dialog, we have to get its own result from input text.
|
||||
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
||||
result = dialog.getResult();
|
||||
} else if (cachedResult) {
|
||||
result = cachedResult;
|
||||
}
|
||||
|
||||
dialog.cleanup();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* It is a bridge to call open or close function.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_navigate: function dm__navigate(method, dialog, options) {
|
||||
method = (method === 'open') ? '_open' : '_close';
|
||||
return this[method](dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to open dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
open: function dm_open(dialog, options) {
|
||||
return this._navigate('open', dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to close dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
close: function dm_close(dialog, type, options) {
|
||||
options._type = type;
|
||||
return this._navigate('close', dialog, options);
|
||||
}
|
||||
};
|
||||
|
||||
var dialogManager = new DialogManager();
|
||||
return dialogManager;
|
||||
});
|
||||
define('modules/dialog/base_dialog',['require'],function(require){'use strict';var BaseDialog=function(panelDOM,options){this.panel=panelDOM;this._options=options||{};};BaseDialog.prototype.DIALOG_CLASS='dialog';BaseDialog.prototype.TRANSITION_CLASS='fade';BaseDialog.prototype.SUBMIT_BUTTON_SELECTOR='[type="submit"]';BaseDialog.prototype.CANCEL_BUTTON_SELECTOR='[type="reset"]';BaseDialog.prototype.MESSAGE_SELECTOR='.settings-dialog-message';BaseDialog.prototype.TITLE_SELECTOR='.settings-dialog-title';BaseDialog.prototype.init=function bd_init(){this.TRANSITION_CLASS=this._options.transition||this.TRANSITION_CLASS;this.panel.classList.add(this.DIALOG_CLASS);this.panel.classList.add(this.TRANSITION_CLASS);};BaseDialog.prototype.initUI=function bd_initUI(){var message=this._options.message;var title=this._options.title;var submitButton=this._options.submitButton;var cancelButton=this._options.cancelButton;this._updateMessage(message);this._updateTitle(title);this._updateSubmitButton(submitButton);this._updateCancelButton(cancelButton);};BaseDialog.prototype.bindEvents=function bd_bindEvent(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};BaseDialog.prototype._updateMessage=function bd__updateMessage(message){var messageDOM=this.panel.querySelector(this.MESSAGE_SELECTOR);if(messageDOM&&message){message=this._getWrapL10nObject(message);document.l10n.setAttributes(messageDOM,message.id,message.args);}};BaseDialog.prototype._updateTitle=function bd__updateTitle(title){var titleDOM=this.panel.querySelector(this.TITLE_SELECTOR);if(titleDOM&&title){title=this._getWrapL10nObject(title);document.l10n.setAttributes(titleDOM,title.id,title.args);}};BaseDialog.prototype._updateSubmitButton=function bd__update(options){var buttonDOM=this.getSubmitButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'recommend';}};BaseDialog.prototype._updateCancelButton=function bd__updateText(options){var buttonDOM=this.getCancelButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'';}};BaseDialog.prototype._getWrapL10nObject=function bd__getWrapL10nObject(input){if(typeof input==='string'){return{id:input,args:null};}else if(typeof input==='object'){if(typeof input.id==='undefined'){throw new Error('You forgot to put l10nId - '+
|
||||
JSON.stringify(input));}else{return{id:input.id,args:input.args||null,style:input.style};}}else{throw new Error('You are using the wrong L10nObject, '+'please check its format again');}};BaseDialog.prototype.getDOM=function bd_getDOM(){return this.panel;};BaseDialog.prototype.getSubmitButton=function bd_getSubmitButton(){return this.panel.querySelector(this.SUBMIT_BUTTON_SELECTOR);};BaseDialog.prototype.getCancelButton=function bd_getCancelButton(){return this.panel.querySelector(this.CANCEL_BUTTON_SELECTOR);};BaseDialog.prototype.cleanup=function bd_cleanup(){if(this.DIALOG_CLASS!=='panel-dialog'){this._updateTitle('settings-'+this.DIALOG_CLASS+'-header');this._updateSubmitButton('ok');this._updateCancelButton('cancel');}
|
||||
this.panel.classList.remove(this.DIALOG_CLASS);this.panel.classList.remove(this.TRANSITION_CLASS);};return BaseDialog;});define('modules/dialog/panel_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PanelDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PanelDialog.prototype=Object.create(BaseDialog.prototype);PanelDialog.prototype.constructor=PanelDialog;PanelDialog.prototype.DIALOG_CLASS='panel-dialog';PanelDialog.prototype.TRANSITION_CLASS='fade';return function ctor_PanelDialog(panelDOM,options){return new PanelDialog(panelDOM,options);};});define('modules/dialog/alert_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var AlertDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};AlertDialog.prototype=Object.create(BaseDialog.prototype);AlertDialog.prototype.constructor=AlertDialog;AlertDialog.prototype.DIALOG_CLASS='alert-dialog';AlertDialog.prototype.TRANSITION_CLASS='fade';AlertDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};};return function ctor_alertDialog(panelDOM,options){return new AlertDialog(panelDOM,options);};});define('modules/dialog/confirm_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var ConfirmDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};ConfirmDialog.prototype=Object.create(BaseDialog.prototype);ConfirmDialog.prototype.constructor=ConfirmDialog;ConfirmDialog.prototype.DIALOG_CLASS='confirm-dialog';ConfirmDialog.prototype.TRANSITION_CLASS='fade';ConfirmDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};return function ctor_confirmDialog(panelDOM,options){return new ConfirmDialog(panelDOM,options);};});define('modules/dialog/prompt_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PromptDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PromptDialog.prototype=Object.create(BaseDialog.prototype);PromptDialog.prototype.constructor=PromptDialog;PromptDialog.prototype.DIALOG_CLASS='prompt-dialog';PromptDialog.prototype.TRANSITION_CLASS='fade';PromptDialog.prototype.INPUT_SELECTOR='.settings-dialog-input';PromptDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};PromptDialog.prototype.initUI=function(){BaseDialog.prototype.initUI.call(this);this.getInput().value=this._options.defaultValue||'';};PromptDialog.prototype.getInput=function(){return this.panel.querySelector(this.INPUT_SELECTOR);};PromptDialog.prototype.getResult=function(){return this.getInput().value;};return function ctor_promptDialog(panelDOM,options){return new PromptDialog(panelDOM,options);};});define('modules/dialog_service',["require", "settings", "modules/defer", "modules/dialog_manager", "modules/dialog/panel_dialog", "modules/dialog/alert_dialog", "modules/dialog/confirm_dialog", "modules/dialog/prompt_dialog"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var Settings = require('settings');
|
||||
var Defer = require('modules/defer');
|
||||
var DialogManager = require('modules/dialog_manager');
|
||||
|
||||
var PanelDialog = require('modules/dialog/panel_dialog');
|
||||
var AlertDialog = require('modules/dialog/alert_dialog');
|
||||
var ConfirmDialog = require('modules/dialog/confirm_dialog');
|
||||
var PromptDialog = require('modules/dialog/prompt_dialog');
|
||||
|
||||
var DialogService = function() {
|
||||
this._navigating = false;
|
||||
this._pendingRequests = [];
|
||||
this._settingsAlertDialogId = 'settings-alert-dialog';
|
||||
this._settingsBaseDialogId = 'settings-base-dialog';
|
||||
this._settingsConfirmDialogId = 'settings-confirm-dialog';
|
||||
this._settingsPromptDialogId = 'settings-prompt-dialog';
|
||||
};
|
||||
|
||||
DialogService.prototype = {
|
||||
/**
|
||||
* Alert dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
alert: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsAlertDialogId, {
|
||||
type: 'alert',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsConfirmDialogId, {
|
||||
type: 'confirm',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
prompt: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsPromptDialogId, {
|
||||
type: 'prompt',
|
||||
message: message,
|
||||
title: options.title,
|
||||
defaultValue: options.defaultValue,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel dialog. If you are going to show any panel as a dialog,
|
||||
* you have to use this method to show them.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} panelId
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
show: function dm_show(panelId, userOptions, _pendingDefer) {
|
||||
var self = this;
|
||||
var defer;
|
||||
var dialog;
|
||||
var dialogDOM = document.getElementById(panelId);
|
||||
var currentPanel = Settings.currentPanel;
|
||||
var options = userOptions || {};
|
||||
|
||||
if (_pendingDefer) {
|
||||
defer = _pendingDefer;
|
||||
} else {
|
||||
defer = Defer();
|
||||
}
|
||||
|
||||
if (this._navigating) {
|
||||
this._pendingRequests.push({
|
||||
defer: defer,
|
||||
panelId: panelId,
|
||||
userOptions: userOptions
|
||||
});
|
||||
} else {
|
||||
if ('#' + panelId === currentPanel) {
|
||||
defer.reject('You are showing the same panel #' + panelId);
|
||||
} else {
|
||||
var opening;
|
||||
var closing = false;
|
||||
function close(type) {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
// A tap during the opening animation must not run hide/show
|
||||
// concurrently, or a queued dialog can inherit stale form values.
|
||||
Promise.resolve(opening).then(function() {
|
||||
return DialogManager.close(dialog, type, options);
|
||||
}).then(function(result) {
|
||||
self._navigating = false;
|
||||
defer.resolve({type: type, value: result});
|
||||
self._execPendingRequest();
|
||||
}, function(error) {
|
||||
// Panel validation rejects to keep the form open. Allow a
|
||||
// correction or cancellation without rejecting the show promise.
|
||||
closing = false;
|
||||
dialogDOM.dispatchEvent(new CustomEvent('dialogvalidationfailed', {
|
||||
detail: error
|
||||
}));
|
||||
});
|
||||
}
|
||||
options.onWrapSubmit = function() { close('submit'); };
|
||||
options.onWrapCancel = function() { close('cancel'); };
|
||||
|
||||
switch (options.type) {
|
||||
case 'alert':
|
||||
dialog = AlertDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'confirm':
|
||||
dialog = ConfirmDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'prompt':
|
||||
dialog = PromptDialog(dialogDOM, options);
|
||||
break;
|
||||
default:
|
||||
dialog = PanelDialog(dialogDOM, options);
|
||||
break;
|
||||
}
|
||||
this._navigating = true;
|
||||
opening = DialogManager.open(dialog, options);
|
||||
opening.catch(function(error) {
|
||||
// A missing host API must not lock every later Settings dialog.
|
||||
dialogDOM.classList.remove('current');
|
||||
dialogDOM.hidden = true;
|
||||
var overlay = document.querySelector('.settings-dialog-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
self._navigating = false;
|
||||
defer.reject(error);
|
||||
self._execPendingRequest();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method can help us pop up any pending request and would try to
|
||||
* show it after previous request was done.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access private
|
||||
*/
|
||||
_execPendingRequest: function() {
|
||||
var request = this._pendingRequests.pop();
|
||||
if (request) {
|
||||
this.show(request.panelId, request.userOptions, request.defer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dialogService = new DialogService();
|
||||
return dialogService;
|
||||
});
|
||||
define('panels/usb_storage/usb_transfer',['require','modules/settings_cache','shared/settings_listener','modules/dialog_service','shared/async_storage','modules/media_storage'],function(require){'use strict';var SettingsCache=require('modules/settings_cache');var SettingsListener=require('shared/settings_listener');var DialogService=require('modules/dialog_service');var AsyncStorage=require('shared/async_storage');var MediaStorage=require('modules/media_storage');var _debug=false;var debug=function(){};if(_debug){debug=function ut_debug(msg){console.log('--> [UsbTransfer]: '+msg);};}
|
||||
var UsbTransfer=function(){this._keyUmsEnabled='ums.enabled';this._keyUmsMode='ums.mode';this._keyTransferProtocol='usb.transfer';this._elements=null;this._usbHotProtocolSwitch=false;this._partialUmsSupport=!navigator.getDeviceStorages('sdcard').every(storage=>storage.canBeShared);this.PROTOCOL_UMS='0';this.PROTOCOL_MTP='1';this.MODE_UMS=1;this.MODE_MTP=3;};UsbTransfer.prototype={init:function ut_init(elements,option){this._elements=elements;if(option&&option.usbHotProtocolSwitch!==undefined){this._usbHotProtocolSwitch=option.usbHotProtocolSwitch;}
|
||||
SettingsListener.observe(this._keyTransferProtocol,this.PROTOCOL_UMS,this._configProtocol.bind(this));SettingsListener.observe(this._keyUmsEnabled,false,this._umsEnabledHandler.bind(this));this._elements.usbEnabledCheckBox.addEventListener('change',this._umsCheckboxChange.bind(this));MediaStorage.observe('volumeState',this._updateUmsState.bind(this));},_umsCheckboxChange:function storage_umsCheckboxChange(evt){var checkbox=evt.target;var cset={};var warningKey='ums-turn-on-warning';debug('state change '+checkbox.checked);if(checkbox.checked){AsyncStorage.getItem(warningKey,(showed)=>{if(!showed){debug('show turn-on warning');this._umsTurnOnWarning(checkbox,warningKey);}else{debug('turn-on warning is showed');SettingsCache.getSettings(this._openIncompatibleSettingsDialogIfNeeded.bind(this));}});}else{cset[this._keyUmsEnabled]=false;navigator.mozSettings.createLock().set(cset);}},_umsTurnOnWarning:function storage_umsTurnOnWarning(checkbox,warningKey){DialogService.confirm('ums-confirm',{title:'ums-warning-title',submitButton:'ok',cancelButton:'cancel'}).then((result)=>{var type=result.type;if(type==='submit'){debug('turn on success');AsyncStorage.setItem(warningKey,true);SettingsCache.getSettings(this._openIncompatibleSettingsDialogIfNeeded.bind(this));}else{debug('turn on fail');var cset={};cset[this._keyUmsEnabled]=false;navigator.mozSettings.createLock().set(cset);checkbox.checked=false;}});},_openIncompatibleSettingsDialogIfNeeded:function storage_openIncompatibleSettingsDialogIfNeeded(settings){var cset={};var umsSettingKey=this._keyUmsEnabled;var oldSetting='tethering.usb.enabled';var usbTetheringSetting=settings[oldSetting];if(!usbTetheringSetting){cset[umsSettingKey]=true;navigator.mozSettings.createLock().set(cset);}else{DialogService.confirm('is-warning-storage-tethering-message',{title:'is-warning-storage-header',submitButton:{id:'enable',style:'recommend'},cancelButton:{id:'cancel'}}).then((result)=>{var enabled=result.type==='submit';var lock=navigator.mozSettings.createLock();var cset={};cset[umsSettingKey]=enabled;cset[oldSetting]=!enabled;lock.set(cset);});}},_umsEnabledHandler:function ut_umsEnabledHandler(enabled){debug('ums.enabled: '+enabled);this._elements.usbEnabledCheckBox.checked=enabled;this._updateUmsState();},_configProtocol:function ut_configProtocol(protocol){SettingsCache.getSettings((results)=>{var enabled=results[this._keyUmsEnabled];var mode=results[this._keyUmsMode];if(enabled){this._changeMode(mode,protocol);}});},_changeMode:function ut_changeMode(mode,protocol){if(mode!==this.MODE_MTP&&protocol===this.PROTOCOL_MTP){this._setMode(this.MODE_MTP);}else if(mode===this.MODE_MTP&&protocol===this.PROTOCOL_UMS){if(this._partialUmsSupport){debug('show partial warning');DialogService.show('ums-partial-warning').then((result)=>{if(result.type==='cancel'){var param={};param[this._keyTransferProtocol]=this.PROTOCOL_MTP;SettingsListener.getSettingsLock().set(param);}else{this._setMode(this.MODE_UMS);}});}else{this._setMode(this.MODE_UMS);}}else{console.log('Error: should not be executed');}},_updateUmsState:function ut_updateUmsState(){var key;debug('enabled:'+this._elements.usbEnabledCheckBox.checked+'/'+'volumeState:'+MediaStorage.volumeState);if(this._elements.usbEnabledCheckBox.checked){key='enabled';if(MediaStorage.volumeState==='shared'){this._disableProtocolSelections(true);}else{this._disableProtocolSelections(false);}}else if(MediaStorage.volumeState==='shared'){key='umsUnplugToDisable';this._disableProtocolSelections(true);}else{key='disabled';this._disableProtocolSelections(false);}
|
||||
this._elements.usbEnabledInfoBlock.setAttribute('data-l10n-id',key);},_disableProtocolSelections:function ut_disableProtocolSelections(disabled){var i;if(disabled){if(!this._usbHotProtocolSwitch){for(i=0;i<this._elements.protocols.length;i++){this._elements.protocols[i].setAttribute('disabled',true);}}}else{for(i=0;i<this._elements.protocols.length;i++){this._elements.protocols[i].removeAttribute('disabled');}}},_setMode:function ut_setMode(mode){var param={};param[this._keyUmsMode]=mode;SettingsListener.getSettingsLock().set(param);}};return function ctor_usb_transfer(){return new UsbTransfer();};});define('panels/usb_storage/panel',['require','modules/settings_panel','panels/usb_storage/usb_transfer','shared/lazy_loader'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var UsbTransferModule=require('panels/usb_storage/usb_transfer');var LazyLoader=require('shared/lazy_loader');var elements={};return function ctor_usb_storage_panel(){var usbTransfer=UsbTransferModule();return SettingsPanel({onInit:function us_onInit(panel){elements={usbEnabledCheckBox:panel.querySelector('.ums-switch'),usbEnabledInfoBlock:panel.querySelector('.ums-switch-desc'),protocols:panel.querySelectorAll('gaia-radio')};LazyLoader.getJSON('/resources/device-features.json').then(({usbHotProtocolSwitch})=>usbTransfer.init(elements,{usbHotProtocolSwitch:usbHotProtocolSwitch}));}});};});
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,485 @@
|
|||
|
||||
define('modules/dialog_manager',["require", "modules/panel_cache", "shared/lazy_loader"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var PanelCache = require('modules/panel_cache');
|
||||
var LazyLoader = require('shared/lazy_loader');
|
||||
|
||||
var DialogManager = function() {
|
||||
this.OVERLAY_SELECTOR = '.settings-dialog-overlay';
|
||||
|
||||
this._overlayDOM = document.querySelector(this.OVERLAY_SELECTOR);
|
||||
};
|
||||
|
||||
DialogManager.prototype = {
|
||||
/**
|
||||
* load panel based on passed in panelId
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_loadPanel: function dm__loadPanel(panelId) {
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
var panelElement = document.getElementById(panelId);
|
||||
if (panelElement.dataset.rendered) { // already initialized
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
panelElement.dataset.rendered = true;
|
||||
|
||||
// XXX remove SubPanel loader once sub panel are modulized
|
||||
if (panelElement.dataset.requireSubPanels) {
|
||||
// load the panel and its sub-panels (dependencies)
|
||||
// (load the main panel last because it contains the scripts)
|
||||
var selector = 'section[id^="' + panelElement.id + '-"]';
|
||||
var subPanels = document.querySelectorAll(selector);
|
||||
for (var i = 0, il = subPanels.length; i < il; i++) {
|
||||
LazyLoader.load([subPanels[i]]);
|
||||
}
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
} else {
|
||||
LazyLoader.load([panelElement], resolve);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* promised version of PanelCache.get()
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} panelId
|
||||
* @return {Promise}
|
||||
*/
|
||||
_getPanel: function dm__getPanel(panelId) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
PanelCache.get(panelId, function(panel) {
|
||||
resolve(panel);
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is used to control visibility of overlayDOM
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {Boolean} show
|
||||
*/
|
||||
_showOverlay: function dm__showOverlay(show) {
|
||||
this._overlayDOM.hidden = !show;
|
||||
},
|
||||
|
||||
/**
|
||||
* It is used to control the timing of transitions so that we can make sure
|
||||
* whether animation is done or not.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_transit: function dm__transit(method, dialog, options) {
|
||||
var promise = new Promise(function(resolve) {
|
||||
var panel = dialog.panel;
|
||||
var completed = false;
|
||||
var fallback;
|
||||
function finish(event) {
|
||||
if (event && (event.target !== panel || event.propertyName !== 'visibility')) return;
|
||||
if (completed) return;
|
||||
completed = true;
|
||||
clearTimeout(fallback);
|
||||
panel.removeEventListener('transitionend', finish);
|
||||
panel.removeEventListener('transitioncancel', finish);
|
||||
if (method === 'close') panel.hidden = true;
|
||||
resolve();
|
||||
}
|
||||
if (method === 'open') panel.hidden = false;
|
||||
setTimeout(function() {
|
||||
panel.addEventListener('transitionend', finish);
|
||||
panel.addEventListener('transitioncancel', finish);
|
||||
panel.classList.toggle('current', method === 'open');
|
||||
var style = window.getComputedStyle(panel);
|
||||
function milliseconds(value) {
|
||||
return parseFloat(value) * (value.trim().endsWith('ms') ? 1 : 1000) || 0;
|
||||
}
|
||||
var durations = style.transitionDuration.split(',').map(milliseconds);
|
||||
var delays = style.transitionDelay.split(',').map(milliseconds);
|
||||
var duration = Math.max.apply(Math, durations.map(function(value, index) {
|
||||
return value + delays[index % delays.length];
|
||||
}));
|
||||
// Reduced motion, display changes or interrupted transitions may
|
||||
// omit the visibility event. Always release the dialog queue.
|
||||
fallback = setTimeout(finish, Math.max(0, duration) + 100);
|
||||
}, 150);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to open panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_open: function dm__open(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: load panel
|
||||
return self._loadPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function() {
|
||||
// 2: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 3: call beforeShow
|
||||
foundPanel = panel;
|
||||
return foundPanel.beforeShow(dialog.panel, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 4. UI stuffs + transition
|
||||
dialog.init();
|
||||
dialog.initUI();
|
||||
dialog.bindEvents();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(true);
|
||||
}
|
||||
|
||||
return self._transit('open', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. show that panel as a dialog
|
||||
return foundPanel.show(dialog.panel, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Do necessary works to close panel like loading panel, doing transition
|
||||
* and call related functions.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_close: function dm__close(dialog, options) {
|
||||
var self = this;
|
||||
var foundPanel;
|
||||
var cachedResult;
|
||||
|
||||
return Promise.resolve()
|
||||
.then(function() {
|
||||
// 1: Get that panel
|
||||
return self._getPanel(dialog.panel.id);
|
||||
})
|
||||
.then(function(panel) {
|
||||
// 2: Let's validate to see whether we can close this dialog or not.
|
||||
foundPanel = panel;
|
||||
|
||||
var promise;
|
||||
// custom dialog - onSubmit
|
||||
if (foundPanel.onSubmit && options._type === 'submit') {
|
||||
promise = foundPanel.onSubmit();
|
||||
// custom dialog - onCancel
|
||||
} else if (foundPanel.onCancel && options._type === 'cancel') {
|
||||
promise = foundPanel.onCancel();
|
||||
// if no onSubmit & onCancel, pass directly
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function(result) {
|
||||
cachedResult = result;
|
||||
|
||||
// 3: call beforeHide
|
||||
return foundPanel.beforeHide();
|
||||
})
|
||||
.then(function() {
|
||||
// 4. transition
|
||||
return self._transit('close', dialog, options);
|
||||
})
|
||||
.then(function() {
|
||||
// 5. call hide
|
||||
return foundPanel.hide();
|
||||
})
|
||||
.then(function() {
|
||||
// 6. Get result and cleanup dialog
|
||||
var result;
|
||||
|
||||
// for prompt dialog, we have to get its own result from input text.
|
||||
if (dialog.DIALOG_CLASS === 'prompt-dialog') {
|
||||
result = dialog.getResult();
|
||||
} else if (cachedResult) {
|
||||
result = cachedResult;
|
||||
}
|
||||
|
||||
dialog.cleanup();
|
||||
|
||||
if (dialog.TRANSITION_CLASS === 'zoom-in-80') {
|
||||
self._showOverlay(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* It is a bridge to call open or close function.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access private
|
||||
* @param {String} method
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
_navigate: function dm__navigate(method, dialog, options) {
|
||||
method = (method === 'open') ? '_open' : '_close';
|
||||
return this[method](dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to open dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
open: function dm_open(dialog, options) {
|
||||
return this._navigate('open', dialog, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DialogService would use this exposed API to close dialog.
|
||||
*
|
||||
* @memberOf DialogManager
|
||||
* @access public
|
||||
* @param {BaseDialog} dialog
|
||||
* @param {Object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
close: function dm_close(dialog, type, options) {
|
||||
options._type = type;
|
||||
return this._navigate('close', dialog, options);
|
||||
}
|
||||
};
|
||||
|
||||
var dialogManager = new DialogManager();
|
||||
return dialogManager;
|
||||
});
|
||||
define('modules/dialog/base_dialog',['require'],function(require){'use strict';var BaseDialog=function(panelDOM,options){this.panel=panelDOM;this._options=options||{};};BaseDialog.prototype.DIALOG_CLASS='dialog';BaseDialog.prototype.TRANSITION_CLASS='fade';BaseDialog.prototype.SUBMIT_BUTTON_SELECTOR='[type="submit"]';BaseDialog.prototype.CANCEL_BUTTON_SELECTOR='[type="reset"]';BaseDialog.prototype.MESSAGE_SELECTOR='.settings-dialog-message';BaseDialog.prototype.TITLE_SELECTOR='.settings-dialog-title';BaseDialog.prototype.init=function bd_init(){this.TRANSITION_CLASS=this._options.transition||this.TRANSITION_CLASS;this.panel.classList.add(this.DIALOG_CLASS);this.panel.classList.add(this.TRANSITION_CLASS);};BaseDialog.prototype.initUI=function bd_initUI(){var message=this._options.message;var title=this._options.title;var submitButton=this._options.submitButton;var cancelButton=this._options.cancelButton;this._updateMessage(message);this._updateTitle(title);this._updateSubmitButton(submitButton);this._updateCancelButton(cancelButton);};BaseDialog.prototype.bindEvents=function bd_bindEvent(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};BaseDialog.prototype._updateMessage=function bd__updateMessage(message){var messageDOM=this.panel.querySelector(this.MESSAGE_SELECTOR);if(messageDOM&&message){message=this._getWrapL10nObject(message);document.l10n.setAttributes(messageDOM,message.id,message.args);}};BaseDialog.prototype._updateTitle=function bd__updateTitle(title){var titleDOM=this.panel.querySelector(this.TITLE_SELECTOR);if(titleDOM&&title){title=this._getWrapL10nObject(title);document.l10n.setAttributes(titleDOM,title.id,title.args);}};BaseDialog.prototype._updateSubmitButton=function bd__update(options){var buttonDOM=this.getSubmitButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'recommend';}};BaseDialog.prototype._updateCancelButton=function bd__updateText(options){var buttonDOM=this.getCancelButton();if(buttonDOM&&options){options=this._getWrapL10nObject(options);document.l10n.setAttributes(buttonDOM,options.id,options.args);buttonDOM.className=options.style||'';}};BaseDialog.prototype._getWrapL10nObject=function bd__getWrapL10nObject(input){if(typeof input==='string'){return{id:input,args:null};}else if(typeof input==='object'){if(typeof input.id==='undefined'){throw new Error('You forgot to put l10nId - '+
|
||||
JSON.stringify(input));}else{return{id:input.id,args:input.args||null,style:input.style};}}else{throw new Error('You are using the wrong L10nObject, '+'please check its format again');}};BaseDialog.prototype.getDOM=function bd_getDOM(){return this.panel;};BaseDialog.prototype.getSubmitButton=function bd_getSubmitButton(){return this.panel.querySelector(this.SUBMIT_BUTTON_SELECTOR);};BaseDialog.prototype.getCancelButton=function bd_getCancelButton(){return this.panel.querySelector(this.CANCEL_BUTTON_SELECTOR);};BaseDialog.prototype.cleanup=function bd_cleanup(){if(this.DIALOG_CLASS!=='panel-dialog'){this._updateTitle('settings-'+this.DIALOG_CLASS+'-header');this._updateSubmitButton('ok');this._updateCancelButton('cancel');}
|
||||
this.panel.classList.remove(this.DIALOG_CLASS);this.panel.classList.remove(this.TRANSITION_CLASS);};return BaseDialog;});define('modules/dialog/panel_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PanelDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PanelDialog.prototype=Object.create(BaseDialog.prototype);PanelDialog.prototype.constructor=PanelDialog;PanelDialog.prototype.DIALOG_CLASS='panel-dialog';PanelDialog.prototype.TRANSITION_CLASS='fade';return function ctor_PanelDialog(panelDOM,options){return new PanelDialog(panelDOM,options);};});define('modules/dialog/alert_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var AlertDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};AlertDialog.prototype=Object.create(BaseDialog.prototype);AlertDialog.prototype.constructor=AlertDialog;AlertDialog.prototype.DIALOG_CLASS='alert-dialog';AlertDialog.prototype.TRANSITION_CLASS='fade';AlertDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};};return function ctor_alertDialog(panelDOM,options){return new AlertDialog(panelDOM,options);};});define('modules/dialog/confirm_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var ConfirmDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};ConfirmDialog.prototype=Object.create(BaseDialog.prototype);ConfirmDialog.prototype.constructor=ConfirmDialog;ConfirmDialog.prototype.DIALOG_CLASS='confirm-dialog';ConfirmDialog.prototype.TRANSITION_CLASS='fade';ConfirmDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};return function ctor_confirmDialog(panelDOM,options){return new ConfirmDialog(panelDOM,options);};});define('modules/dialog/prompt_dialog',['require','modules/dialog/base_dialog'],function(require){'use strict';var BaseDialog=require('modules/dialog/base_dialog');var PromptDialog=function(panelDOM,options){BaseDialog.call(this,panelDOM,options);};PromptDialog.prototype=Object.create(BaseDialog.prototype);PromptDialog.prototype.constructor=PromptDialog;PromptDialog.prototype.DIALOG_CLASS='prompt-dialog';PromptDialog.prototype.TRANSITION_CLASS='fade';PromptDialog.prototype.INPUT_SELECTOR='.settings-dialog-input';PromptDialog.prototype.bindEvents=function(){var self=this;this.getSubmitButton().onclick=function(){self._options.onWrapSubmit();};this.getCancelButton().onclick=function(){self._options.onWrapCancel();};};PromptDialog.prototype.initUI=function(){BaseDialog.prototype.initUI.call(this);this.getInput().value=this._options.defaultValue||'';};PromptDialog.prototype.getInput=function(){return this.panel.querySelector(this.INPUT_SELECTOR);};PromptDialog.prototype.getResult=function(){return this.getInput().value;};return function ctor_promptDialog(panelDOM,options){return new PromptDialog(panelDOM,options);};});define('modules/dialog_service',["require", "settings", "modules/defer", "modules/dialog_manager", "modules/dialog/panel_dialog", "modules/dialog/alert_dialog", "modules/dialog/confirm_dialog", "modules/dialog/prompt_dialog"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var Settings = require('settings');
|
||||
var Defer = require('modules/defer');
|
||||
var DialogManager = require('modules/dialog_manager');
|
||||
|
||||
var PanelDialog = require('modules/dialog/panel_dialog');
|
||||
var AlertDialog = require('modules/dialog/alert_dialog');
|
||||
var ConfirmDialog = require('modules/dialog/confirm_dialog');
|
||||
var PromptDialog = require('modules/dialog/prompt_dialog');
|
||||
|
||||
var DialogService = function() {
|
||||
this._navigating = false;
|
||||
this._pendingRequests = [];
|
||||
this._settingsAlertDialogId = 'settings-alert-dialog';
|
||||
this._settingsBaseDialogId = 'settings-base-dialog';
|
||||
this._settingsConfirmDialogId = 'settings-confirm-dialog';
|
||||
this._settingsPromptDialogId = 'settings-prompt-dialog';
|
||||
};
|
||||
|
||||
DialogService.prototype = {
|
||||
/**
|
||||
* Alert dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
alert: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsAlertDialogId, {
|
||||
type: 'alert',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Confirm dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsConfirmDialogId, {
|
||||
type: 'confirm',
|
||||
message: message,
|
||||
title: options.title,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Prompt dialog with more controls.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} message
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
prompt: function(message, userOptions) {
|
||||
var options = userOptions || {};
|
||||
return this.show(this._settingsPromptDialogId, {
|
||||
type: 'prompt',
|
||||
message: message,
|
||||
title: options.title,
|
||||
defaultValue: options.defaultValue,
|
||||
submitButton: options.submitButton,
|
||||
cancelButton: options.cancelButton
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Panel dialog. If you are going to show any panel as a dialog,
|
||||
* you have to use this method to show them.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access public
|
||||
* @param {String} panelId
|
||||
* @param {Object} userOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
show: function dm_show(panelId, userOptions, _pendingDefer) {
|
||||
var self = this;
|
||||
var defer;
|
||||
var dialog;
|
||||
var dialogDOM = document.getElementById(panelId);
|
||||
var currentPanel = Settings.currentPanel;
|
||||
var options = userOptions || {};
|
||||
|
||||
if (_pendingDefer) {
|
||||
defer = _pendingDefer;
|
||||
} else {
|
||||
defer = Defer();
|
||||
}
|
||||
|
||||
if (this._navigating) {
|
||||
this._pendingRequests.push({
|
||||
defer: defer,
|
||||
panelId: panelId,
|
||||
userOptions: userOptions
|
||||
});
|
||||
} else {
|
||||
if ('#' + panelId === currentPanel) {
|
||||
defer.reject('You are showing the same panel #' + panelId);
|
||||
} else {
|
||||
var opening;
|
||||
var closing = false;
|
||||
function close(type) {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
// A tap during the opening animation must not run hide/show
|
||||
// concurrently, or a queued dialog can inherit stale form values.
|
||||
Promise.resolve(opening).then(function() {
|
||||
return DialogManager.close(dialog, type, options);
|
||||
}).then(function(result) {
|
||||
self._navigating = false;
|
||||
defer.resolve({type: type, value: result});
|
||||
self._execPendingRequest();
|
||||
}, function(error) {
|
||||
// Panel validation rejects to keep the form open. Allow a
|
||||
// correction or cancellation without rejecting the show promise.
|
||||
closing = false;
|
||||
dialogDOM.dispatchEvent(new CustomEvent('dialogvalidationfailed', {
|
||||
detail: error
|
||||
}));
|
||||
});
|
||||
}
|
||||
options.onWrapSubmit = function() { close('submit'); };
|
||||
options.onWrapCancel = function() { close('cancel'); };
|
||||
|
||||
switch (options.type) {
|
||||
case 'alert':
|
||||
dialog = AlertDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'confirm':
|
||||
dialog = ConfirmDialog(dialogDOM, options);
|
||||
break;
|
||||
case 'prompt':
|
||||
dialog = PromptDialog(dialogDOM, options);
|
||||
break;
|
||||
default:
|
||||
dialog = PanelDialog(dialogDOM, options);
|
||||
break;
|
||||
}
|
||||
this._navigating = true;
|
||||
opening = DialogManager.open(dialog, options);
|
||||
opening.catch(function(error) {
|
||||
// A missing host API must not lock every later Settings dialog.
|
||||
dialogDOM.classList.remove('current');
|
||||
dialogDOM.hidden = true;
|
||||
var overlay = document.querySelector('.settings-dialog-overlay');
|
||||
if (overlay) overlay.hidden = true;
|
||||
self._navigating = false;
|
||||
defer.reject(error);
|
||||
self._execPendingRequest();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return defer.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method can help us pop up any pending request and would try to
|
||||
* show it after previous request was done.
|
||||
*
|
||||
* @memberOf DialogService
|
||||
* @access private
|
||||
*/
|
||||
_execPendingRequest: function() {
|
||||
var request = this._pendingRequests.pop();
|
||||
if (request) {
|
||||
this.show(request.panelId, request.userOptions, request.defer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dialogService = new DialogService();
|
||||
return dialogService;
|
||||
});
|
||||
define('panels/wifi_select_certificate_file/panel',['require','modules/settings_panel','modules/settings_service','modules/dialog_service','shared/device_storage/enumerate_all','shared/wifi_helper'],function(require){'use strict';var SettingsPanel=require('modules/settings_panel');var SettingsService=require('modules/settings_service');var DialogService=require('modules/dialog_service');var EnumerateAll=require('shared/device_storage/enumerate_all');var WifiHelper=require('shared/wifi_helper');var wifiManager=WifiHelper.getWifiManager();return function ctor_selectCertificateWifi(){var elements={};return SettingsPanel({onInit:function(panel){elements={};elements.panel=panel;elements.certificateFilesList=panel.querySelector('.wifi-certificate-files-List');},onBeforeShow:function(panel){this._cleanup();this._createScanList(elements.certificateFilesList);},_cleanup:function(){while(elements.certificateFilesList.hasChildNodes()){elements.certificateFilesList.removeChild(elements.certificateFilesList.lastChild);}},_createScanList:function(list){var storages=navigator.getDeviceStorages('sdcard');var cursor=EnumerateAll(storages,'');cursor.onsuccess=function(){var file=cursor.result;if(file){var extension=this._parseExtension(file.name);if(this._isCertificateFile(extension)){var li=this._createLinkAnchor(file);list.appendChild(li);}
|
||||
cursor.continue();}}.bind(this);cursor.onerror=function(){console.warn('failed to get file:'+cursor.error.name);};},_setCertificateItemsEnabled:function(enabled){var items=elements.certificateFilesList.querySelectorAll('li');var update=enabled?function(item){item.classList.remove('disabled');}:function(item){item.classList.add('disabled');};for(var i=0;i<items.length;i++){update(items[i]);}},_createLinkAnchor:function(file){var anchor=document.createElement('a');var certificateName=this._parseFilename(file.name);anchor.textContent=certificateName;var li=document.createElement('li');li.appendChild(anchor);anchor.onclick=()=>{DialogService.show('wifi-enterCertificateNickname',{certificateName:certificateName}).then((result)=>{var type=result.type;var value=result.value;if(type==='submit'){var certRequest=wifiManager.importCert(file,'',value.nickname);this._setCertificateItemsEnabled(false);certRequest.onsuccess=()=>{SettingsService.navigate('wifi-manageCertificates');};certRequest.onerror=()=>{DialogService.alert('certificate-import-failed-description',{title:'certificate-import-failed'}).then(()=>{this._setCertificateItemsEnabled(true);});};}});};return li;},_parseFilename:function(path){return path.slice(path.lastIndexOf('/')+1,path.lastIndexOf('.'));},_parseExtension:function(filename){var array=filename.split('.');return array.length>1?array.pop():'';},_isCertificateFile:function(extension){var cerExtension=['cer','crt','pem','der'];return cerExtension.indexOf(extension)>-1;},});};});
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
define('panels/wifi_status/panel',["require", "modules/dialog_panel", "shared/wifi_helper", "modules/wifi_context"],function(require) {
|
||||
'use strict';
|
||||
|
||||
var DialogPanel = require('modules/dialog_panel');
|
||||
var WifiHelper = require('shared/wifi_helper');
|
||||
var WifiContext = require('modules/wifi_context');
|
||||
var wifiManager = WifiHelper.getWifiManager();
|
||||
|
||||
return function ctor_statusWifi() {
|
||||
var elements = {};
|
||||
|
||||
return DialogPanel({
|
||||
onInit: function(panel) {
|
||||
elements = {};
|
||||
elements.ip = panel.querySelector('[data-ip]');
|
||||
elements.speed = panel.querySelector('[data-speed]');
|
||||
elements.ssid = panel.querySelector('[data-ssid]');
|
||||
elements.signal = panel.querySelector('[data-signal]');
|
||||
elements.security = panel.querySelector('[data-security]');
|
||||
},
|
||||
onBeforeShow: function(panel, options) {
|
||||
this._updateNetworkInfo();
|
||||
elements.ssid.textContent = options.network.ssid;
|
||||
elements.signal.setAttribute('data-l10n-id',
|
||||
'signalLevel' + options.sl);
|
||||
if (options.security) {
|
||||
elements.security.removeAttribute('data-l10n-id');
|
||||
elements.security.textContent = options.security;
|
||||
} else {
|
||||
elements.security.setAttribute('data-l10n-id', 'securityNone');
|
||||
}
|
||||
|
||||
WifiContext.addEventListener('wifiConnectionInfoUpdate',
|
||||
this._updateNetworkInfo);
|
||||
},
|
||||
onBeforeHide: function() {
|
||||
WifiContext.removeEventListener('wifiConnectionInfoUpdate',
|
||||
this._updateNetworkInfo);
|
||||
},
|
||||
_updateNetworkInfo: function() {
|
||||
var info = wifiManager.connectionInformation || {};
|
||||
elements.ip.textContent = info.ipAddress || '';
|
||||
document.l10n.setAttributes(elements.speed,
|
||||
Number.isFinite(info.linkSpeed) ? 'linkSpeedMbs' : 'unavailable',
|
||||
Number.isFinite(info.linkSpeed) ? { linkSpeed: info.linkSpeed } : {});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
1
overrides/settings.gaiamobile.org/resources/support.json
Normal file
1
overrides/settings.gaiamobile.org/resources/support.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"onlinesupport":{"title":"Vulpes OS","href":"https://vulpes-os.org/"},"callsupport":[]}
|
||||
|
|
@ -0,0 +1,693 @@
|
|||
'use strict';
|
||||
/* exported DownloadHelper */
|
||||
/* global DownloadFormatter */
|
||||
/* global DownloadStore */
|
||||
/* global DownloadUI */
|
||||
/* global LazyLoader */
|
||||
/* global MimeMapper */
|
||||
/* global MozActivity */
|
||||
/* global SettingsListener */
|
||||
|
||||
/*
|
||||
* DownloadHelper.js: Perform some utility functions with DOMDownload
|
||||
* objects.
|
||||
*
|
||||
* - You have to include in you HTML:
|
||||
*
|
||||
* <!-- <script src="shared/js/mime_mapper.js"></script> -->
|
||||
* <!-- <script src="shared/js/download/download_store.js"></script> -->
|
||||
* <script defer src="shared/js/lazy_loader.js"></script>
|
||||
*
|
||||
* - How to use this component.
|
||||
*
|
||||
* For launching a download
|
||||
*
|
||||
* var req = DownloadHelper.open(download);
|
||||
*
|
||||
* req.onsuccess = function req_onsuccess() {
|
||||
* alert('The download was opened so we can remove the notification');
|
||||
* }
|
||||
*
|
||||
* req.onerror = function req_onerror() {
|
||||
* alert('Something wrong!: ' + req.error.message);
|
||||
* }
|
||||
*
|
||||
*/
|
||||
var DownloadHelper = (function() {
|
||||
|
||||
// Exception code constants
|
||||
var CODE = {
|
||||
FILE_NOT_FOUND: 'FILE_NOT_FOUND',
|
||||
DEVICE_STORAGE: 'DEVICE_STORAGE',
|
||||
MIME_TYPE_NOT_SUPPORTED: 'MIME_TYPE_NOT_SUPPORTED',
|
||||
INVALID_STATE: 'INVALID_STATE',
|
||||
NO_SDCARD: 'NO_SDCARD',
|
||||
NO_PROVIDER: 'NO_PROVIDER',
|
||||
UNMOUNTED_SDCARD: 'UNMOUNTED_SDCARD'
|
||||
};
|
||||
|
||||
/*
|
||||
* Request auxiliary object to support asynchronous calls
|
||||
*/
|
||||
var Request = function() {
|
||||
this.done = function(result) {
|
||||
if (typeof this.onsuccess === 'function') {
|
||||
this.result = result;
|
||||
window.setTimeout(function() {
|
||||
this.onsuccess({
|
||||
target: this
|
||||
});
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
|
||||
this.failed = function(error) {
|
||||
if (typeof this.onerror === 'function') {
|
||||
this.error = error;
|
||||
window.setTimeout(function() {
|
||||
this.onerror({
|
||||
target: this
|
||||
});
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Storage name by default
|
||||
var STORAGE_NAME_BY_DEFAULT = 'sdcard';
|
||||
|
||||
// Storage name settings key
|
||||
var STORAGE_NAME_KEY = 'device.storage.writable.name';
|
||||
|
||||
// Current storage name
|
||||
var storageName = STORAGE_NAME_BY_DEFAULT;
|
||||
|
||||
LazyLoader.load('shared/js/settings_listener.js', function settingsLoaded() {
|
||||
SettingsListener.observe(STORAGE_NAME_KEY, STORAGE_NAME_BY_DEFAULT,
|
||||
function setStorageName(evt) {
|
||||
var settingValue = evt.settingValue;
|
||||
if (settingValue) {
|
||||
storageName = settingValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
* Error auxiliary method
|
||||
*/
|
||||
function sendError(req, message, code) {
|
||||
req.failed({
|
||||
message: message,
|
||||
code: code
|
||||
});
|
||||
|
||||
console.error(message);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a blob from the sdcard
|
||||
*
|
||||
* @param{Object} The download object
|
||||
*
|
||||
*/
|
||||
function getBlob(download) {
|
||||
var req = new Request();
|
||||
if (download.vulpesDownload) {
|
||||
download.getBlob().then(function(blob) { req.done(blob); }, function(error) { req.failed(error); });
|
||||
return req;
|
||||
}
|
||||
var storage = getVolume(download.storageName);
|
||||
var storeAvailableReq = storage.available();
|
||||
|
||||
storeAvailableReq.onsuccess = function available_onsuccess(e) {
|
||||
var path = download.storagePath;
|
||||
switch (storeAvailableReq.result) {
|
||||
case 'unavailable':
|
||||
sendError(req, ' Could not open the file: ' + path + ' from ' +
|
||||
download.storageName, CODE.NO_SDCARD);
|
||||
break;
|
||||
|
||||
case 'shared':
|
||||
sendError(req, ' Could not open the file: ' + path + ' from ' +
|
||||
download.storageName, CODE.UNMOUNTED_SDCARD);
|
||||
break;
|
||||
|
||||
default:
|
||||
try {
|
||||
var storeGetReq = storage.get(path);
|
||||
|
||||
storeGetReq.onsuccess = function store_onsuccess() {
|
||||
req.done(storeGetReq.result);
|
||||
};
|
||||
|
||||
storeGetReq.onerror = function store_onerror() {
|
||||
sendError(req, storeGetReq.error.name +
|
||||
' Could not open the file: ' + path + ' from ' +
|
||||
download.storageName, CODE.FILE_NOT_FOUND);
|
||||
};
|
||||
} catch (ex) {
|
||||
sendError(req, 'Error getting the file ' + path + ' from ' +
|
||||
download.storageName, CODE.DEVICE_STORAGE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
storeAvailableReq.onerror = function available_onerror() {
|
||||
sendError(req, 'Error getting storage state ', CODE.DEVICE_STORAGE);
|
||||
};
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
* Base action that implements the commom logic for actions based on the
|
||||
* MozActivity API
|
||||
*
|
||||
* @param{Object} Configuration parameters
|
||||
*/
|
||||
var Action = function Action(params) {
|
||||
this.req = params.request;
|
||||
this.name = params.actionType.activityName;
|
||||
};
|
||||
|
||||
Action.prototype = {
|
||||
/*
|
||||
* It performs the action based on MozActivity
|
||||
*/
|
||||
run: function a_run() {
|
||||
var activity = new MozActivity({
|
||||
name: this.name,
|
||||
data: this.data
|
||||
});
|
||||
|
||||
activity.onsuccess = this._onsuccess.bind(this);
|
||||
activity.onerror = this._onerror.bind(this);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method implements the generic onsuccess callback
|
||||
*/
|
||||
_onsuccess: function a_onsuccess() {
|
||||
this.req.done();
|
||||
},
|
||||
|
||||
/*
|
||||
* This method implements the generic onerror callback
|
||||
*/
|
||||
_onerror: function a_onerror(evt) {
|
||||
this.req.failed({
|
||||
message: evt.target.error.name
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This action opens downloads extending the Action Object
|
||||
*
|
||||
* @param{Object} Configuration parameters
|
||||
*/
|
||||
var OpenAction = function OpenAction(params) {
|
||||
Action.call(this, params);
|
||||
|
||||
this.data = {
|
||||
url: params.download.path,
|
||||
filename: DownloadFormatter.getFileName(params.download),
|
||||
type: params.type,
|
||||
blob: params.blob
|
||||
};
|
||||
};
|
||||
|
||||
OpenAction.prototype = {
|
||||
__proto__: Action.prototype
|
||||
};
|
||||
|
||||
/*
|
||||
* This action shares downloads extending the Action Object
|
||||
*
|
||||
* @param{Object} Configuration parameters
|
||||
*/
|
||||
var ShareAction = function ShareAction(params) {
|
||||
Action.call(this, params);
|
||||
|
||||
this.data = {
|
||||
// 'share' activities do not work with specific mime types
|
||||
type: params.type.split('/')[0] + '/*',
|
||||
blobs: [params.blob],
|
||||
filenames: [DownloadFormatter.getFileName(params.download)]
|
||||
};
|
||||
};
|
||||
|
||||
ShareAction.prototype = {
|
||||
__proto__: Action.prototype,
|
||||
|
||||
/*
|
||||
* It overrides the generic onerror callback for another more suitable
|
||||
*/
|
||||
_onerror: function sa_onerror(evt) {
|
||||
if (evt.target.error.name !== 'NO_PROVIDER') {
|
||||
return;
|
||||
}
|
||||
|
||||
sendError(this.req, 'No provider to share file', CODE.NO_PROVIDER);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This action gets the info for a download
|
||||
*
|
||||
* @param{Object} Configuration parameters
|
||||
*/
|
||||
var InfoAction = function InfoAction(params) {
|
||||
Action.call(this, params);
|
||||
|
||||
this.data = {
|
||||
name: DownloadFormatter.getFileName(params.download),
|
||||
type: params.type,
|
||||
blob: params.blob,
|
||||
size: params.download.totalBytes,
|
||||
path: params.download.path
|
||||
};
|
||||
};
|
||||
|
||||
InfoAction.prototype = {
|
||||
__proto__: Action.prototype,
|
||||
|
||||
/*
|
||||
* It overrides the generic run method
|
||||
*/
|
||||
run: function ia_run() {
|
||||
this.req.done(this.data);
|
||||
}
|
||||
};
|
||||
|
||||
// This is a factory that deals with different <Action> objects
|
||||
var ActionsFactory = {
|
||||
TYPE: {
|
||||
OPEN: {
|
||||
activityName: 'open',
|
||||
actionClass: OpenAction
|
||||
},
|
||||
SHARE: {
|
||||
activityName: 'share',
|
||||
actionClass: ShareAction
|
||||
},
|
||||
INFO: {
|
||||
actionClass: InfoAction
|
||||
},
|
||||
WALLPAPER: {
|
||||
activityName: 'setwallpaper',
|
||||
actionClass: ShareAction
|
||||
},
|
||||
RINGTONE: {
|
||||
activityName: 'setringtone',
|
||||
actionClass: ShareAction
|
||||
}
|
||||
},
|
||||
|
||||
create: function af_create(params) {
|
||||
return new params.actionType.actionClass(params);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the mime type
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*
|
||||
* @returns(String) Mime type
|
||||
*/
|
||||
function getType(download) {
|
||||
var fileName = DownloadFormatter.getFileName(download);
|
||||
var type = MimeMapper.guessTypeFromFileProperties(fileName,
|
||||
download.contentType);
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method allows third-parties to open or share downloads
|
||||
*
|
||||
* @param{Object} Action types: <ActionsFactory.TYPE.OPEN> or
|
||||
* <ActionsFactory.TYPE.SHARE>
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
function runAction(actionType, download) {
|
||||
var req = new Request();
|
||||
|
||||
window.setTimeout(function launching() {
|
||||
var state = download.state;
|
||||
if (state === 'succeeded' || state === 'finalized') {
|
||||
LazyLoader.load(['shared/js/mime_mapper.js',
|
||||
'shared/js/download/download_formatter.js'],
|
||||
function loaded() {
|
||||
var type = getType(download);
|
||||
|
||||
//
|
||||
// The 'open' action will always launch an activity using the original
|
||||
// content type to allow for third party applications to handle
|
||||
// arbitrary types of content.
|
||||
//
|
||||
// The 'share' action on the other hand only works with known mime
|
||||
// types at this time.
|
||||
//
|
||||
|
||||
if (type.length === 0) {
|
||||
type = download.contentType;
|
||||
|
||||
if (actionType !== ActionsFactory.TYPE.OPEN &&
|
||||
actionType !== ActionsFactory.TYPE.INFO) {
|
||||
sendError(req, 'Mime type not supported: ' + type,
|
||||
CODE.MIME_TYPE_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var blobReq = getBlob(download);
|
||||
|
||||
blobReq.onsuccess = function() {
|
||||
ActionsFactory.create({
|
||||
actionType: actionType,
|
||||
download: download,
|
||||
type: type,
|
||||
blob: blobReq.result,
|
||||
request: req
|
||||
}).run();
|
||||
};
|
||||
|
||||
blobReq.onerror = function() {
|
||||
// Problem getting the blob from the sdcard
|
||||
req.failed(blobReq.error);
|
||||
};
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
sendError(req, 'Becareful, the download is not finished!',
|
||||
CODE.INVALID_STATE);
|
||||
}, 0);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method allows clients to remove a download, from the
|
||||
* list and the phone.
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
function remove(download) {
|
||||
var req = new Request();
|
||||
if (download.vulpesDownload) {
|
||||
navigator.mozDownloadManager.remove(download).then(function() { req.done(download); }, function(error) { req.failed(error); });
|
||||
return req;
|
||||
}
|
||||
var incompleteDownload = download.state !== 'succeeded';
|
||||
// If is not done, use download manager to remove it,
|
||||
// otherwise, deal with the datastore.
|
||||
setTimeout(function() {
|
||||
if (incompleteDownload) {
|
||||
if (!navigator.mozDownloadManager) {
|
||||
sendError(req, 'DownloadManager not present', CODE.INVALID_STATE);
|
||||
} else {
|
||||
// First we pause the download so that everyone knows it's being
|
||||
// stopped. The Downloads API itself won't stop the download first,
|
||||
// it will simply kill it.
|
||||
// XXXAus: Remove when we fix bug #1090551
|
||||
download.pause().then(
|
||||
function() {
|
||||
navigator.mozDownloadManager.remove(download).then(
|
||||
function success() {
|
||||
req.done(download);
|
||||
},
|
||||
function error() {
|
||||
sendError(req,
|
||||
'DownloadManager doesnt know about this download',
|
||||
CODE.INVALID_STATE);
|
||||
}
|
||||
);
|
||||
},
|
||||
function() {
|
||||
sendError(req,
|
||||
'Failed to pause download before removal',
|
||||
CODE.INVALID_STATE);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
req.done(download);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
return incompleteDownload ? req : doRemoveFromPhone(req, download);
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs the proper delete of the physical file, also
|
||||
* from the datastore if the download has finished.
|
||||
*
|
||||
* @param{Object} This is the Request object
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
function doRemoveFromPhone(deleteRequest, download) {
|
||||
var req = new Request();
|
||||
|
||||
deleteRequest.onsuccess = function() {
|
||||
var storage = getVolume(download.storageName);
|
||||
var storeAvailableReq = storage.available();
|
||||
|
||||
storeAvailableReq.onsuccess = function available_onsuccess(e) {
|
||||
var path = download.path;
|
||||
switch (storeAvailableReq.result) {
|
||||
case 'unavailable':
|
||||
sendError(req, ' Could not delete the file: ' + path + ' from ' +
|
||||
storageName, CODE.NO_SDCARD);
|
||||
break;
|
||||
|
||||
case 'shared':
|
||||
sendError(req, ' Could not delete the file: ' + path + ' from ' +
|
||||
storageName, CODE.UNMOUNTED_SDCARD);
|
||||
break;
|
||||
|
||||
default:
|
||||
var storeDeleteReq = storage.delete(download.storagePath);
|
||||
|
||||
storeDeleteReq.onsuccess = function store_onsuccess() {
|
||||
// Remove from the datastore if status is 'succeeded'
|
||||
// if we find any problem with the datastore, don't send
|
||||
// an error, since the physical remove already happened
|
||||
if (download.state === 'succeeded') {
|
||||
LazyLoader.load(['/shared/js/download/download_store.js'],
|
||||
function() {
|
||||
DownloadStore.remove(download);
|
||||
}
|
||||
);
|
||||
}
|
||||
req.done(storeDeleteReq.result);
|
||||
};
|
||||
|
||||
storeDeleteReq.onerror = function store_onerror() {
|
||||
sendError(req, storeDeleteReq.error.name +
|
||||
' Could not remove the file: ' + download.path + ' from ' +
|
||||
storageName, CODE.FILE_NOT_FOUND);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
storeAvailableReq.onerror = function available_onerror() {
|
||||
sendError(req, 'Error getting storage state ', CODE.DEVICE_STORAGE);
|
||||
};
|
||||
};
|
||||
|
||||
deleteRequest.onerror = function(error) {
|
||||
sendError(req, error.message, error.code);
|
||||
};
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
function handlerError(error, download, cb) {
|
||||
LazyLoader.load('shared/js/download/download_ui.js', (function loaded() {
|
||||
var req;
|
||||
var show = DownloadUI.show;
|
||||
|
||||
// Canceled activites are normal and shouldn't be interpreted as errors.
|
||||
// Unfortunately, this isn't reported in a standard way by our
|
||||
// applications (or third party apps for that matter). This is why we
|
||||
// have this lazy filter here that may need to be updated in the future
|
||||
// but hopefully will just get removed.
|
||||
if (error.message &&
|
||||
(error.message.endsWith('canceled') ||
|
||||
error.message.endsWith('cancelled'))) {
|
||||
// Since this isn't actually an error, we invoke the callback with null.
|
||||
cb && cb(null);
|
||||
// and return early.
|
||||
return;
|
||||
}
|
||||
|
||||
switch (error.code) {
|
||||
case CODE.NO_SDCARD:
|
||||
case CODE.UNMOUNTED_SDCARD:
|
||||
case CODE.FILE_NOT_FOUND:
|
||||
case CODE.NO_PROVIDER:
|
||||
req = show(DownloadUI.TYPE[error.code], download, true);
|
||||
req.onconfirm = cb;
|
||||
|
||||
break;
|
||||
|
||||
case CODE.MIME_TYPE_NOT_SUPPORTED:
|
||||
req = show(DownloadUI.TYPE.UNSUPPORTED_FILE_TYPE, download, true);
|
||||
req.onconfirm = function tuftOnConfirm() {
|
||||
showRemoveFileUI(download, cb);
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
req = show(DownloadUI.TYPE.FILE_OPEN_ERROR, download, true);
|
||||
req.onconfirm = function tfoeOnConfirm() {
|
||||
showRemoveFileUI(download, cb);
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
// We have to remove the notification if the user cancels
|
||||
req.oncancel = cb;
|
||||
}));
|
||||
}
|
||||
|
||||
function showRemoveFileUI(download, cb) {
|
||||
var req = DownloadUI.show(DownloadUI.TYPE.DELETE, download, true);
|
||||
|
||||
req.oncancel = cb;
|
||||
|
||||
req.onconfirm = function doRemove() {
|
||||
if (typeof cb === 'function') {
|
||||
cb(download);
|
||||
} else {
|
||||
remove(download);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getFreeSpace(cb) {
|
||||
var storage = navigator.getDeviceStorage(storageName);
|
||||
|
||||
if (!storage) {
|
||||
console.error('Cannot get free space size in sdcard');
|
||||
cb(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var req = storage.freeSpace();
|
||||
|
||||
req.onsuccess = function(e) {
|
||||
cb(e.target.result);
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
cb(null);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the storage for the download based on the volumen
|
||||
* it was saved (storageName)
|
||||
*/
|
||||
function getVolume(volumeName) {
|
||||
// Per API design, all media type return the same volumes.
|
||||
// So we use 'sdcard' here for no reason.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=856782#c10
|
||||
var volumes = navigator.getDeviceStorages('sdcard');
|
||||
if (!volumeName || volumeName === '') {
|
||||
return volumes[0];
|
||||
}
|
||||
for (var i = 0; i < volumes.length; ++i) {
|
||||
if (volumes[i].storageName === volumeName) {
|
||||
return volumes[i];
|
||||
}
|
||||
}
|
||||
return volumes[0];
|
||||
}
|
||||
|
||||
return {
|
||||
/*
|
||||
* This method allows clients to open a downlaod
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
open: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.OPEN, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method allows clients to share a downlaoded file
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
share: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.SHARE, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method allows clients to set as wallaper a downlaoded file
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
wallpaper: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.WALLPAPER, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method allows clients to set as ringtone a downlaoded file
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
ringtone: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.RINGTONE, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* This method returns information about a download
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
info: function(download) {
|
||||
return runAction(ActionsFactory.TYPE.INFO, download);
|
||||
},
|
||||
|
||||
/*
|
||||
* Given a download, remove it from the DownloadManager
|
||||
* list, and the file system.
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*/
|
||||
remove: remove,
|
||||
|
||||
/*
|
||||
* Returns exception code constants
|
||||
*/
|
||||
get CODE() {
|
||||
return CODE;
|
||||
},
|
||||
|
||||
/*
|
||||
* This method handles different errors when users attemp to open files
|
||||
*
|
||||
* @param{Object} Error object
|
||||
*
|
||||
* @param{Object} It represents a DOMDownload object
|
||||
*
|
||||
* @param{Function} This function is performed when the flow is finished
|
||||
*/
|
||||
handlerError: handlerError,
|
||||
|
||||
/*
|
||||
* Returns the free memory size in bytes
|
||||
*
|
||||
* @param{Function} This function is performed when the free memory size has
|
||||
* been calculated
|
||||
*/
|
||||
getFreeSpace: getFreeSpace
|
||||
};
|
||||
}());
|
||||
1177
overrides/video.gaiamobile.org/gaia_build_defer_index.js
Normal file
1177
overrides/video.gaiamobile.org/gaia_build_defer_index.js
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue