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
31
assets/webapps/camera.gaiamobile.org/bower.json
Normal file
31
assets/webapps/camera.gaiamobile.org/bower.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "camera",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/mozilla-b2g/gaia/tree/master/apps/camera",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "index.html",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"debug": "https://raw.githubusercontent.com/visionmedia/debug/1.0.2/dist/debug.js",
|
||||
"requirejs": "https://raw.githubusercontent.com/jrburke/requirejs/2.1.14/require.js",
|
||||
"evt": "fxos-components/evt#~0.4.0",
|
||||
"attach": "fxos-components/attach#0.1.1",
|
||||
"device-orientation": "fxos-components/device-orientation",
|
||||
"view": "fxos-components/view#~0.1.1",
|
||||
"model": "fxos-components/model#~0.1.0",
|
||||
"drag": "fxos-components/drag#0.1.4",
|
||||
"gaia-header": "fxos-components/gaia-header#~0.9.5",
|
||||
"gaia-theme": "fxos-components/gaia-theme#~0.4.1",
|
||||
"gaia-icons": "fxos-components/gaia-icons#~1.0.2"
|
||||
}
|
||||
}
|
||||
22
assets/webapps/camera.gaiamobile.org/bower_components/attach/bower.json
vendored
Normal file
22
assets/webapps/camera.gaiamobile.org/bower_components/attach/bower.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "attach",
|
||||
"main": "index.js",
|
||||
"version": "0.1.1",
|
||||
"homepage": "https://github.com/wilsonpage/attach",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"description": "Small event delegation library",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"examples",
|
||||
"test",
|
||||
"tests",
|
||||
"package.json",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
]
|
||||
}
|
||||
101
assets/webapps/camera.gaiamobile.org/bower_components/attach/index.js
vendored
Normal file
101
assets/webapps/camera.gaiamobile.org/bower_components/attach/index.js
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
;
|
||||
(function () {
|
||||
'use strict';
|
||||
var ns = '_attach';
|
||||
var proto = Element.prototype;
|
||||
var matches = proto.matchesSelector || proto.webkitMatchesSelector || proto.mozMatchesSelector || proto.msMatchesSelector || proto.oMatchesSelector;
|
||||
function attach(root, type, selector, fn, ctx) {
|
||||
if (arguments.length === 2) {
|
||||
return attach.many.apply(null, arguments);
|
||||
}
|
||||
if (typeof selector === 'function') {
|
||||
ctx = fn;
|
||||
fn = selector;
|
||||
selector = null;
|
||||
}
|
||||
selector = selector || 'null';
|
||||
var store = getStore(root);
|
||||
var master = store.master[type];
|
||||
var delegates = store.delegates[type] = store.delegates[type] || {};
|
||||
delegates[selector] = fn;
|
||||
if (master) {
|
||||
return;
|
||||
}
|
||||
master = store.master[type] = callback;
|
||||
root.addEventListener(type, master);
|
||||
function callback(e) {
|
||||
var el = e.target;
|
||||
var selector;
|
||||
var matched;
|
||||
var out;
|
||||
var fn;
|
||||
while (el) {
|
||||
for (selector in delegates) {
|
||||
fn = delegates[selector];
|
||||
matched = el === root && selector === 'null' || matches.call(el, selector);
|
||||
if (matched) {
|
||||
out = fn.call(ctx || el, e, el);
|
||||
if (out === false) {
|
||||
return e.stopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (el == root)
|
||||
break;
|
||||
el = el.parentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
attach.on = attach;
|
||||
attach.off = function (root, type, selector) {
|
||||
var store = getStore(root);
|
||||
var master = store.master[type];
|
||||
var delegates = store.delegates[type];
|
||||
if (type && selector) {
|
||||
delete delegates[selector];
|
||||
} else if (type) {
|
||||
delete store.delegates[type];
|
||||
} else {
|
||||
for (type in store.master) {
|
||||
attach.off(root, type);
|
||||
}
|
||||
}
|
||||
if (isEmpty(store.delegates[type])) {
|
||||
root.removeEventListener(type, master);
|
||||
delete store.master[type];
|
||||
}
|
||||
};
|
||||
attach.many = function (root, config, ctx) {
|
||||
var parts;
|
||||
var key;
|
||||
for (key in config) {
|
||||
parts = key.split(' ');
|
||||
attach.on(root, parts[0], parts[1], config[key], ctx);
|
||||
}
|
||||
};
|
||||
function getStore(el) {
|
||||
return el[ns] || createStore(el);
|
||||
}
|
||||
function createStore(el) {
|
||||
el[ns] = {
|
||||
master: {},
|
||||
delegates: {}
|
||||
};
|
||||
return el[ns];
|
||||
}
|
||||
function isEmpty(ob) {
|
||||
for (var key in ob) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (typeof exports === 'object') {
|
||||
module.exports = attach;
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define([], function () {
|
||||
return attach;
|
||||
});
|
||||
} else {
|
||||
window.attach = attach;
|
||||
}
|
||||
}());
|
||||
13
assets/webapps/camera.gaiamobile.org/bower_components/device-orientation/bower.json
vendored
Normal file
13
assets/webapps/camera.gaiamobile.org/bower_components/device-orientation/bower.json
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "device-orientation",
|
||||
"main": "device-orientation.js",
|
||||
"homepage": "https://github.com/fxos-components/device-orientation",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
20
assets/webapps/camera.gaiamobile.org/bower_components/drag/bower.json
vendored
Normal file
20
assets/webapps/camera.gaiamobile.org/bower_components/drag/bower.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "drag",
|
||||
"main": "index.js",
|
||||
"version": "0.1.4",
|
||||
"homepage": "https://github.com/gaia-components/drag",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"evt": "gaia-components/evt#~0.4.0"
|
||||
}
|
||||
}
|
||||
19
assets/webapps/camera.gaiamobile.org/bower_components/evt/bower.json
vendored
Normal file
19
assets/webapps/camera.gaiamobile.org/bower_components/evt/bower.json
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "evt",
|
||||
"main": "index.js",
|
||||
"version": "0.4.0",
|
||||
"homepage": "https://github.com/wilsonpage/evt",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"package.json",
|
||||
"test",
|
||||
"tests",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
18
assets/webapps/camera.gaiamobile.org/bower_components/font-fit/bower.json
vendored
Normal file
18
assets/webapps/camera.gaiamobile.org/bower_components/font-fit/bower.json
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "font-fit",
|
||||
"main": "font-fit.js",
|
||||
"version": "0.3.1",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"README.md",
|
||||
"index.html",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
70
assets/webapps/camera.gaiamobile.org/bower_components/font-fit/font-fit.js
vendored
Normal file
70
assets/webapps/camera.gaiamobile.org/bower_components/font-fit/font-fit.js
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
;
|
||||
(function (define) {
|
||||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module'
|
||||
], function (require, exports, module) {
|
||||
'use strict';
|
||||
var debug = 0 ? console.log.bind(console) : function () {
|
||||
};
|
||||
var cache = {};
|
||||
var MIN = 16;
|
||||
var MAX = 24;
|
||||
var BUFFER = 3;
|
||||
module.exports = function (config) {
|
||||
debug('font fit', config);
|
||||
var space = config.space - BUFFER;
|
||||
var min = config.min || MIN;
|
||||
var max = config.max || MAX;
|
||||
var text = trim(config.text);
|
||||
var fontSize = max;
|
||||
var textWidth;
|
||||
var font;
|
||||
do {
|
||||
font = config.font.replace(/\d+px/, fontSize + 'px');
|
||||
textWidth = getTextWidth(text, font);
|
||||
} while (textWidth > space && fontSize !== min && fontSize--);
|
||||
return {
|
||||
textWidth: textWidth,
|
||||
fontSize: fontSize,
|
||||
overflowing: textWidth > space
|
||||
};
|
||||
};
|
||||
function getTextWidth(text, font) {
|
||||
var ctx = getCanvasContext(font);
|
||||
var width = ctx.measureText(text).width;
|
||||
debug('got text width', width);
|
||||
return width;
|
||||
}
|
||||
function getCanvasContext(font) {
|
||||
debug('get canvas context', font);
|
||||
var cached = cache[font];
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.setAttribute('moz-opaque', 'true');
|
||||
canvas.setAttribute('width', '1px');
|
||||
canvas.setAttribute('height', '1px');
|
||||
debug('created canvas', canvas);
|
||||
var ctx = canvas.getContext('2d', { willReadFrequently: true });
|
||||
ctx.font = font;
|
||||
return cache[font] = ctx;
|
||||
}
|
||||
function trim(text) {
|
||||
return text.replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
});
|
||||
}(typeof define == 'function' && define.amd ? define : function (n, w) {
|
||||
'use strict';
|
||||
return typeof module == 'object' ? function (c) {
|
||||
c(require, exports, module);
|
||||
} : function (c) {
|
||||
var m = { exports: {} };
|
||||
c(function (n) {
|
||||
return w[n];
|
||||
}, m.exports, m);
|
||||
w[n] = m.exports;
|
||||
};
|
||||
}('font-fit', this)));
|
||||
18
assets/webapps/camera.gaiamobile.org/bower_components/gaia-component/bower.json
vendored
Normal file
18
assets/webapps/camera.gaiamobile.org/bower_components/gaia-component/bower.json
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "gaia-component",
|
||||
"version": "0.4.0",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-component.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"README.md",
|
||||
"package.json",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
251
assets/webapps/camera.gaiamobile.org/bower_components/gaia-component/gaia-component.js
vendored
Normal file
251
assets/webapps/camera.gaiamobile.org/bower_components/gaia-component/gaia-component.js
vendored
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
;
|
||||
(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module'
|
||||
], function (require, exports, module) {
|
||||
var textContent = Object.getOwnPropertyDescriptor(Node.prototype, 'textContent');
|
||||
var innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
|
||||
var removeAttribute = Element.prototype.removeAttribute;
|
||||
var setAttribute = Element.prototype.setAttribute;
|
||||
var noop = function () {
|
||||
};
|
||||
exports.register = function (name, props) {
|
||||
var baseProto = getBaseProto(props.extends);
|
||||
var template = props.template || baseProto.templateString;
|
||||
var extensible = props.extensible = props.hasOwnProperty('extensible') ? props.extensible : true;
|
||||
delete props.extends;
|
||||
if (template) {
|
||||
if (extensible && props.template) {
|
||||
props.templateString = props.template;
|
||||
}
|
||||
var output = processCss(template, name);
|
||||
props.template = document.createElement('template');
|
||||
props.template.innerHTML = output.template;
|
||||
props.lightCss = output.lightCss;
|
||||
props.globalCss = props.globalCss || '';
|
||||
props.globalCss += output.globalCss;
|
||||
}
|
||||
injectGlobalCss(props.globalCss);
|
||||
delete props.globalCss;
|
||||
var descriptors = mixin(props.attrs || {}, base.descriptors);
|
||||
props._attrs = props.attrs;
|
||||
delete props.attrs;
|
||||
var proto = createProto(baseProto, props);
|
||||
Object.defineProperties(proto, descriptors);
|
||||
try {
|
||||
return document.registerElement(name, { prototype: proto });
|
||||
} catch (e) {
|
||||
if (e.name !== 'NotSupportedError') {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
var base = {
|
||||
properties: {
|
||||
GaiaComponent: true,
|
||||
attributeChanged: noop,
|
||||
attached: noop,
|
||||
detached: noop,
|
||||
created: noop,
|
||||
createdCallback: function () {
|
||||
if (this.dirObserver) {
|
||||
addDirObserver();
|
||||
}
|
||||
injectLightCss(this);
|
||||
this.created();
|
||||
},
|
||||
attributeChangedCallback: function (name, from, to) {
|
||||
var prop = toCamelCase(name);
|
||||
if (this._attrs && this._attrs[prop]) {
|
||||
this[prop] = to;
|
||||
}
|
||||
this.attributeChanged(name, from, to);
|
||||
},
|
||||
attachedCallback: function () {
|
||||
if (this.dirObserver) {
|
||||
this.setInnerDirAttributes = setInnerDirAttributes.bind(null, this);
|
||||
document.addEventListener('dirchanged', this.setInnerDirAttributes);
|
||||
}
|
||||
this.attached();
|
||||
},
|
||||
detachedCallback: function () {
|
||||
if (this.dirObserver) {
|
||||
document.removeEventListener('dirchanged', this.setInnerDirAttributes);
|
||||
}
|
||||
this.detached();
|
||||
},
|
||||
setupShadowRoot: function () {
|
||||
if (!this.template) {
|
||||
return;
|
||||
}
|
||||
var node = document.importNode(this.template.content, true);
|
||||
this.createShadowRoot().appendChild(node);
|
||||
if (this.dirObserver) {
|
||||
setInnerDirAttributes(this);
|
||||
}
|
||||
return this.shadowRoot;
|
||||
},
|
||||
setAttr: function (name, value) {
|
||||
var internal = this.shadowRoot.firstElementChild;
|
||||
setAttribute.call(internal, name, value);
|
||||
setAttribute.call(this, name, value);
|
||||
},
|
||||
removeAttr: function (name) {
|
||||
var internal = this.shadowRoot.firstElementChild;
|
||||
removeAttribute.call(internal, name);
|
||||
removeAttribute.call(this, name);
|
||||
}
|
||||
},
|
||||
descriptors: {
|
||||
textContent: {
|
||||
set: function (value) {
|
||||
textContent.set.call(this, value);
|
||||
if (this.lightStyle) {
|
||||
this.appendChild(this.lightStyle);
|
||||
}
|
||||
},
|
||||
get: function () {
|
||||
return textContent.get();
|
||||
}
|
||||
},
|
||||
innerHTML: {
|
||||
set: function (value) {
|
||||
innerHTML.set.call(this, value);
|
||||
if (this.lightStyle) {
|
||||
this.appendChild(this.lightStyle);
|
||||
}
|
||||
},
|
||||
get: innerHTML.get
|
||||
}
|
||||
}
|
||||
};
|
||||
var defaultPrototype = createProto(HTMLElement.prototype, base.properties);
|
||||
function getBaseProto(proto) {
|
||||
if (!proto) {
|
||||
return defaultPrototype;
|
||||
}
|
||||
proto = proto.prototype || proto;
|
||||
return !proto.GaiaComponent ? createProto(proto, base.properties) : proto;
|
||||
}
|
||||
function createProto(proto, props) {
|
||||
return mixin(Object.create(proto), props);
|
||||
}
|
||||
var hasShadowCSS = function () {
|
||||
var div = document.createElement('div');
|
||||
try {
|
||||
div.querySelector(':host');
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}();
|
||||
var regex = {
|
||||
shadowCss: /(?:\:host|\:\:content)[^{]*\{[^}]*\}/g,
|
||||
':host': /(?:\:host)/g,
|
||||
':host()': /\:host\((.+)\)(?: \:\:content)?/g,
|
||||
':host-context': /\:host-context\((.+)\)([^{,]+)?/g,
|
||||
'::content': /(?:\:\:content)/g
|
||||
};
|
||||
function processCss(template, name) {
|
||||
var globalCss = '';
|
||||
var lightCss = '';
|
||||
if (!hasShadowCSS) {
|
||||
template = template.replace(regex.shadowCss, function (match) {
|
||||
var hostContext = regex[':host-context'].exec(match);
|
||||
if (hostContext) {
|
||||
globalCss += match.replace(regex['::content'], '').replace(regex[':host-context'], '$1 ' + name + '$2').replace(/ +/g, ' ');
|
||||
} else {
|
||||
lightCss += match.replace(regex[':host()'], name + '$1').replace(regex[':host'], name).replace(regex['::content'], name);
|
||||
}
|
||||
return '';
|
||||
});
|
||||
}
|
||||
return {
|
||||
template: template,
|
||||
lightCss: lightCss,
|
||||
globalCss: globalCss
|
||||
};
|
||||
}
|
||||
function injectGlobalCss(css) {
|
||||
if (!css) {
|
||||
return;
|
||||
}
|
||||
var style = document.createElement('style');
|
||||
style.innerHTML = css.trim();
|
||||
headReady().then(function () {
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
}
|
||||
function headReady() {
|
||||
return new Promise(function (resolve) {
|
||||
if (document.head) {
|
||||
return resolve();
|
||||
}
|
||||
window.addEventListener('load', function fn() {
|
||||
window.removeEventListener('load', fn);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
function injectLightCss(el) {
|
||||
if (hasShadowCSS) {
|
||||
return;
|
||||
}
|
||||
var stylesheet = el.querySelector('style');
|
||||
if (!stylesheet) {
|
||||
stylesheet = document.createElement('style');
|
||||
stylesheet.setAttribute('scoped', '');
|
||||
stylesheet.appendChild(document.createTextNode(el.lightCss));
|
||||
el.appendChild(stylesheet);
|
||||
}
|
||||
el.lightStyle = stylesheet;
|
||||
}
|
||||
function toCamelCase(string) {
|
||||
return string.replace(/-(.)/g, function replacer(string, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
}
|
||||
var dirObserver;
|
||||
function setInnerDirAttributes(component) {
|
||||
var dir = component.dir || document.dir;
|
||||
Array.from(component.shadowRoot.children).forEach(element => {
|
||||
if (element.nodeName !== 'STYLE') {
|
||||
element.dir = dir;
|
||||
}
|
||||
});
|
||||
}
|
||||
function addDirObserver() {
|
||||
if (dirObserver) {
|
||||
return;
|
||||
}
|
||||
dirObserver = new MutationObserver(onChanged);
|
||||
dirObserver.observe(document.documentElement, {
|
||||
attributeFilter: ['dir'],
|
||||
attributes: true
|
||||
});
|
||||
function onChanged(mutations) {
|
||||
document.dispatchEvent(new Event('dirchanged'));
|
||||
}
|
||||
}
|
||||
function mixin(target, source) {
|
||||
for (var key in source) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
return target;
|
||||
}
|
||||
});
|
||||
}(typeof define == 'function' && define.amd ? define : function (n, w) {
|
||||
'use strict';
|
||||
return typeof module == 'object' ? function (c) {
|
||||
c(require, exports, module);
|
||||
} : function (c) {
|
||||
var m = { exports: {} };
|
||||
c(function (n) {
|
||||
return w[n];
|
||||
}, m.exports, m);
|
||||
w[n] = m.exports;
|
||||
};
|
||||
}('gaia-component', this)));
|
||||
30
assets/webapps/camera.gaiamobile.org/bower_components/gaia-header/bower.json
vendored
Normal file
30
assets/webapps/camera.gaiamobile.org/bower_components/gaia-header/bower.json
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "gaia-header",
|
||||
"version": "0.9.5",
|
||||
"homepage": "https://github.com/gaia-components/gaia-header",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": "gaia-header.js",
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"index.html",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"gaia-icons": "gaia-components/gaia-icons#~0.10.3",
|
||||
"gaia-component": "gaia-components/gaia-component#~0.4.0",
|
||||
"font-fit": "gaia-components/font-fit#~0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gaia-theme": "gaia-components/gaia-theme",
|
||||
"gaia-fonts": "gaia-components/gaia-fonts",
|
||||
"base": "gaia-components/base"
|
||||
}
|
||||
}
|
||||
847
assets/webapps/camera.gaiamobile.org/bower_components/gaia-header/dist/gaia-header-es5.js
vendored
Normal file
847
assets/webapps/camera.gaiamobile.org/bower_components/gaia-header/dist/gaia-header-es5.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1141
assets/webapps/camera.gaiamobile.org/bower_components/gaia-header/dist/gaia-header.js
vendored
Normal file
1141
assets/webapps/camera.gaiamobile.org/bower_components/gaia-header/dist/gaia-header.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
728
assets/webapps/camera.gaiamobile.org/bower_components/gaia-header/gaia-header.js
vendored
Normal file
728
assets/webapps/camera.gaiamobile.org/bower_components/gaia-header/gaia-header.js
vendored
Normal file
|
|
@ -0,0 +1,728 @@
|
|||
;
|
||||
(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module',
|
||||
'gaia-component',
|
||||
'font-fit',
|
||||
'gaia-icons'
|
||||
], function (require, exports, module) {
|
||||
var component = require('gaia-component');
|
||||
var fontFit = require('font-fit');
|
||||
require('gaia-icons');
|
||||
var debug = 0 ? console.log.bind(console) : function () {
|
||||
};
|
||||
const KNOWN_ACTIONS = {
|
||||
menu: 'menu',
|
||||
back: 'back',
|
||||
close: 'close'
|
||||
};
|
||||
const TITLE_FONT = 'italic 300 24px FiraSans';
|
||||
const TITLE_PADDING = 10;
|
||||
const MINIMUM_FONT_SIZE_CENTERED = 20;
|
||||
const MINIMUM_FONT_SIZE_UNCENTERED = 16;
|
||||
const MAXIMUM_FONT_SIZE = 23;
|
||||
module.exports = component.register('gaia-header', {
|
||||
extensible: false,
|
||||
dirObserver: true,
|
||||
created: function () {
|
||||
debug('created');
|
||||
this.setupShadowRoot();
|
||||
this.els = {
|
||||
actionButton: this.shadowRoot.querySelector('.action-button'),
|
||||
titles: this.getElementsByTagName('h1')
|
||||
};
|
||||
this.els.actionButton.addEventListener('click', e => this.onActionButtonClick(e));
|
||||
this.observer = new MutationObserver(this.onMutation.bind(this));
|
||||
this.ignoreDir = this.hasAttribute('ignore-dir');
|
||||
this.titleEnd = this.getAttribute('title-end');
|
||||
this.titleStart = this.getAttribute('title-start');
|
||||
this.noFontFit = this.getAttribute('no-font-fit');
|
||||
this.notFlush = this.hasAttribute('not-flush');
|
||||
this.action = this.getAttribute('action');
|
||||
this.unresolved = {};
|
||||
this.pending = {};
|
||||
this._resizeThrottlingId = null;
|
||||
this.onResize = this.onResize.bind(this);
|
||||
},
|
||||
attached: function () {
|
||||
debug('attached');
|
||||
this.runFontFitSoon();
|
||||
this.observerStart();
|
||||
window.addEventListener('resize', this.onResize);
|
||||
},
|
||||
detached: function () {
|
||||
debug('detached');
|
||||
window.removeEventListener('resize', this.onResize);
|
||||
this.observerStop();
|
||||
this.clearPending();
|
||||
},
|
||||
clearPending: function () {
|
||||
for (var key in this.pending) {
|
||||
this.pending[key].clear();
|
||||
delete this.pending[key];
|
||||
}
|
||||
window.cancelAnimationFrame(this._resizeThrottlingId);
|
||||
this._resizeThrottlingId = null;
|
||||
},
|
||||
runFontFit: function () {
|
||||
debug('run font-fit');
|
||||
if (this.noFontFit) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
var titles = this.els.titles;
|
||||
var space = this.getTitleSpace();
|
||||
var styles = [].map.call(titles, el => this.getTitleStyle(el, space));
|
||||
return this.setTitleStylesSoon(styles);
|
||||
},
|
||||
runFontFitSoon: function () {
|
||||
debug('run font-fit soon');
|
||||
if (this.pending.runFontFitSoon) {
|
||||
return;
|
||||
}
|
||||
this.pending.runFontFitSoon = this.nextTick(() => {
|
||||
delete this.pending.runFontFitSoon;
|
||||
this.runFontFit();
|
||||
});
|
||||
},
|
||||
getTitleStyle: function (el, space) {
|
||||
debug('get el style', el, space);
|
||||
var text = el.textContent;
|
||||
var styleId = space.start + text + space.end + '#' + space.value;
|
||||
if (!text || !text.trim()) {
|
||||
return debug('exit: no text');
|
||||
}
|
||||
if (getStyleId(el) === styleId) {
|
||||
return debug('exit: no change');
|
||||
}
|
||||
var marginStart = this.getTitleMarginStart();
|
||||
var textSpace = space.value - Math.abs(marginStart);
|
||||
var fontFitResult = this.fontFit(text, textSpace, { min: MINIMUM_FONT_SIZE_CENTERED });
|
||||
var overflowing = fontFitResult.overflowing;
|
||||
var padding = {
|
||||
start: 0,
|
||||
end: 0
|
||||
};
|
||||
if (overflowing) {
|
||||
debug('title overflowing');
|
||||
padding.start = !space.start ? TITLE_PADDING : 0;
|
||||
padding.end = !space.end ? TITLE_PADDING : 0;
|
||||
textSpace = space.value - padding.start - padding.end;
|
||||
fontFitResult = this.fontFit(text, textSpace);
|
||||
marginStart = 0;
|
||||
}
|
||||
return {
|
||||
id: styleId,
|
||||
fontSize: fontFitResult.fontSize,
|
||||
marginStart: marginStart,
|
||||
overflowing: overflowing,
|
||||
padding: padding
|
||||
};
|
||||
},
|
||||
setTitleStylesSoon: function (styles) {
|
||||
debug('set title styles soon', styles);
|
||||
var key = 'setStyleTitlesSoon';
|
||||
this._titleStyles = styles;
|
||||
if (this.unresolved[key]) {
|
||||
return this.unresolved[key];
|
||||
}
|
||||
this.unresolved[key] = new Promise(resolve => {
|
||||
this.pending[key] = this.nextTick(() => {
|
||||
var styles = this._titleStyles;
|
||||
var els = this.els.titles;
|
||||
[].forEach.call(els, (el, i) => {
|
||||
if (!styles[i]) {
|
||||
return debug('exit');
|
||||
}
|
||||
this.setTitleStyle(el, styles[i]);
|
||||
});
|
||||
delete this._titleStyles;
|
||||
delete this.unresolved[key];
|
||||
delete this.pending[key];
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
setTitleStyle: function (el, style) {
|
||||
debug('set title style', style);
|
||||
this.observerStop();
|
||||
if (this.ignoreDir) {
|
||||
el.style.marginLeft = style.marginStart + 'px';
|
||||
el.style.paddingLeft = style.padding.start + 'px';
|
||||
el.style.paddingRight = style.padding.end + 'px';
|
||||
} else {
|
||||
el.style.marginInlineStart = style.marginStart + 'px';
|
||||
el.style.paddingInlineStart = style.padding.start + 'px';
|
||||
el.style.paddingInlineEnd = style.padding.end + 'px';
|
||||
}
|
||||
el.style.fontSize = style.fontSize + 'px';
|
||||
setStyleId(el, style.id);
|
||||
this.observerStart();
|
||||
},
|
||||
fontFit: function (text, space, opts = {}) {
|
||||
debug('font fit:', text, space, opts);
|
||||
var fontFitArgs = {
|
||||
font: TITLE_FONT,
|
||||
min: opts.min || MINIMUM_FONT_SIZE_UNCENTERED,
|
||||
max: MAXIMUM_FONT_SIZE,
|
||||
text: text,
|
||||
space: space
|
||||
};
|
||||
return fontFit(fontFitArgs);
|
||||
},
|
||||
observerStart: function () {
|
||||
if (this.observing) {
|
||||
return;
|
||||
}
|
||||
this.observer.observe(this, {
|
||||
childList: true,
|
||||
attributes: true,
|
||||
subtree: true
|
||||
});
|
||||
this.observing = true;
|
||||
debug('observer started');
|
||||
},
|
||||
observerStop: function () {
|
||||
if (!this.observing) {
|
||||
return;
|
||||
}
|
||||
this.observer.disconnect();
|
||||
this.observing = false;
|
||||
debug('observer stopped');
|
||||
},
|
||||
onResize: function (e) {
|
||||
debug('onResize', this._resizeThrottlingId);
|
||||
if (this._resizeThrottlingId !== null) {
|
||||
return;
|
||||
}
|
||||
this._resizeThrottlingId = window.requestAnimationFrame(() => {
|
||||
this._resizeThrottlingId = null;
|
||||
this.runFontFitSoon();
|
||||
});
|
||||
},
|
||||
onMutation: function (mutations) {
|
||||
debug('on mutation', mutations);
|
||||
if (!this.pending.runFontFitSoon) {
|
||||
this.runFontFit();
|
||||
}
|
||||
},
|
||||
getTitleSpace: function () {
|
||||
var start = this.titleStart;
|
||||
var end = this.titleEnd;
|
||||
var space = this.getWidth() - start - end;
|
||||
var result = {
|
||||
value: space,
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
debug('get title space', result);
|
||||
return result;
|
||||
},
|
||||
getWidth: function () {
|
||||
var value = this.notFlush ? this.clientWidth : window.innerWidth;
|
||||
debug('get width', value);
|
||||
return value;
|
||||
},
|
||||
triggerAction: function () {
|
||||
if (this.action) {
|
||||
this.els.actionButton.click();
|
||||
}
|
||||
},
|
||||
onActionButtonClick: function () {
|
||||
debug('action button click');
|
||||
var config = { detail: { type: this.action } };
|
||||
var e = new CustomEvent('action', config);
|
||||
setTimeout(() => this.dispatchEvent(e));
|
||||
},
|
||||
getTitleMarginStart: function () {
|
||||
var start = this.titleStart;
|
||||
var end = this.titleEnd;
|
||||
var marginStart = end - start;
|
||||
debug('get title margin start', marginStart);
|
||||
return marginStart;
|
||||
},
|
||||
getButtonsBeforeTitle: function () {
|
||||
var children = this.children;
|
||||
var l = children.length;
|
||||
var els = [];
|
||||
for (var i = 0; i < l; i++) {
|
||||
var el = children[i];
|
||||
if (el.tagName === 'H1') {
|
||||
break;
|
||||
}
|
||||
if (!contributesToLayout(el)) {
|
||||
continue;
|
||||
}
|
||||
els.push(el);
|
||||
}
|
||||
if (this.action) {
|
||||
els.push(this.els.actionButton);
|
||||
}
|
||||
return els;
|
||||
},
|
||||
getButtonsAfterTitle: function () {
|
||||
var children = this.children;
|
||||
var els = [];
|
||||
for (var i = children.length - 1; i >= 0; i--) {
|
||||
var el = children[i];
|
||||
if (el.tagName === 'H1') {
|
||||
break;
|
||||
}
|
||||
if (!contributesToLayout(el)) {
|
||||
continue;
|
||||
}
|
||||
els.push(el);
|
||||
}
|
||||
return els;
|
||||
},
|
||||
sumButtonWidths: function (buttons) {
|
||||
var defaultWidth = 50;
|
||||
var sum = buttons.reduce((prev, button) => {
|
||||
var isStandardButton = button === this.els.actionButton;
|
||||
var width = isStandardButton ? defaultWidth : button.clientWidth;
|
||||
return prev + width;
|
||||
}, 0);
|
||||
debug('sum button widths', buttons, sum);
|
||||
return sum;
|
||||
},
|
||||
attrs: {
|
||||
action: {
|
||||
get: function () {
|
||||
return this._action;
|
||||
},
|
||||
set: function (value) {
|
||||
var action = KNOWN_ACTIONS[value];
|
||||
if (action === this._action) {
|
||||
return;
|
||||
}
|
||||
this.setAttr('action', action);
|
||||
this._action = action;
|
||||
}
|
||||
},
|
||||
titleStart: {
|
||||
get: function () {
|
||||
debug('get title-start');
|
||||
if ('_titleStart' in this) {
|
||||
return this._titleStart;
|
||||
}
|
||||
var buttons = this.getButtonsBeforeTitle();
|
||||
var value = this.sumButtonWidths(buttons);
|
||||
debug('get title-start', buttons, value);
|
||||
return value;
|
||||
},
|
||||
set: function (value) {
|
||||
debug('set title-start', value);
|
||||
value = parseInt(value, 10);
|
||||
if (value === this._titleStart || isNaN(value)) {
|
||||
return;
|
||||
}
|
||||
this.setAttr('title-start', value);
|
||||
this._titleStart = value;
|
||||
debug('set');
|
||||
}
|
||||
},
|
||||
titleEnd: {
|
||||
get: function () {
|
||||
debug('get title-end');
|
||||
if ('_titleEnd' in this) {
|
||||
return this._titleEnd;
|
||||
}
|
||||
var buttons = this.getButtonsAfterTitle();
|
||||
return this.sumButtonWidths(buttons);
|
||||
},
|
||||
set: function (value) {
|
||||
debug('set title-end', value);
|
||||
value = parseInt(value, 10);
|
||||
if (value === this._titleEnd || isNaN(value)) {
|
||||
return;
|
||||
}
|
||||
this.setAttr('title-end', value);
|
||||
this._titleEnd = value;
|
||||
}
|
||||
},
|
||||
noFontFit: {
|
||||
get: function () {
|
||||
return this._noFontFit || false;
|
||||
},
|
||||
set: function (value) {
|
||||
debug('set no-font-fit', value);
|
||||
value = !!(value || value === '');
|
||||
if (value === this.noFontFit) {
|
||||
return;
|
||||
}
|
||||
this._noFontFit = value;
|
||||
if (value) {
|
||||
this.setAttr('no-font-fit', '');
|
||||
} else {
|
||||
this.removeAttr('no-font-fit');
|
||||
}
|
||||
}
|
||||
},
|
||||
ignoreDir: {
|
||||
get: function () {
|
||||
return this._ignoreDir || false;
|
||||
},
|
||||
set: function (value) {
|
||||
debug('set ignore-dir', value);
|
||||
value = !!(value || value === '');
|
||||
if (value === this.ignoreDir) {
|
||||
return;
|
||||
}
|
||||
this._ignoreDir = value;
|
||||
this.dirObserver = !value;
|
||||
if (value) {
|
||||
this.setAttr('ignore-dir', '');
|
||||
} else {
|
||||
this.removeAttr('ignore-dir');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `<div class="inner">
|
||||
<button class="action-button">
|
||||
<content select="[l10n-action]"></content>
|
||||
</button>
|
||||
<content></content>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
-moz-user-select: none;
|
||||
|
||||
--gaia-header-button-color:
|
||||
var(--header-button-color,
|
||||
var(--header-color,
|
||||
var(--link-color,
|
||||
inherit)));
|
||||
}
|
||||
|
||||
/**
|
||||
* [hidden]
|
||||
*/
|
||||
|
||||
:host[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/** Reset
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::-moz-focus-inner { border: 0; }
|
||||
|
||||
/** Inner
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
min-height: 50px;
|
||||
-moz-user-select: none;
|
||||
|
||||
background:
|
||||
var(--header-background,
|
||||
var(--background,
|
||||
#fff));
|
||||
}
|
||||
|
||||
/** Action Button
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* 1. Hidden by default
|
||||
*/
|
||||
|
||||
.action-button {
|
||||
position: relative;
|
||||
|
||||
display: none; /* 1 */
|
||||
width: 50px;
|
||||
font-size: 30px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
|
||||
align-items: center;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
transition: opacity 200ms 280ms;
|
||||
color:
|
||||
var(--header-action-button-color,
|
||||
var(--header-icon-color,
|
||||
var(--gaia-header-button-color)));
|
||||
}
|
||||
|
||||
/**
|
||||
* [action=back]
|
||||
* [action=menu]
|
||||
* [action=close]
|
||||
*
|
||||
* 1. For icon vertical-alignment
|
||||
*/
|
||||
|
||||
[action=back] .action-button,
|
||||
[action=menu] .action-button,
|
||||
[action=close] .action-button {
|
||||
display: flex; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* :active
|
||||
*/
|
||||
|
||||
.action-button:active {
|
||||
transition: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
/** Action Button Icon
|
||||
---------------------------------------------------------*/
|
||||
|
||||
.action-button:before {
|
||||
font-family: 'gaia-icons';
|
||||
font-style: normal;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
[action=close] .action-button:before { content: 'close' }
|
||||
[action=menu] .action-button:before { content: 'menu' }
|
||||
|
||||
[action=back]:-moz-dir(ltr) .action-button:before { content: 'left' }
|
||||
[action=back]:-moz-dir(rtl) .action-button:before { content: 'right' }
|
||||
|
||||
/** Action Button Icon
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* 1. To enable vertical alignment.
|
||||
*/
|
||||
|
||||
.action-button:before {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/** Action Button Text
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* To provide custom localized content for
|
||||
* the action-button, we allow the user
|
||||
* to provide an element with the class
|
||||
* .l10n-action. This node is then
|
||||
* pulled inside the real action-button.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <gaia-header action="back">
|
||||
* <span l10n-action aria-label="Back">Localized text</span>
|
||||
* <h1>title</h1>
|
||||
* </gaia-header>
|
||||
*/
|
||||
|
||||
::content [l10n-action] {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
/** Title
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* 1. Vertically center text. We can't use flexbox
|
||||
* here as it breaks text-overflow ellipsis
|
||||
* without an inner div.
|
||||
*/
|
||||
|
||||
::content h1 {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
line-height: 50px; /* 1 */
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
font-size: 24px;
|
||||
|
||||
color:
|
||||
var(--header-title-color,
|
||||
var(--header-color,
|
||||
var(--title-color,
|
||||
var(--text-color,
|
||||
inherit))));
|
||||
}
|
||||
|
||||
/**
|
||||
* [ignore-dir]
|
||||
*
|
||||
* When the <gaia-header> component has an [ignore-dir] attribute, header
|
||||
* direction is forced to LTR but we still want the <h1> text to be reversed
|
||||
* so that strings like '1 selected' become 'selected 1'.
|
||||
*
|
||||
* When we're happy for <gaia-header> to be fully RTL responsive we won't need
|
||||
* these rules anymore, but this depends on all Gaia apps being ready.
|
||||
*
|
||||
* This should be safe to remove when bug 1179459 lands.
|
||||
*/
|
||||
|
||||
:host[ignore-dir] {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
:host[ignore-dir]:-moz-dir(rtl) h1 {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
/** Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
::content a,
|
||||
::content button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-width: 50px;
|
||||
margin: 0;
|
||||
padding: 0 10px;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
background: none;
|
||||
border-radius: 0;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
transition: opacity 200ms 280ms;
|
||||
color: var(--gaia-header-button-color);
|
||||
}
|
||||
|
||||
/**
|
||||
* :active
|
||||
*/
|
||||
|
||||
::content a:active,
|
||||
::content button:active {
|
||||
transition: none;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
/**
|
||||
* [hidden]
|
||||
*/
|
||||
|
||||
::content a[hidden],
|
||||
::content button[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* [disabled]
|
||||
*/
|
||||
|
||||
::content a[disabled],
|
||||
::content button[disabled] {
|
||||
pointer-events: none;
|
||||
color: var(--header-disabled-button-color);
|
||||
}
|
||||
|
||||
/** Icon Buttons
|
||||
---------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Icons are a different color to text
|
||||
*/
|
||||
|
||||
::content .icon,
|
||||
::content [data-icon] {
|
||||
color:
|
||||
var(--header-icon-color,
|
||||
var(--gaia-header-button-color));
|
||||
}
|
||||
|
||||
/**
|
||||
* If users want their action button
|
||||
* to be in the component's light-dom
|
||||
* they can add an .action class
|
||||
* to make it look like the
|
||||
* shadow action button.
|
||||
*/
|
||||
|
||||
::content .action {
|
||||
color:
|
||||
var(--header-action-button-color,
|
||||
var(--header-icon-color,
|
||||
var(--gaia-header-button-color)));
|
||||
}
|
||||
|
||||
/**
|
||||
* [data-icon]:empty
|
||||
*
|
||||
* Icon buttons with no textContent,
|
||||
* should always be 50px.
|
||||
*
|
||||
* This is to prevent buttons being
|
||||
* larger than they should be before
|
||||
* icon-font has loaded.
|
||||
*/
|
||||
|
||||
::content [data-icon]:empty {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
</style>`,
|
||||
nextTick: nextTick
|
||||
});
|
||||
function contributesToLayout(el) {
|
||||
return el.localName !== 'style' && !el.hasAttribute('l10n-action');
|
||||
}
|
||||
function setStyleId(el, id) {
|
||||
el._styleId = id;
|
||||
}
|
||||
function getStyleId(el) {
|
||||
return el._styleId;
|
||||
}
|
||||
function nextTick(fn) {
|
||||
var cleared;
|
||||
Promise.resolve().then(() => {
|
||||
if (!cleared) {
|
||||
fn();
|
||||
}
|
||||
});
|
||||
return {
|
||||
clear: function () {
|
||||
cleared = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}(typeof define == 'function' && define.amd ? define : function (n, w) {
|
||||
'use strict';
|
||||
return typeof module == 'object' ? function (c) {
|
||||
c(require, exports, module);
|
||||
} : function (c) {
|
||||
var m = { exports: {} };
|
||||
c(function (n) {
|
||||
return w[n];
|
||||
}, m.exports, m);
|
||||
w[n] = m.exports;
|
||||
};
|
||||
}('gaia-header', this)));
|
||||
1
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/bidi-helper.css
vendored
Normal file
1
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/bidi-helper.css
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
[data-icon="back"]:-moz-dir(ltr):before{content:'left';}[data-icon="back"]:-moz-dir(rtl):before{content:'right';}[data-icon="forward"]:-moz-dir(ltr):before{content:'right';}[data-icon="forward"]:-moz-dir(rtl):before{content:'left';}[data-icon="back-light"]:-moz-dir(ltr):before{content:'left-light';}[data-icon="back-light"]:-moz-dir(rtl):before{content:'right-light';}[data-icon="forward-light"]:-moz-dir(ltr):before{content:'right-light';}[data-icon="forward-light"]:-moz-dir(rtl):before{content:'left-light';}[data-icon="arrow-back"]:-moz-dir(ltr):before{content:'arrow-left';}[data-icon="arrow-back"]:-moz-dir(rtl):before{content:'arrow-right';}[data-icon="arrow-forward"]:-moz-dir(ltr):before{content:'arrow-right';}[data-icon="arrow-forward"]:-moz-dir(rtl):before{content:'arrow-left';}[data-icon="clear-input"]:-moz-dir(ltr):before{content:'clear-input-left';}[data-icon="clear-input"]:-moz-dir(rtl):before{content:'clear-input-right';}[data-icon="expand"]:-moz-dir(ltr):before{content:'expand-left';}[data-icon="expand"]:-moz-dir(rtl):before{content:'expand-right';}[data-icon="send"]:-moz-dir(ltr):before{content:'send-right';}[data-icon="send"]:-moz-dir(rtl):before{content:'send-left';}[data-icon="edit"]:-moz-dir(ltr):before{content:'edit-left';}[data-icon="edit"]:-moz-dir(rtl):before{content:'edit-right';}
|
||||
32
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/bower.json
vendored
Normal file
32
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/bower.json
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "gaia-icons",
|
||||
"homepage": "https://github.com/wilsonpage/gaia-icons",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"main": [
|
||||
"gaia-icons.js",
|
||||
"gaia-icons.css",
|
||||
"fonts/gaia-icons.ttf",
|
||||
"gaia-icons-embedded.css",
|
||||
"bidi-helper.css"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"templates",
|
||||
"images",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"create-svg-icon-set-file.py",
|
||||
"Gruntfile.js",
|
||||
"Dockerfile",
|
||||
"bin",
|
||||
"README.md",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
BIN
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/fonts/gaia-icons.ttf
vendored
Normal file
BIN
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/fonts/gaia-icons.ttf
vendored
Normal file
Binary file not shown.
1
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/gaia-icons-embedded.css
vendored
Normal file
1
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/gaia-icons-embedded.css
vendored
Normal file
File diff suppressed because one or more lines are too long
40
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/gaia-icons.js
vendored
Normal file
40
assets/webapps/camera.gaiamobile.org/bower_components/gaia-icons/gaia-icons.js
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module'
|
||||
], function (require, exports, module) {
|
||||
var base = window.GAIA_ICONS_BASE_URL || window.COMPONENTS_BASE_URL || 'bower_components/';
|
||||
if (!document.documentElement) {
|
||||
window.addEventListener('load', load);
|
||||
} else {
|
||||
load();
|
||||
}
|
||||
function load() {
|
||||
if (isLoaded()) {
|
||||
return;
|
||||
}
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = base + 'gaia-icons/gaia-icons.css';
|
||||
document.head.appendChild(link);
|
||||
exports.loaded = true;
|
||||
}
|
||||
function isLoaded() {
|
||||
return exports.loaded || document.querySelector('link[href*=gaia-icons]') || document.documentElement.classList.contains('gaia-icons-loaded');
|
||||
}
|
||||
});
|
||||
}(typeof define == 'function' && define.amd ? define : function (n, w) {
|
||||
'use strict';
|
||||
return typeof module == 'object' ? function (c) {
|
||||
c(require, exports, module);
|
||||
} : function (c) {
|
||||
var m = { exports: {} };
|
||||
c(function (n) {
|
||||
return w[n];
|
||||
}, m.exports, m);
|
||||
w[n] = m.exports;
|
||||
};
|
||||
}('gaia-icons', this)));
|
||||
26
assets/webapps/camera.gaiamobile.org/bower_components/gaia-theme/bower.json
vendored
Normal file
26
assets/webapps/camera.gaiamobile.org/bower_components/gaia-theme/bower.json
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "gaia-theme",
|
||||
"version": "0.4.7",
|
||||
"homepage": "https://github.com/gaia-components/gaia-theme",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"!.jshintrc",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"gaia-icons": "gaia-components/gaia-icons",
|
||||
"gaia-header": "gaia-components/gaia-header",
|
||||
"gaia-button": "gaia-components/gaia-button",
|
||||
"gaia-switch": "gaia-components/gaia-switch",
|
||||
"gaia-checkbox": "gaia-components/gaia-checkbox"
|
||||
}
|
||||
}
|
||||
110
assets/webapps/camera.gaiamobile.org/bower_components/gaia-theme/lib/gaia-theme-selector.js
vendored
Normal file
110
assets/webapps/camera.gaiamobile.org/bower_components/gaia-theme/lib/gaia-theme-selector.js
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'require',
|
||||
'exports',
|
||||
'module'
|
||||
], function (require, exports, module) {
|
||||
var proto = Object.create(HTMLElement.prototype);
|
||||
proto.createdCallback = function () {
|
||||
this.createShadowRoot().innerHTML = template;
|
||||
this.els = {
|
||||
themes: this.shadowRoot.querySelector('.themes'),
|
||||
rtl: this.shadowRoot.querySelector('.toggle-rtl')
|
||||
};
|
||||
this.els.rtl.addEventListener('click', this.onRtlClick.bind(this));
|
||||
this.els.themes.addEventListener('click', this.onThemeClick.bind(this));
|
||||
this.styleHack();
|
||||
this.set(localStorage.theme || 'settings');
|
||||
};
|
||||
proto.styleHack = function () {
|
||||
var style = this.shadowRoot.querySelector('style');
|
||||
style.setAttribute('scoped', '');
|
||||
this.appendChild(style.cloneNode(true));
|
||||
};
|
||||
proto.onThemeClick = function (e) {
|
||||
var theme = e.target.value;
|
||||
if (theme) {
|
||||
this.set(theme);
|
||||
}
|
||||
};
|
||||
proto.onRtlClick = function () {
|
||||
document.dir = this.els.rtl.checked ? 'rtl' : 'ltr';
|
||||
};
|
||||
proto.set = function (theme) {
|
||||
var radio = this.shadowRoot.querySelector('[value="' + theme + '"]');
|
||||
document.body.classList.remove('theme-' + this.theme);
|
||||
document.body.classList.add('theme-' + theme);
|
||||
radio.checked = true;
|
||||
this.theme = theme;
|
||||
localStorage.theme = theme;
|
||||
};
|
||||
var template = `
|
||||
<style>
|
||||
|
||||
* { margin: 0; padding: 0; }
|
||||
|
||||
gaia-theme-selector {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
z-index: 101;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background: rgba(255,255,255,0.8);
|
||||
color: #000;
|
||||
font-size: 9px;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
form label {
|
||||
-moz-margin-start: 9px;
|
||||
}
|
||||
|
||||
form input {
|
||||
-moz-margin-end: 6px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.rtl {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<form>
|
||||
<label class="rtl"><input type="checkbox" class="toggle-rtl" />RTL</label>
|
||||
<span class="themes">
|
||||
<label><input type="radio" name="theme" value="productivity"/>Prod</label>
|
||||
<label><input type="radio" name="theme" value="communications"/>Comms</label>
|
||||
<label><input type="radio" name="theme" value="media"/>Media</label>
|
||||
<label><input type="radio" name="theme" value="settings"/>Settings</label>
|
||||
</span>
|
||||
</form>`;
|
||||
document.registerElement('gaia-theme-selector', { prototype: proto });
|
||||
});
|
||||
}(function (n, w) {
|
||||
'use strict';
|
||||
return typeof define == 'function' && define.amd ? define : typeof module == 'object' ? function (c) {
|
||||
c(require, exports, module);
|
||||
} : function (c) {
|
||||
var m = { exports: {} }, r = function (n) {
|
||||
return w[n];
|
||||
};
|
||||
w[n] = c(r, m.exports, m) || m.exports;
|
||||
};
|
||||
}('gaia-theme-selector', this)));
|
||||
21
assets/webapps/camera.gaiamobile.org/bower_components/model/bower.json
vendored
Normal file
21
assets/webapps/camera.gaiamobile.org/bower_components/model/bower.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "model",
|
||||
"main": "index.js",
|
||||
"version": "0.1.0",
|
||||
"homepage": "https://github.com/gaia-components/model",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"evt": "wilsonpage/evt#~0.4.0"
|
||||
}
|
||||
}
|
||||
21
assets/webapps/camera.gaiamobile.org/bower_components/view/bower.json
vendored
Normal file
21
assets/webapps/camera.gaiamobile.org/bower_components/view/bower.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "view",
|
||||
"version": "0.1.1",
|
||||
"main": "index.js",
|
||||
"homepage": "https://github.com/gaia-components/view",
|
||||
"authors": [
|
||||
"Wilson Page <wilsonpage@me.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"evt": "wilsonpage/evt#~0.4.0"
|
||||
}
|
||||
}
|
||||
293
assets/webapps/camera.gaiamobile.org/gaia_build_defer_index.js
Normal file
293
assets/webapps/camera.gaiamobile.org/gaia_build_defer_index.js
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
;var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if('value'in descriptor)
|
||||
descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor);}}
|
||||
return function(Constructor,protoProps,staticProps){if(protoProps)
|
||||
defineProperties(Constructor.prototype,protoProps);if(staticProps)
|
||||
defineProperties(Constructor,staticProps);return Constructor;};}();function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError('Cannot call a class as a function');}}
|
||||
(function(){'use strict';function emit(listeners,...args){const type=args.shift();if(listeners['*']){listeners['*'].slice().forEach(listener=>listener.apply(this,args));}
|
||||
if(listeners[type]){listeners[type].slice().forEach(listener=>listener.apply(this,args));}}
|
||||
function addEventListener(listeners,type,listener){if(!(type in listeners)){listeners[type]=[];}
|
||||
listeners[type].push(listener);}
|
||||
function removeEventListener(listeners,type,listener){const typeListeners=listeners[type];const pos=typeListeners.indexOf(listener);if(pos===-1){return;}
|
||||
typeListeners.splice(pos,1);}
|
||||
let Client=function(){function Client(remote){_classCallCheck(this,Client);this.id=this;this.remote=remote;const listeners={};this.on=(...args)=>addEventListener(listeners,...args);this.emit=(...args)=>emit(listeners,...args);}
|
||||
_createClass(Client,[{key:'method',value:function method(name,...args){return this.remote[name](...args);}}]);return Client;}();function broadcast(type,data){Array.from(this.ctxs.keys()).forEach(client=>client.emit(type,data));}
|
||||
function L10nError(message,id,lang){this.name='L10nError';this.message=message;this.id=id;this.lang=lang;}
|
||||
L10nError.prototype=Object.create(Error.prototype);L10nError.prototype.constructor=L10nError;function load(type,url){return new Promise(function(resolve,reject){const xhr=new XMLHttpRequest();if(xhr.overrideMimeType){xhr.overrideMimeType(type);}
|
||||
xhr.open('GET',url,true);if(type==='application/json'){xhr.responseType='json';}
|
||||
xhr.addEventListener('load',function io_onload(e){if(e.target.status===200||e.target.status===0){resolve(e.target.response||e.target.responseText);}else{reject(new L10nError('Not found: '+url));}});xhr.addEventListener('error',reject);xhr.addEventListener('timeout',reject);try{xhr.send(null);}catch(e){if(e.name==='NS_ERROR_FILE_NOT_FOUND'){reject(new L10nError('Not found: '+url));}else{throw e;}}});}
|
||||
const io={extra:function(code,ver,path,type){return navigator.mozApps.getLocalizationResource(code,ver,path,type);},app:function(code,ver,path,type){switch(type){case'text':return load('text/plain',path);case'json':return load('application/json',path);default:throw new L10nError('Unknown file type: '+type);}}};function fetchResource(res,{code,src,ver}){const url=res.replace('{locale}',code);const type=res.endsWith('.json')?'json':'text';return io[src](code,ver,url,type);}
|
||||
const KNOWN_MACROS=['plural'];const MAX_PLACEABLE_LENGTH=2500;const FSI='\u2068';const PDI='\u2069';const resolutionChain=new WeakSet();function format(ctx,lang,args,entity){if(typeof entity==='string'){return[{},entity];}
|
||||
if(resolutionChain.has(entity)){throw new L10nError('Cyclic reference detected');}
|
||||
resolutionChain.add(entity);let rv;try{rv=resolveValue({},ctx,lang,args,entity.value,entity.index);}finally{resolutionChain.delete(entity);}
|
||||
return rv;}
|
||||
function resolveIdentifier(ctx,lang,args,id){if(KNOWN_MACROS.indexOf(id)>-1){return[{},ctx._getMacro(lang,id)];}
|
||||
if(args&&args.hasOwnProperty(id)){if(typeof args[id]==='string'||typeof args[id]==='number'&&!isNaN(args[id])){return[{},args[id]];}else{throw new L10nError('Arg must be a string or a number: '+id);}}
|
||||
if(id==='__proto__'){throw new L10nError('Illegal id: '+id);}
|
||||
const entity=ctx._getEntity(lang,id);if(entity){return format(ctx,lang,args,entity);}
|
||||
throw new L10nError('Unknown reference: '+id);}
|
||||
function subPlaceable(locals,ctx,lang,args,id){let newLocals,value;try{[newLocals,value]=resolveIdentifier(ctx,lang,args,id);}catch(err){return[{error:err},FSI+'{{ '+id+' }}'+PDI];}
|
||||
if(typeof value==='number'){const formatter=ctx._getNumberFormatter(lang);return[newLocals,formatter.format(value)];}
|
||||
if(typeof value==='string'){if(value.length>=MAX_PLACEABLE_LENGTH){throw new L10nError('Too many characters in placeable ('+value.length+', max allowed is '+MAX_PLACEABLE_LENGTH+')');}
|
||||
return[newLocals,FSI+value+PDI];}
|
||||
return[{},FSI+'{{ '+id+' }}'+PDI];}
|
||||
function interpolate(locals,ctx,lang,args,arr){return arr.reduce(function([localsSeq,valueSeq],cur){if(typeof cur==='string'){return[localsSeq,valueSeq+cur];}else{const[,value]=subPlaceable(locals,ctx,lang,args,cur.name);return[localsSeq,valueSeq+value];}},[locals,'']);}
|
||||
function resolveSelector(ctx,lang,args,expr,index){let selectorName;if(index[0].type==='call'&&index[0].expr.type==='prop'&&index[0].expr.expr.name==='cldr'){selectorName='plural';}else{selectorName=index[0].name;}
|
||||
const selector=resolveIdentifier(ctx,lang,args,selectorName)[1];if(typeof selector!=='function'){return selector;}
|
||||
const argValue=index[0].args?resolveIdentifier(ctx,lang,args,index[0].args[0].name)[1]:undefined;if(selectorName==='plural'){if(argValue===0&&'zero'in expr){return'zero';}
|
||||
if(argValue===1&&'one'in expr){return'one';}
|
||||
if(argValue===2&&'two'in expr){return'two';}}
|
||||
return selector(argValue);}
|
||||
function resolveValue(locals,ctx,lang,args,expr,index){if(!expr){return[locals,expr];}
|
||||
if(typeof expr==='string'||typeof expr==='boolean'||typeof expr==='number'){return[locals,expr];}
|
||||
if(Array.isArray(expr)){return interpolate(locals,ctx,lang,args,expr);}
|
||||
if(index){const selector=resolveSelector(ctx,lang,args,expr,index);if(selector in expr){return resolveValue(locals,ctx,lang,args,expr[selector]);}}
|
||||
const defaultKey=expr.__default||'other';if(defaultKey in expr){return resolveValue(locals,ctx,lang,args,expr[defaultKey]);}
|
||||
throw new L10nError('Unresolvable value');}
|
||||
const locales2rules={'af':3,'ak':4,'am':4,'ar':1,'asa':3,'az':0,'be':11,'bem':3,'bez':3,'bg':3,'bh':4,'bm':0,'bn':3,'bo':0,'br':20,'brx':3,'bs':11,'ca':3,'cgg':3,'chr':3,'cs':12,'cy':17,'da':3,'de':3,'dv':3,'dz':0,'ee':3,'el':3,'en':3,'eo':3,'es':3,'et':3,'eu':3,'fa':0,'ff':5,'fi':3,'fil':4,'fo':3,'fr':5,'fur':3,'fy':3,'ga':8,'gd':24,'gl':3,'gsw':3,'gu':3,'guw':4,'gv':23,'ha':3,'haw':3,'he':2,'hi':4,'hr':11,'hu':0,'id':0,'ig':0,'ii':0,'is':3,'it':3,'iu':7,'ja':0,'jmc':3,'jv':0,'ka':0,'kab':5,'kaj':3,'kcg':3,'kde':0,'kea':0,'kk':3,'kl':3,'km':0,'kn':0,'ko':0,'ksb':3,'ksh':21,'ku':3,'kw':7,'lag':18,'lb':3,'lg':3,'ln':4,'lo':0,'lt':10,'lv':6,'mas':3,'mg':4,'mk':16,'ml':3,'mn':3,'mo':9,'mr':3,'ms':0,'mt':15,'my':0,'nah':3,'naq':7,'nb':3,'nd':3,'ne':3,'nl':3,'nn':3,'no':3,'nr':3,'nso':4,'ny':3,'nyn':3,'om':3,'or':3,'pa':3,'pap':3,'pl':13,'ps':3,'pt':3,'rm':3,'ro':9,'rof':3,'ru':11,'rwk':3,'sah':0,'saq':3,'se':7,'seh':3,'ses':0,'sg':0,'sh':11,'shi':19,'sk':12,'sl':14,'sma':7,'smi':7,'smj':7,'smn':7,'sms':7,'sn':3,'so':3,'sq':3,'sr':11,'ss':3,'ssy':3,'st':3,'sv':3,'sw':3,'syr':3,'ta':3,'te':3,'teo':3,'th':0,'ti':4,'tig':3,'tk':3,'tl':4,'tn':3,'to':0,'tr':0,'ts':3,'tzm':22,'uk':11,'ur':3,'ve':3,'vi':0,'vun':3,'wa':4,'wae':3,'wo':0,'xh':3,'xog':3,'yo':0,'zh':0,'zu':3};function isIn(n,list){return list.indexOf(n)!==-1;}
|
||||
function isBetween(n,start,end){return typeof n===typeof start&&start<=n&&n<=end;}
|
||||
const pluralRules={'0':function(){return'other';},'1':function(n){if(isBetween(n%100,3,10)){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if(isBetween(n%100,11,99)){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'2':function(n){if(n!==0&&n%10===0){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'3':function(n){if(n===1){return'one';}
|
||||
return'other';},'4':function(n){if(isBetween(n,0,1)){return'one';}
|
||||
return'other';},'5':function(n){if(isBetween(n,0,2)&&n!==2){return'one';}
|
||||
return'other';},'6':function(n){if(n===0){return'zero';}
|
||||
if(n%10===1&&n%100!==11){return'one';}
|
||||
return'other';},'7':function(n){if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'8':function(n){if(isBetween(n,3,6)){return'few';}
|
||||
if(isBetween(n,7,10)){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'9':function(n){if(n===0||n!==1&&isBetween(n%100,1,19)){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'10':function(n){if(isBetween(n%10,2,9)&&!isBetween(n%100,11,19)){return'few';}
|
||||
if(n%10===1&&!isBetween(n%100,11,19)){return'one';}
|
||||
return'other';},'11':function(n){if(isBetween(n%10,2,4)&&!isBetween(n%100,12,14)){return'few';}
|
||||
if(n%10===0||isBetween(n%10,5,9)||isBetween(n%100,11,14)){return'many';}
|
||||
if(n%10===1&&n%100!==11){return'one';}
|
||||
return'other';},'12':function(n){if(isBetween(n,2,4)){return'few';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'13':function(n){if(isBetween(n%10,2,4)&&!isBetween(n%100,12,14)){return'few';}
|
||||
if(n!==1&&isBetween(n%10,0,1)||isBetween(n%10,5,9)||isBetween(n%100,12,14)){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'14':function(n){if(isBetween(n%100,3,4)){return'few';}
|
||||
if(n%100===2){return'two';}
|
||||
if(n%100===1){return'one';}
|
||||
return'other';},'15':function(n){if(n===0||isBetween(n%100,2,10)){return'few';}
|
||||
if(isBetween(n%100,11,19)){return'many';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'16':function(n){if(n%10===1&&n!==11){return'one';}
|
||||
return'other';},'17':function(n){if(n===3){return'few';}
|
||||
if(n===0){return'zero';}
|
||||
if(n===6){return'many';}
|
||||
if(n===2){return'two';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'18':function(n){if(n===0){return'zero';}
|
||||
if(isBetween(n,0,2)&&n!==0&&n!==2){return'one';}
|
||||
return'other';},'19':function(n){if(isBetween(n,2,10)){return'few';}
|
||||
if(isBetween(n,0,1)){return'one';}
|
||||
return'other';},'20':function(n){if((isBetween(n%10,3,4)||n%10===9)&&!(isBetween(n%100,10,19)||isBetween(n%100,70,79)||isBetween(n%100,90,99))){return'few';}
|
||||
if(n%1000000===0&&n!==0){return'many';}
|
||||
if(n%10===2&&!isIn(n%100,[12,72,92])){return'two';}
|
||||
if(n%10===1&&!isIn(n%100,[11,71,91])){return'one';}
|
||||
return'other';},'21':function(n){if(n===0){return'zero';}
|
||||
if(n===1){return'one';}
|
||||
return'other';},'22':function(n){if(isBetween(n,0,1)||isBetween(n,11,99)){return'one';}
|
||||
return'other';},'23':function(n){if(isBetween(n%10,1,2)||n%20===0){return'one';}
|
||||
return'other';},'24':function(n){if(isBetween(n,3,10)||isBetween(n,13,19)){return'few';}
|
||||
if(isIn(n,[2,12])){return'two';}
|
||||
if(isIn(n,[1,11])){return'one';}
|
||||
return'other';}};function getPluralRule(code){const index=locales2rules[code.replace(/-.*$/,'')];if(!(index in pluralRules)){return function(){return'other';};}
|
||||
return pluralRules[index];}
|
||||
const L20nIntl=typeof Intl!=='undefined'?Intl:{NumberFormat:function(){return{format:function(v){return v;}};}};let Context=function(){function Context(env,langs,resIds){_classCallCheck(this,Context);this.langs=langs;this.resIds=resIds;this.env=env;this.emit=(type,evt)=>env.emit(type,evt,this);}
|
||||
_createClass(Context,[{key:'_formatTuple',value:function _formatTuple(lang,args,entity,id,key){try{return format(this,lang,args,entity);}catch(err){err.id=key?id+'::'+key:id;err.lang=lang;this.emit('resolveerror',err);return[{error:err},err.id];}}},{key:'_formatEntity',value:function _formatEntity(lang,args,entity,id){const[,value]=this._formatTuple(lang,args,entity,id);const formatted={value,attrs:null};if(entity.attrs){formatted.attrs=Object.create(null);for(let key in entity.attrs){const[,attrValue]=this._formatTuple(lang,args,entity.attrs[key],id,key);formatted.attrs[key]=attrValue;}}
|
||||
return formatted;}},{key:'_formatValue',value:function _formatValue(lang,args,entity,id){return this._formatTuple(lang,args,entity,id)[1];}},{key:'fetch',value:function fetch(langs=this.langs){if(langs.length===0){return Promise.resolve(langs);}
|
||||
return Promise.all(this.resIds.map(resId=>this.env._getResource(langs[0],resId))).then(()=>langs);}},{key:'_resolve',value:function _resolve(langs,keys,formatter,prevResolved){const lang=langs[0];if(!lang){return reportMissing.call(this,keys,formatter,prevResolved);}
|
||||
let hasUnresolved=false;const resolved=keys.map((key,i)=>{if(prevResolved&&prevResolved[i]!==undefined){return prevResolved[i];}
|
||||
const[id,args]=Array.isArray(key)?key:[key,undefined];const entity=this._getEntity(lang,id);if(entity){return formatter.call(this,lang,args,entity,id);}
|
||||
this.emit('notfounderror',new L10nError('"'+id+'"'+' not found in '+lang.code,id,lang));hasUnresolved=true;});if(!hasUnresolved){return resolved;}
|
||||
return this.fetch(langs.slice(1)).then(nextLangs=>this._resolve(nextLangs,keys,formatter,resolved));}},{key:'formatEntities',value:function formatEntities(...keys){return this.fetch().then(langs=>this._resolve(langs,keys,this._formatEntity));}},{key:'formatValues',value:function formatValues(...keys){return this.fetch().then(langs=>this._resolve(langs,keys,this._formatValue));}},{key:'_getEntity',value:function _getEntity(lang,id){const cache=this.env.resCache;for(let i=0,resId;resId=this.resIds[i];i++){const resource=cache.get(resId+lang.code+lang.src);if(resource instanceof L10nError){continue;}
|
||||
if(id in resource){return resource[id];}}
|
||||
return undefined;}},{key:'_getNumberFormatter',value:function _getNumberFormatter(lang){if(!this.env.numberFormatters){this.env.numberFormatters=new Map();}
|
||||
if(!this.env.numberFormatters.has(lang)){const formatter=L20nIntl.NumberFormat(lang);this.env.numberFormatters.set(lang,formatter);return formatter;}
|
||||
return this.env.numberFormatters.get(lang);}},{key:'_getMacro',value:function _getMacro(lang,id){switch(id){case'plural':return getPluralRule(lang.code);default:return undefined;}}}]);return Context;}();function reportMissing(keys,formatter,resolved){const missingIds=new Set();keys.forEach((key,i)=>{if(resolved&&resolved[i]!==undefined){return;}
|
||||
const id=Array.isArray(key)?key[0]:key;missingIds.add(id);resolved[i]=formatter===this._formatValue?id:{value:id,attrs:null};});this.emit('notfounderror',new L10nError('"'+Array.from(missingIds).join(', ')+'"'+' not found in any language',missingIds));return resolved;}
|
||||
var MAX_PLACEABLES=100;var PropertiesParser={patterns:null,entryIds:null,emit:null,init:function(){this.patterns={comment:/^\s*#|^\s*$/,entity:/^([^=\s]+)\s*=\s*(.*)$/,multiline:/[^\\]\\$/,index:/\{\[\s*(\w+)(?:\(([^\)]*)\))?\s*\]\}/i,unicode:/\\u([0-9a-fA-F]{1,4})/g,entries:/[^\r\n]+/g,controlChars:/\\([\\\n\r\t\b\f\{\}\"\'])/g,placeables:/\{\{\s*([^\s]*?)\s*\}\}/};},parse:function(emit,source){if(!this.patterns){this.init();}
|
||||
this.emit=emit;var entries={};var lines=source.match(this.patterns.entries);if(!lines){return entries;}
|
||||
for(var i=0;i<lines.length;i++){var line=lines[i];if(this.patterns.comment.test(line)){continue;}
|
||||
while(this.patterns.multiline.test(line)&&i<lines.length){line=line.slice(0,-1)+lines[++i].trim();}
|
||||
var entityMatch=line.match(this.patterns.entity);if(entityMatch){try{this.parseEntity(entityMatch[1],entityMatch[2],entries);}catch(e){if(!this.emit){throw e;}}}}
|
||||
return entries;},parseEntity:function(id,value,entries){var name,key;var pos=id.indexOf('[');if(pos!==-1){name=id.substr(0,pos);key=id.substring(pos+1,id.length-1);}else{name=id;key=null;}
|
||||
var nameElements=name.split('.');if(nameElements.length>2){throw this.error('Error in ID: "'+name+'".'+' Nested attributes are not supported.');}
|
||||
var attr;if(nameElements.length>1){name=nameElements[0];attr=nameElements[1];if(attr[0]==='$'){throw this.error('Attribute can\'t start with "$"');}}else{attr=null;}
|
||||
this.setEntityValue(name,attr,key,this.unescapeString(value),entries);},setEntityValue:function(id,attr,key,rawValue,entries){var value=rawValue.indexOf('{{')>-1?this.parseString(rawValue):rawValue;var isSimpleValue=typeof value==='string';var root=entries;var isSimpleNode=typeof entries[id]==='string';if(!entries[id]&&(attr||key||!isSimpleValue)){entries[id]=Object.create(null);isSimpleNode=false;}
|
||||
if(attr){if(isSimpleNode){const val=entries[id];entries[id]=Object.create(null);entries[id].value=val;}
|
||||
if(!entries[id].attrs){entries[id].attrs=Object.create(null);}
|
||||
if(!entries[id].attrs&&!isSimpleValue){entries[id].attrs[attr]=Object.create(null);}
|
||||
root=entries[id].attrs;id=attr;}
|
||||
if(key){isSimpleNode=false;if(typeof root[id]==='string'){const val=root[id];root[id]=Object.create(null);root[id].index=this.parseIndex(val);root[id].value=Object.create(null);}
|
||||
root=root[id].value;id=key;isSimpleValue=true;}
|
||||
if(isSimpleValue){if(id in root){throw this.error('Duplicated id: '+id);}
|
||||
root[id]=value;}else{if(!root[id]){root[id]=Object.create(null);}
|
||||
root[id].value=value;}},parseString:function(str){var chunks=str.split(this.patterns.placeables);var complexStr=[];var len=chunks.length;var placeablesCount=(len-1)/2;if(placeablesCount>=MAX_PLACEABLES){throw this.error('Too many placeables ('+placeablesCount+', max allowed is '+MAX_PLACEABLES+')');}
|
||||
for(var i=0;i<chunks.length;i++){if(chunks[i].length===0){continue;}
|
||||
if(i%2===1){complexStr.push({type:'idOrVar',name:chunks[i]});}else{complexStr.push(chunks[i]);}}
|
||||
return complexStr;},unescapeString:function(str){if(str.lastIndexOf('\\')!==-1){str=str.replace(this.patterns.controlChars,'$1');}
|
||||
return str.replace(this.patterns.unicode,function(match,token){return String.fromCodePoint(parseInt(token,16));});},parseIndex:function(str){var match=str.match(this.patterns.index);if(!match){throw new L10nError('Malformed index');}
|
||||
if(match[2]){return[{type:'call',expr:{type:'prop',expr:{type:'glob',name:'cldr'},prop:'plural',cmpt:false},args:[{type:'idOrVar',name:match[2]}]}];}else{return[{type:'idOrVar',name:match[1]}];}},error:function(msg,type='parsererror'){const err=new L10nError(msg);if(this.emit){this.emit(type,err);}
|
||||
return err;}};const MAX_PLACEABLES$1=100;var L20nParser={parse:function(emit,string){this._source=string;this._index=0;this._length=string.length;this.entries=Object.create(null);this.emit=emit;return this.getResource();},getResource:function(){this.getWS();while(this._index<this._length){try{this.getEntry();}catch(e){if(e instanceof L10nError){this.getJunkEntry();if(!this.emit){throw e;}}else{throw e;}}
|
||||
if(this._index<this._length){this.getWS();}}
|
||||
return this.entries;},getEntry:function(){if(this._source[this._index]==='<'){++this._index;const id=this.getIdentifier();if(this._source[this._index]==='['){++this._index;return this.getEntity(id,this.getItemList(this.getExpression,']'));}
|
||||
return this.getEntity(id);}
|
||||
if(this._source.startsWith('/*',this._index)){return this.getComment();}
|
||||
throw this.error('Invalid entry');},getEntity:function(id,index){if(!this.getRequiredWS()){throw this.error('Expected white space');}
|
||||
const ch=this._source[this._index];const hasIndex=index!==undefined;const value=this.getValue(ch,hasIndex,hasIndex);let attrs;if(value===undefined){if(ch==='>'){throw this.error('Expected ">"');}
|
||||
attrs=this.getAttributes();}else{const ws1=this.getRequiredWS();if(this._source[this._index]!=='>'){if(!ws1){throw this.error('Expected ">"');}
|
||||
attrs=this.getAttributes();}}
|
||||
++this._index;if(id in this.entries){throw this.error('Duplicate entry ID "'+id,'duplicateerror');}
|
||||
if(!attrs&&!index&&typeof value==='string'){this.entries[id]=value;}else{this.entries[id]={value,attrs,index};}},getValue:function(ch=this._source[this._index],index=false,required=true){switch(ch){case'\'':case'"':return this.getString(ch,1);case'{':return this.getHash(index);}
|
||||
if(required){throw this.error('Unknown value type');}
|
||||
return;},getWS:function(){let cc=this._source.charCodeAt(this._index);while(cc===32||cc===10||cc===9||cc===13){cc=this._source.charCodeAt(++this._index);}},getRequiredWS:function(){const pos=this._index;let cc=this._source.charCodeAt(pos);while(cc===32||cc===10||cc===9||cc===13){cc=this._source.charCodeAt(++this._index);}
|
||||
return this._index!==pos;},getIdentifier:function(){const start=this._index;let cc=this._source.charCodeAt(this._index);if(cc>=97&&cc<=122||cc>=65&&cc<=90||cc===95){cc=this._source.charCodeAt(++this._index);}else{throw this.error('Identifier has to start with [a-zA-Z_]');}
|
||||
while(cc>=97&&cc<=122||cc>=65&&cc<=90||cc>=48&&cc<=57||cc===95){cc=this._source.charCodeAt(++this._index);}
|
||||
return this._source.slice(start,this._index);},getUnicodeChar:function(){for(let i=0;i<4;i++){let cc=this._source.charCodeAt(++this._index);if(cc>96&&cc<103||cc>64&&cc<71||cc>47&&cc<58){continue;}
|
||||
throw this.error('Illegal unicode escape sequence');}
|
||||
this._index++;return String.fromCharCode(parseInt(this._source.slice(this._index-4,this._index),16));},stringRe:/"|'|{{|\\/g,getString:function(opchar,opcharLen){const body=[];let placeables=0;this._index+=opcharLen;const start=this._index;let bufStart=start;let buf='';while(true){this.stringRe.lastIndex=this._index;const match=this.stringRe.exec(this._source);if(!match){throw this.error('Unclosed string literal');}
|
||||
if(match[0]==='"'||match[0]==='\''){if(match[0]!==opchar){this._index+=opcharLen;continue;}
|
||||
this._index=match.index+opcharLen;break;}
|
||||
if(match[0]==='{{'){if(placeables>MAX_PLACEABLES$1-1){throw this.error('Too many placeables, maximum allowed is '+MAX_PLACEABLES$1);}
|
||||
placeables++;if(match.index>bufStart||buf.length>0){body.push(buf+this._source.slice(bufStart,match.index));buf='';}
|
||||
this._index=match.index+2;this.getWS();body.push(this.getExpression());this.getWS();this._index+=2;bufStart=this._index;continue;}
|
||||
if(match[0]==='\\'){this._index=match.index+1;const ch2=this._source[this._index];if(ch2==='u'){buf+=this._source.slice(bufStart,match.index)+this.getUnicodeChar();}else if(ch2===opchar||ch2==='\\'){buf+=this._source.slice(bufStart,match.index)+ch2;this._index++;}else if(this._source.startsWith('{{',this._index)){buf+=this._source.slice(bufStart,match.index)+'{{';this._index+=2;}else{throw this.error('Illegal escape sequence');}
|
||||
bufStart=this._index;}}
|
||||
if(body.length===0){return buf+this._source.slice(bufStart,this._index-opcharLen);}
|
||||
if(this._index-opcharLen>bufStart||buf.length>0){body.push(buf+this._source.slice(bufStart,this._index-opcharLen));}
|
||||
return body;},getAttributes:function(){const attrs=Object.create(null);while(true){this.getAttribute(attrs);const ws1=this.getRequiredWS();const ch=this._source.charAt(this._index);if(ch==='>'){break;}else if(!ws1){throw this.error('Expected ">"');}}
|
||||
return attrs;},getAttribute:function(attrs){const key=this.getIdentifier();let index;if(this._source[this._index]==='['){++this._index;this.getWS();index=this.getItemList(this.getExpression,']');}
|
||||
this.getWS();if(this._source[this._index]!==':'){throw this.error('Expected ":"');}
|
||||
++this._index;this.getWS();const hasIndex=index!==undefined;const value=this.getValue(undefined,hasIndex);if(key in attrs){throw this.error('Duplicate attribute "'+key,'duplicateerror');}
|
||||
if(!index&&typeof value==='string'){attrs[key]=value;}else{attrs[key]={value,index};}},getHash:function(index){const items=Object.create(null);++this._index;this.getWS();let defKey;while(true){const[key,value,def]=this.getHashItem();items[key]=value;if(def){if(defKey){throw this.error('Default item redefinition forbidden');}
|
||||
defKey=key;}
|
||||
this.getWS();const comma=this._source[this._index]===',';if(comma){++this._index;this.getWS();}
|
||||
if(this._source[this._index]==='}'){++this._index;break;}
|
||||
if(!comma){throw this.error('Expected "}"');}}
|
||||
if(defKey){items.__default=defKey;}else if(!index){throw this.error('Unresolvable Hash Value');}
|
||||
return items;},getHashItem:function(){let defItem=false;if(this._source[this._index]==='*'){++this._index;defItem=true;}
|
||||
const key=this.getIdentifier();this.getWS();if(this._source[this._index]!==':'){throw this.error('Expected ":"');}
|
||||
++this._index;this.getWS();return[key,this.getValue(),defItem];},getComment:function(){this._index+=2;const start=this._index;const end=this._source.indexOf('*/',start);if(end===-1){throw this.error('Comment without a closing tag');}
|
||||
this._index=end+2;},getExpression:function(){let exp=this.getPrimaryExpression();while(true){let ch=this._source[this._index];if(ch==='.'||ch==='['){++this._index;exp=this.getPropertyExpression(exp,ch==='[');}else if(ch==='('){++this._index;exp=this.getCallExpression(exp);}else{break;}}
|
||||
return exp;},getPropertyExpression:function(idref,computed){let exp;if(computed){this.getWS();exp=this.getExpression();this.getWS();if(this._source[this._index]!==']'){throw this.error('Expected "]"');}
|
||||
++this._index;}else{exp=this.getIdentifier();}
|
||||
return{type:'prop',expr:idref,prop:exp,cmpt:computed};},getCallExpression:function(callee){this.getWS();return{type:'call',expr:callee,args:this.getItemList(this.getExpression,')')};},getPrimaryExpression:function(){const ch=this._source[this._index];switch(ch){case'$':++this._index;return{type:'var',name:this.getIdentifier()};case'@':++this._index;return{type:'glob',name:this.getIdentifier()};default:return{type:'id',name:this.getIdentifier()};}},getItemList:function(callback,closeChar){const items=[];let closed=false;this.getWS();if(this._source[this._index]===closeChar){++this._index;closed=true;}
|
||||
while(!closed){items.push(callback.call(this));this.getWS();let ch=this._source.charAt(this._index);switch(ch){case',':++this._index;this.getWS();break;case closeChar:++this._index;closed=true;break;default:throw this.error('Expected "," or "'+closeChar+'"');}}
|
||||
return items;},getJunkEntry:function(){const pos=this._index;let nextEntity=this._source.indexOf('<',pos);let nextComment=this._source.indexOf('/*',pos);if(nextEntity===-1){nextEntity=this._length;}
|
||||
if(nextComment===-1){nextComment=this._length;}
|
||||
let nextEntry=Math.min(nextEntity,nextComment);this._index=nextEntry;},error:function(message,type='parsererror'){const pos=this._index;let start=this._source.lastIndexOf('<',pos-1);const lastClose=this._source.lastIndexOf('>',pos-1);start=lastClose>start?lastClose+1:start;const context=this._source.slice(start,pos+10);const msg=message+' at pos '+pos+': `'+context+'`';const err=new L10nError(msg);if(this.emit){this.emit(type,err);}
|
||||
return err;}};function walkEntry(entry,fn){if(typeof entry==='string'){return fn(entry);}
|
||||
const newEntry=Object.create(null);if(entry.value){newEntry.value=walkValue(entry.value,fn);}
|
||||
if(entry.index){newEntry.index=entry.index;}
|
||||
if(entry.attrs){newEntry.attrs=Object.create(null);for(let key in entry.attrs){newEntry.attrs[key]=walkEntry(entry.attrs[key],fn);}}
|
||||
return newEntry;}
|
||||
function walkValue(value,fn){if(typeof value==='string'){return fn(value);}
|
||||
if(value.type){return value;}
|
||||
const newValue=Array.isArray(value)?[]:Object.create(null);const keys=Object.keys(value);for(let i=0,key;key=keys[i];i++){newValue[key]=walkValue(value[key],fn);}
|
||||
return newValue;}
|
||||
function createGetter(id,name){let _pseudo=null;return function getPseudo(){if(_pseudo){return _pseudo;}
|
||||
const reAlphas=/[a-zA-Z]/g;const reVowels=/[aeiouAEIOU]/g;const reWords=/[^\W0-9_]+/g;const reExcluded=/(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/;const charMaps={'fr-x-psaccent':'ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ[\\]^_`ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ','ar-x-psbidi':'\u2200ԐↃpƎɟפHIſӼ\u02E5WNOԀÒᴚS\u22A5\u2229ɅMXʎZ[\\]ᵥ_,ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz'};const mods={'fr-x-psaccent':val=>val.replace(reVowels,match=>match+match.toLowerCase()),'ar-x-psbidi':val=>val.replace(reWords,match=>'\u202E'+match+'\u202C')};const replaceChars=(map,val)=>val.replace(reAlphas,match=>map.charAt(match.charCodeAt(0)-65));const transform=val=>replaceChars(charMaps[id],mods[id](val));const apply=(fn,val)=>{if(!val){return val;}
|
||||
const parts=val.split(reExcluded);const modified=parts.map(function(part){if(reExcluded.test(part)){return part;}
|
||||
return fn(part);});return modified.join('');};return _pseudo={name:transform(name),process:str=>apply(transform,str)};};}
|
||||
const pseudo=Object.defineProperties(Object.create(null),{'fr-x-psaccent':{enumerable:true,get:createGetter('fr-x-psaccent','Runtime Accented')},'ar-x-psbidi':{enumerable:true,get:createGetter('ar-x-psbidi','Runtime Bidi')}});let Env=function(){function Env(fetchResource){_classCallCheck(this,Env);this.fetchResource=fetchResource;this.resCache=new Map();this.resRefs=new Map();this.numberFormatters=null;this.parsers={properties:PropertiesParser,l20n:L20nParser};const listeners={};this.emit=emit.bind(this,listeners);this.addEventListener=addEventListener.bind(this,listeners);this.removeEventListener=removeEventListener.bind(this,listeners);}
|
||||
_createClass(Env,[{key:'createContext',value:function createContext(langs,resIds){const ctx=new Context(this,langs,resIds);resIds.forEach(resId=>{const usedBy=this.resRefs.get(resId)||0;this.resRefs.set(resId,usedBy+1);});return ctx;}},{key:'destroyContext',value:function destroyContext(ctx){ctx.resIds.forEach(resId=>{const usedBy=this.resRefs.get(resId)||0;if(usedBy>1){return this.resRefs.set(resId,usedBy-1);}
|
||||
this.resRefs.delete(resId);this.resCache.forEach((val,key)=>key.startsWith(resId)?this.resCache.delete(key):null);});}},{key:'_parse',value:function _parse(syntax,lang,data){const parser=this.parsers[syntax];if(!parser){return data;}
|
||||
const emit=(type,err)=>this.emit(type,amendError(lang,err));return parser.parse.call(parser,emit,data);}},{key:'_create',value:function _create(lang,entries){if(lang.src!=='pseudo'){return entries;}
|
||||
const pseudoentries=Object.create(null);for(let key in entries){pseudoentries[key]=walkEntry(entries[key],pseudo[lang.code].process);}
|
||||
return pseudoentries;}},{key:'_getResource',value:function _getResource(lang,res){const cache=this.resCache;const id=res+lang.code+lang.src;if(cache.has(id)){return cache.get(id);}
|
||||
const syntax=res.substr(res.lastIndexOf('.')+1);const saveEntries=data=>{const entries=this._parse(syntax,lang,data);cache.set(id,this._create(lang,entries));};const recover=err=>{err.lang=lang;this.emit('fetcherror',err);cache.set(id,err);};const langToFetch=lang.src==='pseudo'?{code:'en-US',src:'app',ver:lang.ver}:lang;const resource=this.fetchResource(res,langToFetch).then(saveEntries,recover);cache.set(id,resource);return resource;}}]);return Env;}();function amendError(lang,err){err.lang=lang;return err;}
|
||||
function prioritizeLocales(def,availableLangs,requested){let supportedLocale;for(let i=0;i<requested.length;i++){const locale=requested[i];if(availableLangs.indexOf(locale)!==-1){supportedLocale=locale;break;}}
|
||||
if(!supportedLocale||supportedLocale===def){return[def];}
|
||||
return[supportedLocale,def];}
|
||||
function negotiateLanguages({appVersion,defaultLang,availableLangs},additionalLangs,prevLangs,requestedLangs){const allAvailableLangs=Object.keys(availableLangs).concat(Object.keys(additionalLangs)).concat(Object.keys(pseudo));const newLangs=prioritizeLocales(defaultLang,allAvailableLangs,requestedLangs);const langs=newLangs.map(code=>({code:code,src:getLangSource(appVersion,availableLangs,additionalLangs,code),ver:appVersion}));return{langs,haveChanged:!arrEqual(prevLangs,newLangs)};}
|
||||
function arrEqual(arr1,arr2){return arr1.length===arr2.length&&arr1.every((elem,i)=>elem===arr2[i]);}
|
||||
function getMatchingLangpack(appVersion,langpacks){for(let i=0,langpack;langpack=langpacks[i];i++){if(langpack.target===appVersion){return langpack;}}
|
||||
return null;}
|
||||
function getLangSource(appVersion,availableLangs,additionalLangs,code){if(additionalLangs&&additionalLangs[code]){const lp=getMatchingLangpack(appVersion,additionalLangs[code]);if(lp&&(!(code in availableLangs)||parseInt(lp.revision)>availableLangs[code])){return'extra';}}
|
||||
if(code in pseudo&&!(code in availableLangs)){return'pseudo';}
|
||||
return'app';}
|
||||
let Remote=function(){function Remote(fetchResource,broadcast){_classCallCheck(this,Remote);this.broadcast=broadcast;this.env=new Env(fetchResource);this.ctxs=new Map();}
|
||||
_createClass(Remote,[{key:'registerView',value:function registerView(view,resources,meta,additionalLangs,requestedLangs){const{langs}=negotiateLanguages(meta,additionalLangs,[],requestedLangs);this.ctxs.set(view,this.env.createContext(langs,resources));return langs;}},{key:'unregisterView',value:function unregisterView(view){this.ctxs.delete(view);return true;}},{key:'formatEntities',value:function formatEntities(view,keys){return this.ctxs.get(view).formatEntities(...keys);}},{key:'formatValues',value:function formatValues(view,keys){return this.ctxs.get(view).formatValues(...keys);}},{key:'changeLanguages',value:function changeLanguages(view,meta,additionalLangs,requestedLangs){const oldCtx=this.ctxs.get(view);const prevLangs=oldCtx.langs;const newLangs=negotiateLanguages(meta,additionalLangs,prevLangs,requestedLangs);this.ctxs.set(view,this.env.createContext(newLangs.langs,oldCtx.resIds));return newLangs;}},{key:'requestLanguages',value:function requestLanguages(requestedLangs){this.broadcast('languageschangerequest',requestedLangs);}},{key:'getName',value:function getName(code){return pseudo[code].name;}},{key:'processString',value:function processString(code,str){return pseudo[code].process(str);}}]);return Remote;}();const observerConfig={attributes:true,characterData:false,childList:true,subtree:true,attributeFilter:['data-l10n-id','data-l10n-args']};const observers=new WeakMap();function initMutationObserver(view){observers.set(view,{roots:new Set(),observer:new MutationObserver(mutations=>translateMutations(view,mutations))});}
|
||||
function translateRoots(view){return Promise.all([...observers.get(view).roots].map(root=>_translateFragment(view,root)));}
|
||||
function observe(view,root){const obs=observers.get(view);if(obs){obs.roots.add(root);obs.observer.observe(root,observerConfig);}}
|
||||
function disconnect(view,root,allRoots){const obs=observers.get(view);if(obs){obs.observer.disconnect();if(allRoots){return;}
|
||||
obs.roots.delete(root);obs.roots.forEach(other=>obs.observer.observe(other,observerConfig));}}
|
||||
function reconnect(view){const obs=observers.get(view);if(obs){obs.roots.forEach(root=>obs.observer.observe(root,observerConfig));}}
|
||||
const reOverlay=/<|&#?\w+;/;const allowed={elements:['a','em','strong','small','s','cite','q','dfn','abbr','data','time','code','var','samp','kbd','sub','sup','i','b','u','mark','ruby','rt','rp','bdi','bdo','span','br','wbr'],attributes:{global:['title','aria-label','aria-valuetext','aria-moz-hint'],a:['download'],area:['download','alt'],input:['alt','placeholder'],menuitem:['label'],menu:['label'],optgroup:['label'],option:['label'],track:['label'],img:['alt'],textarea:['placeholder'],th:['abbr']}};function overlayElement(element,translation){const value=translation.value;if(typeof value==='string'){if(!reOverlay.test(value)){element.textContent=value;}else{const tmpl=element.ownerDocument.createElement('template');tmpl.innerHTML=value;overlay(element,tmpl.content);}}
|
||||
for(let key in translation.attrs){const attrName=camelCaseToDashed(key);if(isAttrAllowed({name:attrName},element)){element.setAttribute(attrName,translation.attrs[key]);}}}
|
||||
function overlay(sourceElement,translationElement){const result=translationElement.ownerDocument.createDocumentFragment();let k,attr;let childElement;while(childElement=translationElement.childNodes[0]){translationElement.removeChild(childElement);if(childElement.nodeType===childElement.TEXT_NODE){result.appendChild(childElement);continue;}
|
||||
const index=getIndexOfType(childElement);const sourceChild=getNthElementOfType(sourceElement,childElement,index);if(sourceChild){overlay(sourceChild,childElement);result.appendChild(sourceChild);continue;}
|
||||
if(isElementAllowed(childElement)){const sanitizedChild=childElement.ownerDocument.createElement(childElement.nodeName);overlay(sanitizedChild,childElement);result.appendChild(sanitizedChild);continue;}
|
||||
result.appendChild(translationElement.ownerDocument.createTextNode(childElement.textContent));}
|
||||
sourceElement.textContent='';sourceElement.appendChild(result);if(translationElement.attributes){for(k=0,attr;attr=translationElement.attributes[k];k++){if(isAttrAllowed(attr,sourceElement)){sourceElement.setAttribute(attr.name,attr.value);}}}}
|
||||
function isElementAllowed(element){return allowed.elements.indexOf(element.tagName.toLowerCase())!==-1;}
|
||||
function isAttrAllowed(attr,element){const attrName=attr.name.toLowerCase();const tagName=element.tagName.toLowerCase();if(allowed.attributes.global.indexOf(attrName)!==-1){return true;}
|
||||
if(!allowed.attributes[tagName]){return false;}
|
||||
if(allowed.attributes[tagName].indexOf(attrName)!==-1){return true;}
|
||||
if(tagName==='input'&&attrName==='value'){const type=element.type.toLowerCase();if(type==='submit'||type==='button'||type==='reset'){return true;}}
|
||||
return false;}
|
||||
function getNthElementOfType(context,element,index){let nthOfType=0;for(let i=0,child;child=context.children[i];i++){if(child.nodeType===child.ELEMENT_NODE&&child.tagName===element.tagName){if(nthOfType===index){return child;}
|
||||
nthOfType++;}}
|
||||
return null;}
|
||||
function getIndexOfType(element){let index=0;let child;while(child=element.previousElementSibling){if(child.tagName===element.tagName){index++;}}
|
||||
return index;}
|
||||
function camelCaseToDashed(string){if(string==='ariaValueText'){return'aria-valuetext';}
|
||||
return string.replace(/[A-Z]/g,function(match){return'-'+match.toLowerCase();}).replace(/^-/,'');}
|
||||
const reHtml=/[&<>]/g;const htmlEntities={'&':'&','<':'<','>':'>'};function setAttributes(element,id,args){element.setAttribute('data-l10n-id',id);if(args){element.setAttribute('data-l10n-args',JSON.stringify(args));}}
|
||||
function getAttributes(element){return{id:element.getAttribute('data-l10n-id'),args:JSON.parse(element.getAttribute('data-l10n-args'))};}
|
||||
function getTranslatables(element){const nodes=Array.from(element.querySelectorAll('[data-l10n-id]'));if(typeof element.hasAttribute==='function'&&element.hasAttribute('data-l10n-id')){nodes.push(element);}
|
||||
return nodes;}
|
||||
function translateMutations(view,mutations){const targets=new Set();for(let mutation of mutations){switch(mutation.type){case'attributes':targets.add(mutation.target);break;case'childList':for(let addedNode of mutation.addedNodes){if(addedNode.nodeType===addedNode.ELEMENT_NODE){if(addedNode.childElementCount){getTranslatables(addedNode).forEach(targets.add.bind(targets));}else{if(addedNode.hasAttribute('data-l10n-id')){targets.add(addedNode);}}}}
|
||||
break;}}
|
||||
if(targets.size===0){return;}
|
||||
translateElements(view,Array.from(targets));}
|
||||
function _translateFragment(view,frag){return translateElements(view,getTranslatables(frag));}
|
||||
function getElementsTranslation(view,elems){const keys=elems.map(elem=>{const id=elem.getAttribute('data-l10n-id');const args=elem.getAttribute('data-l10n-args');return args?[id,JSON.parse(args.replace(reHtml,match=>htmlEntities[match]))]:id;});return view.formatEntities(...keys);}
|
||||
function translateElements(view,elements){return getElementsTranslation(view,elements).then(translations=>applyTranslations(view,elements,translations));}
|
||||
function applyTranslations(view,elems,translations){disconnect(view,null,true);for(let i=0;i<elems.length;i++){overlayElement(elems[i],translations[i]);}
|
||||
reconnect(view);}
|
||||
if(typeof NodeList==='function'&&!NodeList.prototype[Symbol.iterator]){NodeList.prototype[Symbol.iterator]=Array.prototype[Symbol.iterator];}
|
||||
function documentReady(){if(document.readyState!=='loading'){return Promise.resolve();}
|
||||
return new Promise(resolve=>{document.addEventListener('readystatechange',function onrsc(){document.removeEventListener('readystatechange',onrsc);resolve();});});}
|
||||
function getDirection(code){const tag=code.split('-')[0];return['ar','he','fa','ps','ur'].indexOf(tag)>=0?'rtl':'ltr';}
|
||||
if(navigator.languages===undefined){navigator.languages=[navigator.language];}
|
||||
function getResourceLinks(head){return Array.prototype.map.call(head.querySelectorAll('link[rel="localization"]'),el=>el.getAttribute('href'));}
|
||||
function getMeta(head){let availableLangs=Object.create(null);let defaultLang=null;let appVersion=null;const metas=Array.from(head.querySelectorAll('meta[name="availableLanguages"],'+'meta[name="defaultLanguage"],'+'meta[name="appVersion"]'));for(let meta of metas){const name=meta.getAttribute('name');const content=meta.getAttribute('content').trim();switch(name){case'availableLanguages':availableLangs=getLangRevisionMap(availableLangs,content);break;case'defaultLanguage':const[lang,rev]=getLangRevisionTuple(content);defaultLang=lang;if(!(lang in availableLangs)){availableLangs[lang]=rev;}
|
||||
break;case'appVersion':appVersion=content;}}
|
||||
return{defaultLang,availableLangs,appVersion};}
|
||||
function getLangRevisionMap(seq,str){return str.split(',').reduce((seq,cur)=>{const[lang,rev]=getLangRevisionTuple(cur);seq[lang]=rev;return seq;},seq);}
|
||||
function getLangRevisionTuple(str){const[lang,rev]=str.trim().split(':');return[lang,parseInt(rev)];}
|
||||
const viewProps=new WeakMap();let View=function(){function View(client,doc){_classCallCheck(this,View);this.pseudo={'fr-x-psaccent':createPseudo(this,'fr-x-psaccent'),'ar-x-psbidi':createPseudo(this,'ar-x-psbidi')};const initialized=documentReady().then(()=>init(this,client));this._interactive=initialized.then(()=>client);this.ready=initialized.then(langs=>translateView(this,langs));initMutationObserver(this);viewProps.set(this,{doc:doc,ready:false});client.on('languageschangerequest',requestedLangs=>this.requestLanguages(requestedLangs));}
|
||||
_createClass(View,[{key:'requestLanguages',value:function requestLanguages(requestedLangs,isGlobal){const method=isGlobal?client=>client.method('requestLanguages',requestedLangs):client=>changeLanguages(this,client,requestedLangs);return this._interactive.then(method);}},{key:'handleEvent',value:function handleEvent(){return this.requestLanguages(navigator.languages);}},{key:'formatEntities',value:function formatEntities(...keys){return this._interactive.then(client=>client.method('formatEntities',client.id,keys));}},{key:'formatValue',value:function formatValue(id,args){return this._interactive.then(client=>client.method('formatValues',client.id,[[id,args]])).then(values=>values[0]);}},{key:'formatValues',value:function formatValues(...keys){return this._interactive.then(client=>client.method('formatValues',client.id,keys));}},{key:'translateFragment',value:function translateFragment(frag){return _translateFragment(this,frag);}},{key:'observeRoot',value:function observeRoot(root){observe(this,root);}},{key:'disconnectRoot',value:function disconnectRoot(root){disconnect(this,root);}}]);return View;}();View.prototype.setAttributes=setAttributes;View.prototype.getAttributes=getAttributes;function createPseudo(view,code){return{getName:()=>view._interactive.then(client=>client.method('getName',code)),processString:str=>view._interactive.then(client=>client.method('processString',code,str))};}
|
||||
function init(view,client){const doc=viewProps.get(view).doc;const resources=getResourceLinks(doc.head);const meta=getMeta(doc.head);view.observeRoot(doc.documentElement);return getAdditionalLanguages().then(additionalLangs=>client.method('registerView',client.id,resources,meta,additionalLangs,navigator.languages));}
|
||||
function changeLanguages(view,client,requestedLangs){const doc=viewProps.get(view).doc;const meta=getMeta(doc.head);return getAdditionalLanguages().then(additionalLangs=>client.method('changeLanguages',client.id,meta,additionalLangs,requestedLangs)).then(({langs,haveChanged})=>haveChanged?translateView(view,langs):undefined);}
|
||||
function getAdditionalLanguages(){if(navigator.mozApps&&navigator.mozApps.getAdditionalLanguages){return navigator.mozApps.getAdditionalLanguages().catch(()=>Object.create(null));}
|
||||
return Promise.resolve(Object.create(null));}
|
||||
function translateView(view,langs){const props=viewProps.get(view);const html=props.doc.documentElement;if(props.ready){return translateRoots(view).then(()=>setAllAndEmit(html,langs));}
|
||||
const translated=langs[0].code===html.getAttribute('lang')?Promise.resolve():translateRoots(view).then(()=>setLangDir(html,langs));return translated.then(()=>{setLangs(html,langs);props.ready=true;});}
|
||||
function setLangs(html,langs){const codes=langs.map(lang=>lang.code);html.setAttribute('langs',codes.join(' '));}
|
||||
function setLangDir(html,langs){const code=langs[0].code;html.setAttribute('lang',code);html.setAttribute('dir',getDirection(code));}
|
||||
function setAllAndEmit(html,langs){setLangDir(html,langs);setLangs(html,langs);html.parentNode.dispatchEvent(new CustomEvent('DOMRetranslated',{bubbles:false,cancelable:false}));}
|
||||
const remote=new Remote(fetchResource,broadcast);const client=new Client(remote);document.l10n=new View(client,document);window.addEventListener('languagechange',document.l10n);document.addEventListener('additionallanguageschange',document.l10n);navigator.mozL10n={setAttributes:document.l10n.setAttributes,getAttributes:document.l10n.getAttributes,formatValue:(...args)=>document.l10n.formatValue(...args),translateFragment:(...args)=>document.l10n.translateFragment(...args),once:cb=>document.l10n.ready.then(cb),ready:cb=>document.l10n.ready.then(()=>{document.addEventListener('DOMRetranslated',cb);cb();})};}());;var LazyLoader=function(){function LazyLoader(){this._loaded={};this._isLoading={};}
|
||||
LazyLoader.prototype={_js:function(file,callback){var script=document.createElement('script');script.src=file;script.async=false;script.addEventListener('load',callback);document.head.appendChild(script);this._isLoading[file]=script;},_css:function(file,callback){var style=document.createElement('link');style.type='text/css';style.rel='stylesheet';style.href=file;document.head.appendChild(style);callback();},_html:function(domNode,callback){if(domNode.getAttribute('is')){this.load(['/shared/js/html_imports.js'],function(){HtmlImports.populate(callback);}.bind(this));return;}
|
||||
for(var i=0;i<domNode.childNodes.length;i++){if(domNode.childNodes[i].nodeType==document.COMMENT_NODE){domNode.innerHTML=domNode.childNodes[i].nodeValue;break;}}
|
||||
window.dispatchEvent(new CustomEvent('lazyload',{detail:domNode}));callback();},getJSON:function(file,mozSystem){return new Promise(function(resolve,reject){var xhr;if(mozSystem){xhr=new XMLHttpRequest({mozSystem:true});}else{xhr=new XMLHttpRequest();}
|
||||
xhr.open('GET',file,true);xhr.responseType='json';xhr.onerror=function(error){reject(error);};xhr.onload=function(){if(xhr.response!==null){resolve(xhr.response);}else{reject(new Error('No valid JSON object was found ('+xhr.status+' '+xhr.statusText+')'));}};xhr.send();});},load:function(files,callback){var deferred={};deferred.promise=new Promise(resolve=>{deferred.resolve=resolve;});if(!Array.isArray(files)){files=[files];}
|
||||
var loadsRemaining=files.length,self=this;function perFileCallback(file){if(self._isLoading[file]){delete self._isLoading[file];}
|
||||
self._loaded[file]=true;if(--loadsRemaining===0){deferred.resolve();if(callback){callback();}}}
|
||||
for(var i=0;i<files.length;i++){var file=files[i];if(this._loaded[file.id||file]){perFileCallback(file);}else if(this._isLoading[file]){this._isLoading[file].addEventListener('load',perFileCallback.bind(null,file));}else{var method,idx;if(typeof file==='string'){method=file.match(/\.([^.]+)$/)[1];idx=file;}else{method='html';idx=file.id;}
|
||||
this['_'+method](file,perFileCallback.bind(null,idx));}}
|
||||
return deferred.promise;}};return new LazyLoader();}();
|
||||
47
assets/webapps/camera.gaiamobile.org/index.html
Normal file
47
assets/webapps/camera.gaiamobile.org/index.html
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US" class="gaia-icons-loaded" dir="ltr">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<meta name="theme-color" content="#242d33">
|
||||
<title>Camera</title>
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="ar:201601260729, bg:201601260729, bn-BD:201601260729, ca:201601260729, cs:201601260729, da:201601260729, de:201601260729, el:201601260729, en-US:201601260729, es:201601260729, eu:201601260729, fr:201601260729, gl:201601260729, hr:201601260729, hu:201601260729, it:201601260729, ja:201601260729, km:201601260729, ko:201601260729, lt:201601260729, mk:201601260729, ms:201601260729, ne-NP:201601260729, nl:201601260729, pa:201601260729, pl:201601260729, pt-BR:201601260729, ro:201601260729, ru:201601260729, sk:201601260729, sq:201601260729, sr-Cyrl:201601260729, sr-Latn:201601260729, sv-SE:201601260729, tr:201601260729, zh-CN:201601260729, zh-TW:201601260729">
|
||||
<link rel="localization" href="/locales-obj/index.{locale}.json">
|
||||
|
||||
|
||||
<!-- <script defer="" src="/shared/js/intl/l20n.js"></script> -->
|
||||
<!-- <script defer="" src="shared/js/lazy_loader.js"></script> --><script src="./gaia_build_defer_index.js" defer="" type="text/javascript"></script>
|
||||
<!-- <link rel="stylesheet" type="text/css" href="/shared/style/confirm.css" /> -->
|
||||
<!-- <link rel="stylesheet" type="text/css" href="/shared/style/buttons.css" /> -->
|
||||
<!-- <link rel="stylesheet" type="text/css" href="/shared/style/action_menu.css" /> -->
|
||||
<!-- <link rel="stylesheet" type="text/css" href="/shared/style/progress_activity.css" /> -->
|
||||
|
||||
<!--
|
||||
The following scripts are lazy loaded but left here to ensure
|
||||
they get copied over from shared during the build
|
||||
-->
|
||||
|
||||
<!-- <script src="/shared/js/async_storage.js"></script> -->
|
||||
<!-- <script src="/shared/js/media/jpeg_metadata_parser.js"></script> -->
|
||||
<!-- <script src="/shared/js/media/get_video_rotation.js"></script> -->
|
||||
<!-- <script src="/shared/js/media/video_player.js"></script> -->
|
||||
<!-- <script src="/shared/js/media/media_frame.js"></script> -->
|
||||
<!-- <script src="/shared/js/media/downsample.js"></script> -->
|
||||
<!-- <script src="/shared/js/media/image_size.js"></script> -->
|
||||
<!-- <script src="/shared/js/media/jpeg-exif.js"></script> -->
|
||||
<!-- <script src="/shared/js/media/crop_resize_rotate.js"></script> -->
|
||||
<!-- <script src="/shared/js/gesture_detector.js"></script> -->
|
||||
<!-- <script src="/shared/js/blobview.js"></script> -->
|
||||
<!-- <script src="/shared/js/custom_dialog.js"></script> -->
|
||||
<!-- <script src="/shared/js/format.js"></script> -->
|
||||
<!-- <script src="/shared/js/stop_recording_event.js"></script> -->
|
||||
<!-- <script src="/shared/js/sanitizer.js"></script> -->
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="style/main.css">
|
||||
<meta content="2.5" name="appVersion"></head>
|
||||
<body class="theme-media">
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -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');
|
||||
}
|
||||
});
|
||||
});
|
||||
18
assets/webapps/camera.gaiamobile.org/jsdoc.json
Normal file
18
assets/webapps/camera.gaiamobile.org/jsdoc.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "camera",
|
||||
"tags": {
|
||||
"dictionaries": ["jsdoc"]
|
||||
},
|
||||
"source": {
|
||||
"include": ["apps/camera/js/**/*.js"]
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown",
|
||||
"plugins/summarize"
|
||||
],
|
||||
"opts": {
|
||||
"private": true,
|
||||
"recurse": true,
|
||||
"destination": "docs/camera"
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
assets/webapps/camera.gaiamobile.org/manifest.webapp
Executable file
1
assets/webapps/camera.gaiamobile.org/manifest.webapp
Executable file
|
|
@ -0,0 +1 @@
|
|||
{"name":"Camera","description":"Gaia Camera","type":"certified","launch_path":"/index.html","orientation":"default","fullscreen":true,"developer":{"name":"The Gaia Team","url":"https://github.com/mozilla-b2g/gaia"},"permissions":{"themeable":{},"device-storage:pictures":{"access":"readwrite"},"device-storage:videos":{"access":"readwrite"},"settings":{"access":"readonly"},"camera":{},"geolocation":{},"audio-channel-notification":{},"audio-channel-content":{},"feature-detection":{},"moz-extremely-unstable-and-will-change-webcomponents":{}},"activities":{"record":{"filters":{"type":["photos","videos"]},"disposition":"window"},"pick":{"filters":{"type":["image/*","image/jpeg","video/*","video/3gpp"]},"returnValue":true,"disposition":"inline","href":"/index.html#pick"}},"locales":{"ar":{"name":"الكاميرا","description":"كاميرا چايا"},"bg":{"name":"Камера","description":"Камера"},"bn-BD":{"name":"ক্যামেরা","description":"গায়া ক্যামেরা"},"ca":{"name":"Càmera","description":"Càmera del Gaia"},"cs":{"name":"Fotoaparát","description":"Fotoaparát systému Gaia"},"da":{"name":"Kamera","description":"Gaia Kamera"},"de":{"name":"Kamera","description":"Kamera für Gaia"},"el":{"name":"Κάμερα","description":"Κάμερα του Gaia"},"en-US":{"name":"Camera","description":"Gaia Camera"},"es":{"name":"Cámara","description":"Cámara de Gaia"},"eu":{"name":"Kamera","description":"Gaia kamera"},"fr":{"name":"Photo","description":"Appareil photo de Gaia"},"gl":{"name":"Cámara","description":"Cámara de Gaia"},"hr":{"name":"Fotoaparat","description":"Gaia fotoaparat"},"hu":{"name":"Kamera","description":"Gaia kamera"},"it":{"name":"Fotocamera","description":"Fotocamera Gaia"},"ja":{"name":"カメラ","description":"Gaia カメラ"},"km":{"name":"កាមេរ៉ា","description":"កាមេរ៉ា Gaia"},"ko":{"name":"카메라","description":"Gaia 카메라"},"lt":{"name":"Kamera","description":"„Gaia“ kamera"},"mk":{"name":"Камера","description":"Камера на Гаја"},"ms":{"name":"Kamera","description":"Kamera Gaia"},"ne-NP":{"name":"क्यामेरा","description":"गाइया क्यामेरा"},"nl":{"name":"Camera","description":"Gaia-camera"},"pa":{"name":"ਕੈਮਰਾ","description":"ਗੀਗਾ ਕੈਮਰਾ"},"pl":{"name":"Aparat","description":"Aparat Gaia"},"pt-BR":{"name":"Câmera","description":"Gaia Camera"},"ro":{"name":"Cameră","description":"Cameră Gaia"},"ru":{"name":"Камера","description":"Камера Gaia"},"sk":{"name":"Fotoaparát","description":"Fotoaparát Gaia"},"sq":{"name":"Kamera","description":"Kamera Gaia"},"sr-Cyrl":{"name":"Камера","description":"Gaia камера"},"sr-Latn":{"name":"Kamera","description":"Gaia kamera"},"sv-SE":{"name":"Kamera","description":"Gaia kamera"},"tr":{"name":"Kamera","description":"Gaia Kamera"},"zh-CN":{"name":"相机","description":"Gaia 相机"},"zh-TW":{"name":"相機","description":"Gaia 相機"}},"default_locale":"en-US","icons":{"84":"/style/icons/camera_84.png","126":"/style/icons/camera_126.png","142":"/style/icons/camera_142.png","189":"/style/icons/camera_189.png","284":"/style/icons/camera_284.png"},"origin":"app://camera.gaiamobile.org","version":"2.5"}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
119
assets/webapps/camera.gaiamobile.org/shared/js/async_storage.js
Normal file
119
assets/webapps/camera.gaiamobile.org/shared/js/async_storage.js
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
this.asyncStorage = function () {
|
||||
'use strict';
|
||||
var DBNAME = 'asyncStorage';
|
||||
var DBVERSION = 1;
|
||||
var STORENAME = 'keyvaluepairs';
|
||||
var db = null;
|
||||
function withDatabase(f) {
|
||||
if (db) {
|
||||
f();
|
||||
} else {
|
||||
var openreq = indexedDB.open(DBNAME, DBVERSION);
|
||||
openreq.onerror = function withStoreOnError() {
|
||||
console.error('asyncStorage: can\'t open database:', openreq.error.name);
|
||||
};
|
||||
openreq.onupgradeneeded = function withStoreOnUpgradeNeeded() {
|
||||
openreq.result.createObjectStore(STORENAME);
|
||||
};
|
||||
openreq.onsuccess = function withStoreOnSuccess() {
|
||||
db = openreq.result;
|
||||
f();
|
||||
};
|
||||
}
|
||||
}
|
||||
function withStore(type, callback, oncomplete) {
|
||||
withDatabase(function () {
|
||||
var transaction = db.transaction(STORENAME, type);
|
||||
if (oncomplete) {
|
||||
transaction.oncomplete = oncomplete;
|
||||
}
|
||||
callback(transaction.objectStore(STORENAME));
|
||||
});
|
||||
}
|
||||
function getItem(key, callback) {
|
||||
var req;
|
||||
withStore('readonly', function getItemBody(store) {
|
||||
req = store.get(key);
|
||||
req.onerror = function getItemOnError() {
|
||||
console.error('Error in asyncStorage.getItem(): ', req.error.name);
|
||||
};
|
||||
}, function onComplete() {
|
||||
var value = req.result;
|
||||
if (value === undefined) {
|
||||
value = null;
|
||||
}
|
||||
callback(value);
|
||||
});
|
||||
}
|
||||
function setItem(key, value, callback) {
|
||||
withStore('readwrite', function setItemBody(store) {
|
||||
var req = store.put(value, key);
|
||||
req.onerror = function setItemOnError() {
|
||||
console.error('Error in asyncStorage.setItem(): ', req.error.name);
|
||||
};
|
||||
}, callback);
|
||||
}
|
||||
function removeItem(key, callback) {
|
||||
withStore('readwrite', function removeItemBody(store) {
|
||||
var req = store.delete(key);
|
||||
req.onerror = function removeItemOnError() {
|
||||
console.error('Error in asyncStorage.removeItem(): ', req.error.name);
|
||||
};
|
||||
}, callback);
|
||||
}
|
||||
function clear(callback) {
|
||||
withStore('readwrite', function clearBody(store) {
|
||||
var req = store.clear();
|
||||
req.onerror = function clearOnError() {
|
||||
console.error('Error in asyncStorage.clear(): ', req.error.name);
|
||||
};
|
||||
}, callback);
|
||||
}
|
||||
function length(callback) {
|
||||
var req;
|
||||
withStore('readonly', function lengthBody(store) {
|
||||
req = store.count();
|
||||
req.onerror = function lengthOnError() {
|
||||
console.error('Error in asyncStorage.length(): ', req.error.name);
|
||||
};
|
||||
}, function onComplete() {
|
||||
callback(req.result);
|
||||
});
|
||||
}
|
||||
function key(n, callback) {
|
||||
if (n < 0) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
var req;
|
||||
withStore('readonly', function keyBody(store) {
|
||||
var advanced = false;
|
||||
req = store.openCursor();
|
||||
req.onsuccess = function keyOnSuccess() {
|
||||
var cursor = req.result;
|
||||
if (!cursor) {
|
||||
return;
|
||||
}
|
||||
if (n === 0 || advanced) {
|
||||
return;
|
||||
}
|
||||
advanced = true;
|
||||
cursor.advance(n);
|
||||
};
|
||||
req.onerror = function keyOnError() {
|
||||
console.error('Error in asyncStorage.key(): ', req.error.name);
|
||||
};
|
||||
}, function onComplete() {
|
||||
var cursor = req.result;
|
||||
callback(cursor ? cursor.key : null);
|
||||
});
|
||||
}
|
||||
return {
|
||||
getItem: getItem,
|
||||
setItem: setItem,
|
||||
removeItem: removeItem,
|
||||
clear: clear,
|
||||
length: length,
|
||||
key: key
|
||||
};
|
||||
}();
|
||||
301
assets/webapps/camera.gaiamobile.org/shared/js/blobview.js
Normal file
301
assets/webapps/camera.gaiamobile.org/shared/js/blobview.js
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
var BlobView = function () {
|
||||
function fail(msg) {
|
||||
throw Error(msg);
|
||||
}
|
||||
var decoderCache = {};
|
||||
function getDecoder(encoding) {
|
||||
if (encoding in decoderCache) {
|
||||
return decoderCache[encoding];
|
||||
}
|
||||
var decoder = decoderCache[encoding] = new TextDecoder(encoding);
|
||||
return decoder;
|
||||
}
|
||||
function BlobView(blob, sliceOffset, sliceLength, slice, viewOffset, viewLength, littleEndian) {
|
||||
this.blob = blob;
|
||||
this.sliceOffset = sliceOffset;
|
||||
this.sliceLength = sliceLength;
|
||||
this.slice = slice;
|
||||
this.viewOffset = viewOffset;
|
||||
this.viewLength = viewLength;
|
||||
this.littleEndian = littleEndian;
|
||||
this.view = new DataView(slice, viewOffset, viewLength);
|
||||
this.buffer = slice;
|
||||
this.byteLength = viewLength;
|
||||
this.byteOffset = viewOffset;
|
||||
this.index = 0;
|
||||
}
|
||||
BlobView.get = function (blob, offset, length, callback, littleEndian) {
|
||||
if (offset < 0) {
|
||||
fail('negative offset');
|
||||
}
|
||||
if (length < 0) {
|
||||
fail('negative length');
|
||||
}
|
||||
if (offset > blob.size) {
|
||||
fail('offset larger than blob size');
|
||||
}
|
||||
if (offset + length > blob.size) {
|
||||
length = blob.size - offset;
|
||||
}
|
||||
var slice = blob.slice(offset, offset + length);
|
||||
var reader = new FileReader();
|
||||
reader.readAsArrayBuffer(slice);
|
||||
reader.onloadend = function () {
|
||||
var result = null;
|
||||
if (reader.result) {
|
||||
result = new BlobView(blob, offset, length, reader.result, 0, length, littleEndian || false);
|
||||
}
|
||||
callback(result, reader.error);
|
||||
};
|
||||
};
|
||||
BlobView.getFromArrayBuffer = function (buffer, offset, length, littleEndian) {
|
||||
return new BlobView(null, offset, length, buffer, offset, length, littleEndian);
|
||||
};
|
||||
BlobView.prototype = {
|
||||
constructor: BlobView,
|
||||
getMore: function (offset, length, callback) {
|
||||
if (!this.blob) {
|
||||
fail('no blob backing this BlobView');
|
||||
}
|
||||
if (offset >= this.sliceOffset && offset + length <= this.sliceOffset + this.sliceLength) {
|
||||
callback(new BlobView(this.blob, this.sliceOffset, this.sliceLength, this.slice, offset - this.sliceOffset, length, this.littleEndian));
|
||||
} else {
|
||||
BlobView.get(this.blob, offset, length, callback, this.littleEndian);
|
||||
}
|
||||
},
|
||||
littleEndian: function () {
|
||||
this.littleEndian = true;
|
||||
},
|
||||
bigEndian: function () {
|
||||
this.littleEndian = false;
|
||||
},
|
||||
getUint8: function (offset) {
|
||||
return this.view.getUint8(offset);
|
||||
},
|
||||
getInt8: function (offset) {
|
||||
return this.view.getInt8(offset);
|
||||
},
|
||||
getUint16: function (offset, le) {
|
||||
return this.view.getUint16(offset, le !== undefined ? le : this.littleEndian);
|
||||
},
|
||||
getInt16: function (offset, le) {
|
||||
return this.view.getInt16(offset, le !== undefined ? le : this.littleEndian);
|
||||
},
|
||||
getUint32: function (offset, le) {
|
||||
return this.view.getUint32(offset, le !== undefined ? le : this.littleEndian);
|
||||
},
|
||||
getInt32: function (offset, le) {
|
||||
return this.view.getInt32(offset, le !== undefined ? le : this.littleEndian);
|
||||
},
|
||||
getFloat32: function (offset, le) {
|
||||
return this.view.getFloat32(offset, le !== undefined ? le : this.littleEndian);
|
||||
},
|
||||
getFloat64: function (offset, le) {
|
||||
return this.view.getFloat64(offset, le !== undefined ? le : this.littleEndian);
|
||||
},
|
||||
readByte: function () {
|
||||
return this.view.getInt8(this.index++);
|
||||
},
|
||||
readUnsignedByte: function () {
|
||||
return this.view.getUint8(this.index++);
|
||||
},
|
||||
readShort: function (le) {
|
||||
var val = this.view.getInt16(this.index, le !== undefined ? le : this.littleEndian);
|
||||
this.index += 2;
|
||||
return val;
|
||||
},
|
||||
readUnsignedShort: function (le) {
|
||||
var val = this.view.getUint16(this.index, le !== undefined ? le : this.littleEndian);
|
||||
this.index += 2;
|
||||
return val;
|
||||
},
|
||||
readInt: function (le) {
|
||||
var val = this.view.getInt32(this.index, le !== undefined ? le : this.littleEndian);
|
||||
this.index += 4;
|
||||
return val;
|
||||
},
|
||||
readUnsignedInt: function (le) {
|
||||
var val = this.view.getUint32(this.index, le !== undefined ? le : this.littleEndian);
|
||||
this.index += 4;
|
||||
return val;
|
||||
},
|
||||
readFloat: function (le) {
|
||||
var val = this.view.getFloat32(this.index, le !== undefined ? le : this.littleEndian);
|
||||
this.index += 4;
|
||||
return val;
|
||||
},
|
||||
readDouble: function (le) {
|
||||
var val = this.view.getFloat64(this.index, le !== undefined ? le : this.littleEndian);
|
||||
this.index += 8;
|
||||
return val;
|
||||
},
|
||||
tell: function () {
|
||||
return this.index;
|
||||
},
|
||||
remaining: function () {
|
||||
return this.byteLength - this.index;
|
||||
},
|
||||
seek: function (index) {
|
||||
if (index < 0) {
|
||||
fail('negative index');
|
||||
}
|
||||
if (index > this.byteLength) {
|
||||
fail('index greater than buffer size');
|
||||
}
|
||||
this.index = index;
|
||||
},
|
||||
advance: function (n) {
|
||||
var index = this.index + n;
|
||||
if (index < 0) {
|
||||
fail('advance past beginning of buffer');
|
||||
}
|
||||
if (index > this.byteLength) {
|
||||
fail('advance past end of buffer');
|
||||
}
|
||||
this.index = index;
|
||||
},
|
||||
getUnsignedByteArray: function (offset, n) {
|
||||
return new Uint8Array(this.buffer, offset + this.viewOffset, n);
|
||||
},
|
||||
readUnsignedByteArray: function (n) {
|
||||
var val = new Uint8Array(this.buffer, this.index + this.viewOffset, n);
|
||||
this.index += n;
|
||||
return val;
|
||||
},
|
||||
getBit: function (offset, bit) {
|
||||
var byte = this.view.getUint8(offset);
|
||||
return (byte & 1 << bit) !== 0;
|
||||
},
|
||||
getUint24: function (offset, le) {
|
||||
var b1, b2, b3;
|
||||
if (le !== undefined ? le : this.littleEndian) {
|
||||
b1 = this.view.getUint8(offset);
|
||||
b2 = this.view.getUint8(offset + 1);
|
||||
b3 = this.view.getUint8(offset + 2);
|
||||
} else {
|
||||
b3 = this.view.getUint8(offset);
|
||||
b2 = this.view.getUint8(offset + 1);
|
||||
b1 = this.view.getUint8(offset + 2);
|
||||
}
|
||||
return (b3 << 16) + (b2 << 8) + b1;
|
||||
},
|
||||
readUint24: function (le) {
|
||||
var value = this.getUint24(this.index, le);
|
||||
this.index += 3;
|
||||
return value;
|
||||
},
|
||||
getBinaryText: function (offset, len) {
|
||||
var bytes = new Uint8Array(this.buffer, offset + this.viewOffset, len);
|
||||
return String.fromCharCode.apply(String, bytes);
|
||||
},
|
||||
readBinaryText: function (len) {
|
||||
var s = this.getBinaryText(this.index, len);
|
||||
this.index += len;
|
||||
return s;
|
||||
},
|
||||
getLatin1Text: function (offset, len) {
|
||||
var bytes = new Uint8Array(this.buffer, offset + this.viewOffset, len);
|
||||
return getDecoder('latin1').decode(bytes);
|
||||
},
|
||||
readLatin1Text: function (len) {
|
||||
var s = this.getLatin1Text(this.index, len);
|
||||
this.index += len;
|
||||
return s;
|
||||
},
|
||||
getUTF8Text: function (offset, len) {
|
||||
var bytes = new Uint8Array(this.buffer, offset + this.viewOffset, len);
|
||||
return getDecoder('utf-8').decode(bytes);
|
||||
},
|
||||
readUTF8Text: function (len) {
|
||||
var s = this.getUTF8Text(this.index, len);
|
||||
this.index += len;
|
||||
return s;
|
||||
},
|
||||
getUTF16Text: function (offset, len, le) {
|
||||
if (len % 2) {
|
||||
fail('len must be a multiple of two');
|
||||
}
|
||||
var bytes = new Uint8Array(this.buffer, offset + this.viewOffset, len);
|
||||
if (le === null || le === undefined) {
|
||||
var BOM = (bytes[0] << 8) + bytes[1];
|
||||
if (BOM === 65279) {
|
||||
bytes = bytes.subarray(2);
|
||||
le = false;
|
||||
} else if (BOM === 65534) {
|
||||
bytes = bytes.subarray(2);
|
||||
le = true;
|
||||
} else {
|
||||
le = true;
|
||||
}
|
||||
}
|
||||
var encoding = le ? 'utf-16le' : 'utf-16be';
|
||||
return getDecoder(encoding).decode(bytes);
|
||||
},
|
||||
readUTF16Text: function (len, le) {
|
||||
var s = this.getUTF16Text(this.index, len, le);
|
||||
this.index += len;
|
||||
return s;
|
||||
},
|
||||
getID3Uint28BE: function (offset) {
|
||||
var b1 = this.view.getUint8(offset) & 127;
|
||||
var b2 = this.view.getUint8(offset + 1) & 127;
|
||||
var b3 = this.view.getUint8(offset + 2) & 127;
|
||||
var b4 = this.view.getUint8(offset + 3) & 127;
|
||||
return b1 << 21 | b2 << 14 | b3 << 7 | b4;
|
||||
},
|
||||
readID3Uint28BE: function () {
|
||||
var value = this.getID3Uint28BE(this.index);
|
||||
this.index += 4;
|
||||
return value;
|
||||
},
|
||||
readNullTerminatedLatin1Text: function (size, advance_by_size = false) {
|
||||
var bytes = new Uint8Array(this.buffer, this.viewOffset + this.index, size);
|
||||
var nil = bytes.indexOf(0);
|
||||
if (nil !== -1) {
|
||||
bytes = bytes.subarray(0, nil);
|
||||
}
|
||||
var s = getDecoder('latin1').decode(bytes);
|
||||
if (nil === -1 || advance_by_size) {
|
||||
this.index += size;
|
||||
} else {
|
||||
this.index += nil + 1;
|
||||
}
|
||||
return s;
|
||||
},
|
||||
readNullTerminatedUTF8Text: function (size, advance_by_size = false) {
|
||||
var bytes = new Uint8Array(this.buffer, this.viewOffset + this.index, size);
|
||||
var nil = bytes.indexOf(0);
|
||||
if (nil !== -1) {
|
||||
bytes = bytes.subarray(0, nil);
|
||||
}
|
||||
var s = getDecoder('utf-8').decode(bytes);
|
||||
if (nil === -1 || advance_by_size) {
|
||||
this.index += size;
|
||||
} else {
|
||||
this.index += nil + 1;
|
||||
}
|
||||
return s;
|
||||
},
|
||||
readNullTerminatedUTF16Text: function (size, le, advance_by_size = false) {
|
||||
if (size % 2) {
|
||||
fail('size must be a multiple of two');
|
||||
}
|
||||
for (var len = 0; len < size; len += 2) {
|
||||
if (this.getUint16(this.index + len, le) === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var s = this.getUTF16Text(this.index, len, le);
|
||||
if (len === size || advance_by_size) {
|
||||
this.index += size;
|
||||
} else {
|
||||
this.index += len + 2;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
};
|
||||
return {
|
||||
get: BlobView.get,
|
||||
getFromArrayBuffer: BlobView.getFromArrayBuffer
|
||||
};
|
||||
}();
|
||||
110
assets/webapps/camera.gaiamobile.org/shared/js/custom_dialog.js
Normal file
110
assets/webapps/camera.gaiamobile.org/shared/js/custom_dialog.js
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
var CustomDialog = function () {
|
||||
var container = null;
|
||||
var screen = null;
|
||||
var dialog = null;
|
||||
var header = null;
|
||||
var message = null;
|
||||
var yes = null;
|
||||
var no = null;
|
||||
return {
|
||||
hide: function dialog_hide() {
|
||||
if (screen === null) {
|
||||
return;
|
||||
}
|
||||
if (!container) {
|
||||
container = document.body;
|
||||
}
|
||||
container.removeChild(screen);
|
||||
screen = null;
|
||||
dialog = null;
|
||||
header = null;
|
||||
message = null;
|
||||
yes = null;
|
||||
no = null;
|
||||
},
|
||||
show: function dialog_show(title, msg, cancel, confirm, containerElement) {
|
||||
container = containerElement || document.body;
|
||||
if (screen === null) {
|
||||
screen = document.createElement('form');
|
||||
screen.setAttribute('role', 'dialog');
|
||||
screen.setAttribute('data-type', 'confirm');
|
||||
screen.id = 'dialog-screen';
|
||||
dialog = document.createElement('section');
|
||||
screen.appendChild(dialog);
|
||||
var decorateWithOptions = function cd_decorateWithOptions(type, options, elm, dialog) {
|
||||
if ('string' === typeof options) {
|
||||
elm.setAttribute('data-l10n-id', options);
|
||||
return elm;
|
||||
}
|
||||
var icon = options.icon;
|
||||
var textElm = elm;
|
||||
if (icon && '' !== icon) {
|
||||
textElm = document.createElement('span');
|
||||
var iconImg = new Image();
|
||||
iconImg.src = icon;
|
||||
iconImg.classList.add('custom-dialog-' + type + '-icon');
|
||||
elm.insertBefore(iconImg, elm.firstChild);
|
||||
elm.appendChild(textElm);
|
||||
}
|
||||
if (options.id) {
|
||||
document.l10n.setAttributes(textElm, options.id, options.args);
|
||||
} else {
|
||||
var text = options[type];
|
||||
textElm.textContent = text;
|
||||
}
|
||||
return elm;
|
||||
};
|
||||
var setElementText = function (element, options) {
|
||||
if ('string' === typeof options) {
|
||||
element.setAttribute('data-l10n-id', options);
|
||||
}
|
||||
if (options.id) {
|
||||
document.l10n.setAttributes(element, options.id, options.args);
|
||||
}
|
||||
};
|
||||
header = document.createElement('h1');
|
||||
header.id = 'dialog-title';
|
||||
if (title && title !== '') {
|
||||
header = decorateWithOptions('title', title, header, dialog);
|
||||
}
|
||||
dialog.appendChild(header);
|
||||
message = document.createElement('p');
|
||||
message.id = 'dialog-message';
|
||||
message = decorateWithOptions('message', msg, message, dialog);
|
||||
dialog.appendChild(message);
|
||||
var menu = document.createElement('menu');
|
||||
menu.dataset.items = 1;
|
||||
no = document.createElement('button');
|
||||
no.type = 'button';
|
||||
setElementText(no, cancel.title);
|
||||
no.id = 'dialog-no';
|
||||
no.addEventListener('click', clickHandler);
|
||||
menu.appendChild(no);
|
||||
if (confirm) {
|
||||
menu.dataset.items = 2;
|
||||
yes = document.createElement('button');
|
||||
yes.type = 'button';
|
||||
setElementText(yes, confirm.title);
|
||||
yes.id = 'dialog-yes';
|
||||
yes.className = confirm.recommend ? 'recommend' : 'danger';
|
||||
yes.addEventListener('click', clickHandler);
|
||||
menu.appendChild(yes);
|
||||
} else {
|
||||
no.classList.add('full');
|
||||
}
|
||||
screen.appendChild(menu);
|
||||
container.appendChild(screen);
|
||||
}
|
||||
screen.classList.add('visible');
|
||||
function clickHandler(evt) {
|
||||
screen.classList.remove('visible');
|
||||
if (evt.target === yes && confirm.callback) {
|
||||
confirm.callback();
|
||||
} else if (evt.target === no && cancel.callback) {
|
||||
cancel.callback();
|
||||
}
|
||||
}
|
||||
return screen;
|
||||
}
|
||||
};
|
||||
}();
|
||||
10
assets/webapps/camera.gaiamobile.org/shared/js/format.js
Normal file
10
assets/webapps/camera.gaiamobile.org/shared/js/format.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
var Format = {
|
||||
padLeft: function (input, len, padWith) {
|
||||
padWith = padWith || ' ';
|
||||
var pad = len - (input + '').length;
|
||||
while (--pad > -1) {
|
||||
input = padWith + input;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,401 @@
|
|||
var GestureDetector = function () {
|
||||
function GD(e, options) {
|
||||
this.element = e;
|
||||
this.options = options || {};
|
||||
this.options.panThreshold = this.options.panThreshold || GD.PAN_THRESHOLD;
|
||||
this.state = initialState;
|
||||
this.timers = {};
|
||||
}
|
||||
GD.prototype.startDetecting = function () {
|
||||
var self = this;
|
||||
eventtypes.forEach(function (t) {
|
||||
self.element.addEventListener(t, self);
|
||||
});
|
||||
};
|
||||
GD.prototype.stopDetecting = function () {
|
||||
var self = this;
|
||||
eventtypes.forEach(function (t) {
|
||||
self.element.removeEventListener(t, self);
|
||||
});
|
||||
};
|
||||
GD.prototype.handleEvent = function (e) {
|
||||
var handler = this.state[e.type];
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
if (e.changedTouches) {
|
||||
for (var i = 0; i < e.changedTouches.length; i++) {
|
||||
handler(this, e, e.changedTouches[i]);
|
||||
handler = this.state[e.type];
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handler(this, e);
|
||||
}
|
||||
};
|
||||
GD.prototype.startTimer = function (type, time) {
|
||||
this.clearTimer(type);
|
||||
var self = this;
|
||||
this.timers[type] = setTimeout(function () {
|
||||
self.timers[type] = null;
|
||||
var handler = self.state[type];
|
||||
if (handler) {
|
||||
handler(self, type);
|
||||
}
|
||||
}, time);
|
||||
};
|
||||
GD.prototype.clearTimer = function (type) {
|
||||
if (this.timers[type]) {
|
||||
clearTimeout(this.timers[type]);
|
||||
this.timers[type] = null;
|
||||
}
|
||||
};
|
||||
GD.prototype.switchTo = function (state, event, touch) {
|
||||
this.state = state;
|
||||
if (state.init) {
|
||||
state.init(this, event, touch);
|
||||
}
|
||||
};
|
||||
GD.prototype.emitEvent = function (type, detail) {
|
||||
if (!this.target) {
|
||||
console.error('Attempt to emit event with no target');
|
||||
return;
|
||||
}
|
||||
var event = this.element.ownerDocument.createEvent('CustomEvent');
|
||||
event.initCustomEvent(type, true, true, detail);
|
||||
this.target.dispatchEvent(event);
|
||||
};
|
||||
GD.HOLD_INTERVAL = 1000;
|
||||
GD.PAN_THRESHOLD = 20;
|
||||
GD.DOUBLE_TAP_DISTANCE = 50;
|
||||
GD.DOUBLE_TAP_TIME = 500;
|
||||
GD.VELOCITY_SMOOTHING = 0.5;
|
||||
GD.SCALE_THRESHOLD = 20;
|
||||
GD.ROTATE_THRESHOLD = 22.5;
|
||||
GD.THRESHOLD_SMOOTHING = 0.9;
|
||||
var abs = Math.abs, floor = Math.floor, sqrt = Math.sqrt, atan2 = Math.atan2;
|
||||
var PI = Math.PI;
|
||||
var eventtypes = [
|
||||
'touchstart',
|
||||
'touchmove',
|
||||
'touchend'
|
||||
];
|
||||
function eventTime(e) {
|
||||
var ts = e.timeStamp;
|
||||
if (ts > 2 * Date.now()) {
|
||||
return Math.floor(ts / 1000);
|
||||
} else {
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
function coordinates(e, t) {
|
||||
return Object.freeze({
|
||||
screenX: t.screenX,
|
||||
screenY: t.screenY,
|
||||
clientX: t.clientX,
|
||||
clientY: t.clientY,
|
||||
timeStamp: eventTime(e)
|
||||
});
|
||||
}
|
||||
function midpoints(e, t1, t2) {
|
||||
return Object.freeze({
|
||||
screenX: floor((t1.screenX + t2.screenX) / 2),
|
||||
screenY: floor((t1.screenY + t2.screenY) / 2),
|
||||
clientX: floor((t1.clientX + t2.clientX) / 2),
|
||||
clientY: floor((t1.clientY + t2.clientY) / 2),
|
||||
timeStamp: eventTime(e)
|
||||
});
|
||||
}
|
||||
function between(c1, c2) {
|
||||
var r = GD.THRESHOLD_SMOOTHING;
|
||||
return Object.freeze({
|
||||
screenX: floor(c1.screenX + r * (c2.screenX - c1.screenX)),
|
||||
screenY: floor(c1.screenY + r * (c2.screenY - c1.screenY)),
|
||||
clientX: floor(c1.clientX + r * (c2.clientX - c1.clientX)),
|
||||
clientY: floor(c1.clientY + r * (c2.clientY - c1.clientY)),
|
||||
timeStamp: floor(c1.timeStamp + r * (c2.timeStamp - c1.timeStamp))
|
||||
});
|
||||
}
|
||||
function touchDistance(t1, t2) {
|
||||
var dx = t2.screenX - t1.screenX;
|
||||
var dy = t2.screenY - t1.screenY;
|
||||
return sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
function touchDirection(t1, t2) {
|
||||
return atan2(t2.screenY - t1.screenY, t2.screenX - t1.screenX) * 180 / PI;
|
||||
}
|
||||
function touchRotation(d1, d2) {
|
||||
var angle = d2 - d1;
|
||||
if (angle > 180) {
|
||||
angle -= 360;
|
||||
} else if (angle <= -180) {
|
||||
angle += 360;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
function isDoubleTap(lastTap, thisTap) {
|
||||
var dx = abs(thisTap.screenX - lastTap.screenX);
|
||||
var dy = abs(thisTap.screenY - lastTap.screenY);
|
||||
var dt = thisTap.timeStamp - lastTap.timeStamp;
|
||||
return dx < GD.DOUBLE_TAP_DISTANCE && dy < GD.DOUBLE_TAP_DISTANCE && dt < GD.DOUBLE_TAP_TIME;
|
||||
}
|
||||
var initialState = {
|
||||
name: 'initialState',
|
||||
init: function (d, e, t) {
|
||||
d.target = null;
|
||||
d.start = d.last = null;
|
||||
d.touch1 = d.touch2 = null;
|
||||
d.vx = d.vy = null;
|
||||
d.startDistance = d.lastDistance = null;
|
||||
d.startDirection = d.lastDirection = null;
|
||||
d.lastMidpoint = null;
|
||||
d.scaled = d.rotated = null;
|
||||
if (e && t && e.type === 'touchstart') {
|
||||
initialState.touchstart(d, e, t);
|
||||
}
|
||||
},
|
||||
touchstart: function (d, e, t) {
|
||||
d.switchTo(touchStartedState, e, t);
|
||||
}
|
||||
};
|
||||
var touchStartedState = {
|
||||
name: 'touchStartedState',
|
||||
init: function (d, e, t) {
|
||||
d.target = e.target;
|
||||
d.touch1 = t.identifier;
|
||||
d.start = d.last = coordinates(e, t);
|
||||
if (d.options.holdEvents) {
|
||||
d.startTimer('holdtimeout', GD.HOLD_INTERVAL);
|
||||
}
|
||||
},
|
||||
touchstart: function (d, e, t) {
|
||||
d.clearTimer('holdtimeout');
|
||||
if (e.touches.length > 1) {
|
||||
d.switchTo(transformState, e, t);
|
||||
} else {
|
||||
console.warn('Ignoring missing touchend event. See bug 1162771.');
|
||||
d.switchTo(initialState, e, t);
|
||||
}
|
||||
},
|
||||
touchmove: function (d, e, t) {
|
||||
if (t.identifier !== d.touch1) {
|
||||
return;
|
||||
}
|
||||
if (abs(t.screenX - d.start.screenX) > d.options.panThreshold || abs(t.screenY - d.start.screenY) > d.options.panThreshold) {
|
||||
d.clearTimer('holdtimeout');
|
||||
d.switchTo(panStartedState, e, t);
|
||||
}
|
||||
},
|
||||
touchend: function (d, e, t) {
|
||||
if (t.identifier !== d.touch1) {
|
||||
return;
|
||||
}
|
||||
if (d.lastTap && isDoubleTap(d.lastTap, d.start)) {
|
||||
d.emitEvent('tap', d.start);
|
||||
d.emitEvent('dbltap', d.start);
|
||||
d.lastTap = null;
|
||||
} else {
|
||||
d.emitEvent('tap', d.start);
|
||||
d.lastTap = coordinates(e, t);
|
||||
}
|
||||
d.clearTimer('holdtimeout');
|
||||
d.switchTo(initialState);
|
||||
},
|
||||
holdtimeout: function (d) {
|
||||
d.switchTo(holdState);
|
||||
}
|
||||
};
|
||||
var panStartedState = {
|
||||
name: 'panStartedState',
|
||||
init: function (d, e, t) {
|
||||
d.start = d.last = between(d.start, coordinates(e, t));
|
||||
if (e.type === 'touchmove') {
|
||||
panStartedState.touchmove(d, e, t);
|
||||
}
|
||||
},
|
||||
touchmove: function (d, e, t) {
|
||||
if (t.identifier !== d.touch1) {
|
||||
return;
|
||||
}
|
||||
var current = coordinates(e, t);
|
||||
d.emitEvent('pan', {
|
||||
absolute: {
|
||||
dx: current.screenX - d.start.screenX,
|
||||
dy: current.screenY - d.start.screenY
|
||||
},
|
||||
relative: {
|
||||
dx: current.screenX - d.last.screenX,
|
||||
dy: current.screenY - d.last.screenY
|
||||
},
|
||||
position: current
|
||||
});
|
||||
var dt = current.timeStamp - d.last.timeStamp;
|
||||
var vx = (current.screenX - d.last.screenX) / dt;
|
||||
var vy = (current.screenY - d.last.screenY) / dt;
|
||||
if (d.vx == null) {
|
||||
d.vx = vx;
|
||||
d.vy = vy;
|
||||
} else {
|
||||
d.vx = d.vx * GD.VELOCITY_SMOOTHING + vx * (1 - GD.VELOCITY_SMOOTHING);
|
||||
d.vy = d.vy * GD.VELOCITY_SMOOTHING + vy * (1 - GD.VELOCITY_SMOOTHING);
|
||||
}
|
||||
d.last = current;
|
||||
},
|
||||
touchend: function (d, e, t) {
|
||||
if (t.identifier !== d.touch1) {
|
||||
return;
|
||||
}
|
||||
var current = coordinates(e, t);
|
||||
var dx = current.screenX - d.start.screenX;
|
||||
var dy = current.screenY - d.start.screenY;
|
||||
var angle = atan2(dy, dx) * 180 / PI;
|
||||
if (angle < 0) {
|
||||
angle += 360;
|
||||
}
|
||||
var direction;
|
||||
if (angle >= 315 || angle < 45) {
|
||||
direction = 'right';
|
||||
} else if (angle >= 45 && angle < 135) {
|
||||
direction = 'down';
|
||||
} else if (angle >= 135 && angle < 225) {
|
||||
direction = 'left';
|
||||
} else if (angle >= 225 && angle < 315) {
|
||||
direction = 'up';
|
||||
}
|
||||
d.emitEvent('swipe', {
|
||||
start: d.start,
|
||||
end: current,
|
||||
dx: dx,
|
||||
dy: dy,
|
||||
dt: e.timeStamp - d.start.timeStamp,
|
||||
vx: d.vx,
|
||||
vy: d.vy,
|
||||
direction: direction,
|
||||
angle: angle
|
||||
});
|
||||
d.switchTo(initialState);
|
||||
}
|
||||
};
|
||||
var holdState = {
|
||||
name: 'holdState',
|
||||
init: function (d) {
|
||||
d.emitEvent('holdstart', d.start);
|
||||
},
|
||||
touchmove: function (d, e, t) {
|
||||
var current = coordinates(e, t);
|
||||
d.emitEvent('holdmove', {
|
||||
absolute: {
|
||||
dx: current.screenX - d.start.screenX,
|
||||
dy: current.screenY - d.start.screenY
|
||||
},
|
||||
relative: {
|
||||
dx: current.screenX - d.last.screenX,
|
||||
dy: current.screenY - d.last.screenY
|
||||
},
|
||||
position: current
|
||||
});
|
||||
d.last = current;
|
||||
},
|
||||
touchend: function (d, e, t) {
|
||||
var current = coordinates(e, t);
|
||||
d.emitEvent('holdend', {
|
||||
start: d.start,
|
||||
end: current,
|
||||
dx: current.screenX - d.start.screenX,
|
||||
dy: current.screenY - d.start.screenY
|
||||
});
|
||||
d.switchTo(initialState);
|
||||
}
|
||||
};
|
||||
var transformState = {
|
||||
name: 'transformState',
|
||||
init: function (d, e, t) {
|
||||
d.touch2 = t.identifier;
|
||||
var t1 = e.touches.identifiedTouch(d.touch1);
|
||||
var t2 = e.touches.identifiedTouch(d.touch2);
|
||||
d.startDistance = d.lastDistance = touchDistance(t1, t2);
|
||||
d.startDirection = d.lastDirection = touchDirection(t1, t2);
|
||||
d.scaled = d.rotated = false;
|
||||
},
|
||||
touchmove: function (d, e, t) {
|
||||
if (t.identifier !== d.touch1 && t.identifier !== d.touch2) {
|
||||
return;
|
||||
}
|
||||
var t1 = e.touches.identifiedTouch(d.touch1);
|
||||
var t2 = e.touches.identifiedTouch(d.touch2);
|
||||
var midpoint = midpoints(e, t1, t2);
|
||||
var distance = touchDistance(t1, t2);
|
||||
var direction = touchDirection(t1, t2);
|
||||
var rotation = touchRotation(d.startDirection, direction);
|
||||
if (!d.scaled) {
|
||||
if (abs(distance - d.startDistance) > GD.SCALE_THRESHOLD) {
|
||||
d.scaled = true;
|
||||
d.startDistance = d.lastDistance = floor(d.startDistance + GD.THRESHOLD_SMOOTHING * (distance - d.startDistance));
|
||||
} else {
|
||||
distance = d.startDistance;
|
||||
}
|
||||
}
|
||||
if (!d.rotated) {
|
||||
if (abs(rotation) > GD.ROTATE_THRESHOLD) {
|
||||
d.rotated = true;
|
||||
} else {
|
||||
direction = d.startDirection;
|
||||
}
|
||||
}
|
||||
if (d.scaled || d.rotated) {
|
||||
d.emitEvent('transform', {
|
||||
absolute: {
|
||||
scale: distance / d.startDistance,
|
||||
rotate: touchRotation(d.startDirection, direction)
|
||||
},
|
||||
relative: {
|
||||
scale: distance / d.lastDistance,
|
||||
rotate: touchRotation(d.lastDirection, direction)
|
||||
},
|
||||
midpoint: midpoint
|
||||
});
|
||||
d.lastDistance = distance;
|
||||
d.lastDirection = direction;
|
||||
d.lastMidpoint = midpoint;
|
||||
}
|
||||
},
|
||||
touchend: function (d, e, t) {
|
||||
if (t.identifier === d.touch2) {
|
||||
d.touch2 = null;
|
||||
} else if (t.identifier === d.touch1) {
|
||||
d.touch1 = d.touch2;
|
||||
d.touch2 = null;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (d.scaled || d.rotated) {
|
||||
d.emitEvent('transformend', {
|
||||
absolute: {
|
||||
scale: d.lastDistance / d.startDistance,
|
||||
rotate: touchRotation(d.startDirection, d.lastDirection)
|
||||
},
|
||||
relative: {
|
||||
scale: 1,
|
||||
rotate: 0
|
||||
},
|
||||
midpoint: d.lastMidpoint
|
||||
});
|
||||
}
|
||||
d.switchTo(afterTransformState);
|
||||
}
|
||||
};
|
||||
var afterTransformState = {
|
||||
name: 'afterTransformState',
|
||||
touchstart: function (d, e, t) {
|
||||
d.switchTo(transformState, e, t);
|
||||
},
|
||||
touchend: function (d, e, t) {
|
||||
if (t.identifier === d.touch1) {
|
||||
d.switchTo(initialState);
|
||||
}
|
||||
}
|
||||
};
|
||||
return GD;
|
||||
}();
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue