Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
|
|
@ -0,0 +1,82 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'lib/prepare-preview-blob',
|
||||
'debug',
|
||||
'views/confirm',
|
||||
'lib/bind-all'
|
||||
], function (require, exports, module) {
|
||||
var prepareBlob = require('lib/prepare-preview-blob');
|
||||
var debug = require('debug')('controller:confirm');
|
||||
var ConfirmView = require('views/confirm');
|
||||
var bindAll = require('lib/bind-all');
|
||||
module.exports = function (options) {
|
||||
return new ConfirmController(options);
|
||||
};
|
||||
module.exports.ConfirmController = ConfirmController;
|
||||
function ConfirmController(app) {
|
||||
this.app = app;
|
||||
this.settings = app.settings;
|
||||
this.activity = app.activity;
|
||||
this.camera = app.camera;
|
||||
this.container = app.el;
|
||||
this.ConfirmView = app.ConfirmView || ConfirmView;
|
||||
this.prepareBlob = app.prepareBlob || prepareBlob;
|
||||
bindAll(this);
|
||||
this.bindEvents();
|
||||
debug('initialized');
|
||||
}
|
||||
ConfirmController.prototype.renderView = function () {
|
||||
if (!this.activity.pick) {
|
||||
return;
|
||||
}
|
||||
if (!this.view) {
|
||||
this.view = new this.ConfirmView();
|
||||
this.view.maxPreviewSize = window.CONFIG_MAX_IMAGE_PIXEL_SIZE;
|
||||
this.view.render().appendTo(this.container);
|
||||
this.view.once('click:select', this.onSelectMedia);
|
||||
this.view.on('click:retake', this.onRetakeMedia);
|
||||
this.view.on('loadingvideo', this.app.firer('busy'));
|
||||
this.view.on('playingvideo', this.app.firer('ready'));
|
||||
}
|
||||
this.view.show();
|
||||
this.app.set('confirmViewVisible', true);
|
||||
};
|
||||
ConfirmController.prototype.bindEvents = function () {
|
||||
this.camera.on('newimage', this.renderView);
|
||||
this.camera.on('change:recording', this.onRecordingChange);
|
||||
this.app.on('newmedia', this.onNewMedia);
|
||||
};
|
||||
ConfirmController.prototype.onRecordingChange = function (recording) {
|
||||
if (!this.activity.pick) {
|
||||
return;
|
||||
}
|
||||
if (recording === 'starting') {
|
||||
this.recording = true;
|
||||
} else if (recording === 'error') {
|
||||
this.recording = false;
|
||||
} else if (recording === 'stopped' && this.recording) {
|
||||
this.renderView();
|
||||
}
|
||||
};
|
||||
ConfirmController.prototype.onNewMedia = function (newMedia) {
|
||||
if (!this.activity.pick) {
|
||||
return;
|
||||
}
|
||||
this.newMedia = newMedia;
|
||||
if (newMedia.isVideo) {
|
||||
this.view.showVideo(newMedia);
|
||||
} else {
|
||||
this.prepareBlob(this.newMedia.blob, this.view.showImage);
|
||||
}
|
||||
};
|
||||
ConfirmController.prototype.onSelectMedia = function () {
|
||||
this.app.emit('confirm:selected', this.newMedia);
|
||||
};
|
||||
ConfirmController.prototype.onRetakeMedia = function () {
|
||||
this.view.hide();
|
||||
this.view.clearMediaFrame();
|
||||
this.app.set('confirmViewVisible', false);
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'views/countdown',
|
||||
'lib/bind-all'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('controller:countdown');
|
||||
var CountdownView = require('views/countdown');
|
||||
var bindAll = require('lib/bind-all');
|
||||
module.exports = function (app) {
|
||||
return new CountdownController(app);
|
||||
};
|
||||
module.exports.CountdownController = CountdownController;
|
||||
function CountdownController(app) {
|
||||
bindAll(this);
|
||||
this.app = app;
|
||||
this.view = app.views.countdown || new CountdownView();
|
||||
this.view.appendTo(app.el);
|
||||
this.bindEvents();
|
||||
debug('initialized');
|
||||
}
|
||||
CountdownController.prototype.bindEvents = function () {
|
||||
this.app.on('countdown:started', this.start);
|
||||
this.app.on('countdown:tick', this.update);
|
||||
this.app.on('countdown:ended', this.clear);
|
||||
};
|
||||
CountdownController.prototype.start = function (seconds) {
|
||||
this.update(seconds);
|
||||
this.view.show();
|
||||
debug('started');
|
||||
};
|
||||
CountdownController.prototype.update = function (seconds) {
|
||||
if (seconds < 1) {
|
||||
seconds = '';
|
||||
}
|
||||
var immanent = seconds <= 3;
|
||||
this.view.set(seconds).setImmanent(immanent);
|
||||
if (immanent && seconds > 0) {
|
||||
this.app.emit('countdown:immanent');
|
||||
}
|
||||
};
|
||||
CountdownController.prototype.clear = function () {
|
||||
this.view.hide(this.view.reset);
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'views/indicators'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('controller:indicators');
|
||||
var IndicatorsView = require('views/indicators');
|
||||
module.exports = function (app) {
|
||||
return new IndicatorsController(app);
|
||||
};
|
||||
module.exports.IndicatorsController = IndicatorsController;
|
||||
function IndicatorsController(app) {
|
||||
this.app = app;
|
||||
this.settings = app.settings;
|
||||
this.configure = this.configure.bind(this);
|
||||
this.createView();
|
||||
this.configure();
|
||||
this.bindEvents();
|
||||
debug('initialized');
|
||||
}
|
||||
IndicatorsController.prototype.createView = function () {
|
||||
debug('create view');
|
||||
this.view = this.app.views.indicators || new IndicatorsView();
|
||||
this.view.appendTo(this.app.el);
|
||||
debug('view created');
|
||||
};
|
||||
IndicatorsController.prototype.bindEvents = function () {
|
||||
this.settings.countdown.on('change:selected', this.view.setter('countdown'));
|
||||
this.settings.mode.on('change:selected', this.view.setter('mode'));
|
||||
this.settings.hdr.on('change:selected', this.view.setter('hdr'));
|
||||
this.app.on('change:batteryStatus', this.view.setter('battery'));
|
||||
this.app.on('change:recording', this.view.setter('recording'));
|
||||
this.app.on('settings:configured', this.configure);
|
||||
this.app.on('settings:opened', this.view.setter('ariaHidden', true));
|
||||
this.app.on('settings:closed', this.view.setter('ariaHidden', false));
|
||||
this.app.on('previewgallery:opened', this.view.hide);
|
||||
this.app.on('previewgallery:closed', this.view.show);
|
||||
debug('events bound');
|
||||
};
|
||||
IndicatorsController.prototype.configure = function () {
|
||||
debug('configuring');
|
||||
this.view.set('hdr', this.settings.hdr.selected('key'));
|
||||
this.view.set('countdown', this.settings.countdown.selected('key'));
|
||||
this.view.set('battery', this.app.get('batteryStatus'));
|
||||
debug('configured');
|
||||
};
|
||||
});
|
||||
151
assets/webapps/camera.gaiamobile.org/js/controllers/media.js
Normal file
151
assets/webapps/camera.gaiamobile.org/js/controllers/media.js
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'lib/bind-all'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('controller:media');
|
||||
var bindAll = require('lib/bind-all');
|
||||
module.exports = function (app) {
|
||||
return new MediaController(app);
|
||||
};
|
||||
module.exports.MediaController = MediaController;
|
||||
function MediaController(app) {
|
||||
bindAll(this);
|
||||
this.app = app;
|
||||
this.require = app.require;
|
||||
this.settings = app.settings;
|
||||
this.bindEvents();
|
||||
this.configure();
|
||||
debug('initialized');
|
||||
}
|
||||
MediaController.prototype.bindEvents = function () {
|
||||
this.app.on('storage:itemdeleted', this.onItemDeleted);
|
||||
this.app.on('previewgallery:deletevideo', this.onItemDeleted);
|
||||
this.app.on('previewgallery:deletepicture', this.onItemDeleted);
|
||||
this.app.on('storage:changed', this.onStorageChanged);
|
||||
this.app.on('newmedia', this.onNewMedia);
|
||||
this.app.on('hidden', this.onHidden);
|
||||
this.app.on('lazyloaded', this.configured);
|
||||
var self = this;
|
||||
this.app.once('capture', function () {
|
||||
var lib = [
|
||||
'lib/create-thumbnail-image',
|
||||
'lib/prepare-preview-blob'
|
||||
];
|
||||
self.require(lib, function () {
|
||||
});
|
||||
});
|
||||
debug('events bound');
|
||||
};
|
||||
MediaController.prototype.configure = function () {
|
||||
this.items = [];
|
||||
this.currentItem = null;
|
||||
var dpr = window.devicePixelRatio;
|
||||
this.thumbnailSize = {
|
||||
width: this.settings.previewGallery.get('thumbnailWidth') * dpr,
|
||||
height: this.settings.previewGallery.get('thumbnailHeight') * dpr
|
||||
};
|
||||
this.configured();
|
||||
};
|
||||
MediaController.prototype.configured = function () {
|
||||
this.app.emit('media:configured', this.items);
|
||||
};
|
||||
MediaController.prototype.onNewMedia = function (item) {
|
||||
if (this.app.activity.pick) {
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
if (item.isVideo) {
|
||||
addNewMedia(item);
|
||||
} else {
|
||||
this.require(['lib/prepare-preview-blob'], function (preparePreview) {
|
||||
preparePreview(item.blob, function (metadata) {
|
||||
metadata.blob = item.blob;
|
||||
metadata.filepath = item.filepath;
|
||||
addNewMedia(metadata);
|
||||
});
|
||||
});
|
||||
}
|
||||
function addNewMedia(item) {
|
||||
self.items.unshift(item);
|
||||
self.updateThumbnail();
|
||||
}
|
||||
};
|
||||
MediaController.prototype.onStorageChanged = function (status) {
|
||||
if (status === 'unavailable') {
|
||||
this.configure();
|
||||
this.updateThumbnail();
|
||||
}
|
||||
};
|
||||
MediaController.prototype.onHidden = function () {
|
||||
if (this.app.inSecureMode) {
|
||||
this.configure();
|
||||
this.updateThumbnail();
|
||||
}
|
||||
};
|
||||
MediaController.prototype.updateThumbnail = function () {
|
||||
var media = this.items[0] || null;
|
||||
if (media === this.currentItem) {
|
||||
return;
|
||||
}
|
||||
if (media === null) {
|
||||
this.app.emit('newthumbnail', null);
|
||||
return;
|
||||
}
|
||||
var blob;
|
||||
var metadata = {
|
||||
rotation: media.rotation,
|
||||
mirrored: media.mirrored
|
||||
};
|
||||
if (media.isVideo) {
|
||||
blob = media.poster.blob;
|
||||
metadata.width = media.poster.width;
|
||||
metadata.height = media.poster.height;
|
||||
} else {
|
||||
if (media.preview) {
|
||||
var fullRatio = media.width / media.height;
|
||||
var previewRatio = media.preview.width / media.preview.height;
|
||||
if (Math.abs(fullRatio - previewRatio) < 0.01) {
|
||||
blob = media.blob.slice(media.preview.start, media.preview.end, 'image/jpeg');
|
||||
metadata.width = media.preview.width;
|
||||
metadata.height = media.preview.height;
|
||||
}
|
||||
}
|
||||
if (!blob) {
|
||||
blob = media.blob;
|
||||
metadata.width = media.width;
|
||||
metadata.height = media.height;
|
||||
}
|
||||
}
|
||||
var self = this;
|
||||
this.require(['lib/create-thumbnail-image'], function (createThumbnailImage) {
|
||||
createThumbnailImage(blob, metadata, self.thumbnailSize, thumb => {
|
||||
self.currentItem = media;
|
||||
self.app.emit('newthumbnail', thumb);
|
||||
});
|
||||
});
|
||||
};
|
||||
MediaController.prototype.onItemDeleted = function (data) {
|
||||
var deleteIdx = -1;
|
||||
var deletedFilepath = typeof data === 'object' ? data.path : data;
|
||||
for (var n = 0; n < this.items.length; n++) {
|
||||
if (this.items[n].filepath === deletedFilepath) {
|
||||
deleteIdx = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (n === this.items.length) {
|
||||
return;
|
||||
}
|
||||
var item = this.items[deleteIdx];
|
||||
if (item.resizing) {
|
||||
return;
|
||||
}
|
||||
this.items.splice(deleteIdx, 1);
|
||||
this.updateThumbnail();
|
||||
this.app.emit('media:deleted');
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'lib/string-utils',
|
||||
'lib/bind-all',
|
||||
'views/preview-gallery',
|
||||
'CustomDialog'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('controller:preview-gallery');
|
||||
var StringUtils = require('lib/string-utils');
|
||||
var bindAll = require('lib/bind-all');
|
||||
var PreviewGalleryView = require('views/preview-gallery');
|
||||
var CustomDialog = require('CustomDialog');
|
||||
module.exports = function (app) {
|
||||
return new PreviewGalleryController(app);
|
||||
};
|
||||
module.exports.PreviewGalleryController = PreviewGalleryController;
|
||||
function PreviewGalleryController(app) {
|
||||
bindAll(this);
|
||||
this.app = app;
|
||||
this.require = app.require;
|
||||
this.settings = app.settings;
|
||||
this.dialog = app.dialog || CustomDialog;
|
||||
this.resizeImageAndSave = app.resizeImageAndSave;
|
||||
this.currentItemIndex = 0;
|
||||
this.bindEvents();
|
||||
debug('initialized');
|
||||
}
|
||||
PreviewGalleryController.prototype.bindEvents = function () {
|
||||
this.app.on('media:configured', this.updatePreviewGallery);
|
||||
this.app.on('media:deleted', this.updatePreviewGallery);
|
||||
this.app.on('preview', this.openPreview);
|
||||
this.app.on('hidden', this.closePreview);
|
||||
debug('events bound');
|
||||
};
|
||||
PreviewGalleryController.prototype.resetPreviewGallery = function (items) {
|
||||
this.items = items;
|
||||
};
|
||||
PreviewGalleryController.prototype.openPreview = function () {
|
||||
if (this.app.activity.pick || this.view || this.app.hidden) {
|
||||
return;
|
||||
}
|
||||
var maxPreviewSize = window.CONFIG_MAX_IMAGE_PIXEL_SIZE;
|
||||
this.view = new PreviewGalleryView();
|
||||
this.view.maxPreviewSize = maxPreviewSize;
|
||||
this.view.render().appendTo(this.app.el);
|
||||
this.view.on('click:gallery', this.onGalleryButtonClick);
|
||||
this.view.on('click:share', this.shareCurrentItem);
|
||||
this.view.on('click:delete', this.deleteCurrentItem);
|
||||
this.view.on('click:back', this.closePreview);
|
||||
this.view.on('swipe', this.handleSwipe);
|
||||
this.view.on('click:options', this.onOptionsClick);
|
||||
this.view.on('loadingvideo', this.app.firer('busy'));
|
||||
this.view.on('playingvideo', this.app.firer('ready'));
|
||||
var secureMode = this.app.inSecureMode;
|
||||
this.view.set('secure-mode', secureMode);
|
||||
this.view.open();
|
||||
this.previewItem();
|
||||
this.app.emit('previewgallery:opened');
|
||||
};
|
||||
PreviewGalleryController.prototype.closePreview = function () {
|
||||
if (this.view) {
|
||||
this.currentItemIndex = 0;
|
||||
this.view.close();
|
||||
this.view.destroy();
|
||||
this.view = null;
|
||||
}
|
||||
this.app.emit('previewgallery:closed');
|
||||
};
|
||||
PreviewGalleryController.prototype.onGalleryButtonClick = function () {
|
||||
if (this.app.inSecureMode) {
|
||||
return;
|
||||
}
|
||||
var MozActivity = window.MozActivity;
|
||||
this.mozActivity = new MozActivity({
|
||||
name: 'browse',
|
||||
data: { type: 'photos' }
|
||||
});
|
||||
};
|
||||
PreviewGalleryController.prototype.onOptionsClick = function () {
|
||||
if (this.app.inSecureMode) {
|
||||
return;
|
||||
}
|
||||
this.view.showOptionsMenu();
|
||||
};
|
||||
PreviewGalleryController.prototype.shareCurrentItem = function () {
|
||||
var index = this.currentItemIndex;
|
||||
var item = this.items[index];
|
||||
if (this.app.inSecureMode || !item || item.resizing) {
|
||||
return;
|
||||
}
|
||||
var type = item.isVideo ? 'video/*' : 'image/*';
|
||||
var filename = StringUtils.lastPathComponent(item.filepath);
|
||||
var launchShareActivity = function (blob) {
|
||||
var activity = new window.MozActivity({
|
||||
name: 'share',
|
||||
data: {
|
||||
type: type,
|
||||
number: 1,
|
||||
blobs: [blob],
|
||||
filenames: [filename],
|
||||
filepaths: [item.filepath]
|
||||
}
|
||||
});
|
||||
activity.onerror = function (e) {
|
||||
console.warn('Share activity error:', activity.error.name);
|
||||
};
|
||||
};
|
||||
if (item.isVideo) {
|
||||
launchShareActivity(item.blob);
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
item.resizing = true;
|
||||
this.app.emit('busy', 'resizingImage');
|
||||
var maxSize = this.settings.activity.get('maxSharePixelSize');
|
||||
this.require(['lib/resize-image-and-save'], function (resize) {
|
||||
if (!self.resizeImageAndSave) {
|
||||
self.resizeImageAndSave = resize;
|
||||
}
|
||||
self.resizeImageAndSave({
|
||||
blob: item.blob,
|
||||
size: maxSize
|
||||
}, function (resizedBlob) {
|
||||
if (resizedBlob !== item.blob) {
|
||||
item.blob = resizedBlob;
|
||||
if (maxSize && maxSize.width && maxSize.height) {
|
||||
item.width = maxSize.width;
|
||||
item.height = maxSize.height;
|
||||
} else if (item.rotation === 90 || item.rotation === 270) {
|
||||
var tmp = item.width;
|
||||
item.width = item.height;
|
||||
item.height = tmp;
|
||||
}
|
||||
delete item.rotation;
|
||||
}
|
||||
delete item.resizing;
|
||||
self.app.emit('ready');
|
||||
launchShareActivity(resizedBlob);
|
||||
});
|
||||
});
|
||||
};
|
||||
PreviewGalleryController.prototype.deleteCurrentItem = function () {
|
||||
if (this.app.inSecureMode) {
|
||||
return;
|
||||
}
|
||||
var index = this.currentItemIndex;
|
||||
var item = this.items[index];
|
||||
var filepath = item.filepath;
|
||||
var dialog = this.dialog;
|
||||
var self = this;
|
||||
var msg;
|
||||
if (item.isVideo) {
|
||||
msg = 'delete-video?';
|
||||
} else {
|
||||
msg = 'delete-photo?';
|
||||
}
|
||||
dialog.show('', msg, {
|
||||
title: 'cancel',
|
||||
callback: closeDialog
|
||||
}, {
|
||||
title: 'delete',
|
||||
callback: deleteItem,
|
||||
recommend: false
|
||||
});
|
||||
function closeDialog() {
|
||||
dialog.hide();
|
||||
}
|
||||
function deleteItem() {
|
||||
dialog.hide();
|
||||
if (item.isVideo) {
|
||||
self.app.emit('previewgallery:deletevideo', filepath);
|
||||
} else {
|
||||
self.app.emit('previewgallery:deletepicture', filepath);
|
||||
}
|
||||
}
|
||||
};
|
||||
PreviewGalleryController.prototype.updatePreviewGallery = function (items) {
|
||||
if (items) {
|
||||
this.items = items;
|
||||
}
|
||||
if (this.items.length === 0) {
|
||||
this.closePreview();
|
||||
} else {
|
||||
if (this.currentItemIndex === this.items.length) {
|
||||
this.currentItemIndex = this.items.length - 1;
|
||||
}
|
||||
var isOpened = this.view ? true : false;
|
||||
if (isOpened) {
|
||||
this.previewItem();
|
||||
}
|
||||
}
|
||||
};
|
||||
PreviewGalleryController.prototype.handleSwipe = function (direction) {
|
||||
if (direction === 'left') {
|
||||
this.next();
|
||||
} else if (direction === 'right') {
|
||||
this.previous();
|
||||
}
|
||||
};
|
||||
PreviewGalleryController.prototype.next = function () {
|
||||
if (this.currentItemIndex < this.items.length - 1) {
|
||||
this.currentItemIndex += 1;
|
||||
this.previewItem();
|
||||
}
|
||||
};
|
||||
PreviewGalleryController.prototype.previous = function () {
|
||||
if (this.currentItemIndex > 0) {
|
||||
this.currentItemIndex -= 1;
|
||||
this.previewItem();
|
||||
}
|
||||
};
|
||||
PreviewGalleryController.prototype.previewItem = function () {
|
||||
var index = this.currentItemIndex;
|
||||
var item = this.items[index];
|
||||
this.view.updateCountText(index + 1, this.items.length);
|
||||
if (item.isVideo) {
|
||||
this.view.showVideo(item);
|
||||
} else {
|
||||
this.view.showImage(item);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'views/recording-timer'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('controller:recording-timer');
|
||||
var RecordingTimerView = require('views/recording-timer');
|
||||
module.exports = function (app) {
|
||||
return new RecordingTimerController(app);
|
||||
};
|
||||
module.exports.RecordingTimerController = RecordingTimerController;
|
||||
function RecordingTimerController(app) {
|
||||
this.onRecordingChange = this.onRecordingChange.bind(this);
|
||||
this.app = app;
|
||||
this.createView();
|
||||
this.bindEvents();
|
||||
debug('initialized');
|
||||
}
|
||||
RecordingTimerController.prototype.createView = function () {
|
||||
this.view = this.app.view || new RecordingTimerView();
|
||||
this.view.appendTo(this.app.el);
|
||||
};
|
||||
RecordingTimerController.prototype.bindEvents = function () {
|
||||
this.app.on('change:recording', this.onRecordingChange);
|
||||
this.app.on('camera:recorderTimeUpdate', this.view.value);
|
||||
debug('events bound');
|
||||
};
|
||||
RecordingTimerController.prototype.onRecordingChange = function (recording) {
|
||||
debug('recording: %s', recording);
|
||||
if (!recording) {
|
||||
this.view.hide();
|
||||
return;
|
||||
}
|
||||
this.view.value(0);
|
||||
this.view.show();
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'lib/sounds'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('controller:sounds');
|
||||
var Sounds = require('lib/sounds');
|
||||
module.exports = function (app) {
|
||||
return new SoundsController(app);
|
||||
};
|
||||
module.exports.SoundsController = SoundsController;
|
||||
function SoundsController(app) {
|
||||
var list = app.settings.sounds.get('list');
|
||||
this.sounds = new Sounds(list);
|
||||
this.app = app;
|
||||
this.bindEvents();
|
||||
debug('initialized');
|
||||
}
|
||||
SoundsController.prototype.bindEvents = function () {
|
||||
this.app.on('change:recording', this.onRecordingChange.bind(this));
|
||||
this.app.on('camera:willrecord', this.sounds.player('recordingStart'));
|
||||
this.app.on('camera:shutter', this.sounds.player('shutter'));
|
||||
this.app.on('countdown:immanent', this.sounds.player('countdown'));
|
||||
};
|
||||
SoundsController.prototype.onRecordingChange = function (recording) {
|
||||
if (!recording) {
|
||||
this.sounds.play('recordingEnd');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'views/zoom-bar',
|
||||
'lib/bind-all'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('controller:zoom-bar');
|
||||
var ZoomBar = require('views/zoom-bar');
|
||||
var bindAll = require('lib/bind-all');
|
||||
module.exports = function (app) {
|
||||
return new ZoomBarController(app);
|
||||
};
|
||||
module.exports.ZoomBarController = ZoomBarController;
|
||||
function ZoomBarController(app) {
|
||||
bindAll(this);
|
||||
this.app = app;
|
||||
this.camera = app.camera;
|
||||
this.createView();
|
||||
this.bindEvents();
|
||||
this.configureZoomBar();
|
||||
debug('initialized');
|
||||
}
|
||||
ZoomBarController.prototype.createView = function () {
|
||||
this.view = this.app.views.zoombar || new ZoomBar();
|
||||
this.view.hide();
|
||||
this.view.appendTo(this.app.el);
|
||||
};
|
||||
ZoomBarController.prototype.bindEvents = function () {
|
||||
this.view.on('change', this.onChange);
|
||||
this.camera.on('zoomconfigured', this.onZoomConfigured);
|
||||
this.camera.on('zoomchanged', this.setZoom);
|
||||
};
|
||||
ZoomBarController.prototype.configureZoomBar = function () {
|
||||
var zoomSupported = this.camera.isZoomSupported();
|
||||
var zoomEnabled = this.app.settings.zoom.enabled();
|
||||
this.enableZoom = zoomSupported && zoomEnabled;
|
||||
};
|
||||
ZoomBarController.prototype.onChange = function (value) {
|
||||
var minimumZoom = this.camera.getMinimumZoom();
|
||||
var maximumZoom = this.camera.getMaximumZoom();
|
||||
var range = maximumZoom - minimumZoom;
|
||||
var zoom = range * value / 100 + minimumZoom;
|
||||
this.camera.setZoom(zoom);
|
||||
};
|
||||
ZoomBarController.prototype.onZoomConfigured = function (zoom) {
|
||||
this.configureZoomBar();
|
||||
this.setZoom(zoom);
|
||||
this.view.hide();
|
||||
};
|
||||
ZoomBarController.prototype.setZoom = function (zoom) {
|
||||
if (!this.enableZoom) {
|
||||
return;
|
||||
}
|
||||
debug('set zoom');
|
||||
var minimumZoom = this.camera.getMinimumZoom();
|
||||
var maximumZoom = this.camera.getMaximumZoom();
|
||||
var range = maximumZoom - minimumZoom;
|
||||
var percent = (zoom - minimumZoom) / range * 100;
|
||||
this.view.setValue(percent);
|
||||
debug('zoom set');
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'cropResizeRotate'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var cropResizeRotate = require('cropResizeRotate');
|
||||
module.exports = function (imageBlob, metadata, thumbnailSize, done) {
|
||||
cropResizeRotate(imageBlob, null, thumbnailSize, null, metadata, function (error, resizedBlob) {
|
||||
if (error) {
|
||||
console.error('Error while resizing image: ' + error);
|
||||
done(imageBlob);
|
||||
return;
|
||||
}
|
||||
done(resizedBlob);
|
||||
});
|
||||
};
|
||||
});
|
||||
84
assets/webapps/camera.gaiamobile.org/js/lib/dcf.js
Normal file
84
assets/webapps/camera.gaiamobile.org/js/lib/dcf.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'asyncStorage',
|
||||
'debug',
|
||||
'format'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var asyncStorage = require('asyncStorage');
|
||||
var debug = require('debug')('dcf');
|
||||
var format = require('format');
|
||||
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: '3gp',
|
||||
image: 'jpg'
|
||||
}
|
||||
};
|
||||
exports.init = function () {
|
||||
debug('initializing');
|
||||
exports.checkFileCounter(function () {
|
||||
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) {
|
||||
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;
|
||||
var req = storage.get(filepath);
|
||||
req.onsuccess = function () {
|
||||
dcfConfig.seq.file = 1;
|
||||
dcfConfig.seq.dir += 1;
|
||||
asyncStorage.setItem(dcfConfig.key, dcfConfig.seq, function () {
|
||||
exports.createDCFFilename(storage, type, callback);
|
||||
});
|
||||
};
|
||||
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);
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
||||
26
assets/webapps/camera.gaiamobile.org/js/lib/format-timer.js
Normal file
26
assets/webapps/camera.gaiamobile.org/js/lib/format-timer.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'format'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var format = require('format');
|
||||
function digits(value) {
|
||||
return format.padLeft(value, 2, '0');
|
||||
}
|
||||
module.exports = function (ms) {
|
||||
var totalSeconds = ms / 1000;
|
||||
var seconds = Math.round(totalSeconds % 60);
|
||||
var minutes = Math.floor(totalSeconds / 60);
|
||||
var hours;
|
||||
if (minutes < 60) {
|
||||
return digits(minutes) + ':' + digits(seconds);
|
||||
} else {
|
||||
hours = Math.floor(minutes / 60);
|
||||
minutes = Math.round(minutes % 60);
|
||||
return hours + ':' + digits(minutes) + ':' + digits(seconds);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
});
|
||||
51
assets/webapps/camera.gaiamobile.org/js/lib/panzoom.js
Normal file
51
assets/webapps/camera.gaiamobile.org/js/lib/panzoom.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'GestureDetector'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var GestureDetector = require('GestureDetector');
|
||||
module.exports = addPanAndZoomHandlers;
|
||||
function addPanAndZoomHandlers(frame, swipeCallback) {
|
||||
var container = frame.container;
|
||||
var gestureDetector = new GestureDetector(container);
|
||||
gestureDetector.startDetecting();
|
||||
var swipeAmount = 0;
|
||||
container.addEventListener('dbltap', handleDoubleTap);
|
||||
container.addEventListener('transform', handleTransform);
|
||||
container.addEventListener('pan', handlePan);
|
||||
if (swipeCallback) {
|
||||
container.addEventListener('swipe', handleSwipe);
|
||||
}
|
||||
function handleDoubleTap(e) {
|
||||
var scale;
|
||||
if (frame.fit.scale > frame.fit.baseScale) {
|
||||
scale = frame.fit.baseScale / frame.fit.scale;
|
||||
} else {
|
||||
scale = 2;
|
||||
}
|
||||
frame.zoom(scale, e.detail.clientX, e.detail.clientY, 200);
|
||||
}
|
||||
function handleTransform(e) {
|
||||
frame.zoom(e.detail.relative.scale, e.detail.midpoint.clientX, e.detail.midpoint.clientY);
|
||||
}
|
||||
function handlePan(e) {
|
||||
var dx = e.detail.relative.dx;
|
||||
var dy = e.detail.relative.dy;
|
||||
if (swipeCallback) {
|
||||
dx += swipeAmount;
|
||||
swipeAmount = frame.pan(dx, dy);
|
||||
swipeCallback(swipeAmount);
|
||||
} else {
|
||||
frame.pan(dx, dy);
|
||||
}
|
||||
}
|
||||
function handleSwipe(e) {
|
||||
if (swipeAmount !== 0) {
|
||||
swipeCallback(swipeAmount, e.detail.vx);
|
||||
swipeAmount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'jpegMetaDataParser'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var parseJpegMetadata = require('jpegMetaDataParser');
|
||||
module.exports = function (blob, done) {
|
||||
parseJpegMetadata(blob, onJpegParsed);
|
||||
function onJpegParsed(metadata) {
|
||||
metadata.blob = blob;
|
||||
if (!metadata.preview) {
|
||||
done(metadata);
|
||||
return;
|
||||
}
|
||||
var start = metadata.preview.start;
|
||||
var end = metadata.preview.end;
|
||||
var previewBlob = blob.slice(start, end, 'image/jpeg');
|
||||
parseJpegMetadata(previewBlob, onSuccess, onError);
|
||||
function onSuccess(previewMetadata) {
|
||||
metadata.preview.width = previewMetadata.width;
|
||||
metadata.preview.height = previewMetadata.height;
|
||||
done(metadata);
|
||||
}
|
||||
function onError() {
|
||||
done(metadata);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'lib/storage',
|
||||
'cropResizeRotate'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var Storage = require('lib/storage');
|
||||
var cropResizeRotate = require('cropResizeRotate');
|
||||
module.exports = function (options, done) {
|
||||
var blob = options.blob;
|
||||
var outputSize = options.width && options.height ? {
|
||||
width: options.width,
|
||||
height: options.height
|
||||
} : options.size || null;
|
||||
cropResizeRotate(blob, null, outputSize, 'image/jpeg+exif', function (error, resizedBlob) {
|
||||
if (error) {
|
||||
console.error('Error while resizing image: ' + error);
|
||||
done(blob);
|
||||
return;
|
||||
}
|
||||
if (resizedBlob === blob) {
|
||||
done(blob);
|
||||
return;
|
||||
}
|
||||
var storage = new Storage();
|
||||
storage.deletePicture(blob.name, addPicture);
|
||||
function addPicture(error) {
|
||||
if (error) {
|
||||
done(blob);
|
||||
return;
|
||||
}
|
||||
storage.addPicture(resizedBlob, { filepath: blob.name }, onSavedPicture);
|
||||
}
|
||||
function onSavedPicture(error, filepath, absolutePath, fileBlob) {
|
||||
done(fileBlob);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
75
assets/webapps/camera.gaiamobile.org/js/lib/sounds.js
Normal file
75
assets/webapps/camera.gaiamobile.org/js/lib/sounds.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
module.exports = Sounds;
|
||||
function Sounds(list) {
|
||||
this.items = {};
|
||||
(list || []).forEach(this.add, this);
|
||||
}
|
||||
Sounds.prototype.add = function (data) {
|
||||
var self = this;
|
||||
var sound = {
|
||||
name: data.name,
|
||||
url: data.url,
|
||||
setting: data.setting,
|
||||
enabled: false
|
||||
};
|
||||
this.items[data.name] = sound;
|
||||
this.isEnabled(sound, function (value) {
|
||||
self.setEnabled(sound, value);
|
||||
self.observeSetting(sound);
|
||||
});
|
||||
};
|
||||
Sounds.prototype.isEnabled = function (sound, done) {
|
||||
setTimeout(function () {
|
||||
var mozSettings = navigator.mozSettings;
|
||||
var key = sound.setting;
|
||||
if (!mozSettings) {
|
||||
return;
|
||||
}
|
||||
mozSettings.createLock().get(key).onsuccess = onSuccess;
|
||||
function onSuccess(e) {
|
||||
var result = e.target.result[key];
|
||||
done(result);
|
||||
}
|
||||
});
|
||||
};
|
||||
Sounds.prototype.observeSetting = function (sound) {
|
||||
var mozSettings = navigator.mozSettings;
|
||||
var key = sound.setting;
|
||||
var self = this;
|
||||
if (mozSettings) {
|
||||
mozSettings.addObserver(key, function (e) {
|
||||
self.setEnabled(sound, e.settingValue);
|
||||
});
|
||||
}
|
||||
};
|
||||
Sounds.prototype.setEnabled = function (sound, value) {
|
||||
sound.enabled = value;
|
||||
};
|
||||
Sounds.prototype.play = function (name) {
|
||||
var sound = this.items[name];
|
||||
if (!sound.audio) {
|
||||
sound.audio = this.createAudio(sound.url);
|
||||
}
|
||||
this.playSound(sound);
|
||||
};
|
||||
Sounds.prototype.player = function (name) {
|
||||
return function () {
|
||||
this.play(name);
|
||||
}.bind(this);
|
||||
};
|
||||
Sounds.prototype.playSound = function (sound) {
|
||||
if (sound.enabled) {
|
||||
sound.audio.cloneNode(true).play();
|
||||
}
|
||||
};
|
||||
Sounds.prototype.createAudio = function (url) {
|
||||
var audio = new Audio(url);
|
||||
audio.mozAudioChannelType = 'notification';
|
||||
return audio;
|
||||
};
|
||||
});
|
||||
20
assets/webapps/camera.gaiamobile.org/js/lib/string-utils.js
Normal file
20
assets/webapps/camera.gaiamobile.org/js/lib/string-utils.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
define(['require'], function (require) {
|
||||
'use strict';
|
||||
var StringUtils = {};
|
||||
StringUtils.toCamelCase = function (str) {
|
||||
str = str.toLowerCase().replace(/_/g, '-');
|
||||
return str.replace(/-([a-z])/g, function (g) {
|
||||
return g[1].toUpperCase();
|
||||
});
|
||||
};
|
||||
StringUtils.toHyphenate = function (str) {
|
||||
return str.replace(/([a-z][A-Z])/g, function (g) {
|
||||
return g[0] + '-' + g[1].toLowerCase();
|
||||
});
|
||||
};
|
||||
StringUtils.lastPathComponent = function (path) {
|
||||
var pathComponents = path.split('/');
|
||||
return pathComponents[pathComponents.length - 1];
|
||||
};
|
||||
return StringUtils;
|
||||
});
|
||||
7291
assets/webapps/camera.gaiamobile.org/js/main.js
Normal file
7291
assets/webapps/camera.gaiamobile.org/js/main.js
Normal file
File diff suppressed because it is too large
Load diff
103
assets/webapps/camera.gaiamobile.org/js/views/confirm.js
Normal file
103
assets/webapps/camera.gaiamobile.org/js/views/confirm.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'lib/panzoom',
|
||||
'lib/orientation',
|
||||
'MediaFrame',
|
||||
'lib/bind',
|
||||
'view'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('view:confirm');
|
||||
var addPanAndZoomHandlers = require('lib/panzoom');
|
||||
var orientation = require('lib/orientation');
|
||||
var MediaFrame = require('MediaFrame');
|
||||
var bind = require('lib/bind');
|
||||
var View = require('view');
|
||||
module.exports = View.extend({
|
||||
name: 'confirm',
|
||||
initialize: function () {
|
||||
this.on('destroy', this.onDestroy);
|
||||
},
|
||||
render: function () {
|
||||
this.el.innerHTML = this.template();
|
||||
this.els.mediaFrame = this.find('.js-media-frame');
|
||||
this.els.retake = this.find('.js-retake');
|
||||
this.els.select = this.find('.js-select');
|
||||
this.disableButtons();
|
||||
this.setupMediaFrame();
|
||||
delete this.template;
|
||||
debug('rendered');
|
||||
return this.bindEvents();
|
||||
},
|
||||
bindEvents: function () {
|
||||
bind(this.els.retake, 'click', this.onButtonClick);
|
||||
bind(this.els.select, 'click', this.onButtonClick);
|
||||
return this;
|
||||
},
|
||||
setupMediaFrame: function () {
|
||||
this.mediaFrame = new MediaFrame(this.els.mediaFrame, true, this.maxPreviewSize);
|
||||
this.mediaFrame.video.onloading = this.onVideoLoading;
|
||||
this.mediaFrame.video.onplaying = this.onVideoPlaying;
|
||||
addPanAndZoomHandlers(this.mediaFrame);
|
||||
window.addEventListener('resize', this.onResize);
|
||||
return this;
|
||||
},
|
||||
onVideoLoading: function () {
|
||||
this.emit('loadingvideo', 'loadingVideo');
|
||||
},
|
||||
onVideoPlaying: function () {
|
||||
this.emit('playingvideo');
|
||||
},
|
||||
clearMediaFrame: function () {
|
||||
this.mediaFrame.clear();
|
||||
this.disableButtons();
|
||||
},
|
||||
hide: function () {
|
||||
this.el.classList.add('hidden');
|
||||
orientation.lock();
|
||||
},
|
||||
show: function () {
|
||||
this.el.classList.remove('hidden');
|
||||
orientation.unlock();
|
||||
},
|
||||
disableButtons: function () {
|
||||
this.els.retake.setAttribute('disabled', true);
|
||||
this.els.select.setAttribute('disabled', true);
|
||||
},
|
||||
enableButtons: function () {
|
||||
this.els.retake.removeAttribute('disabled');
|
||||
this.els.select.removeAttribute('disabled');
|
||||
},
|
||||
showImage: function (image) {
|
||||
this.enableButtons();
|
||||
this.mediaFrame.displayImage(image.blob, image.width, image.height, image.preview, image.rotation, image.mirrored);
|
||||
return this;
|
||||
},
|
||||
showVideo: function (video) {
|
||||
this.enableButtons();
|
||||
this.mediaFrame.displayVideo(video.blob, video.poster.blob, video.poster.width / video.poster.height, video.rotation);
|
||||
return this;
|
||||
},
|
||||
template: function () {
|
||||
return '<div class="confirm-media-frame js-media-frame"></div>' + '<footer id="confirm-controls" class="confirm-controls">' + '<button class="retake-button js-retake" data-l10n-id="retake-button" ' + 'name="retake"></button>' + '<button class="select-button test-confirm-select js-select" ' + 'data-l10n-id="select-button" name="select"></button></footer>';
|
||||
},
|
||||
onButtonClick: function (event) {
|
||||
var el = event.currentTarget;
|
||||
var name = el.getAttribute('name');
|
||||
this.emit('click:' + name);
|
||||
},
|
||||
onResize: function () {
|
||||
this.mediaFrame.resize();
|
||||
if (this.mediaFrame.displayingVideo) {
|
||||
this.mediaFrame.video.setPlayerSize();
|
||||
}
|
||||
},
|
||||
onDestroy: function () {
|
||||
window.removeEventListener('resize', this.onResize);
|
||||
this.mediaFrame.clear();
|
||||
}
|
||||
});
|
||||
});
|
||||
52
assets/webapps/camera.gaiamobile.org/js/views/countdown.js
Normal file
52
assets/webapps/camera.gaiamobile.org/js/views/countdown.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'view'
|
||||
], function (require, exports, module) {
|
||||
var debug = require('debug')('view:countdown');
|
||||
var View = require('view');
|
||||
module.exports = View.extend({
|
||||
name: 'countdown',
|
||||
initialize: function () {
|
||||
this.render();
|
||||
},
|
||||
render: function () {
|
||||
this.el.innerHTML = this.template();
|
||||
this.els.count = this.find('.js-count');
|
||||
delete this.template;
|
||||
debug('rendered');
|
||||
return this;
|
||||
},
|
||||
set: function (time) {
|
||||
this.els.count.textContent = time;
|
||||
this.el.classList.remove('shrink');
|
||||
this.reflow = this.el.offsetTop;
|
||||
this.el.classList.add('shrink');
|
||||
debug('set time: %s, near: %s', time);
|
||||
return this;
|
||||
},
|
||||
show: function () {
|
||||
this.el.classList.remove('hidden');
|
||||
this.el.classList.add('visible');
|
||||
},
|
||||
hide: function (callback) {
|
||||
this.el.classList.remove('visible');
|
||||
this.el.classList.add('hidden');
|
||||
setTimeout(callback, 200);
|
||||
return this;
|
||||
},
|
||||
setImmanent: function (value) {
|
||||
this.el.classList.toggle('immanent', value);
|
||||
},
|
||||
reset: function () {
|
||||
this.els.count.textContent = '';
|
||||
this.el.classList.remove('immanent', 'shrink');
|
||||
return this;
|
||||
},
|
||||
template: function () {
|
||||
return '<div class="countdown_circle-1"></div>' + '<div class="countdown_circle-2"></div>' + '<div class="countdown_count">' + '<div class="rotates js-count"></div>' + '</div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
27
assets/webapps/camera.gaiamobile.org/js/views/indicators.js
Normal file
27
assets/webapps/camera.gaiamobile.org/js/views/indicators.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'view'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('view:indicators');
|
||||
var View = require('view');
|
||||
module.exports = View.extend({
|
||||
name: 'indicators',
|
||||
tag: 'ul',
|
||||
initialize: function () {
|
||||
this.render();
|
||||
},
|
||||
render: function () {
|
||||
this.el.innerHTML = this.template();
|
||||
delete this.template;
|
||||
debug('rendered');
|
||||
return this;
|
||||
},
|
||||
template: function () {
|
||||
return '<li class="indicator_countdown rotates" data-icon="self-timer" ' + 'data-l10n-id="self-timer-indicator"></li>' + '<li class="indicator_hdr rotates" data-icon="hdr" ' + 'data-l10n-id="hdr-indicator"></li>' + '<li class="indicator_geolocation rotates" data-icon="location" ' + 'data-l10n-id="location-indicator"></li>' + '<li class="indicator_battery rotates" data-icon="battery-3" ' + 'data-l10n-id="battery-low-indicator"></li>';
|
||||
}
|
||||
});
|
||||
});
|
||||
84
assets/webapps/camera.gaiamobile.org/js/views/overlay.js
Normal file
84
assets/webapps/camera.gaiamobile.org/js/views/overlay.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'view',
|
||||
'lib/bind'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('view:overlay');
|
||||
var View = require('view');
|
||||
var bind = require('lib/bind');
|
||||
module.exports = View.extend({
|
||||
className: 'overlay',
|
||||
initialize: function (options) {
|
||||
var data = this.getData(options.type);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
this.data('type', options.type);
|
||||
this.data('closable', options.closable);
|
||||
this.render(data);
|
||||
},
|
||||
render: function (data) {
|
||||
this.el.innerHTML = this.template(data);
|
||||
this.els.buttons = { close: this.find('.js-close-btn') };
|
||||
delete this.template;
|
||||
debug('rendered');
|
||||
return this.bindEvents();
|
||||
},
|
||||
rendered: function () {
|
||||
return this.el.innerHTML.length > 0;
|
||||
},
|
||||
bindEvents: function () {
|
||||
bind(this.els.buttons.close, 'click', this.onButtonClick);
|
||||
return this;
|
||||
},
|
||||
template: function (data) {
|
||||
return '<form role="dialog" data-type="confirm">' + '<section>' + '<h1 class="overlay-title" data-l10n-id="' + data.title + '"></h1>' + '<p id="overlay-text" data-l10n-id="' + data.body + '"></p>' + '</section>' + '<menu class="overlay-menu-close">' + '<button class="full js-close-btn" type="button" ' + 'data-l10n-id="' + data.closeButtonText + '" name="close-btn">' + '</button>' + '</menu>' + '</form>';
|
||||
},
|
||||
getData: function (type) {
|
||||
var data = {};
|
||||
switch (type) {
|
||||
case 'unavailable':
|
||||
data.title = 'nocard2-title';
|
||||
data.body = 'nocard3-text';
|
||||
break;
|
||||
case 'nospace':
|
||||
data.title = 'nospace2-title';
|
||||
data.body = 'nospace2-text';
|
||||
break;
|
||||
case 'shared':
|
||||
data.title = 'pluggedin2-title';
|
||||
data.body = 'pluggedin2-text';
|
||||
break;
|
||||
case 'shutdown':
|
||||
data.title = 'battery-shutdown-title';
|
||||
data.body = 'battery-shutdown-text';
|
||||
break;
|
||||
case 'request-fail':
|
||||
data.title = 'camera-unavailable-title';
|
||||
data.body = 'camera-unavailable-text';
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
data.closeButtonText = 'close-button';
|
||||
return data;
|
||||
},
|
||||
data: function (key, value) {
|
||||
switch (arguments.length) {
|
||||
case 1:
|
||||
return this.el.getAttribute('data-' + key);
|
||||
case 2:
|
||||
this.el.setAttribute('data-' + key, value);
|
||||
}
|
||||
},
|
||||
onButtonClick: function (event) {
|
||||
var el = event.currentTarget;
|
||||
var name = el.getAttribute('name');
|
||||
this.emit('click:' + name);
|
||||
}
|
||||
});
|
||||
});
|
||||
230
assets/webapps/camera.gaiamobile.org/js/views/preview-gallery.js
Normal file
230
assets/webapps/camera.gaiamobile.org/js/views/preview-gallery.js
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
define([
|
||||
'require',
|
||||
'debug',
|
||||
'lib/panzoom',
|
||||
'lib/orientation',
|
||||
'MediaFrame',
|
||||
'lib/bind',
|
||||
'attach',
|
||||
'view',
|
||||
'gaia-header'
|
||||
], function (require) {
|
||||
'use strict';
|
||||
var debug = require('debug')('view:preview-gallery');
|
||||
var addPanAndZoomHandlers = require('lib/panzoom');
|
||||
var orientation = require('lib/orientation');
|
||||
var MediaFrame = require('MediaFrame');
|
||||
var bind = require('lib/bind');
|
||||
var attach = require('attach');
|
||||
var View = require('view');
|
||||
require('gaia-header');
|
||||
var SWIPE_DISTANCE_THRESHOLD = window.innerWidth / 3;
|
||||
var SWIPE_VELOCITY_THRESHOLD = 1;
|
||||
var SWIPE_DURATION = 150;
|
||||
var FADE_IN_DURATION = 450;
|
||||
return View.extend({
|
||||
name: 'preview-gallery',
|
||||
className: 'offscreen',
|
||||
initialize: function () {
|
||||
debug('initialized');
|
||||
},
|
||||
render: function () {
|
||||
this.el.innerHTML = this.template();
|
||||
this.els.frameContainer = this.find('.js-frame-container');
|
||||
this.els.previewMenu = this.find('.js-preview-menu');
|
||||
this.els.mediaFrame = this.find('.js-media-frame');
|
||||
this.els.countText = this.find('.js-count-text');
|
||||
this.els.options = this.find('.js-options');
|
||||
this.els.header = this.find('.js-header');
|
||||
this.els.share = this.find('.js-share');
|
||||
window.dispatchEvent(new CustomEvent('lazyload', { detail: this.el }));
|
||||
this.configure();
|
||||
delete this.template;
|
||||
debug('rendered');
|
||||
return this.bindEvents();
|
||||
},
|
||||
bindEvents: function () {
|
||||
bind(this.el, 'tap', this.onTap);
|
||||
bind(this.els.header, 'action', this.firer('click:back'));
|
||||
bind(this.els.options, 'click', this.onButtonClick);
|
||||
bind(this.els.share, 'click', this.onButtonClick);
|
||||
bind(this.els.mediaFrame, 'wheel', this.onFrameWheel);
|
||||
return this;
|
||||
},
|
||||
configure: function () {
|
||||
this.currentIndex = this.lastIndex = 0;
|
||||
this.frame = new MediaFrame(this.els.mediaFrame, true, this.maxPreviewSize);
|
||||
this.frame.video.onloading = this.onVideoLoading;
|
||||
this.frame.video.onplaying = this.onVideoPlaying;
|
||||
this.frame.video.onpaused = this.onVideoPaused;
|
||||
addPanAndZoomHandlers(this.frame, this.swipeCallback);
|
||||
},
|
||||
template: function () {
|
||||
return '<div class="preview-menu js-preview-menu">' + '<gaia-header class="js-header" action="back" ignore-dir>' + '<h1 data-l10n-id="preview">Preview</h1>' + '<button class="preview-share-icon js-share"' + 'name="share" data-icon="share" ' + 'data-l10n-id="share-button"></button>' + '<button class="preview-option-icon ' + 'js-options" name="options" data-icon="more" ' + 'data-l10n-id="more-button"></button>' + '</gaia-header>' + '</div>' + '<div class="frame-container js-frame-container">' + '<div class="media-frame js-media-frame"></div>' + '</div>' + '<div class="count-text js-count-text"></div>';
|
||||
},
|
||||
onTap: function () {
|
||||
if (this.videoPlaying) {
|
||||
return;
|
||||
}
|
||||
var isShown = this.els.previewMenu.classList.contains('visible');
|
||||
if (isShown) {
|
||||
this.previewMenuFadeOut();
|
||||
} else {
|
||||
this.previewMenuFadeIn();
|
||||
}
|
||||
},
|
||||
previewMenuFadeIn: function () {
|
||||
this.els.previewMenu.classList.add('visible');
|
||||
},
|
||||
previewMenuFadeOut: function () {
|
||||
this.els.previewMenu.classList.remove('visible');
|
||||
},
|
||||
onFrameWheel: function (event) {
|
||||
if (event.deltaMode !== event.DOM_DELTA_PAGE || event.deltaY) {
|
||||
return;
|
||||
}
|
||||
if (this.videoPlaying) {
|
||||
this.onVideoPaused();
|
||||
}
|
||||
if (event.deltaX > 0) {
|
||||
this.emit('swipe', 'left');
|
||||
} else if (event.deltaX < 0) {
|
||||
this.emit('swipe', 'right');
|
||||
}
|
||||
},
|
||||
swipeCallback: function (swipeAmount, swipeVelocity) {
|
||||
var self = this;
|
||||
if (swipeVelocity === undefined) {
|
||||
this.els.frameContainer.style.transform = 'translate(' + swipeAmount + 'px, 0)';
|
||||
return;
|
||||
}
|
||||
var direction, translation;
|
||||
if (swipeAmount > 0 && swipeVelocity > 0 && this.currentIndex > 1 && (swipeAmount > SWIPE_DISTANCE_THRESHOLD || swipeVelocity > SWIPE_VELOCITY_THRESHOLD)) {
|
||||
direction = 'right';
|
||||
translation = '100%';
|
||||
} else if (swipeAmount <= 0 && swipeVelocity <= 0 && this.currentIndex < this.lastIndex && (swipeAmount < -SWIPE_DISTANCE_THRESHOLD || swipeVelocity < -SWIPE_VELOCITY_THRESHOLD)) {
|
||||
direction = 'left';
|
||||
translation = '-100%';
|
||||
}
|
||||
if (!direction) {
|
||||
animate('transform', 'translate(0,0)', SWIPE_DURATION);
|
||||
return;
|
||||
}
|
||||
if (this.videoPlaying) {
|
||||
this.onVideoPaused();
|
||||
}
|
||||
animate('transform', 'translate(' + translation + ', 0)', SWIPE_DURATION, function () {
|
||||
self.emit('swipe', direction);
|
||||
window.requestAnimationFrame(function () {
|
||||
self.els.frameContainer.style.opacity = 0;
|
||||
self.els.frameContainer.style.transform = 'translate(0,0)';
|
||||
window.requestAnimationFrame(function () {
|
||||
animate('opacity', 1, FADE_IN_DURATION);
|
||||
});
|
||||
});
|
||||
});
|
||||
function animate(property, value, duration, done) {
|
||||
var e = self.els.frameContainer;
|
||||
e.addEventListener('transitionend', onTransitionEnd);
|
||||
e.style.transitionProperty = property;
|
||||
e.style.transitionDuration = duration + 'ms';
|
||||
e.style[property] = value;
|
||||
function onTransitionEnd() {
|
||||
e.removeEventListener('transitionend', onTransitionEnd);
|
||||
delete e.style.transitionProperty;
|
||||
delete e.style.transitionDuration;
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onButtonClick: function (e, el) {
|
||||
if (this.videoPlaying) {
|
||||
return;
|
||||
}
|
||||
el = el || e.currentTarget;
|
||||
var name = el.getAttribute('name');
|
||||
if (this.optionsMenuContainer) {
|
||||
this.hideOptionsMenu();
|
||||
}
|
||||
this.emit('click:' + name, e);
|
||||
e.stopPropagation();
|
||||
},
|
||||
open: function () {
|
||||
window.addEventListener('resize', this.onResize);
|
||||
orientation.unlock();
|
||||
this.previewMenuFadeIn();
|
||||
this.el.classList.remove('offscreen');
|
||||
},
|
||||
close: function () {
|
||||
window.removeEventListener('resize', this.onResize);
|
||||
orientation.lock();
|
||||
this.previewMenuFadeOut();
|
||||
this.el.classList.add('offscreen');
|
||||
this.frame.clear();
|
||||
},
|
||||
updateCountText: function (current, total) {
|
||||
this.currentIndex = current;
|
||||
this.lastIndex = total;
|
||||
this.els.countText.textContent = current + '/' + total;
|
||||
this.els.mediaFrame.setAttribute('data-l10n-id', 'media-frame');
|
||||
this.els.mediaFrame.setAttribute('data-l10n-args', JSON.stringify({
|
||||
total: total,
|
||||
current: current
|
||||
}));
|
||||
},
|
||||
onResize: function () {
|
||||
this.frame.resize();
|
||||
if (this.frame.displayingVideo) {
|
||||
this.frame.video.setPlayerSize();
|
||||
}
|
||||
},
|
||||
showImage: function (image) {
|
||||
this.frame.displayImage(image.blob, image.width, image.height, image.preview, image.rotation, image.mirrored);
|
||||
},
|
||||
showVideo: function (video) {
|
||||
this.frame.displayVideo(video.blob, video.poster.blob, video.poster.width / video.poster.height, video.rotation);
|
||||
},
|
||||
onVideoLoading: function () {
|
||||
this.emit('loadingvideo', 'loadingVideo');
|
||||
},
|
||||
onVideoPlaying: function () {
|
||||
if (this.videoPlaying) {
|
||||
return;
|
||||
}
|
||||
this.videoPlaying = true;
|
||||
this.previewMenuFadeOut();
|
||||
this.emit('playingvideo');
|
||||
},
|
||||
onVideoPaused: function () {
|
||||
if (!this.videoPlaying) {
|
||||
return;
|
||||
}
|
||||
this.videoPlaying = false;
|
||||
this.previewMenuFadeIn();
|
||||
},
|
||||
showOptionsMenu: function () {
|
||||
this.optionsMenuContainer = document.createElement('div');
|
||||
this.optionsMenuContainer.innerHTML = this.optionTemplate();
|
||||
this.el.appendChild(this.optionsMenuContainer);
|
||||
this.el.classList.add('action-menu');
|
||||
this.menu = this.find('.js-menu');
|
||||
var cancelButton = this.find('.js-cancel');
|
||||
bind(cancelButton, 'click', this.hideOptionsMenu);
|
||||
if (this.menu) {
|
||||
attach.on(this.menu, 'click', '.js-btn', this.onButtonClick);
|
||||
}
|
||||
},
|
||||
hideOptionsMenu: function () {
|
||||
if (this.optionsMenuContainer) {
|
||||
this.el.classList.remove('action-menu');
|
||||
this.optionsMenuContainer.parentElement.removeChild(this.optionsMenuContainer);
|
||||
this.optionsMenuContainer = null;
|
||||
}
|
||||
},
|
||||
optionTemplate: function () {
|
||||
return '<form class="visible" data-type="action"' + 'role="dialog" data-z-index-level="action-menu">' + '<header data-l10n-id="options">Options</header>' + '<menu class="js-menu">' + '<button class="js-btn" name="gallery" data-l10n-id="open-gallery">' + 'Open Gallery' + '</button>' + '<button class="js-btn" name="delete" data-l10n-id="delete">' + 'Delete' + '</button>' + '<button class="js-cancel" data-action="cancel" data-l10n-id="cancel">' + 'Cancel' + '</button>' + '</menu>' + '</form>';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'lib/format-timer',
|
||||
'view'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('view:recording-timer');
|
||||
var formatTimer = require('lib/format-timer');
|
||||
var View = require('view');
|
||||
module.exports = View.extend({
|
||||
name: 'recording-timer',
|
||||
initialize: function () {
|
||||
this.value(0);
|
||||
this.el.setAttribute('role', 'timer');
|
||||
},
|
||||
value: function (value) {
|
||||
this.el.textContent = formatTimer(value);
|
||||
debug('set value: %s', value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'attach',
|
||||
'view'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('view:setting-options');
|
||||
var attach = require('attach');
|
||||
var View = require('view');
|
||||
module.exports = View.extend({
|
||||
name: 'setting-options',
|
||||
initialize: function (options) {
|
||||
this.model = options.model;
|
||||
this.l10n = options.l10n || document.l10n;
|
||||
this.on('destroy', this.onDestroy);
|
||||
attach(this.el, 'click', 'li', this.onOptionClick);
|
||||
attach(this.el, 'click', '.js-back', this.firer('click:back'));
|
||||
this.model.on('change:selected', this.onSelectedChange);
|
||||
debug('initialized');
|
||||
},
|
||||
onDestroy: function () {
|
||||
this.model.off('change:selected', this.onSelectedChange);
|
||||
},
|
||||
onOptionClick: function (event, el) {
|
||||
var key = el.getAttribute('data-key');
|
||||
this.emit('click:option', key, this.model);
|
||||
},
|
||||
onSelectedChange: function (key) {
|
||||
var next = this.els[key];
|
||||
this.els.selected.classList.remove('selected');
|
||||
next.classList.add('selected');
|
||||
this.els.selected = next;
|
||||
},
|
||||
render: function () {
|
||||
var data = this.model.get();
|
||||
this.selectedKey = data.selected;
|
||||
this.el.innerHTML = this.template(data);
|
||||
this.els.ul = this.find('.js-list');
|
||||
var localizable = data.optionsLocalizable === false ? false : true;
|
||||
data.options.forEach(this.renderOption.bind(this, localizable));
|
||||
delete this.template;
|
||||
debug('rendered');
|
||||
return this;
|
||||
},
|
||||
renderOption: function (localizable, option) {
|
||||
var li = document.createElement('li');
|
||||
var isSelected = option.key === this.selectedKey;
|
||||
if (localizable) {
|
||||
li.setAttribute('data-l10n-id', option.title);
|
||||
this.l10n.formatValue(option.title).then(title => {
|
||||
li.setAttribute('aria-label', title);
|
||||
});
|
||||
} else {
|
||||
li.textContent = option.title;
|
||||
li.setAttribute('aria-label', option.title);
|
||||
}
|
||||
li.setAttribute('data-key', option.key);
|
||||
li.setAttribute('role', 'option');
|
||||
li.className = 'setting-option';
|
||||
li.dataset.icon = 'tick';
|
||||
this.els.ul.appendChild(li);
|
||||
this.els[option.key] = li;
|
||||
if (isSelected) {
|
||||
li.classList.add('selected');
|
||||
li.setAttribute('aria-selected', true);
|
||||
this.els.selected = li;
|
||||
}
|
||||
},
|
||||
template: function (data) {
|
||||
return '<div class="inner">' + '<div class="settings_header">' + '<div class="settings-back-btn js-back" ' + 'data-icon="left" role="button" data-l10n-id="back-button"></div>' + '<h2 aria-level="1" class="settings_title" data-l10n-id="' + data.header + '"></h2>' + '</div>' + '<div class="settings_items">' + '<ul role="listbox" class="inner js-list"></ul>' + '</div>' + '</div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
58
assets/webapps/camera.gaiamobile.org/js/views/setting.js
Normal file
58
assets/webapps/camera.gaiamobile.org/js/views/setting.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'lib/bind',
|
||||
'view'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('view:setting');
|
||||
var bind = require('lib/bind');
|
||||
var View = require('view');
|
||||
module.exports = View.extend({
|
||||
tag: 'li',
|
||||
name: 'setting',
|
||||
initialize: function (options) {
|
||||
this.l10n = options.l10n || document.l10n;
|
||||
this.model = options.model;
|
||||
this.model.on('change', this.render);
|
||||
this.on('destroy', this.onDestroy);
|
||||
this.el.dataset.icon = this.model.get('icon');
|
||||
this.el.classList.add('test-' + this.model.get('title') + '-setting');
|
||||
bind(this.el, 'click', this.onClick);
|
||||
},
|
||||
onClick: function () {
|
||||
this.emit('click', this);
|
||||
},
|
||||
onDestroy: function () {
|
||||
this.model.off('change', this.render);
|
||||
},
|
||||
render: function () {
|
||||
var data = this.model.get();
|
||||
data.selected = this.model.selected();
|
||||
data.value = data.selected && data.selected.title;
|
||||
this.el.setAttribute('role', 'option');
|
||||
var localizedValue;
|
||||
if (data.optionsLocalizable === false) {
|
||||
localizedValue = Promise.resolve(data.value);
|
||||
} else {
|
||||
localizedValue = this.l10n.formatValue(data.value);
|
||||
}
|
||||
localizedValue.then(valueString => {
|
||||
this.l10n.setAttributes(this.el, 'setting-option-' + data.title, { value: valueString });
|
||||
this.el.innerHTML = this.template({
|
||||
titleL10n: 'data-l10n-id="' + data.title + '"',
|
||||
valueL10n: data.optionsLocalizable !== false ? 'data-l10n-id="' + data.value + '"' : '',
|
||||
valueText: data.optionsLocalizable === false ? data.value : ''
|
||||
});
|
||||
delete this.template;
|
||||
debug('rendered (item %s)', data.key);
|
||||
});
|
||||
return this;
|
||||
},
|
||||
template: function (data) {
|
||||
return '<div class="setting_text">' + '<h4 class="setting_title" ' + data.titleL10n + '></h4>' + '<h5 class="setting_value" ' + data.valueL10n + '>' + data.valueText + '</h5>' + '</div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
97
assets/webapps/camera.gaiamobile.org/js/views/settings.js
Normal file
97
assets/webapps/camera.gaiamobile.org/js/views/settings.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'views/setting-options',
|
||||
'views/setting',
|
||||
'view',
|
||||
'lib/bind'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('view:settings');
|
||||
var OptionsView = require('views/setting-options');
|
||||
var SettingView = require('views/setting');
|
||||
var View = require('view');
|
||||
var bind = require('lib/bind');
|
||||
module.exports = View.extend({
|
||||
name: 'settings',
|
||||
fadeTime: 150,
|
||||
initialize: function (options) {
|
||||
this.OptionsView = options.OptionsView || OptionsView;
|
||||
this.items = options.items;
|
||||
this.children = [];
|
||||
this.on('destroy', this.onDestroy);
|
||||
bind(this.el, 'click', this.onClick);
|
||||
},
|
||||
onClick: function (e) {
|
||||
e.stopPropagation();
|
||||
},
|
||||
onItemClick: function (view) {
|
||||
this.showSetting(view.model);
|
||||
},
|
||||
showSetting: function (model) {
|
||||
this.optionsView = new this.OptionsView({ model: model }).render().appendTo(this.els.pane2).on('click:option', this.firer('click:option')).on('click:back', this.goBack);
|
||||
this.showPane(2);
|
||||
},
|
||||
onDestroy: function () {
|
||||
this.children.forEach(this.destroyChild);
|
||||
this.destroyOptionsView();
|
||||
debug('destroyed');
|
||||
},
|
||||
render: function () {
|
||||
this.el.innerHTML = this.template();
|
||||
this.els.items = this.find('.js-items');
|
||||
this.els.pane2 = this.find('.js-pane-2');
|
||||
this.els.close = this.find('.js-close');
|
||||
this.items.forEach(this.addItem);
|
||||
delete this.template;
|
||||
debug('rendered');
|
||||
return this.bindEvents();
|
||||
},
|
||||
bindEvents: function () {
|
||||
bind(this.els.close, 'click', this.firer('click:close'));
|
||||
return this;
|
||||
},
|
||||
goBack: function () {
|
||||
this.showPane(1);
|
||||
this.destroyOptionsView();
|
||||
},
|
||||
destroyChild: function (view) {
|
||||
view.destroy();
|
||||
debug('destroyed child');
|
||||
},
|
||||
destroyOptionsView: function () {
|
||||
if (this.optionsView) {
|
||||
this.optionsView.destroy();
|
||||
this.optionsView = null;
|
||||
debug('options view destroyed');
|
||||
}
|
||||
},
|
||||
addItem: function (model) {
|
||||
var setting = new SettingView({ model: model }).render().appendTo(this.els.items).on('click', this.onItemClick);
|
||||
this.children.push(setting);
|
||||
debug('add item key: %s', model.key);
|
||||
},
|
||||
showPane: function (name) {
|
||||
this.el.setAttribute('show-pane', 'pane-' + name);
|
||||
},
|
||||
fadeIn: function (done) {
|
||||
setTimeout(this.show);
|
||||
if (!done) {
|
||||
return;
|
||||
}
|
||||
setTimeout(done, this.fadeTime);
|
||||
},
|
||||
fadeOut: function (done) {
|
||||
this.hide();
|
||||
if (!done) {
|
||||
return;
|
||||
}
|
||||
setTimeout(done, this.fadeTime);
|
||||
},
|
||||
template: function () {
|
||||
return '<div class="pane pane-1">' + '<div class="inner">' + '<div class="settings_inner">' + '<div class="settings_header">' + '<h2 class="settings_title" data-l10n-id="options" ' + 'aria-level="1"></h2>' + '</div>' + '<div class="settings_items">' + '<ul class="inner js-items" role="listbox"></ul>' + '</div>' + '</div>' + '</div>' + '</div>' + '<div class="pane pane-2">' + '<div class="inner js-pane-2"></div>' + '</div>' + '<div role="button" class="settings_close js-close" data-icon="menu" ' + 'data-l10n-id="close-menu-button"></div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
170
assets/webapps/camera.gaiamobile.org/js/views/zoom-bar.js
Normal file
170
assets/webapps/camera.gaiamobile.org/js/views/zoom-bar.js
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'debug',
|
||||
'view',
|
||||
'lib/bind',
|
||||
'lib/orientation'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = require('debug')('view:zoom-bar');
|
||||
var View = require('view');
|
||||
var bind = require('lib/bind');
|
||||
var orientation = require('lib/orientation');
|
||||
var lastTouch = null;
|
||||
var clamp = function (value, minimum, maximum) {
|
||||
return Math.min(Math.max(value, minimum), maximum);
|
||||
};
|
||||
module.exports = View.extend({
|
||||
name: 'zoom-bar',
|
||||
initialize: function () {
|
||||
this.render();
|
||||
this._orientation = orientation.get();
|
||||
this.zoomBarIndicatorInterval = 10;
|
||||
this.zoomBarInactivityTimeout = 3000;
|
||||
},
|
||||
render: function () {
|
||||
this.el.innerHTML = this.template();
|
||||
this.els.maxIndicator = this.find('.zoom-bar-max-indicator');
|
||||
this.els.minIndicator = this.find('.zoom-bar-min-indicator');
|
||||
this.els.inner = this.find('.zoom-bar-inner');
|
||||
this.els.track = this.find('.zoom-bar-track');
|
||||
this.els.scrubber = this.find('.zoom-bar-scrubber');
|
||||
delete this.template;
|
||||
debug('rendered');
|
||||
return this.bindEvents();
|
||||
},
|
||||
bindEvents: function () {
|
||||
bind(this.els.scrubber, 'touchstart', this.onTouchStart);
|
||||
bind(this.els.scrubber, 'touchmove', this.onTouchMove);
|
||||
bind(this.els.scrubber, 'touchend', this.onTouchEnd);
|
||||
bind(this.els.maxIndicator, 'click', this.onIncrement);
|
||||
bind(this.els.minIndicator, 'click', this.onDecrement);
|
||||
orientation.on('orientation', this.setOrientation);
|
||||
return this;
|
||||
},
|
||||
template: function () {
|
||||
return '<div class="zoom-bar-max-indicator"></div>' + '<div class="zoom-bar-inner">' + '<div class="zoom-bar-track"></div>' + '<div class="zoom-bar-scrubber"></div>' + '</div>' + '<div class="zoom-bar-min-indicator"></div>';
|
||||
},
|
||||
onTouchStart: function (evt) {
|
||||
lastTouch = evt.touches[0];
|
||||
this.resetInactivityTimeout();
|
||||
this.setScrubberActive(true);
|
||||
this._innerHeight = this.els.inner.offsetHeight;
|
||||
evt.stopPropagation();
|
||||
},
|
||||
onTouchMove: function (evt) {
|
||||
if (!lastTouch) {
|
||||
return;
|
||||
}
|
||||
var touch = evt.touches[0];
|
||||
var deltaX = lastTouch.pageX - touch.pageX;
|
||||
var deltaY = lastTouch.pageY - touch.pageY;
|
||||
var scale = 100 / this._innerHeight;
|
||||
deltaX *= scale;
|
||||
deltaY *= scale;
|
||||
switch (this._orientation) {
|
||||
case 0:
|
||||
this.setValue(this._value + deltaY, true);
|
||||
break;
|
||||
case 90:
|
||||
this.setValue(this._value + deltaX, true);
|
||||
break;
|
||||
case 180:
|
||||
this.setValue(this._value - deltaY, true);
|
||||
break;
|
||||
case 270:
|
||||
this.setValue(this._value - deltaX, true);
|
||||
break;
|
||||
}
|
||||
lastTouch = touch;
|
||||
},
|
||||
onTouchEnd: function (evt) {
|
||||
if (!lastTouch) {
|
||||
return;
|
||||
}
|
||||
lastTouch = null;
|
||||
this.resetInactivityTimeout();
|
||||
this.setScrubberActive(false);
|
||||
},
|
||||
onIncrement: function (evt) {
|
||||
this.setValue(this._value + this.zoomBarIndicatorInterval, true);
|
||||
this.flashScrubberActive();
|
||||
evt.stopPropagation();
|
||||
},
|
||||
onDecrement: function (evt) {
|
||||
this.setValue(this._value - this.zoomBarIndicatorInterval, true);
|
||||
this.flashScrubberActive();
|
||||
evt.stopPropagation();
|
||||
},
|
||||
_orientation: 0,
|
||||
setOrientation: function (orientation) {
|
||||
var el = this.el;
|
||||
el.classList.remove('zooming');
|
||||
window.requestAnimationFrame(function () {
|
||||
el.style.transitionDuration = '0ms';
|
||||
window.requestAnimationFrame(function () {
|
||||
el.style.transitionDuration = '';
|
||||
});
|
||||
});
|
||||
this._orientation = orientation;
|
||||
},
|
||||
_value: 0,
|
||||
setValue: function (value, emitChange) {
|
||||
this.resetInactivityTimeout();
|
||||
var lastValue = this._value;
|
||||
this._value = clamp(value, 0, 100);
|
||||
if (this._value === lastValue) {
|
||||
return;
|
||||
}
|
||||
if (this._value === 0) {
|
||||
this.els.minIndicator.classList.add('active');
|
||||
} else {
|
||||
this.els.minIndicator.classList.remove('active');
|
||||
}
|
||||
if (this._value === 100) {
|
||||
this.els.maxIndicator.classList.add('active');
|
||||
} else {
|
||||
this.els.maxIndicator.classList.remove('active');
|
||||
}
|
||||
var self = this;
|
||||
window.requestAnimationFrame(function () {
|
||||
self.els.track.style.top = 100 - self._value + '%';
|
||||
self.els.scrubber.style.bottom = self._value + '%';
|
||||
});
|
||||
if (emitChange) {
|
||||
this.emit('change', this._value);
|
||||
}
|
||||
},
|
||||
setScrubberActive: function (active) {
|
||||
window.clearTimeout(this._scrubberTimeout);
|
||||
if (active) {
|
||||
this.els.scrubber.classList.add('active');
|
||||
} else {
|
||||
this.els.scrubber.classList.remove('active');
|
||||
}
|
||||
},
|
||||
flashScrubberActive: function () {
|
||||
this.setScrubberActive(true);
|
||||
var self = this;
|
||||
this._scrubberTimeout = window.setTimeout(function () {
|
||||
self.setScrubberActive(false);
|
||||
}, 150);
|
||||
},
|
||||
resetInactivityTimeout: function () {
|
||||
window.clearTimeout(this._inactivityTimeout);
|
||||
this.show();
|
||||
var self = this;
|
||||
this._inactivityTimeout = window.setTimeout(function () {
|
||||
self.hide();
|
||||
}, this.zoomBarInactivityTimeout);
|
||||
},
|
||||
show: function () {
|
||||
this.el.classList.add('zooming');
|
||||
},
|
||||
hide: function () {
|
||||
this.el.classList.remove('zooming');
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue