diff --git a/Gruntfile.js b/Gruntfile.js
index df99d29..0b97138 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -2,6 +2,7 @@ module.exports = function(grunt) {
require('grunt-task-loader')(grunt);
grunt.initConfig({
+ pkg: require('./package.json'),
browserify: {
dev: {
files: [{
@@ -53,15 +54,6 @@ module.exports = function(grunt) {
}
}
},
- mochify: {
- options: {
- reporter: 'land'
- },
- tests: {
- src: 'test/**/*.js',
- options: '<%= browserify.dev.options %>'
- }
- },
less: {
dev: {
files: [{
@@ -90,12 +82,11 @@ module.exports = function(grunt) {
}]
}
},
- mochaTest: {
- tests: {
- src: ['tests/**/*.js'],
- options: {
- reporter: 'landing'
- }
+ zip: {
+ release: {
+ dest: 'releases/hawk-<%= pkg.version %>.zip',
+ src: 'build/**/*',
+ cwd: 'build/'
}
},
watch: {
@@ -116,6 +107,6 @@ module.exports = function(grunt) {
});
grunt.registerTask('default', ['browserify:dev', 'less:dev', 'copy']);
- grunt.registerTask('production', ['browserify:prod', 'less:prod', 'copy']);
+ grunt.registerTask('production', ['browserify', 'less:prod', 'copy', 'zip']);
grunt.registerTask('test', 'mochaTest');
};
diff --git a/build/img/trianglified.svg b/build/img/trianglified.svg
deleted file mode 100644
index 0d755fd..0000000
--- a/build/img/trianglified.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/build/js/libs/l10n.js b/build/js/libs/l10n.js
deleted file mode 100644
index cfc5c51..0000000
--- a/build/js/libs/l10n.js
+++ /dev/null
@@ -1,1571 +0,0 @@
-// This is the Gaia version of l20n: https://github.com/l20n/l20n.js
-// l20n is Apache 2.0 licensed: https://github.com/l20n/l20n.js/blob/master/LICENSE
-// You can find the latest build for Gaia here: https://github.com/mozilla-b2g/gaia/blob/master/shared/js/l10n.js
-(function(window, undefined) {
- 'use strict';
-
- /* jshint validthis:true */
- function L10nError(message, id, loc) {
- this.name = 'L10nError';
- this.message = message;
- this.id = id;
- this.loc = loc;
- }
- L10nError.prototype = Object.create(Error.prototype);
- L10nError.prototype.constructor = L10nError;
-
-
- /* jshint browser:true */
-
- var io = {
- load: function load(url, callback, sync) {
- var xhr = new XMLHttpRequest();
-
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType('text/plain');
- }
-
- xhr.open('GET', url, !sync);
-
- xhr.addEventListener('load', function io_load(e) {
- if (e.target.status === 200 || e.target.status === 0) {
- callback(null, e.target.responseText);
- } else {
- callback(new L10nError('Not found: ' + url));
- }
- });
- xhr.addEventListener('error', callback);
- xhr.addEventListener('timeout', callback);
-
- // the app: protocol throws on 404, see https://bugzil.la/827243
- try {
- xhr.send(null);
- } catch (e) {
- callback(new L10nError('Not found: ' + url));
- }
- },
-
- loadJSON: function loadJSON(url, callback) {
- var xhr = new XMLHttpRequest();
-
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType('application/json');
- }
-
- xhr.open('GET', url);
-
- xhr.responseType = 'json';
- xhr.addEventListener('load', function io_loadjson(e) {
- if (e.target.status === 200 || e.target.status === 0) {
- callback(null, e.target.response);
- } else {
- callback(new L10nError('Not found: ' + url));
- }
- });
- xhr.addEventListener('error', callback);
- xhr.addEventListener('timeout', callback);
-
- // the app: protocol throws on 404, see https://bugzil.la/827243
- try {
- xhr.send(null);
- } catch (e) {
- callback(new L10nError('Not found: ' + url));
- }
- }
- };
-
- function EventEmitter() {}
-
- EventEmitter.prototype.emit = function ee_emit() {
- if (!this._listeners) {
- return;
- }
-
- var args = Array.prototype.slice.call(arguments);
- var type = args.shift();
- if (!this._listeners[type]) {
- return;
- }
-
- var typeListeners = this._listeners[type].slice();
- for (var i = 0; i < typeListeners.length; i++) {
- typeListeners[i].apply(this, args);
- }
- };
-
- EventEmitter.prototype.addEventListener = function ee_add(type, listener) {
- if (!this._listeners) {
- this._listeners = {};
- }
- if (!(type in this._listeners)) {
- this._listeners[type] = [];
- }
- this._listeners[type].push(listener);
- };
-
- EventEmitter.prototype.removeEventListener = function ee_rm(type, listener) {
- if (!this._listeners) {
- return;
- }
-
- var typeListeners = this._listeners[type];
- var pos = typeListeners.indexOf(listener);
- if (pos === -1) {
- return;
- }
-
- typeListeners.splice(pos, 1);
- };
-
-
- function getPluralRule(lang) {
- var 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
- };
-
- // utility functions for plural rules methods
- function isIn(n, list) {
- return list.indexOf(n) !== -1;
- }
- function isBetween(n, start, end) {
- return start <= n && n <= end;
- }
-
- // list of all plural rules methods:
- // map an integer to the plural form name to use
- var 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';
- }
- };
-
- // return a function that gives the plural form name for a given integer
- var index = locales2rules[lang.replace(/-.*$/, '')];
- if (!(index in pluralRules)) {
- return function() { return 'other'; };
- }
- return pluralRules[index];
- }
-
-
-
-
- var nestedProps = ['style', 'dataset'];
-
- var parsePatterns;
-
- function parse(ctx, source) {
- var ast = {};
-
- if (!parsePatterns) {
- parsePatterns = {
- comment: /^\s*#|^\s*$/,
- entity: /^([^=\s]+)\s*=\s*(.+)$/,
- multiline: /[^\\]\\$/,
- macro: /\{\[\s*(\w+)\(([^\)]*)\)\s*\]\}/i,
- unicode: /\\u([0-9a-fA-F]{1,4})/g,
- entries: /[\r\n]+/,
- controlChars: /\\([\\\n\r\t\b\f\{\}\"\'])/g
- };
- }
-
- var entries = source.split(parsePatterns.entries);
- for (var i = 0; i < entries.length; i++) {
- var line = entries[i];
-
- if (parsePatterns.comment.test(line)) {
- continue;
- }
-
- while (parsePatterns.multiline.test(line) && i < entries.length) {
- line = line.slice(0, -1) + entries[++i].trim();
- }
-
- var entityMatch = line.match(parsePatterns.entity);
- if (entityMatch) {
- try {
- parseEntity(entityMatch[1], entityMatch[2], ast);
- } catch (e) {
- if (ctx) {
- ctx._emitter.emit('error', e);
- } else {
- throw e;
- }
- }
- }
- }
- return ast;
- }
-
- function setEntityValue(id, attr, key, value, ast) {
- var obj = ast;
- var prop = id;
-
- if (attr) {
- if (!(id in obj)) {
- obj[id] = {};
- }
- if (typeof(obj[id]) === 'string') {
- obj[id] = {'_': obj[id]};
- }
- obj = obj[id];
- prop = attr;
- }
-
- if (!key) {
- obj[prop] = value;
- return;
- }
-
- if (!(prop in obj)) {
- obj[prop] = {'_': {}};
- } else if (typeof(obj[prop]) === 'string') {
- obj[prop] = {'_index': parseMacro(obj[prop]), '_': {}};
- }
- obj[prop]._[key] = value;
- }
-
- function parseEntity(id, value, ast) {
- 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('.');
-
- var attr;
- if (nameElements.length > 1) {
- var attrElements = [];
- attrElements.push(nameElements.pop());
- if (nameElements.length > 1) {
- // Usually the last dot separates an attribute from an id
- //
- // In case when there are more than one dot in the id
- // and the second to last item is "style" or "dataset" then the last two
- // items are becoming the attribute.
- //
- // ex.
- // id.style.color = foo =>
- //
- // id:
- // style.color: foo
- //
- // id.other.color = foo =>
- //
- // id.other:
- // color: foo
- if (nestedProps.indexOf(nameElements[nameElements.length - 1]) !== -1) {
- attrElements.push(nameElements.pop());
- }
- }
- name = nameElements.join('.');
- attr = attrElements.reverse().join('.');
- } else {
- attr = null;
- }
-
- setEntityValue(name, attr, key, unescapeString(value), ast);
- }
-
- function unescapeControlCharacters(str) {
- return str.replace(parsePatterns.controlChars, '$1');
- }
-
- function unescapeUnicode(str) {
- return str.replace(parsePatterns.unicode, function(match, token) {
- return unescape('%u' + '0000'.slice(token.length) + token);
- });
- }
-
- function unescapeString(str) {
- if (str.lastIndexOf('\\') !== -1) {
- str = unescapeControlCharacters(str);
- }
- return unescapeUnicode(str);
- }
-
- function parseMacro(str) {
- var match = str.match(parsePatterns.macro);
- if (!match) {
- throw new L10nError('Malformed macro');
- }
- return [match[1], match[2]];
- }
-
-
-
- var MAX_PLACEABLE_LENGTH = 2500;
- var MAX_PLACEABLES = 100;
- var rePlaceables = /\{\{\s*(.+?)\s*\}\}/g;
-
- function Entity(id, node, env) {
- this.id = id;
- this.env = env;
- // the dirty guard prevents cyclic or recursive references from other
- // Entities; see Entity.prototype.resolve
- this.dirty = false;
- if (typeof node === 'string') {
- this.value = node;
- } else {
- // it's either a hash or it has attrs, or both
- for (var key in node) {
- if (node.hasOwnProperty(key) && key[0] !== '_') {
- if (!this.attributes) {
- this.attributes = {};
- }
- this.attributes[key] = new Entity(this.id + '.' + key, node[key],
- env);
- }
- }
- this.value = node._ || null;
- this.index = node._index;
- }
- }
-
- Entity.prototype.resolve = function E_resolve(ctxdata) {
- if (this.dirty) {
- return undefined;
- }
-
- this.dirty = true;
- var val;
- // if resolve fails, we want the exception to bubble up and stop the whole
- // resolving process; however, we still need to clean up the dirty flag
- try {
- val = resolve(ctxdata, this.env, this.value, this.index);
- } finally {
- this.dirty = false;
- }
- return val;
- };
-
- Entity.prototype.toString = function E_toString(ctxdata) {
- try {
- return this.resolve(ctxdata);
- } catch (e) {
- return undefined;
- }
- };
-
- Entity.prototype.valueOf = function E_valueOf(ctxdata) {
- if (!this.attributes) {
- return this.toString(ctxdata);
- }
-
- var entity = {
- value: this.toString(ctxdata),
- attributes: {}
- };
-
- for (var key in this.attributes) {
- if (this.attributes.hasOwnProperty(key)) {
- entity.attributes[key] = this.attributes[key].toString(ctxdata);
- }
- }
-
- return entity;
- };
-
- function subPlaceable(ctxdata, env, match, id) {
- if (ctxdata && ctxdata.hasOwnProperty(id) &&
- (typeof ctxdata[id] === 'string' ||
- (typeof ctxdata[id] === 'number' && !isNaN(ctxdata[id])))) {
- return ctxdata[id];
- }
-
- if (env.hasOwnProperty(id)) {
- if (!(env[id] instanceof Entity)) {
- env[id] = new Entity(id, env[id], env);
- }
- var value = env[id].resolve(ctxdata);
- if (typeof value === 'string') {
- // prevent Billion Laughs attacks
- if (value.length >= MAX_PLACEABLE_LENGTH) {
- throw new L10nError('Too many characters in placeable (' +
- value.length + ', max allowed is ' +
- MAX_PLACEABLE_LENGTH + ')');
- }
- return value;
- }
- }
- return match;
- }
-
- function interpolate(ctxdata, env, str) {
- var placeablesCount = 0;
- var value = str.replace(rePlaceables, function(match, id) {
- // prevent Quadratic Blowup attacks
- if (placeablesCount++ >= MAX_PLACEABLES) {
- throw new L10nError('Too many placeables (' + placeablesCount +
- ', max allowed is ' + MAX_PLACEABLES + ')');
- }
- return subPlaceable(ctxdata, env, match, id);
- });
- placeablesCount = 0;
- return value;
- }
-
- function resolve(ctxdata, env, expr, index) {
- if (typeof expr === 'string') {
- return interpolate(ctxdata, env, expr);
- }
-
- if (typeof expr === 'boolean' ||
- typeof expr === 'number' ||
- !expr) {
- return expr;
- }
-
- // otherwise, it's a dict
-
- if (index && ctxdata && ctxdata.hasOwnProperty(index[1])) {
- var argValue = ctxdata[index[1]];
-
- // special cases for zero, one, two if they are defined on the hash
- if (argValue === 0 && 'zero' in expr) {
- return resolve(ctxdata, env, expr.zero);
- }
- if (argValue === 1 && 'one' in expr) {
- return resolve(ctxdata, env, expr.one);
- }
- if (argValue === 2 && 'two' in expr) {
- return resolve(ctxdata, env, expr.two);
- }
-
- var selector = env.__plural(argValue);
- if (expr.hasOwnProperty(selector)) {
- return resolve(ctxdata, env, expr[selector]);
- }
- }
-
- // if there was no index or no selector was found, try 'other'
- if ('other' in expr) {
- return resolve(ctxdata, env, expr.other);
- }
-
- return undefined;
- }
-
- function compile(env, ast) {
- env = env || {};
- for (var id in ast) {
- if (ast.hasOwnProperty(id)) {
- env[id] = new Entity(id, ast[id], env);
- }
- }
- return env;
- }
-
-
-
- function Locale(id, ctx) {
- this.id = id;
- this.ctx = ctx;
- this.isReady = false;
- this.entries = {
- __plural: getPluralRule(id)
- };
- }
-
- Locale.prototype.getEntry = function L_getEntry(id) {
- /* jshint -W093 */
-
- var entries = this.entries;
-
- if (!entries.hasOwnProperty(id)) {
- return undefined;
- }
-
- if (entries[id] instanceof Entity) {
- return entries[id];
- }
-
- return entries[id] = new Entity(id, entries[id], entries);
- };
-
- Locale.prototype.build = function L_build(callback) {
- var sync = !callback;
- var ctx = this.ctx;
- var self = this;
-
- var l10nLoads = ctx.resLinks.length;
-
- function onL10nLoaded(err) {
- if (err) {
- ctx._emitter.emit('error', err);
- }
- if (--l10nLoads <= 0) {
- self.isReady = true;
- if (callback) {
- callback();
- }
- }
- }
-
- if (l10nLoads === 0) {
- onL10nLoaded();
- return;
- }
-
- function onJSONLoaded(err, json) {
- if (!err && json) {
- self.addAST(json);
- }
- onL10nLoaded(err);
- }
-
- function onPropLoaded(err, source) {
- if (!err && source) {
- var ast = parse(ctx, source);
- self.addAST(ast);
- }
- onL10nLoaded(err);
- }
-
-
- for (var i = 0; i < ctx.resLinks.length; i++) {
- var path = ctx.resLinks[i].replace('{{locale}}', this.id);
- var type = path.substr(path.lastIndexOf('.') + 1);
-
- switch (type) {
- case 'json':
- io.loadJSON(path, onJSONLoaded, sync);
- break;
- case 'properties':
- io.load(path, onPropLoaded, sync);
- break;
- }
- }
- };
-
- Locale.prototype.addAST = function(ast) {
- for (var id in ast) {
- if (ast.hasOwnProperty(id)) {
- this.entries[id] = ast[id];
- }
- }
- };
-
- Locale.prototype.getEntity = function(id, ctxdata) {
- var entry = this.getEntry(id);
-
- if (!entry) {
- return null;
- }
- return entry.valueOf(ctxdata);
- };
-
-
-
- function Context(id) {
-
- this.id = id;
- this.isReady = false;
- this.isLoading = false;
-
- this.supportedLocales = [];
- this.resLinks = [];
- this.locales = {};
-
- this._emitter = new EventEmitter();
-
-
- // Getting translations
-
- function getWithFallback(id) {
- /* jshint -W084 */
-
- if (!this.isReady) {
- throw new L10nError('Context not ready');
- }
-
- var cur = 0;
- var loc;
- var locale;
- while (loc = this.supportedLocales[cur]) {
- locale = this.getLocale(loc);
- if (!locale.isReady) {
- // build without callback, synchronously
- locale.build(null);
- }
- var entry = locale.getEntry(id);
- if (entry === undefined) {
- cur++;
- warning.call(this, new L10nError(id + ' not found in ' + loc, id,
- loc));
- continue;
- }
- return entry;
- }
-
- error.call(this, new L10nError(id + ' not found', id));
- return null;
- }
-
- this.get = function get(id, ctxdata) {
- var entry = getWithFallback.call(this, id);
- if (entry === null) {
- return '';
- }
-
- return entry.toString(ctxdata) || '';
- };
-
- this.getEntity = function getEntity(id, ctxdata) {
- var entry = getWithFallback.call(this, id);
- if (entry === null) {
- return null;
- }
-
- return entry.valueOf(ctxdata);
- };
-
-
- // Helpers
-
- this.getLocale = function getLocale(code) {
- /* jshint -W093 */
-
- var locales = this.locales;
- if (locales[code]) {
- return locales[code];
- }
-
- return locales[code] = new Locale(code, this);
- };
-
-
- // Getting ready
-
- function negotiate(available, requested, defaultLocale) {
- if (available.indexOf(requested[0]) === -1 ||
- requested[0] === defaultLocale) {
- return [defaultLocale];
- } else {
- return [requested[0], defaultLocale];
- }
- }
-
- function freeze(supported) {
- var locale = this.getLocale(supported[0]);
- if (locale.isReady) {
- setReady.call(this, supported);
- } else {
- locale.build(setReady.bind(this, supported));
- }
- }
-
- function setReady(supported) {
- this.supportedLocales = supported;
- this.isReady = true;
- this._emitter.emit('ready');
- }
-
- this.requestLocales = function requestLocales() {
- if (this.isLoading && !this.isReady) {
- throw new L10nError('Context not ready');
- }
-
- this.isLoading = true;
- var requested = Array.prototype.slice.call(arguments);
-
- var supported = negotiate(requested.concat('en-US'), requested, 'en-US');
- freeze.call(this, supported);
- };
-
-
- // Events
-
- this.addEventListener = function addEventListener(type, listener) {
- this._emitter.addEventListener(type, listener);
- };
-
- this.removeEventListener = function removeEventListener(type, listener) {
- this._emitter.removeEventListener(type, listener);
- };
-
- this.ready = function ready(callback) {
- if (this.isReady) {
- setTimeout(callback);
- }
- this.addEventListener('ready', callback);
- };
-
- this.once = function once(callback) {
- /* jshint -W068 */
- if (this.isReady) {
- setTimeout(callback);
- return;
- }
-
- var callAndRemove = (function() {
- this.removeEventListener('ready', callAndRemove);
- callback();
- }).bind(this);
- this.addEventListener('ready', callAndRemove);
- };
-
-
- // Errors
-
- function warning(e) {
- this._emitter.emit('warning', e);
- return e;
- }
-
- function error(e) {
- this._emitter.emit('error', e);
- return e;
- }
- }
-
-
- /* jshint -W104 */
-
- var DEBUG = false;
- var isPretranslated = false;
- var rtlList = ['ar', 'he', 'fa', 'ps', 'qps-plocm', 'ur'];
-
- // Public API
-
- navigator.mozL10n = {
- ctx: new Context(),
- get: function get(id, ctxdata) {
- return navigator.mozL10n.ctx.get(id, ctxdata);
- },
- localize: function localize(element, id, args) {
- return localizeElement.call(navigator.mozL10n, element, id, args);
- },
- translate: function translate(element) {
- return translateFragment.call(navigator.mozL10n, element);
- },
- ready: function ready(callback) {
- return navigator.mozL10n.ctx.ready(callback);
- },
- once: function once(callback) {
- return navigator.mozL10n.ctx.once(callback);
- },
- get readyState() {
- return navigator.mozL10n.ctx.isReady ? 'complete' : 'loading';
- },
- language: {
- set code(lang) {
- navigator.mozL10n.ctx.requestLocales(lang);
- },
- get code() {
- return navigator.mozL10n.ctx.supportedLocales[0];
- },
- get direction() {
- return getDirection(navigator.mozL10n.ctx.supportedLocales[0]);
- }
- },
- _getInternalAPI: function() {
- return {
- Error: L10nError,
- Context: Context,
- Locale: Locale,
- Entity: Entity,
- getPluralRule: getPluralRule,
- rePlaceables: rePlaceables,
- getTranslatableChildren: getTranslatableChildren,
- getL10nAttributes: getL10nAttributes,
- loadINI: loadINI,
- fireLocalizedEvent: fireLocalizedEvent,
- parse: parse,
- compile: compile
- };
- }
- };
-
- navigator.mozL10n.ctx.ready(onReady.bind(navigator.mozL10n));
-
- if (DEBUG) {
- navigator.mozL10n.ctx.addEventListener('error', console.error);
- navigator.mozL10n.ctx.addEventListener('warning', console.warn);
- }
-
- function getDirection(lang) {
- return (rtlList.indexOf(lang) >= 0) ? 'rtl' : 'ltr';
- }
-
- var readyStates = {
- 'loading': 0,
- 'interactive': 1,
- 'complete': 2
- };
-
- function waitFor(state, callback) {
- state = readyStates[state];
- if (readyStates[document.readyState] >= state) {
- callback();
- return;
- }
-
- document.addEventListener('readystatechange', function l10n_onrsc() {
- if (readyStates[document.readyState] >= state) {
- document.removeEventListener('readystatechange', l10n_onrsc);
- callback();
- }
- });
- }
-
- if (window.document) {
- isPretranslated = (document.documentElement.lang === navigator.language);
-
- // this is a special case for netError bug; see https://bugzil.la/444165
- if (document.documentElement.dataset.noCompleteBug) {
- pretranslate.call(navigator.mozL10n);
- return;
- }
-
-
- if (isPretranslated) {
- waitFor('interactive', function() {
- window.setTimeout(initResources.bind(navigator.mozL10n));
- });
- } else {
- if (document.readyState === 'complete') {
- window.setTimeout(initResources.bind(navigator.mozL10n));
- } else {
- waitFor('interactive', pretranslate.bind(navigator.mozL10n));
- }
- }
-
- }
-
- function pretranslate() {
- /* jshint -W068 */
- if (inlineLocalization.call(this)) {
- waitFor('interactive', (function() {
- window.setTimeout(initResources.bind(this));
- }).bind(this));
- } else {
- initResources.call(this);
- }
- }
-
- function inlineLocalization() {
- var script = document.documentElement
- .querySelector('script[type="application/l10n"]' +
- '[lang="' + navigator.language + '"]');
- if (!script) {
- return false;
- }
-
- var locale = this.ctx.getLocale(navigator.language);
- // the inline localization is happenning very early, when the ctx is not
- // yet ready and when the resources haven't been downloaded yet; add the
- // inlined JSON directly to the current locale
- locale.addAST(JSON.parse(script.innerHTML));
- // localize the visible DOM
- var l10n = {
- ctx: locale,
- language: {
- code: locale.id,
- direction: getDirection(locale.id)
- }
- };
- translateFragment.call(l10n);
- // the visible DOM is now pretranslated
- isPretranslated = true;
- return true;
- }
-
- function initResources() {
- var resLinks = document.head
- .querySelectorAll('link[type="application/l10n"]');
- var iniLinks = [];
- var i;
-
- for (i = 0; i < resLinks.length; i++) {
- var link = resLinks[i];
- var url = link.getAttribute('href');
- var type = url.substr(url.lastIndexOf('.') + 1);
- if (type === 'ini') {
- iniLinks.push(url);
- }
- this.ctx.resLinks.push(url);
- }
-
- var iniLoads = iniLinks.length;
- if (iniLoads === 0) {
- initLocale.call(this);
- return;
- }
-
- function onIniLoaded(err) {
- if (err) {
- this.ctx._emitter.emit('error', err);
- }
- if (--iniLoads === 0) {
- initLocale.call(this);
- }
- }
-
- for (i = 0; i < iniLinks.length; i++) {
- loadINI.call(this, iniLinks[i], onIniLoaded.bind(this));
- }
- }
-
- function initLocale() {
- this.ctx.requestLocales(navigator.language);
- window.addEventListener('languagechange', function l10n_langchange() {
- navigator.mozL10n.language.code = navigator.language;
- });
- }
-
- function onReady() {
- if (!isPretranslated) {
- this.translate();
- }
- isPretranslated = false;
-
- fireLocalizedEvent.call(this);
- }
-
- function fireLocalizedEvent() {
- var event = new CustomEvent('localized', {
- 'bubbles': false,
- 'cancelable': false,
- 'detail': {
- 'language': this.ctx.supportedLocales[0]
- }
- });
- window.dispatchEvent(event);
- }
-
- /* jshint -W104 */
-
- function loadINI(url, callback) {
- var ctx = this.ctx;
- io.load(url, function(err, source) {
- var pos = ctx.resLinks.indexOf(url);
-
- if (err) {
- // remove the ini link from resLinks
- ctx.resLinks.splice(pos, 1);
- return callback(err);
- }
-
- if (!source) {
- ctx.resLinks.splice(pos, 1);
- return callback(new Error('Empty file: ' + url));
- }
-
- var patterns = parseINI(source, url).resources.map(function(x) {
- return x.replace('en-US', '{{locale}}');
- });
- ctx.resLinks.splice.apply(ctx.resLinks, [pos, 1].concat(patterns));
- callback();
- });
- }
-
- function relativePath(baseUrl, url) {
- if (url[0] === '/') {
- return url;
- }
-
- var dirs = baseUrl.split('/')
- .slice(0, -1)
- .concat(url.split('/'))
- .filter(function(path) {
- return path !== '.';
- });
-
- return dirs.join('/');
- }
-
- var iniPatterns = {
- 'section': /^\s*\[(.*)\]\s*$/,
- 'import': /^\s*@import\s+url\((.*)\)\s*$/i,
- 'entry': /[\r\n]+/
- };
-
- function parseINI(source, iniPath) {
- var entries = source.split(iniPatterns.entry);
- var locales = ['en-US'];
- var genericSection = true;
- var uris = [];
- var match;
-
- for (var i = 0; i < entries.length; i++) {
- var line = entries[i];
- // we only care about en-US resources
- if (genericSection && iniPatterns['import'].test(line)) {
- match = iniPatterns['import'].exec(line);
- var uri = relativePath(iniPath, match[1]);
- uris.push(uri);
- continue;
- }
-
- // but we need the list of all locales in the ini, too
- if (iniPatterns.section.test(line)) {
- genericSection = false;
- match = iniPatterns.section.exec(line);
- locales.push(match[1]);
- }
- }
- return {
- locales: locales,
- resources: uris
- };
- }
-
- /* jshint -W104 */
-
- function translateFragment(element) {
- if (!element) {
- element = document.documentElement;
- document.documentElement.lang = this.language.code;
- document.documentElement.dir = this.language.direction;
- }
- translateElement.call(this, element);
-
- var nodes = getTranslatableChildren(element);
- for (var i = 0; i < nodes.length; i++ ) {
- translateElement.call(this, nodes[i]);
- }
- }
-
- function getTranslatableChildren(element) {
- return element ? element.querySelectorAll('*[data-l10n-id]') : [];
- }
-
- function localizeElement(element, id, args) {
- if (!element) {
- return;
- }
-
- if (!id) {
- element.removeAttribute('data-l10n-id');
- element.removeAttribute('data-l10n-args');
- setTextContent(element, '');
- return;
- }
-
- element.setAttribute('data-l10n-id', id);
- if (args && typeof args === 'object') {
- element.setAttribute('data-l10n-args', JSON.stringify(args));
- } else {
- element.removeAttribute('data-l10n-args');
- }
-
- if (this.ctx.isReady) {
- translateElement.call(this, element);
- }
- }
-
- function getL10nAttributes(element) {
- if (!element) {
- return {};
- }
-
- var l10nId = element.getAttribute('data-l10n-id');
- var l10nArgs = element.getAttribute('data-l10n-args');
-
- var args = l10nArgs ? JSON.parse(l10nArgs) : null;
-
- return {id: l10nId, args: args};
- }
-
-
-
- function translateElement(element) {
- var l10n = getL10nAttributes(element);
-
- if (!l10n.id) {
- return;
- }
-
- var entity = this.ctx.getEntity(l10n.id, l10n.args);
-
- if (!entity) {
- return;
- }
-
- if (typeof entity === 'string') {
- setTextContent(element, entity);
- return true;
- }
-
- if (entity.value) {
- setTextContent(element, entity.value);
- }
-
- for (var key in entity.attributes) {
- if (entity.attributes.hasOwnProperty(key)) {
- var attr = entity.attributes[key];
- var pos = key.indexOf('.');
- if (pos !== -1) {
- element[key.substr(0, pos)][key.substr(pos + 1)] = attr;
- } else if (key === 'ariaLabel') {
- element.setAttribute('aria-label', attr);
- } else {
- element[key] = attr;
- }
- }
- }
-
- return true;
- }
-
- function setTextContent(element, text) {
- // standard case: no element children
- if (!element.firstElementChild) {
- element.textContent = text;
- return;
- }
-
- // this element has element children: replace the content of the first
- // (non-blank) child textNode and clear other child textNodes
- var found = false;
- var reNotBlank = /\S/;
- for (var child = element.firstChild; child; child = child.nextSibling) {
- if (child.nodeType === Node.TEXT_NODE &&
- reNotBlank.test(child.nodeValue)) {
- if (found) {
- child.nodeValue = '';
- } else {
- child.nodeValue = text;
- found = true;
- }
- }
- }
- // if no (non-empty) textNode is found, insert a textNode before the
- // element's first child.
- if (!found) {
- element.insertBefore(document.createTextNode(text), element.firstChild);
- }
- }
-
-})(this);
-
diff --git a/build/locales/en-US.properties b/build/locales/en-US.properties
deleted file mode 100644
index 26f9a84..0000000
--- a/build/locales/en-US.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-appname = Hawk
-app_description.innerHTML = This app is empty. Fill it with your own stuff!
-message = Hello world
diff --git a/build/locales/locales.ini b/build/locales/locales.ini
deleted file mode 100644
index 173dfb0..0000000
--- a/build/locales/locales.ini
+++ /dev/null
@@ -1 +0,0 @@
-@import url(en-US.properties)
diff --git a/build/main.js b/build/main.js
index 5ffd63a..a2c30ea 100644
--- a/build/main.js
+++ b/build/main.js
@@ -29964,16 +29964,18 @@ var children = _asyncToGenerator(function* (dir, gatherInfo) {
for (var _iterator = childs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var child = _step.value;
- if ((0, _utils.type)(child) !== 'Directory') continue;
+ if ((0, _utils.type)(child) === 'Directory') {
+ var subchildren = undefined;
+ try {
+ subchildren = yield child.getFilesAndDirectories();
+ } catch (e) {
+ subchildren = [];
+ }
- var subchildren = undefined;
- try {
- subchildren = yield child.getFilesAndDirectories();
- } catch (e) {
- subchildren = [];
+ child.children = subchildren.length;
+ } else {
+ child.path = dir + '/';
}
-
- child.children = subchildren.length;
}
} catch (err) {
_didIteratorError = true;
@@ -29989,6 +29991,8 @@ var children = _asyncToGenerator(function* (dir, gatherInfo) {
}
}
}
+
+ ;
}
return childs;
@@ -30073,7 +30077,7 @@ var copy = _asyncToGenerator(function* (file, newPath) {
child.path = oldPath + '/';
}
- yield move(child, newPath + '/' + child.name);
+ yield copy(child, newPath + '/' + child.name);
}
} catch (err) {
_didIteratorError2 = true;
@@ -30354,12 +30358,6 @@ var _actionsChangedir = require('actions/changedir');
var _actionsChangedir2 = _interopRequireDefault(_actionsChangedir);
-var _actionsMenu = require('actions/menu');
-
-var _actionsFile = require('actions/file');
-
-var _menu = require('./menu');
-
var _store = require('store');
var _store2 = _interopRequireDefault(_store);
@@ -30368,8 +30366,6 @@ var _mixinsEntry = require('./mixins/entry');
var _mixinsEntry2 = _interopRequireDefault(_mixinsEntry);
-var MENU_TOP_SPACE = 20;
-
var Directory = (function (_Component) {
_inherits(Directory, _Component);
@@ -30430,7 +30426,7 @@ var Directory = (function (_Component) {
exports['default'] = Directory;
module.exports = exports['default'];
-},{"./menu":233,"./mixins/entry":234,"actions/changedir":217,"actions/file":219,"actions/menu":221,"react":207,"store":"store"}],230:[function(require,module,exports){
+},{"./mixins/entry":234,"actions/changedir":217,"react":207,"store":"store"}],230:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
@@ -30505,7 +30501,7 @@ var FileList = (function (_Component) {
var settings = _store2['default'].getState().get('settings');
var els = files.map(function (file, index) {
- var selected = activeFile.length && activeFile.indexOf(file) > -1;
+ var selected = activeFile.indexOf(file) > -1;
if ((0, _utils.type)(file) === 'File') {
return _react2['default'].createElement(_file2['default'], { selectView: selectView, selected: selected, key: index, index: index, name: file.name, size: file.size });
} else {
@@ -30574,12 +30570,6 @@ var _react = require('react');
var _react2 = _interopRequireDefault(_react);
-var _actionsMenu = require('actions/menu');
-
-var _actionsFile = require('actions/file');
-
-var _menu = require('./menu');
-
var _store = require('store');
var _store2 = _interopRequireDefault(_store);
@@ -30590,8 +30580,6 @@ var _mixinsEntry = require('./mixins/entry');
var _mixinsEntry2 = _interopRequireDefault(_mixinsEntry);
-var MENU_TOP_SPACE = 20;
-
var File = (function (_Component) {
_inherits(File, _Component);
@@ -30610,7 +30598,7 @@ var File = (function (_Component) {
var input = undefined,
label = undefined;
if (this.props.selectView) {
- input = _react2['default'].createElement('input', { type: 'checkbox', id: checkId, defaultChecked: this.props.selected, readOnly: true });
+ input = _react2['default'].createElement('input', { type: 'checkbox', id: checkId, checked: this.props.selected, readOnly: true });
label = _react2['default'].createElement('label', { htmlFor: checkId });
}
@@ -30658,7 +30646,7 @@ var File = (function (_Component) {
exports['default'] = File;
module.exports = exports['default'];
-},{"./menu":233,"./mixins/entry":234,"actions/file":219,"actions/menu":221,"react":207,"store":"store","utils":"utils"}],232:[function(require,module,exports){
+},{"./mixins/entry":234,"react":207,"store":"store","utils":"utils"}],232:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
@@ -30780,6 +30768,7 @@ var Menu = (function (_Component) {
var items = _props.items;
var active = _props.active;
var style = _props.style;
+ var id = _props.id;
items = items || [];
@@ -30797,7 +30786,7 @@ var Menu = (function (_Component) {
return _react2['default'].createElement(
'div',
- { className: className, style: style },
+ { className: className, style: style, id: id },
_react2['default'].createElement(
'ul',
null,
@@ -30818,25 +30807,48 @@ exports['default'] = Menu;
Object.defineProperty(exports, '__esModule', {
value: true
});
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+var _react = require('react');
+
+var _react2 = _interopRequireDefault(_react);
+
+var _actionsFile = require('actions/file');
+
+var _actionsMenu = require('actions/menu');
+
+var _componentsMenu = require('components/menu');
+
+var MENU_TOP_SPACE = 20;
+
exports['default'] = {
contextMenu: function contextMenu(e) {
e.preventDefault();
var file = store.getState().get('files')[this.props.index];
- var rect = React.findDOMNode(this.refs.container).getBoundingClientRect();
+ var rect = _react2['default'].findDOMNode(this.refs.container).getBoundingClientRect();
var x = rect.x;
var y = rect.y;
var width = rect.width;
var height = rect.height;
- var left = x + width / 2 - MENU_WIDTH / 2,
+ var left = x + width / 2 - _componentsMenu.MENU_WIDTH / 2,
top = y + height / 2 + MENU_TOP_SPACE;
- store.dispatch(show('fileMenu', { style: { left: left, top: top } }));
- store.dispatch(active([file]));
+
+ var dialogHeight = document.getElementById('fileMenu').offsetHeight;
+
+ var diff = window.innerHeight - (dialogHeight + top);
+ if (diff <= 0) {
+ top -= Math.abs(diff);
+ }
+
+ store.dispatch((0, _actionsMenu.show)('fileMenu', { style: { left: left, top: top } }));
+ store.dispatch((0, _actionsFile.active)([file]));
},
select: function select() {
- var current = store.getState().get('activeFile').slice(0);
+ var current = (store.getState().get('activeFile') || []).slice(0);
var file = store.getState().get('files')[this.props.index];
if (current.indexOf(file) > -1) {
@@ -30844,12 +30856,12 @@ exports['default'] = {
} else {
current.push(file);
}
- store.dispatch(active(current));
+ store.dispatch((0, _actionsFile.active)(current));
}
};
module.exports = exports['default'];
-},{}],235:[function(require,module,exports){
+},{"actions/file":219,"actions/menu":221,"components/menu":233,"react":207}],235:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, '__esModule', {
@@ -30917,7 +30929,7 @@ var Navigation = (function (_Component) {
_react2['default'].createElement(
'li',
null,
- _react2['default'].createElement('input', { id: 'filter-all', name: 'filter', value: '', type: 'radio', defaultChecked: !settings.filter }),
+ _react2['default'].createElement('input', { id: 'filter-all', name: 'filter', 'data-value': '', type: 'radio', defaultChecked: !settings.filter }),
_react2['default'].createElement(
'label',
{ htmlFor: 'filter-all' },
@@ -30927,7 +30939,7 @@ var Navigation = (function (_Component) {
_react2['default'].createElement(
'li',
null,
- _react2['default'].createElement('input', { id: 'filter-image', name: 'filter', value: 'image', type: 'radio', defaultChecked: settings.filter === 'image' }),
+ _react2['default'].createElement('input', { id: 'filter-image', name: 'filter', 'data-value': 'image', type: 'radio', defaultChecked: settings.filter === 'image' }),
_react2['default'].createElement(
'label',
{ htmlFor: 'filter-image' },
@@ -30937,7 +30949,7 @@ var Navigation = (function (_Component) {
_react2['default'].createElement(
'li',
null,
- _react2['default'].createElement('input', { id: 'filter-video', name: 'filter', value: 'video', type: 'radio', defaultChecked: settings.filter === 'video' }),
+ _react2['default'].createElement('input', { id: 'filter-video', name: 'filter', 'data-value': 'video', type: 'radio', defaultChecked: settings.filter === 'video' }),
_react2['default'].createElement(
'label',
{ htmlFor: 'filter-video' },
@@ -30947,7 +30959,7 @@ var Navigation = (function (_Component) {
_react2['default'].createElement(
'li',
null,
- _react2['default'].createElement('input', { id: 'filter-audio', name: 'filter', value: 'audio', type: 'radio', defaultChecked: settings.filter === 'audio' }),
+ _react2['default'].createElement('input', { id: 'filter-audio', name: 'filter', 'data-value': 'audio', type: 'radio', defaultChecked: settings.filter === 'audio' }),
_react2['default'].createElement(
'label',
{ htmlFor: 'filter-audio' },
@@ -31078,7 +31090,7 @@ var Navigation = (function (_Component) {
key: 'onChange',
value: function onChange(e) {
var key = e.target.name || e.target.id;
- var value = e.target.value === undefined ? e.target.checked : e.target.value;
+ var value = typeof e.target.dataset.value !== 'undefined' ? e.target.dataset.value : e.target.checked;
var action = (0, _actionsSettings2['default'])(_defineProperty({}, key, value));
@@ -31186,9 +31198,7 @@ window.changedir = _actionsChangedir2['default'];
var FileMenu = (0, _reactRedux.connect)(function (state) {
return state.get('fileMenu');
})(_componentsMenu2['default']);
-var DirectoryMenu = (0, _reactRedux.connect)(function (state) {
- return state.get('directoryMenu');
-})(_componentsMenu2['default']);
+// let DirectoryMenu = connect(state => state.get('directoryMenu'))(Menu);
var MoreMenu = (0, _reactRedux.connect)(function (state) {
return state.get('moreMenu');
})(_componentsMenu2['default']);
@@ -31223,21 +31233,22 @@ var Root = (function (_Component) {
value: function render() {
return _react2['default'].createElement(
'div',
- { onTouchStart: this.touchStart.bind(this), onClick: this.onClick.bind(this) },
+ { onTouchStart: this.touchStart.bind(this),
+ onClick: this.onClick.bind(this) },
_react2['default'].createElement(_componentsHeader2['default'], null),
_react2['default'].createElement(_componentsBreadcrumb2['default'], null),
_react2['default'].createElement(_componentsNavigation2['default'], null),
_react2['default'].createElement(_componentsFileList2['default'], null),
_react2['default'].createElement(_componentsToolbar2['default'], null),
- _react2['default'].createElement(FileMenu, null),
- _react2['default'].createElement(DirectoryMenu, null),
- _react2['default'].createElement(MoreMenu, null),
+ _react2['default'].createElement(FileMenu, { id: 'fileMenu' }),
+ _react2['default'].createElement(MoreMenu, { id: 'moreMenu' }),
_react2['default'].createElement(RenameDialog, null),
_react2['default'].createElement(DeleteDialog, null),
_react2['default'].createElement(ErrorDialog, null),
_react2['default'].createElement(CreateDialog, null),
_react2['default'].createElement(SearchDialog, null),
_react2['default'].createElement(_componentsSpinner2['default'], null),
+ _react2['default'].createElement('div', { className: 'swipe-instruction tour-item' }),
_react2['default'].createElement(
'div',
{ className: 'tour-dialog' },
@@ -31621,7 +31632,7 @@ var entryMenu = {
action: function action() {
var files = _store2['default'].getState().get('files');
var active = _store2['default'].getState().get('activeFile');
- var description = 'Enter the new name for ' + active[0].name + '?';
+ var description = 'Enter the new name for ' + active[0].name;
_store2['default'].dispatch((0, _actionsMenu.hideAll)());
_store2['default'].dispatch((0, _actionsDialog.show)('renameDialog', { description: description }));
@@ -31973,7 +31984,8 @@ exports['default'] = function (state, action) {
if (action.type === _actionsTypes.RENAME_FILE) {
var all = Promise.all(action.file.map(function (file) {
- return (0, _apiFiles.move)(file, (file.path || '') + action.name);
+ var cwd = _store2['default'].getState().get('cwd');
+ return (0, _apiFiles.move)(file, cwd + '/' + action.name);
}));
all.then(boundRefresh, _utils.reportError);
@@ -32214,7 +32226,11 @@ exports['default'] = function (state, action) {
if (state === undefined) state = DEFAULT;
if (action.type === _actionsTypes.SETTINGS) {
- return Object.assign({}, state, (0, _lodashObjectOmit2['default'])(action, 'type'));
+ var newSettings = Object.assign({}, state, (0, _lodashObjectOmit2['default'])(action, 'type'));
+
+ localStorage.setItem('settings', JSON.stringify(newSettings));
+
+ return newSettings;
}
return state;
@@ -32245,6 +32261,13 @@ exports['default'] = function (state, action) {
switch (action.type) {
case _actionsTypes.CHANGE_DIRECTORY:
case _actionsTypes.REFRESH:
+ case _actionsTypes.SETTINGS:
+ case _actionsTypes.CREATE_FILE:
+ case _actionsTypes.MOVE_FILE:
+ case _actionsTypes.DELETE_FILE:
+ case _actionsTypes.RENAME_FILE:
+ case _actionsTypes.COPY_FILE:
+ case _actionsTypes.SEARCH:
return true;
case _actionsTypes.LIST_FILES:
return false;
@@ -32288,7 +32311,8 @@ var _dialogs = require('./dialogs');
var _dialogs2 = _interopRequireDefault(_dialogs);
var DEFAULT = new _immutable2['default'].Map(Object.assign({
- dir: ''
+ dir: '',
+ settings: JSON.parse(localStorage.getItem('settings') || '{}')
}, _dialogs2['default'], _menus2['default']));
var store = (0, _redux.createStore)(_reducersAll2['default'], DEFAULT);
@@ -32318,7 +32342,8 @@ var MESSAGES = {
'icon-select': 'Select files for batch actions',
'icon-more': 'Actions used on selected files such as Copy, Delete, Move, …',
'drawer': 'Extra options, tools and links are here',
- 'icon-search': 'Search your storage for a certain file'
+ 'icon-search': 'Search your storage for a certain file',
+ 'swipe-instruction': 'Swipe from left to right to go to parent folder'
};
var DIALOG_HIDE_DELAY = 2000;
diff --git a/build/manifest.webapp b/build/manifest.webapp
index eb4152c..509526c 100644
--- a/build/manifest.webapp
+++ b/build/manifest.webapp
@@ -1,7 +1,7 @@
{
"version": "0.1.0",
"name": "Hawk",
- "description": "Keep an eye on your files with a full-featured file-manager",
+ "description": "Keep an eye on your files with a full-featured file manager",
"launch_path": "/index.html",
"icons": {
"16": "/icon/Icon-16.png",
@@ -15,17 +15,26 @@
},
"type": "privileged",
"permissions": {
- "device-storage:sdcard": {"access": "readwrite"},
- "device-storage:videos": {"access": "readwrite"},
- "device-storage:pictures": {"access": "readwrite"},
- "device-storage:music": {"access": "readwrite"},
- "device-storage:apps": {"access": "readwrite"},
- "webapps-manage": {}
+ "device-storage:sdcard": {
+ "access": "readwrite",
+ "description": "We need access to your files in order to give you the functionality of listing, reading, opening and writing files in your storage"
+ },
+ "device-storage:videos": {
+ "access": "readwrite",
+ "description": "We need access to your files in order to give you the functionality of listing, reading, opening and writing files in your storage"
+ },
+ "device-storage:pictures": {
+ "access": "readwrite",
+ "description": "We need access to your files in order to give you the functionality of listing, reading, opening and writing files in your storage"
+ },
+ "device-storage:music": {
+ "access": "readwrite",
+ "description": "We need access to your files in order to give you the functionality of listing, reading, opening and writing files in your storage"
+ }
},
"installs_allowed_from": [
- "*"
+ "https://marketplace.firefox.com",
+ "https://marketplace-dev.allizom.org"
],
- "locales": {
- },
"default_locale": "en"
}
diff --git a/build/style.css b/build/style.css
index e4ab0eb..0ae9d64 100644
--- a/build/style.css
+++ b/build/style.css
@@ -172,6 +172,28 @@ input:checked + label::after {
transform: scale(0);
animation: pulse 2s ease-out infinite;
}
+.tour .swipe-instruction {
+ position: fixed;
+ left: 50%;
+ top: 20%;
+ width: 70vw;
+ height: 5rem;
+ margin-left: -35vw;
+ z-index: 1;
+ background: white;
+ border-radius: 3rem;
+}
+.tour .swipe-instruction::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 5rem;
+ height: 5rem;
+ background: #d9d9d9;
+ border-radius: 50%;
+ animation: swipe 3s ease infinite;
+}
.tour .tour-dialog {
display: block;
box-sizing: border-box;
@@ -196,6 +218,24 @@ input:checked + label::after {
transform: scale(5);
}
}
+@keyframes swipe {
+ 80% {
+ left: calc(100% - 5rem);
+ opacity: 1;
+ }
+ 90% {
+ opacity: 0;
+ left: calc(100% - 5rem);
+ }
+ 91% {
+ left: 0;
+ opacity: 0;
+ }
+ 100% {
+ left: 0;
+ opacity: 1;
+ }
+}
.coming-soon::after {
content: 'soon...';
background: #f7c59f;
diff --git a/package.json b/package.json
index 9e52d28..647f110 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
"grunt-contrib-watch": "^0.6.1",
"grunt-fxos": "^0.1.2",
"grunt-task-loader": "^0.6.0",
+ "grunt-zip": "^0.17.0",
"hammerjs": "^2.0.4",
"immutable": "^3.7.5",
"less-plugin-clean-css": "^1.5.1",
diff --git a/releases/hawk-1.0.0.zip b/releases/hawk-1.0.0.zip
index 9759571..95f237a 100644
Binary files a/releases/hawk-1.0.0.zip and b/releases/hawk-1.0.0.zip differ
diff --git a/src/js/api/files.js b/src/js/api/files.js
index ae5bc8c..5e0710c 100644
--- a/src/js/api/files.js
+++ b/src/js/api/files.js
@@ -32,17 +32,19 @@ export async function children(dir, gatherInfo) {
if (gatherInfo) {
for (let child of childs) {
- if (type(child) !== 'Directory') continue;
+ if (type(child) === 'Directory') {
+ let subchildren;
+ try {
+ subchildren = await child.getFilesAndDirectories();
+ } catch(e) {
+ subchildren = [];
+ }
- let subchildren;
- try {
- subchildren = await child.getFilesAndDirectories();
- } catch(e) {
- subchildren = [];
+ child.children = subchildren.length;
+ } else {
+ child.path = dir + '/';
}
-
- child.children = subchildren.length;
- }
+ };
}
return childs;
@@ -108,7 +110,7 @@ export async function copy(file, newPath) {
child.path = oldPath + '/';
}
- await move(child, newPath + '/' + child.name);
+ await copy(child, newPath + '/' + child.name);
}
return;
diff --git a/src/js/components/directory.js b/src/js/components/directory.js
index c2fe9c0..046eca9 100644
--- a/src/js/components/directory.js
+++ b/src/js/components/directory.js
@@ -1,13 +1,8 @@
import React, { Component } from 'react';
import changedir from 'actions/changedir';
-import { show } from 'actions/menu';
-import { active } from 'actions/file';
-import { MENU_WIDTH } from './menu';
import store from 'store';
import entry from './mixins/entry';
-const MENU_TOP_SPACE = 20;
-
export default class Directory extends Component {
constructor() {
super();
diff --git a/src/js/components/file-list.js b/src/js/components/file-list.js
index 0519b34..48369f5 100644
--- a/src/js/components/file-list.js
+++ b/src/js/components/file-list.js
@@ -19,7 +19,7 @@ export default class FileList extends Component {
let settings = store.getState().get('settings');
let els = files.map((file, index) => {
- let selected = activeFile.length && activeFile.indexOf(file) > -1;
+ let selected = activeFile.indexOf(file) > -1;
if (type(file) === 'File') {
return
Filter