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:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,31 @@
{
"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"
],
"_release": "0.1.1",
"_resolution": {
"type": "version",
"tag": "v0.1.1",
"commit": "f5195ba8fe78c2a4f421b26caf431f385fa6b61d"
},
"_source": "git://github.com/fxos-components/attach.git",
"_target": "0.1.1",
"_originalSource": "fxos-components/attach"
}

View 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"
]
}

View file

@ -0,0 +1,254 @@
;(function() {
'use strict';
/**
* Namespace to store
* references under on root
*/
var ns = '_attach';
/**
* Normalize `matchesSelector`
*/
var proto = Element.prototype;
var matches = proto.matchesSelector
|| proto.webkitMatchesSelector
|| proto.mozMatchesSelector
|| proto.msMatchesSelector
|| proto.oMatchesSelector;
/**
* Bind an event listener
* to the given element.
*
* Example:
*
* attach(myEl, 'click', '.my-class', function(event, el) {
* // Do stuff
* });
*
* @param {Element} root
* @param {String} type
* @param {String} selector (optional)
* @param {Function} fn
* @param {Object} ctx (optional)
*/
function attach(root, type, selector, fn, ctx) {
if (arguments.length === 2) {
return attach.many.apply(null, arguments);
}
// `selector` is optional
if (typeof selector === 'function') {
ctx = fn;
fn = selector;
selector = null;
}
// We use the key 'null' to
// indicate that we are binding
// an event handler to the root.
selector = selector || 'null';
var store = getStore(root);
var master = store.master[type];
var delegates = store.delegates[type] = (store.delegates[type] || {});
// Add the function to the delegates
delegates[selector] = fn;
// Only one master event listener
// is needed per event type.
if (master) { return; }
// Add the master callbak to the
// root node and to the store.
master = store.master[type] = callback;
root.addEventListener(type, master);
/**
* The master callback passed
* to `addEventListener`.
*
* @param {Event} event
*/
function callback(e) {
var el = e.target;
var selector;
var matched;
var out;
var fn;
// Walk up the DOM tree
// until we hit the root
while (el) {
// Loop over each selector
// bound to this e type.
for (selector in delegates) {
fn = delegates[selector];
// There are two types of match. A
// 'null' selector at the root node,
// or a selector match on the current el.
matched = (el === root && selector === 'null') ||
matches.call(el, selector);
if (matched) {
out = fn.call(ctx || el, e, el);
// Stop propagation if the
// user returns false from the
// callback. Ideally we would
// use .stopPropagation, but I
// don't know of any way to detect
// if this has been called.
if (out === false) { return e.stopPropagation(); }
}
}
// Don't go any higher
// than the root element.
if (el == root) break;
// Move on up!
el = el.parentNode;
}
}
}
attach.on = attach;
/**
* Unbind an event attach
* from the given root element.
*
* If no selector if given, all
* callbacks for the given event
* type are removed.
*
* Example:
*
* // Remove one
* attach.off(myEl, 'click', '.my-class');
*
* // Remove all
* attach.off(myEl, 'click');
*
* @param {Element} root
* @param {String} type (optional)
* @param {String} selector (optional)
*/
attach.off = function(root, type, selector) {
var store = getStore(root);
var master = store.master[type];
var delegates = store.delegates[type];
// Remove just one
if (type && selector) {
delete delegates[selector];
}
// Remove all of type
else if (type) {
delete store.delegates[type];
}
// Remove all
else {
for (type in store.master) {
attach.off(root, type);
}
}
// If there aren't any callbacks
// of this type left, remove the master.
if (isEmpty(store.delegates[type])) {
root.removeEventListener(type, master);
delete store.master[type];
}
};
/**
* Handles Backbone style
* shorthand event binding.
*
* Example:
*
* attach(myElement, {
* 'click .foo': onFooClick,
* 'click .bar': onBarClick
* });
*
* @param {element} root
* @param {Object} config
* @param {Object} ctx
*/
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);
}
};
/**
* Gets the reference store
* attached to the given node.
*
* If one is not found, we
* create a fresh one.
*
* @param {Element} el
* @return {Object}
*/
function getStore(el) {
return el[ns] || createStore(el);
}
/**
* Creates a fresh reference
* store on the given element.
*
* @param {Element} el
* @return {Object}
*/
function createStore(el) {
el[ns] = { master: {}, delegates: {} };
return el[ns];
}
/**
* Checks if the given
* element has no keys.
*
* @param {Object} ob
* @return {Boolean}
*/
function isEmpty(ob) {
for (var key in ob) { return false; }
return true;
}
/**
* Expose 'attach' (UMD)
*/
if (typeof exports === "object") {
module.exports = attach;
} else if (typeof define === "function" && define.amd) {
define(function(){ return attach; });
} else {
window.attach = attach;
}
})();

View file

@ -0,0 +1,13 @@
{
"name": "debug",
"_cacheHeaders": {
"ETag": "\"3fa8ae0741057b60f6e8629b9ede7fda1e09af27\"",
"Content-Length": "10073",
"Content-Type": "text/plain; charset=utf-8"
},
"_release": "e-tag:3fa8ae074",
"main": "index.js",
"_source": "https://raw.githubusercontent.com/visionmedia/debug/1.0.2/dist/debug.js",
"_target": "*",
"_originalSource": "https://raw.githubusercontent.com/visionmedia/debug/1.0.2/dist/debug.js"
}

View file

@ -0,0 +1,461 @@
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.debug=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
/**
* This is the web browser implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = _dereq_('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
/**
* Colors.
*/
exports.colors = [
'lightseagreen',
'forestgreen',
'goldenrod',
'dodgerblue',
'darkorchid',
'crimson'
];
/**
* Currently only WebKit-based Web Inspectors and the Firebug
* extension (*not* the built-in Firefox web inpector) are
* known to support "%c" CSS customizations.
*
* TODO: add a `localStorage` variable to explicitly enable/disable colors
*/
function useColors() {
// is webkit? http://stackoverflow.com/a/16459606/376773
return ('WebkitAppearance' in document.documentElement.style) ||
// is firebug? http://stackoverflow.com/a/398120/376773
(window.console && (console.firebug || (console.exception && console.table)));
}
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
exports.formatters.j = function(v) {
return JSON.stringify(v);
};
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs() {
var args = arguments;
var useColors = this.useColors;
args[0] = (useColors ? '%c' : '')
+ this.namespace
+ (useColors ? '%c ' : ' ')
+ args[0]
+ (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff);
if (!useColors) return args
var c = 'color: ' + this.color;
args = [args[0], c, ''].concat(Array.prototype.slice.call(args, 1));
// the final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to
// figure out the correct index to insert the CSS into
var index = 0;
var lastC = 0;
args[0].replace(/%[a-z%]/g, function(match) {
if ('%%' === match) return;
index++;
if ('%c' === match) {
// we only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
args.splice(lastC, 0, c);
return args;
}
/**
* Invokes `console.log()` when available.
* No-op when `console.log` is not a "function".
*
* @api public
*/
function log() {
// This hackery is required for IE8,
// where the `console.log` function doesn't have 'apply'
return 'object' == typeof console
&& 'function' == typeof console.log
&& Function.prototype.apply.call(console.log, console, arguments);
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (null == namespaces) {
localStorage.removeItem('debug');
} else {
localStorage.debug = namespaces;
}
} catch(e) {}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
var r;
try {
r = localStorage.debug;
} catch(e) {}
return r;
}
/**
* Enable namespaces listed in `localStorage.debug` initially.
*/
exports.enable(load());
},{"./debug":2}],2:[function(_dereq_,module,exports){
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = debug;
exports.coerce = coerce;
exports.disable = disable;
exports.enable = enable;
exports.enabled = enabled;
exports.humanize = _dereq_('ms');
/**
* The currently active debug mode names, and names to skip.
*/
exports.names = [];
exports.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lowercased letter, i.e. "n".
*/
exports.formatters = {};
/**
* Previously assigned color.
*/
var prevColor = 0;
/**
* Previous log timestamp.
*/
var prevTime;
/**
* Select a color.
*
* @return {Number}
* @api private
*/
function selectColor() {
return exports.colors[prevColor++ % exports.colors.length];
}
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function debug(namespace) {
// define the `disabled` version
function disabled() {
}
disabled.enabled = false;
// define the `enabled` version
function enabled() {
var self = enabled;
// set `diff` timestamp
var curr = +new Date();
var ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
// add the `color` if not set
if (null == self.useColors) self.useColors = exports.useColors();
if (null == self.color && self.useColors) self.color = selectColor();
var args = Array.prototype.slice.call(arguments);
args[0] = exports.coerce(args[0]);
if ('string' !== typeof args[0]) {
// anything else let's inspect with %o
args = ['%o'].concat(args);
}
// apply any `formatters` transformations
var index = 0;
args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
// if we encounter an escaped % then don't increase the array index
if (match === '%%') return match;
index++;
var formatter = exports.formatters[format];
if ('function' === typeof formatter) {
var val = args[index];
match = formatter.call(self, val);
// now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
}
return match;
});
if ('function' === typeof exports.formatArgs) {
args = exports.formatArgs.apply(self, args);
}
var logFn = exports.log || enabled.log || console.log.bind(console);
logFn.apply(self, args);
}
enabled.enabled = true;
var fn = exports.enabled(namespace) ? enabled : disabled;
fn.namespace = namespace;
return fn;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
exports.save(namespaces);
var split = (namespaces || '').split(/[\s,]+/);
var len = split.length;
for (var i = 0; i < len; i++) {
if (!split[i]) continue; // ignore empty strings
namespaces = split[i].replace('*', '.*?');
if (namespaces[0] === '-') {
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
} else {
exports.names.push(new RegExp('^' + namespaces + '$'));
}
}
}
/**
* Disable debug output.
*
* @api public
*/
function disable() {
exports.enable('');
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
var i, len;
for (i = 0, len = exports.skips.length; i < len; i++) {
if (exports.skips[i].test(name)) {
return false;
}
}
for (i = 0, len = exports.names.length; i < len; i++) {
if (exports.names[i].test(name)) {
return true;
}
}
return false;
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) return val.stack || val.message;
return val;
}
},{"ms":3}],3:[function(_dereq_,module,exports){
/**
* Helpers.
*/
var s = 1000;
var m = s * 60;
var h = m * 60;
var d = h * 24;
var y = d * 365.25;
/**
* Parse or format the given `val`.
*
* Options:
*
* - `long` verbose formatting [false]
*
* @param {String|Number} val
* @param {Object} options
* @return {String|Number}
* @api public
*/
module.exports = function(val, options){
options = options || {};
if ('string' == typeof val) return parse(val);
return options.long
? long(val)
: short(val);
};
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
function parse(str) {
var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str);
if (!match) return;
var n = parseFloat(match[1]);
var type = (match[2] || 'ms').toLowerCase();
switch (type) {
case 'years':
case 'year':
case 'y':
return n * y;
case 'days':
case 'day':
case 'd':
return n * d;
case 'hours':
case 'hour':
case 'h':
return n * h;
case 'minutes':
case 'minute':
case 'm':
return n * m;
case 'seconds':
case 'second':
case 's':
return n * s;
case 'ms':
return n;
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function short(ms) {
if (ms >= d) return Math.round(ms / d) + 'd';
if (ms >= h) return Math.round(ms / h) + 'h';
if (ms >= m) return Math.round(ms / m) + 'm';
if (ms >= s) return Math.round(ms / s) + 's';
return ms + 'ms';
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function long(ms) {
return plural(ms, d, 'day')
|| plural(ms, h, 'hour')
|| plural(ms, m, 'minute')
|| plural(ms, s, 'second')
|| ms + ' ms';
}
/**
* Pluralization helper.
*/
function plural(ms, n, name) {
if (ms < n) return;
if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name;
return Math.ceil(ms / n) + ' ' + name + 's';
}
},{}]},{},[1])
(1)
});

View file

@ -0,0 +1,23 @@
{
"name": "device-orientation",
"main": "device-orientation.js",
"homepage": "https://github.com/fxos-components/device-orientation",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests",
"README.md"
],
"version": "1.0.0",
"_release": "1.0.0",
"_resolution": {
"type": "version",
"tag": "v1.0.0",
"commit": "6f6921e80c4f1ab8a2600f9ecebf6afabdca02c9"
},
"_source": "git://github.com/fxos-components/device-orientation.git",
"_target": "*",
"_originalSource": "fxos-components/device-orientation"
}

View 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"
]
}

View file

@ -0,0 +1,192 @@
(function(define){define(function(require,exports,module){
'use strict';
// The time interval is allowed between two orientation change event
const ORIENTATION_CHANGE_INTERVAL = 300;
// The maximum sample inter-arrival time in milliseconds. If the acceleration
// samples are further apart than this amount in time, we reset the state of
// the low-pass filter and orientation properties. This helps to handle
// boundary conditions when the app becomes invisisble, wakes from suspend or
// there is a significant gap in samples.
const MAX_MOTION_FILTER_TIME = 1000;
// Filtering adds latency proportional the time constant (inversely
// proportional to the cutoff frequency) so we don't want to make the time
// constant too large or we can lose responsiveness. Likewise we don't want
// to make it too small or we do a poor job suppressing acceleration spikes.
// Empirically, 100ms seems to be too small and 500ms is too large. Android
// default is 200. The original version is from:
// http://mxr.mozilla.org/mozilla-central/source/widget/gonk/
// ProcessOrientation.cpp
const MOTION_FILTER_TIME_CONSTANT = 200;
var lastMotionFilteredTime = 0;
var lastMotionData = {x: 0, y: 0, z: 0, t: 0};
var pendingOrientation = null;
var orientationChangeTimer = 0;
var eventListeners = {'orientation': []};
function applyFilter(x, y, z) {
var now = new Date().getTime();
var filterReset = false;
// The motion event is too far from last filtered data, reset the data.
// This may be the case of hide app and go back.
if (now > lastMotionData.t + MAX_MOTION_FILTER_TIME) {
// clear data to re-initialize it.
lastMotionData.x = 0;
lastMotionData.y = 0;
lastMotionData.z = 0;
filterReset = true;
}
// applying the exponential moving average to x, y, z, when we already have
// value of it.
if (lastMotionData.x || lastMotionData.y || lastMotionData.z) {
// use time to calculate alpha
var diff = now - lastMotionFilteredTime;
var alpha = diff / (MOTION_FILTER_TIME_CONSTANT + diff);
// weight the x, y, z with alpha
x = alpha * (x - lastMotionData.x) + lastMotionData.x;
y = alpha * (y - lastMotionData.y) + lastMotionData.y;
z = alpha * (z - lastMotionData.z) + lastMotionData.z;
}
// update the filter state.
lastMotionData.x = x;
lastMotionData.y = y;
lastMotionData.z = z;
lastMotionData.t = now;
return filterReset;
}
function calcOrientation(x, y) {
// use atan2(-x, y) to calculate the rotation on z axis.
var orientationAngle = (Math.atan2(-x, y) * 180 / Math.PI);
// The value range of atan2 is [-180, 180]. To have the [0, 360] value
// range, we need to add 360 degrees when the angle is less than 0.
if (orientationAngle < 0) {
orientationAngle += 360;
}
// find the nearest orientation.
// If an angle is >= 45 degrees, we view it as 90 degrees. If an angle is <
// 45, we view it as 0 degree.
var orientation = (((orientationAngle + 45) / 90) >> 0) % 4 * 90;
return orientation;
}
function handleMotionEvent(e) {
if (!e.accelerationIncludingGravity) {
return;
}
var filterReset = applyFilter(e.accelerationIncludingGravity.x,
e.accelerationIncludingGravity.y,
e.accelerationIncludingGravity.z);
// We don't need to process the event when filter is reset or no data.
if (filterReset) {
return;
}
var x = lastMotionData.x;
var y = lastMotionData.y;
var z = lastMotionData.z;
// We only want to measure gravity, so ignore events when there is
// significant acceleration in addition to gravity because this means the
// user is moving the phone.
if ((x * x + y * y + z * z) > 110) {
return;
}
// If the camera is close to horizontal (pointing up or down) then we can't
// tell what orientation the user intends, so we just return now without
// changing the orientation. The constant 9.2 is the force of gravity (9.8)
// times the cosine of 20 degrees. So if the phone is within 20 degrees of
// horizontal, we will never change the orientation.
if (z > 9.2 || z < -9.2) {
return;
}
var orientation = calcOrientation(x, y);
if (orientation === pendingOrientation) {
return;
}
// When phone keeps the same orientation for ORIENTATION_CHANGE_INTERVAL
// time interval, we change the orientation. Otherwrise the change is
// cancelled. This may be that user rotates phone rapidly but captured by
// device motion.
if (orientationChangeTimer) {
window.clearTimeout(orientationChangeTimer);
}
// If we don't have any current orientation, then send an event right away
// Otherwise, wait to make sure we're stable before sending it.
if (pendingOrientation === null) {
pendingOrientation = orientation;
fireOrientationChangeEvent(pendingOrientation);
}
else {
// create timer for waiting to rotate the phone
pendingOrientation = orientation;
orientationChangeTimer = window.setTimeout(function doOrient() {
fireOrientationChangeEvent(pendingOrientation);
orientationChangeTimer = 0;
}, ORIENTATION_CHANGE_INTERVAL);
}
}
function start() {
// Reset our state so that the first devicemotion event we get
// will always generate an orientation event.
pendingOrientation = null;
window.addEventListener('devicemotion', handleMotionEvent);
}
function stop() {
window.removeEventListener('devicemotion', handleMotionEvent);
if (orientationChangeTimer) {
clearTimeout(orientationChangeTimer);
orientationChangeTimer = 0;
}
}
function addEventListener(type, listener) {
if (eventListeners[type] && listener) {
eventListeners[type].push(listener);
}
}
function removeEventListener(type, listener) {
if (!eventListeners[type]) {
return;
}
var idx = eventListeners[type].indexOf(listener);
if (idx > -1) {
eventListeners.slice(idx, 1);
}
}
function fireOrientationChangeEvent(orientation) {
eventListeners.orientation.forEach(function(listener) {
if (listener.handleEvent) {
listener.handleEvent(orientation);
} else if ((typeof listener) === 'function') {
listener(orientation);
}
});
}
module.exports = {
start: start,
stop: stop,
on: addEventListener,
off: removeEventListener
};
});})((function(n,w){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;};})('device-orientation',this));

View file

@ -0,0 +1,17 @@
{
"name": "device-orientation",
"description": "Tracks device orientation using accelerometer when orientation locked.",
"main": "device-orientation.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/fxos-components/device-orientation.git"
},
"bugs": {
"url": "https://github.com/fxos-components/device-orientation/issues"
},
"homepage": "https://github.com/fxos-components/device-orientation",
"version": "1.0.0"
}

View file

@ -0,0 +1,29 @@
{
"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"
},
"_release": "0.1.4",
"_resolution": {
"type": "version",
"tag": "v0.1.4",
"commit": "c56942e7e15abd42f38d3634e540739f96d545d3"
},
"_source": "git://github.com/fxos-components/drag.git",
"_target": "0.1.4",
"_originalSource": "fxos-components/drag"
}

View 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"
}
}

View file

@ -0,0 +1,229 @@
(function(define){'use strict';define(function(require,exports,module){
/*globals define*//*jshint node:true*/
/**
* Dependencies
*/
var events = require('evt');
/**
* Exports
*/
module.exports = Drag;
/**
* Mixin Emitter
*/
events(Drag.prototype);
/**
* Pointer event abstraction to make
* it work for touch and mouse.
*
* @type {Object}
*/
var pointer = [
{ down: 'touchstart', up: 'touchend', move: 'touchmove' },
{ down: 'mousedown', up: 'mouseup', move: 'mousemove' }
]['ontouchstart' in window ? 0 : 1];
/**
* Drag creates a draggable 'handle' element,
* constrained within a 'container' element.
*
* Drag instances emit useful events and provides
* methods to support common draggable UI use-cases,
* like `snapToClosestEdge`
*
* In Gaia we use `Drag` for our switch components.
*
* Usage:
*
* // Create a new `Drag`
* var drag = new Drag({
* container: myContainer,
* handle: myHandle
* });
*
* // Once elements are in the DOM we
* // need to take some measurements
* drag.updateDimensions();
*
* Options:
*
* - {Element} `container`
* - {Element} `handle`
*
* @param {Object} options
*/
function Drag(options) {
this.container = { el: options.container };
this.handle = { el: options.handle };
this.onTouchStart = this.onTouchStart.bind(this);
this.onTouchMove = this.onTouchMove.bind(this);
this.onTouchEnd = this.onTouchEnd.bind(this);
this.slideDuration = options.slideDuration || 140;
this.tapTime = options.tapTime || 180;
this.bindEvents();
}
Drag.prototype.bindEvents = function() {
this.container.el.addEventListener(pointer.down, this.onTouchStart);
};
Drag.prototype.onTouchStart = function(e) {
this.updateDimensions();
this.touch = ~e.type.indexOf('mouse') ? e : e.touches[0];
this.firstTouch = this.touch;
this.startTime = e.timeStamp;
addEventListener(pointer.move, this.onTouchMove);
addEventListener(pointer.up, this.onTouchEnd);
};
Drag.prototype.onTouchMove = function(e) {
e.preventDefault();
e = ~e.type.indexOf('mouse') ? e : e.touches[0];
var delta = {
x: e.clientX - this.touch.clientX,
y: e.clientY - this.touch.clientY
};
this.dragging = true;
this.move(delta);
this.touch = e;
};
Drag.prototype.onTouchEnd = function(e) {
var tapped = (e.timeStamp - this.startTime) < this.tapTime;
this.dragging = false;
removeEventListener(pointer.move, this.onTouchMove);
removeEventListener(pointer.up, this.onTouchEnd);
if (tapped) { this.emit('tapped', e); }
else { this.emit('ended', e); }
};
Drag.prototype.move = function(delta) {
this.translate({
x: this.handle.position.x + delta.x,
y: this.handle.position.y + delta.y
});
};
Drag.prototype.set = function(pos) {
if (!this.edges) { this.pendingSet = pos; return; }
var x = typeof pos.x === 'string' ? this.edges[pos.x] : (pos.x || 0);
var y = typeof pos.y === 'string' ? this.edges[pos.y] : (pos.y || 0);
this.translate({ x: x, y: y });
};
Drag.prototype.snapToClosestEdge = function() {
var edges = this.getClosestEdges();
this.translate({
x: this.edges[edges.x],
y: this.edges[edges.y]
});
this.emit('snapped', edges);
};
Drag.prototype.translate = function(options) {
var position = this.clamp(options);
var translate = 'translate(' + position.x + 'px,' + position.y + 'px)';
var ratio = {
x: (position.x / this.max.x) || 0,
y: (position.y / this.max.y) || 0
};
this.setTransition(position);
// Set the transform to move the handle
this.handle.el.style.transform = translate;
// Update the handle position reference
this.handle.position = position;
// Emit event with useful data
this.emit('translate', {
position: {
px: position,
ratio: ratio
}
});
};
Drag.prototype.clamp = function(position) {
return {
x: Math.max(this.min.x, Math.min(this.max.x, position.x)),
y: Math.max(this.min.y, Math.min(this.max.y, position.y)),
};
};
/**
* [setTransition description]
* @param {[type]} position [description]
*/
Drag.prototype.setTransition = function(position) {
var duration = !this.dragging ? this.transitionDuration(position) : 0;
this.handle.el.style.transitionDuration = duration + 'ms';
};
Drag.prototype.transitionDuration = function(position) {
var current = this.handle.position;
var distanceX = Math.abs(current.x - position.x);
var distanceY = Math.abs(current.y - position.y);
var distance = Math.max(distanceX, distanceY);
var axis = distanceY > distanceX ? 'y' : 'x';
var ratio = distance / this.max[axis];
return this.slideDuration * ratio;
};
Drag.prototype.getClosestEdges = function() {
return {
x: this.handle.position.x <= (this.max.x / 2) ? 'left' : 'right',
y: this.handle.position.y <= (this.max.y / 2) ? 'top' : 'bottom'
};
};
Drag.prototype.updateDimensions = function() {
var container = this.container.el.getBoundingClientRect();
var handle = this.handle.el.getBoundingClientRect();
this.min = { x: 0, y: 0 };
this.max = {
x: container.width - handle.width,
y: container.height - handle.height
};
this.edges = {
top: this.min.y,
right: this.max.x,
bottom: this.max.y,
left: this.min.x
};
this.handle.position = {
x: handle.left - container.left,
y: handle.top - container.top
};
this.clearPendingSet();
};
Drag.prototype.clearPendingSet = function() {
if (!this.pendingSet) { return; }
this.set(this.pendingSet);
delete this.pendingSet;
};
});})((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;};})('drag',this));

View file

@ -0,0 +1,25 @@
{
"name": "drag",
"version": "0.1.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "gaia-test-unit"
},
"repository": {
"type": "git",
"url": "https://github.com/gaia-components/drag.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/gaia-components/drag/issues"
},
"homepage": "https://github.com/gaia-components/drag",
"dependencies": {
"evt": "git://github.com/gaia-components/evt#v0.4.0"
},
"devDependencies": {
"gaia-test": "git://github.com/gaia-components/gaia-test"
}
}

View file

@ -0,0 +1,28 @@
{
"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"
],
"_release": "0.4.0",
"_resolution": {
"type": "version",
"tag": "v0.4.0",
"commit": "3593dd6b4dd19f683d00fd0eaf2581c11a128da6"
},
"_source": "git://github.com/fxos-components/evt.git",
"_target": "~0.4.0",
"_originalSource": "fxos-components/evt"
}

View 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"
]
}

154
gaia/apps/camera/bower_components/evt/index.js vendored Executable file
View file

@ -0,0 +1,154 @@
/**
* Evt
*
* A super lightweight
* event emitter library.
*
* @version 0.3.3
* @author Wilson Page <wilson.page@me.com>
*/
;(function() {
/**
* Locals
*/
var proto = Events.prototype;
var slice = [].slice;
/**
* Creates a new event emitter
* instance, or if passed an
* object, mixes the event logic
* into it.
*
* @param {Object} obj
* @return {Object}
*/
function Events(obj) {
if (!(this instanceof Events)) return new Events(obj);
if (obj) return mixin(obj, proto);
}
/**
* Registers a callback
* with an event name.
*
* @param {String} name
* @param {Function} cb
* @return {Event}
*/
proto.on = function(name, cb) {
this._cbs = this._cbs || {};
(this._cbs[name] || (this._cbs[name] = [])).push(cb);
return this;
};
/**
* Attach a callback that once
* called, detaches itself.
*
* TODO: Implement `.off()` to work
* with `once()` callbacks.
*
* @param {String} name
* @param {Function} cb
* @public
*/
proto.once = function(name, cb) {
this.on(name, one);
function one() {
cb.apply(this, arguments);
this.off(name, one);
}
};
/**
* Removes a single callback,
* or all callbacks associated
* with the passed event name.
*
* @param {String} name
* @param {Function} cb
* @return {Event}
*/
proto.off = function(name, cb) {
this._cbs = this._cbs || {};
if (!name) { this._cbs = {}; return; }
if (!cb) { return delete this._cbs[name]; }
var cbs = this._cbs[name] || [];
var i;
while (cbs && ~(i = cbs.indexOf(cb))) { cbs.splice(i, 1); }
return this;
};
/**
* Fires an event, triggering
* all callbacks registered on this
* event name.
*
* @param {String} name
* @return {Event}
*/
proto.fire = proto.emit = function(options) {
var cbs = this._cbs = this._cbs || {};
var name = options.name || options;
var batch = (cbs[name] || []).concat(cbs['*'] || []);
var ctx = options.ctx || this;
if (batch.length) {
this._fireArgs = arguments;
var args = slice.call(arguments, 1);
while (batch.length) {
batch.shift().apply(ctx, args);
}
}
return this;
};
proto.firer = function(name) {
var self = this;
return function() {
var args = slice.call(arguments);
args.unshift(name);
self.fire.apply(self, args);
};
};
/**
* Util
*/
/**
* Mixes in the properties
* of the second object into
* the first.
*
* @param {Object} a
* @param {Object} b
* @return {Object}
*/
function mixin(a, b) {
for (var key in b) a[key] = b[key];
return a;
}
/**
* Expose 'Event' (UMD)
*/
if (typeof exports === 'object') {
module.exports = Events;
} else if (typeof define === 'function' && define.amd) {
define(function(){ return Events; });
} else {
window.evt = Events;
}
})();

View file

@ -0,0 +1,28 @@
{
"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"
],
"homepage": "https://github.com/gaia-components/font-fit",
"_release": "0.3.1",
"_resolution": {
"type": "version",
"tag": "v0.3.1",
"commit": "07a497519b335a3d86a66268f990d8844b83d517"
},
"_source": "git://github.com/gaia-components/font-fit.git",
"_target": "~0.3.0",
"_originalSource": "gaia-components/font-fit"
}

View 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"
]
}

View file

@ -0,0 +1,132 @@
;(function(define){define(function(require,exports,module){
'use strict';
/**
* Simple logger.
*
* @return {Function}
*/
var debug = 0 ? console.log.bind(console) : function(){};
/**
* Global canvas cache.
*
* @type {Object}
*/
var cache = {};
/**
* Default min/max font-size.
*
* @type {Number}
*/
var MIN = 16;
var MAX = 24;
/**
* The number of pixels to subtract from
* the given `config.space` to ensure
* HTML text doesn't overflow container.
*
* Ideally we would use 1px, but in some
* cases italicised text in canvas is ~2px
* longer than the same text in HTML.
*
* http://bugzil.la/1126391
*
* @type {Number}
*/
var BUFFER = 3;
/**
* Get the font-size that closest fits
* the given space with the given font.
*
* Config:
*
* - {String} `text` The text string
* - {String} `font` Font shorthand string
* - {Number} `space` Width (px) to fit the text into
* - {Number} `min` Min font-size (px) (optional)
* - {Number} `max` Max font-size (px) (optional)
*
* @param {Object} config
* @return {Object} {fontSize,overflowing,textWidth}
*/
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
};
};
/**
* Get the width of the given text
* with the given font style.
*
* @param {String} text
* @param {String} font (CSS shorthand)
* @return {Number} (px)
*/
function getTextWidth(text, font) {
var ctx = getCanvasContext(font);
var width = ctx.measureText(text).width;
debug('got text width', width);
return width;
}
/**
* Get a canvas context configured
* to the given font style.
*
* @param {String} font
* @return {CanvasRenderingContext2D}
*/
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;
}
/**
* Trim leading, trailing
* and excess whitespace.
*
* @param {String} text
* @return {String}
*/
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));

View file

@ -0,0 +1,28 @@
{
"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"
],
"homepage": "https://github.com/gaia-components/gaia-component",
"_release": "0.4.0",
"_resolution": {
"type": "version",
"tag": "v0.4.0",
"commit": "8667e09f44553c32f1a97860cc2a18a0d9d313ef"
},
"_source": "git://github.com/gaia-components/gaia-component.git",
"_target": "~0.4.0",
"_originalSource": "gaia-components/gaia-component"
}

View 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"
]
}

View file

@ -0,0 +1,460 @@
/* globals define */
;(function(define){'use strict';define(function(require,exports,module){
/**
* Locals
*/
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() {};
/**
* Register a new component.
*
* @param {String} name
* @param {Object} props
* @return {constructor}
* @public
*/
exports.register = function(name, props) {
var baseProto = getBaseProto(props.extends);
var template = props.template || baseProto.templateString;
// Components are extensible by default but can be declared
// as non extensible as an optimization to avoid
// storing the template strings
var extensible = props.extensible = props.hasOwnProperty('extensible')?
props.extensible : true;
// Clean up
delete props.extends;
// Pull out CSS that needs to be in the light-dom
if (template) {
// Stores the string to be reprocessed when
// a new component extends this one
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;
}
// Inject global CSS into the document,
// and delete as no longer needed
injectGlobalCss(props.globalCss);
delete props.globalCss;
// Merge base getter/setter attributes with the user's,
// then define the property descriptors on the prototype.
var descriptors = mixin(props.attrs || {}, base.descriptors);
// Store the orginal descriptors somewhere
// a little more private and delete the original
props._attrs = props.attrs;
delete props.attrs;
// Create the prototype, extended from base and
// define the descriptors directly on the prototype
var proto = createProto(baseProto, props);
Object.defineProperties(proto, descriptors);
// Register the custom-element and return the constructor
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();
},
/**
* It is very common to want to keep object
* properties in-sync with attributes,
* for example:
*
* el.value = 'foo';
* el.setAttribute('value', 'foo');
*
* So we support an object on the prototype
* named 'attrs' to provide a consistent
* way for component authors to define
* these properties. When an attribute
* changes we keep the attr[name]
* up-to-date.
*
* @param {String} name
* @param {String||null} from
* @param {String||null} to
*/
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();
},
/**
* A convenient method for setting up
* a shadow-root using the defined template.
*
* @return {ShadowRoot}
*/
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;
},
/**
* Sets an attribute internally
* and externally. This is so that
* we can style internal shadow-dom
* content.
*
* @param {String} name
* @param {String} value
*/
setAttr: function(name, value) {
var internal = this.shadowRoot.firstElementChild;
setAttribute.call(internal, name, value);
setAttribute.call(this, name, value);
},
/**
* Removes an attribute internally
* and externally. This is so that
* we can style internal shadow-dom
* content.
*
* @param {String} name
* @param {String} 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
}
}
};
/**
* The default base prototype to use
* when `extends` is undefined.
*
* @type {Object}
*/
var defaultPrototype = createProto(HTMLElement.prototype, base.properties);
/**
* Returns a suitable prototype based
* on the object passed.
*
* @private
* @param {HTMLElementPrototype|undefined} proto
* @return {HTMLElementPrototype}
*/
function getBaseProto(proto) {
if (!proto) { return defaultPrototype; }
proto = proto.prototype || proto;
return !proto.GaiaComponent ?
createProto(proto, base.properties) : proto;
}
/**
* Extends the given proto and mixes
* in the given properties.
*
* @private
* @param {Object} proto
* @param {Object} props
* @return {Object}
*/
function createProto(proto, props) {
return mixin(Object.create(proto), props);
}
/**
* Detects presence of shadow-dom
* CSS selectors.
*
* @private
* @return {Boolean}
*/
var hasShadowCSS = (function() {
var div = document.createElement('div');
try { div.querySelector(':host'); div.querySelector('::content'); return true; }
catch (e) { return false; }
})();
/**
* Regexs used to extract shadow-css
*
* @type {Object}
*/
var regex = {
shadowCss: /(?:\:host|\:\:content)[^{]*\{[^}]*\}/g,
':host': /(?:\:host)/g,
':host()': /\:host\((.+)\)(?: \:\:content)?/g,
':host-context': /\:host-context\((.+)\)([^{,]+)?/g,
'::content': /(?:\:\:content)/g
};
/**
* Extracts the :host and ::content rules
* from the shadow-dom CSS and rewrites
* them to work from the <style scoped>
* injected at the root of the component.
*
* @private
* @return {String}
*/
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, ' '); // excess whitespace
} else {
lightCss += match
.replace(regex[':host()'], name + '$1')
.replace(regex[':host'], name)
.replace(regex['::content'], name);
}
return '';
});
}
return {
template: template,
lightCss: lightCss,
globalCss: globalCss
};
}
/**
* Some CSS rules, such as @keyframes
* and @font-face don't work inside
* scoped or shadow <style>. So we
* have to put them into 'global'
* <style> in the head of the
* document.
*
* @private
* @param {String} css
*/
function injectGlobalCss(css) {
if (!css) {return;}
var style = document.createElement('style');
style.innerHTML = css.trim();
headReady().then(function() {
document.head.appendChild(style);
});
}
/**
* Resolves a promise once document.head is ready.
*
* @private
*/
function headReady() {
return new Promise(function(resolve) {
if (document.head) { return resolve(); }
window.addEventListener('load', function fn() {
window.removeEventListener('load', fn);
resolve();
});
});
}
/**
* The Gecko platform doesn't yet have
* `::content` or `:host`, selectors,
* without these we are unable to style
* user-content in the light-dom from
* within our shadow-dom style-sheet.
*
* To workaround this, we clone the <style>
* node into the root of the component,
* so our selectors are able to target
* light-dom content.
*
* @private
*/
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;
}
/**
* Convert hyphen separated
* string to camel-case.
*
* Example:
*
* toCamelCase('foo-bar'); //=> 'fooBar'
*
* @private
* @param {String} string
* @return {String}
*/
function toCamelCase(string) {
return string.replace(/-(.)/g, function replacer(string, p1) {
return p1.toUpperCase();
});
}
/**
* Observer (singleton)
*
* @type {MutationObserver|undefined}
*/
var dirObserver;
/**
* Workaround for bug 1100912: applies a `dir` attribute to all shadowRoot
* children so that :-moz-dir() selectors work on shadow DOM elements.
*
* In order to keep decent performances, the `dir` is the component dir if
* defined, or the document dir otherwise. This won't work if the component's
* direction is defined by CSS or inherited from a parent container.
*
* This method should be removed when bug 1100912 is fixed.
*
* @private
* @param {WebComponent}
*/
function setInnerDirAttributes(component) {
var dir = component.dir || document.dir;
Array.from(component.shadowRoot.children).forEach(element => {
if (element.nodeName !== 'STYLE') {
element.dir = dir;
}
});
}
/**
* Observes the document `dir` (direction) attribute and when it changes:
* - dispatches a global `dirchanged` event;
* - forces the `dir` attribute of all shadowRoot children.
*
* Components can listen to this event and make internal changes if needed.
*
* @private
*/
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'));
}
}
/**
* Copy the values of all properties from
* source object `target` to a target object `source`.
* It will return the target object.
*
* @private
* @param {Object} target
* @param {Object} source
* @returns {Object}
*/
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));

View file

@ -0,0 +1,39 @@
{
"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"
},
"_release": "0.9.5",
"_resolution": {
"type": "version",
"tag": "v0.9.5",
"commit": "aa4c9c5cb83ce8813c8472860671ea514f07274f"
},
"_source": "git://github.com/fxos-components/gaia-header.git",
"_target": "~0.9.5",
"_originalSource": "fxos-components/gaia-header"
}

View file

@ -0,0 +1,33 @@
{
"camelcase": false,
"curly": true,
"forin": false,
"latedef": "nofunc",
"newcap": false,
"noarg": true,
"node": true,
"nonew": true,
"quotmark": "single",
"undef": true,
"unused": "vars",
"strict": true,
"trailing": true,
"maxlen": 80,
"eqnull": true,
"esnext": true,
"expr": true,
"globalstrict": true,
"maxerr": 1000,
"regexdash": true,
"laxcomma": true,
"proto": true,
"browser": true,
"devel": true,
"nonstandard": true,
"worker": true,
"-W078": true
}

View 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"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
{
"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"
],
"version": "1.0.2",
"_release": "1.0.2",
"_resolution": {
"type": "version",
"tag": "v1.0.2",
"commit": "42dfa3ed9a9ffa09733dc8db08a06d3dd9d0a081"
},
"_source": "git://github.com/fxos-components/gaia-icons.git",
"_target": "~1.0.2",
"_originalSource": "fxos-components/gaia-icons"
}

View file

@ -0,0 +1,33 @@
{
"camelcase": false,
"curly": true,
"forin": false,
"latedef": "nofunc",
"newcap": false,
"noarg": true,
"node": true,
"nonew": true,
"quotmark": "single",
"undef": true,
"unused": "vars",
"strict": true,
"trailing": true,
"maxlen": 80,
"eqnull": true,
"esnext": true,
"expr": true,
"globalstrict": true,
"maxerr": 1000,
"regexdash": true,
"laxcomma": true,
"proto": true,
"browser": true,
"devel": true,
"nonstandard": true,
"worker": true,
"-W078": true
}

View file

@ -0,0 +1,74 @@
/* Some BiDi-proof icons
* In the future, we might handle this at the font level, see
* https://bugzilla.mozilla.org/show_bug.cgi?id=1214127
*/
[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';
}

View 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"
]
}

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,40 @@
/* Generated by grunt-webfont */
/* Based on https://github.com/endtwist/fontcustom/blob/master/lib/fontcustom/templates/fontcustom.css */
@font-face {
font-family: "gaia-icons";
src: url("fonts/gaia-icons.ttf") format("truetype");
font-weight: 500;
font-style: normal;
}
[data-icon]:before,
.ligature-icons {
font-family: "gaia-icons";
content: attr(data-icon);
display: inline-block;
font-weight: 500;
font-style: normal;
text-decoration: inherit;
text-transform: none;
text-rendering: optimizeLegibility;
font-size: 30px;
-webkit-font-smoothing: antialiased;
}
[data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):before {
/* Highlight cases where icons are not localized and still visible to screen
reader */
background: rgba(255, 0, 0, 0.4);
}
[data-icon]:not([data-l10n-id]):not([aria-label]):not([aria-hidden="true"]):after {
background: #333;
color: #fff;
position: absolute;
width: 20rem;
border-radius: 0.5rem;
padding: 0.5rem;
content: "Icon is not localized and is still visible to screen reader. Please add data-l10n-id or aria-label for localization or use aria-hidden to hide the icon from screen reader.";
z-index: 9999;
}

View file

@ -0,0 +1,38 @@
/* global define */
(function(define){'use strict';define(function(require,exports,module){
/**
* Exports
*/
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));

View file

@ -0,0 +1,35 @@
{
"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"
},
"_release": "0.4.7",
"_resolution": {
"type": "version",
"tag": "v0.4.7",
"commit": "a1b3220e6ae53503b0809eb04de22f28bc701612"
},
"_source": "git://github.com/fxos-components/gaia-theme.git",
"_target": "~0.4.1",
"_originalSource": "fxos-components/gaia-theme"
}

View file

@ -0,0 +1,4 @@
{
"strict": false,
"node": true
}

View 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"
}
}

View file

@ -0,0 +1,244 @@
/*
Gaia theme variables
*/
:root {
/** Grey Colors
---------------------------------------------------------*/
--color-alpha: #333333;
--color-beta: #ffffff;
--color-gamma: #4d4d4d;
--color-delta: #5f5f5f;
--color-epsilon: #858585;
--color-zeta: #a6a6a6;
--color-eta: #c7c7c7;
--color-theta: #e7e7e7;
--color-iota: #f4f4f4;
/** Brand Colors
---------------------------------------------------------*/
--color-darkblue: #00539f;
--color-blue: #00caf2;
--color-turquoise: #27c8c2;
--color-darkorange: #e66000;
--color-orange: #ff9500;
--color-yellow: #ffcb00;
--color-violet: #c40c84;
--color-warning: #fbbd3c;
--color-destructive: #e2443a;
--color-preffered: #00ba91;
/** Background
---------------------------------------------------------*/
--background: var(--color-beta);
--background-plus: var(--color-theta);
--background-minus: var(--color-zeta);
/** Borders
---------------------------------------------------------*/
--border-color: var(--color-theta);
/** Text Color
---------------------------------------------------------*/
--text-color: var(--color-gamma);
--text-color-minus: var(--color-eta);
/** Button
---------------------------------------------------------*/
--button-background: var(--color-iota);
/** Links
---------------------------------------------------------*/
--link-color: var(--highlight-color);
/** Inputs
---------------------------------------------------------*/
--input-background: var(--color-beta);
--input-color: var(--color-alpha);
--input-clear-background: #909ca7;
/** Buttons
---------------------------------------------------------*/
--button-box-shadow:
inset 0 -1px 0 #d8d8d8,
inset 0 1px 0 #eeeeee;
--button-box-shadow-active: var(--button-box-shadow);
/** Header
---------------------------------------------------------*/
--header-disabled-button-color: rgba(255,255,255,0.3);
/** Transitions
---------------------------------------------------------*/
--transition-duration: 0.2s;
}
.theme-productivity {
/** Brand Color
---------------------------------------------------------*/
--highlight-color: var(--color-orange);
/** Header
---------------------------------------------------------*/
--header-background: var(--highlight-color);
--header-color: var(--color-beta);
}
.theme-communications {
/** Brand Color
---------------------------------------------------------*/
--highlight-color: var(--color-turquoise);
/** Links
---------------------------------------------------------*/
--link-color: #177874;
/** Header
---------------------------------------------------------*/
--header-background: var(--highlight-color);
--header-color: var(--color-beta);
--header-icon-color: var(--color-beta);
--header-button-color: var(--link-color);
}
.theme-settings {
/** Background
---------------------------------------------------------*/
--background: var(--color-iota);
--background-plus: var(--color-beta);
--background-minus: var(--color-epsilon);
--background-minus-minus: var(--color-zeta);
/** Highlight
---------------------------------------------------------*/
--highlight-color: var(--color-blue);
/** Borders
---------------------------------------------------------*/
--border-color: var(--color-theta);
/** Text Color
---------------------------------------------------------*/
--text-color: var(--color-gamma);
/** Titles
---------------------------------------------------------*/
--title-color: var(--background-minus);
/** Buttons
---------------------------------------------------------*/
--button-background: var(--background-plus);
/** Switch
---------------------------------------------------------*/
--switch-background: var(--background-minus-minus);
/** Checkbox
---------------------------------------------------------*/
--checkbox-border-color: var(--background-minus-minus);
/** Header
---------------------------------------------------------*/
--header-color: var(--title-color);
--header-background: var(--background);
--header-button-color: var(--highlight-color);
--header-disabled-button-color: var(--color-theta);
--header-action-button-color: var(--title-color);
}
.theme-media {
/** Highlight Color
---------------------------------------------------------*/
--highlight-color: var(--color-blue);
/** Background
---------------------------------------------------------*/
--background: var(--color-alpha);
--background-plus: var(--color-gamma);
--background-minus: #2B2B2B;
--background-minus-minus: #1a1a1a;
/** Text Color
---------------------------------------------------------*/
--text-color: var(--color-beta);
/** Button
---------------------------------------------------------*/
--button-background: var(--background-plus);
/** Header
---------------------------------------------------------*/
--header-background: var(--background);
--header-icon-color: var(--text-color);
--header-button-color: var(--highlight-color);
/** Borders
---------------------------------------------------------*/
--border-color: var(--color-gamma);
/** Inputs
---------------------------------------------------------*/
--input-background: var(--background-plus);
/** Text Input
---------------------------------------------------------*/
--text-input-background: var(--background-minus);
/** Switch
---------------------------------------------------------*/
--switch-head-border-color: var(--background-minus-minus);
--switch-background: var(--background-minus-minus);
/** Checkbox
---------------------------------------------------------*/
--checkbox-border-color: var(--background-minus-minus);
/** Buttons
---------------------------------------------------------*/
--button-box-shadow: none;
--button-box-shadow-active: none;
}

View file

@ -0,0 +1,111 @@
(function(define){'use strict';define(function(require,exports,module){
/*jshint esnext:true*/
/**
* Extend from the `HTMLElement` prototype
*
* @type {Object}
*/
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));

View file

@ -0,0 +1,30 @@
{
"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"
},
"_release": "0.1.0",
"_resolution": {
"type": "version",
"tag": "v0.1.0",
"commit": "7464471d4da0743ba4042918083a3429f0fc0fd2"
},
"_source": "git://github.com/fxos-components/model.git",
"_target": "~0.1.0",
"_originalSource": "fxos-components/model"
}

View 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"
}
}

View file

@ -0,0 +1,104 @@
(function(define){define(function(require,exports,module){
'use strict';
/**
* Dependencies
*/
var events = require('evt');
/**
* Exports
*/
module.exports = Model;
function Model(obj) {
if (!(this instanceof Model)) { return mix(obj, Model.prototype); }
this.reset(obj, { silent: true });
this.id = obj.id || obj.key;
}
Model.prototype = events({
/**
* Returns the value of the given
* key, or if not key is given a
* shallow clone of the model is
* returned.
*
* We check `aguments.length` so that
* when calling `get()` with an unknown
* key, `undefined` is returned and not
* the entire model.
*
* Example:
*
* model.get(); //=> { ... }
* model.get('undefinedKey'); //=> undefined
*
* @param {String} key
* @return {*}
* @public
*/
get: function(key) {
var data = this._getData();
return arguments.length ? data[key] : mix({}, data);
},
set: function(key, value, options) {
options = typeof key === 'object' ? value : options;
var silent = options && options.silent;
var data = this._getData();
var keys;
switch (typeof key) {
case 'string':
data[key] = value;
if (!silent) {
this.onKeyChange(key);
this.emit('change', [key]);
}
return;
case 'object':
mix(data, key);
if (!silent) {
keys = Object.keys(key);
keys.forEach(this.onKeyChange, this);
this.emit('change', keys);
}
return;
}
},
setter: function(key, value1) {
return (function(value2) { this.set(key, value1 || value2); }).bind(this);
},
reset: function(data, options) {
if (!data) { return; }
var silent = options && options.silent;
var isArray = data instanceof Array;
this._data = !isArray ? mix({}, data) : data;
if (!silent) { this.emit('reset'); }
},
onKeyChange: function(key) {
var data = this._getData();
this.emit('change:' + key, data[key]);
},
_getData: function() {
this._data = this._data || {};
return this._data;
}
});
function mix(a, b) {
for (var key in b) { a[key] = b[key]; }
return a;
}
});})((function(n,w){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;};})('model',this));

View file

@ -0,0 +1,13 @@
{
"name": "requirejs",
"_cacheHeaders": {
"ETag": "\"7f31fa20417d5e7f9bd26dde74d8678c34ff20af\"",
"Content-Length": "83083",
"Content-Type": "text/plain; charset=utf-8"
},
"_release": "e-tag:7f31fa204",
"main": "index.js",
"_source": "https://raw.githubusercontent.com/jrburke/requirejs/2.1.14/require.js",
"_target": "*",
"_originalSource": "https://raw.githubusercontent.com/jrburke/requirejs/2.1.14/require.js"
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,30 @@
{
"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"
},
"_release": "0.1.1",
"_resolution": {
"type": "version",
"tag": "v0.1.1",
"commit": "5363c45a7150dcdba00815bea91611c14237a9e6"
},
"_source": "git://github.com/fxos-components/view.git",
"_target": "~0.1.1",
"_originalSource": "fxos-components/view"
}

View 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"
}
}

View file

@ -0,0 +1,292 @@
(function(define){define(function(require,exports,module){
'use strict';
/**
* Module Dependencies
*/
var events = require('evt');
/**
* Locals
*/
var counter = 1;
var noop = function() {};
/**
* Exports
*/
module.exports = View;
/**
* Base view class. Accepts
* or creates a root element
* which we template into.
*
* @constructor
*/
function View(options) {
options = options || {};
this.el = options.el || this.el || document.createElement(this.tag);
this.el.id = this.el.id || ('view' + counter++);
this.name = options.name || this.name;
this.els = {};
if (!this.el.className) {
if (this.name) this.el.className = this.name;
if (this.className) this.el.className += ' ' + this.className;
}
bindAll(this);
this.initialize.apply(this, arguments);
}
/**
* Base view prototype,
* mixed in event emitter.
*
* @type {Object}
*/
events(View.prototype);
// Allow for 'emit' or
// 'fire' to trigger events
View.prototype.fire = View.prototype.fire || View.prototype.emit;
/**
* Default tagName
*
* @type {String}
*/
View.prototype.tag = 'div';
View.prototype.name = 'noname';
/**
* Appends the root element
* to the given parent.
*
* @param {Element} parent
* @return {View}
*/
View.prototype.appendTo = function(parent) {
if (!parent) return this;
parent.appendChild(this.el);
this.fire('inserted');
return this;
};
/**
* Prepends the root element
* to the given parent.
*
* @param {Element} parent
* @return {View}
*/
View.prototype.prependTo = function(parent) {
if (!parent) return this;
var first = parent.firstChild;
if (first) parent.insertBefore(this.el, first);
else this.appendTo(parent);
this.fire('inserted');
return this;
};
/**
* Convenient shorthand
* querySelector.
*
* @param {String} query
* @return { Element | null}
*/
View.prototype.find = function(query) {
return this.el.querySelector(query);
};
/**
* Removes the element from
* its current DOM location.
*
* @param {Object} options
* @return {View}
*/
View.prototype.remove = function(options) {
var silent = options && options.silent;
var parent = this.el.parentNode;
if (!parent) return this;
parent.removeChild(this.el);
if (!silent) this.fire('remove');
return this;
};
View.prototype.set = function(key, value) {
value = value === undefined ? '' : value;
this.el.setAttribute(toDashed(key), value);
};
View.prototype.get = function(key) {
return this.el.getAttribute(key);
};
/**
* Returns a function that when called
* will .set() the given key.
*
* If a value is passed to .setter(),
* that value will always be used
* when the returned function is called.
* Else the value passed to the given
* function will be used.
*
* Example:
*
* var setter = this.setter('key', 'value');
* setter(); //=> this.set('key', 'value');
* setter('value2'); //=> this.set('key', 'value');
*
* var setter = this.setter('key');
* setter('value'); //=> this.set('key', 'value');
* setter(); //=> this.set('key');
*
* @param {String} key
* @param {*} value
* @return {Function}
*/
View.prototype.setter = function(key, forced) {
var self = this;
return function(passed) {
var value = forced !== undefined ? forced : passed;
self.set(key, value);
};
};
View.prototype.enable = function(key, value) {
switch(arguments.length) {
case 0:
value = true;
key = 'enabled';
break;
case 1:
if (typeof key === 'boolean') {
value = key;
key = 'enabled';
} else {
value = true;
key = key ? key + '-enabled' : 'enabled';
}
break;
default:
key = key ? key + '-enabled' : 'enabled';
}
this.set(key, !!value);
};
View.prototype.disable = function(key) {
this.enable(key, false);
};
View.prototype.enabler = function(key) {
return (function(value) { this.enable(key, value); }).bind(this);
};
View.prototype.hide = function(key) {
this.toggle(key, false);
};
View.prototype.show = function(key) {
this.toggle(key, true);
};
View.prototype.toggle = function(key, value) {
if (arguments.length === 1 && typeof key === 'boolean') {
value = key;
key = '';
} else {
key = key ? key + '-' : '';
}
this.el.classList.toggle(key + 'hidden', !value);
this.el.classList.toggle(key + 'visible', value);
};
/**
* Removes the element from
* it's current context, firing
* a 'destroy' event to allow
* views to perform cleanup.
*
* Then clears any internal
* references to aid GC.
*
* @return {[type]} [description]
*/
View.prototype.destroy = function(options) {
var noRemove = options && options.noRemove;
if (!noRemove) this.remove();
this.fire('destroy');
this.el = null;
};
View.prototype.toString = function() {
return '[object View]';
};
// Overwrite as required
View.prototype.initialize = noop;
View.prototype.template = function() { return ''; };
/**
* Extends the base view
* class with the given
* properties.
*
* TODO: Pull this out to
* standalone module.
*
* @param {Object} properties
* @return {Function}
*/
View.extend = function(props) {
var Parent = this;
// The extended constructor
// calls the parent constructor
var Child = function() {
Parent.apply(this, arguments);
};
Child.prototype = Object.create(Parent.prototype);
Child.extend = View.extend;
mixin(Child.prototype, props);
return Child;
};
function toDashed(s) {
return s.replace(/\W+/g, '-')
.replace(/([a-z\d])([A-Z])/g, '$1-$2')
.toLowerCase();
}
function mixin(a, b) {
for (var key in b) { a[key] = b[key]; }
return a;
}
function bindAll(object) {
var key;
var fn;
for (key in object) {
fn = object[key];
if (typeof fn === 'function') {
object[key] = fn.bind(object);
}
}
}
});})((function(n,w){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;};})('view',this));