fix hawk: a lot of bugfixes here and there
This commit is contained in:
		
							
								
								
									
										23
									
								
								Gruntfile.js
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Gruntfile.js
									
									
									
									
									
								
							@@ -2,6 +2,7 @@ module.exports = function(grunt) {
 | 
				
			|||||||
  require('grunt-task-loader')(grunt);
 | 
					  require('grunt-task-loader')(grunt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  grunt.initConfig({
 | 
					  grunt.initConfig({
 | 
				
			||||||
 | 
					    pkg: require('./package.json'),
 | 
				
			||||||
    browserify: {
 | 
					    browserify: {
 | 
				
			||||||
      dev: {
 | 
					      dev: {
 | 
				
			||||||
        files: [{
 | 
					        files: [{
 | 
				
			||||||
@@ -53,15 +54,6 @@ module.exports = function(grunt) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mochify: {
 | 
					 | 
				
			||||||
      options: {
 | 
					 | 
				
			||||||
        reporter: 'land'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      tests: {
 | 
					 | 
				
			||||||
        src: 'test/**/*.js',
 | 
					 | 
				
			||||||
        options: '<%= browserify.dev.options %>'
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    less: {
 | 
					    less: {
 | 
				
			||||||
      dev: {
 | 
					      dev: {
 | 
				
			||||||
        files: [{
 | 
					        files: [{
 | 
				
			||||||
@@ -90,12 +82,11 @@ module.exports = function(grunt) {
 | 
				
			|||||||
        }]
 | 
					        }]
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mochaTest: {
 | 
					    zip: {
 | 
				
			||||||
      tests: {
 | 
					      release: {
 | 
				
			||||||
        src: ['tests/**/*.js'],
 | 
					        dest: 'releases/hawk-<%= pkg.version %>.zip',
 | 
				
			||||||
        options: {
 | 
					        src: 'build/**/*',
 | 
				
			||||||
          reporter: 'landing'
 | 
					        cwd: 'build/'
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    watch: {
 | 
					    watch: {
 | 
				
			||||||
@@ -116,6 +107,6 @@ module.exports = function(grunt) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  grunt.registerTask('default', ['browserify:dev', 'less:dev', 'copy']);
 | 
					  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');
 | 
					  grunt.registerTask('test', 'mochaTest');
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 Before Width: | Height: | Size: 46 KiB  | 
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,3 +0,0 @@
 | 
				
			|||||||
appname = Hawk
 | 
					 | 
				
			||||||
app_description.innerHTML = This app is empty. Fill it with your own stuff!
 | 
					 | 
				
			||||||
message = Hello world
 | 
					 | 
				
			||||||
@@ -1 +0,0 @@
 | 
				
			|||||||
@import url(en-US.properties)
 | 
					 | 
				
			||||||
							
								
								
									
										133
									
								
								build/main.js
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								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) {
 | 
					      for (var _iterator = childs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
 | 
				
			||||||
        var child = _step.value;
 | 
					        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;
 | 
					          child.children = subchildren.length;
 | 
				
			||||||
        try {
 | 
					        } else {
 | 
				
			||||||
          subchildren = yield child.getFilesAndDirectories();
 | 
					          child.path = dir + '/';
 | 
				
			||||||
        } catch (e) {
 | 
					 | 
				
			||||||
          subchildren = [];
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        child.children = subchildren.length;
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (err) {
 | 
					    } catch (err) {
 | 
				
			||||||
      _didIteratorError = true;
 | 
					      _didIteratorError = true;
 | 
				
			||||||
@@ -29989,6 +29991,8 @@ var children = _asyncToGenerator(function* (dir, gatherInfo) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return childs;
 | 
					  return childs;
 | 
				
			||||||
@@ -30073,7 +30077,7 @@ var copy = _asyncToGenerator(function* (file, newPath) {
 | 
				
			|||||||
          child.path = oldPath + '/';
 | 
					          child.path = oldPath + '/';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        yield move(child, newPath + '/' + child.name);
 | 
					        yield copy(child, newPath + '/' + child.name);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (err) {
 | 
					    } catch (err) {
 | 
				
			||||||
      _didIteratorError2 = true;
 | 
					      _didIteratorError2 = true;
 | 
				
			||||||
@@ -30354,12 +30358,6 @@ var _actionsChangedir = require('actions/changedir');
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var _actionsChangedir2 = _interopRequireDefault(_actionsChangedir);
 | 
					var _actionsChangedir2 = _interopRequireDefault(_actionsChangedir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _actionsMenu = require('actions/menu');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _actionsFile = require('actions/file');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _menu = require('./menu');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _store = require('store');
 | 
					var _store = require('store');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _store2 = _interopRequireDefault(_store);
 | 
					var _store2 = _interopRequireDefault(_store);
 | 
				
			||||||
@@ -30368,8 +30366,6 @@ var _mixinsEntry = require('./mixins/entry');
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var _mixinsEntry2 = _interopRequireDefault(_mixinsEntry);
 | 
					var _mixinsEntry2 = _interopRequireDefault(_mixinsEntry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var MENU_TOP_SPACE = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var Directory = (function (_Component) {
 | 
					var Directory = (function (_Component) {
 | 
				
			||||||
  _inherits(Directory, _Component);
 | 
					  _inherits(Directory, _Component);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30430,7 +30426,7 @@ var Directory = (function (_Component) {
 | 
				
			|||||||
exports['default'] = Directory;
 | 
					exports['default'] = Directory;
 | 
				
			||||||
module.exports = exports['default'];
 | 
					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';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Object.defineProperty(exports, '__esModule', {
 | 
					Object.defineProperty(exports, '__esModule', {
 | 
				
			||||||
@@ -30505,7 +30501,7 @@ var FileList = (function (_Component) {
 | 
				
			|||||||
      var settings = _store2['default'].getState().get('settings');
 | 
					      var settings = _store2['default'].getState().get('settings');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var els = files.map(function (file, index) {
 | 
					      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') {
 | 
					        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 });
 | 
					          return _react2['default'].createElement(_file2['default'], { selectView: selectView, selected: selected, key: index, index: index, name: file.name, size: file.size });
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -30574,12 +30570,6 @@ var _react = require('react');
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var _react2 = _interopRequireDefault(_react);
 | 
					var _react2 = _interopRequireDefault(_react);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _actionsMenu = require('actions/menu');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _actionsFile = require('actions/file');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _menu = require('./menu');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _store = require('store');
 | 
					var _store = require('store');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _store2 = _interopRequireDefault(_store);
 | 
					var _store2 = _interopRequireDefault(_store);
 | 
				
			||||||
@@ -30590,8 +30580,6 @@ var _mixinsEntry = require('./mixins/entry');
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
var _mixinsEntry2 = _interopRequireDefault(_mixinsEntry);
 | 
					var _mixinsEntry2 = _interopRequireDefault(_mixinsEntry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var MENU_TOP_SPACE = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var File = (function (_Component) {
 | 
					var File = (function (_Component) {
 | 
				
			||||||
  _inherits(File, _Component);
 | 
					  _inherits(File, _Component);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30610,7 +30598,7 @@ var File = (function (_Component) {
 | 
				
			|||||||
      var input = undefined,
 | 
					      var input = undefined,
 | 
				
			||||||
          label = undefined;
 | 
					          label = undefined;
 | 
				
			||||||
      if (this.props.selectView) {
 | 
					      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 });
 | 
					        label = _react2['default'].createElement('label', { htmlFor: checkId });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30658,7 +30646,7 @@ var File = (function (_Component) {
 | 
				
			|||||||
exports['default'] = File;
 | 
					exports['default'] = File;
 | 
				
			||||||
module.exports = exports['default'];
 | 
					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';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Object.defineProperty(exports, '__esModule', {
 | 
					Object.defineProperty(exports, '__esModule', {
 | 
				
			||||||
@@ -30780,6 +30768,7 @@ var Menu = (function (_Component) {
 | 
				
			|||||||
      var items = _props.items;
 | 
					      var items = _props.items;
 | 
				
			||||||
      var active = _props.active;
 | 
					      var active = _props.active;
 | 
				
			||||||
      var style = _props.style;
 | 
					      var style = _props.style;
 | 
				
			||||||
 | 
					      var id = _props.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      items = items || [];
 | 
					      items = items || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30797,7 +30786,7 @@ var Menu = (function (_Component) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return _react2['default'].createElement(
 | 
					      return _react2['default'].createElement(
 | 
				
			||||||
        'div',
 | 
					        'div',
 | 
				
			||||||
        { className: className, style: style },
 | 
					        { className: className, style: style, id: id },
 | 
				
			||||||
        _react2['default'].createElement(
 | 
					        _react2['default'].createElement(
 | 
				
			||||||
          'ul',
 | 
					          'ul',
 | 
				
			||||||
          null,
 | 
					          null,
 | 
				
			||||||
@@ -30818,25 +30807,48 @@ exports['default'] = Menu;
 | 
				
			|||||||
Object.defineProperty(exports, '__esModule', {
 | 
					Object.defineProperty(exports, '__esModule', {
 | 
				
			||||||
  value: true
 | 
					  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'] = {
 | 
					exports['default'] = {
 | 
				
			||||||
  contextMenu: function contextMenu(e) {
 | 
					  contextMenu: function contextMenu(e) {
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var file = store.getState().get('files')[this.props.index];
 | 
					    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 x = rect.x;
 | 
				
			||||||
    var y = rect.y;
 | 
					    var y = rect.y;
 | 
				
			||||||
    var width = rect.width;
 | 
					    var width = rect.width;
 | 
				
			||||||
    var height = rect.height;
 | 
					    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;
 | 
					        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() {
 | 
					  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];
 | 
					    var file = store.getState().get('files')[this.props.index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (current.indexOf(file) > -1) {
 | 
					    if (current.indexOf(file) > -1) {
 | 
				
			||||||
@@ -30844,12 +30856,12 @@ exports['default'] = {
 | 
				
			|||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      current.push(file);
 | 
					      current.push(file);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    store.dispatch(active(current));
 | 
					    store.dispatch((0, _actionsFile.active)(current));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
module.exports = exports['default'];
 | 
					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';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Object.defineProperty(exports, '__esModule', {
 | 
					Object.defineProperty(exports, '__esModule', {
 | 
				
			||||||
@@ -30917,7 +30929,7 @@ var Navigation = (function (_Component) {
 | 
				
			|||||||
          _react2['default'].createElement(
 | 
					          _react2['default'].createElement(
 | 
				
			||||||
            'li',
 | 
					            'li',
 | 
				
			||||||
            null,
 | 
					            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(
 | 
					            _react2['default'].createElement(
 | 
				
			||||||
              'label',
 | 
					              'label',
 | 
				
			||||||
              { htmlFor: 'filter-all' },
 | 
					              { htmlFor: 'filter-all' },
 | 
				
			||||||
@@ -30927,7 +30939,7 @@ var Navigation = (function (_Component) {
 | 
				
			|||||||
          _react2['default'].createElement(
 | 
					          _react2['default'].createElement(
 | 
				
			||||||
            'li',
 | 
					            'li',
 | 
				
			||||||
            null,
 | 
					            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(
 | 
					            _react2['default'].createElement(
 | 
				
			||||||
              'label',
 | 
					              'label',
 | 
				
			||||||
              { htmlFor: 'filter-image' },
 | 
					              { htmlFor: 'filter-image' },
 | 
				
			||||||
@@ -30937,7 +30949,7 @@ var Navigation = (function (_Component) {
 | 
				
			|||||||
          _react2['default'].createElement(
 | 
					          _react2['default'].createElement(
 | 
				
			||||||
            'li',
 | 
					            'li',
 | 
				
			||||||
            null,
 | 
					            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(
 | 
					            _react2['default'].createElement(
 | 
				
			||||||
              'label',
 | 
					              'label',
 | 
				
			||||||
              { htmlFor: 'filter-video' },
 | 
					              { htmlFor: 'filter-video' },
 | 
				
			||||||
@@ -30947,7 +30959,7 @@ var Navigation = (function (_Component) {
 | 
				
			|||||||
          _react2['default'].createElement(
 | 
					          _react2['default'].createElement(
 | 
				
			||||||
            'li',
 | 
					            'li',
 | 
				
			||||||
            null,
 | 
					            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(
 | 
					            _react2['default'].createElement(
 | 
				
			||||||
              'label',
 | 
					              'label',
 | 
				
			||||||
              { htmlFor: 'filter-audio' },
 | 
					              { htmlFor: 'filter-audio' },
 | 
				
			||||||
@@ -31078,7 +31090,7 @@ var Navigation = (function (_Component) {
 | 
				
			|||||||
    key: 'onChange',
 | 
					    key: 'onChange',
 | 
				
			||||||
    value: function onChange(e) {
 | 
					    value: function onChange(e) {
 | 
				
			||||||
      var key = e.target.name || e.target.id;
 | 
					      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));
 | 
					      var action = (0, _actionsSettings2['default'])(_defineProperty({}, key, value));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -31186,9 +31198,7 @@ window.changedir = _actionsChangedir2['default'];
 | 
				
			|||||||
var FileMenu = (0, _reactRedux.connect)(function (state) {
 | 
					var FileMenu = (0, _reactRedux.connect)(function (state) {
 | 
				
			||||||
  return state.get('fileMenu');
 | 
					  return state.get('fileMenu');
 | 
				
			||||||
})(_componentsMenu2['default']);
 | 
					})(_componentsMenu2['default']);
 | 
				
			||||||
var DirectoryMenu = (0, _reactRedux.connect)(function (state) {
 | 
					// let DirectoryMenu = connect(state => state.get('directoryMenu'))(Menu);
 | 
				
			||||||
  return state.get('directoryMenu');
 | 
					 | 
				
			||||||
})(_componentsMenu2['default']);
 | 
					 | 
				
			||||||
var MoreMenu = (0, _reactRedux.connect)(function (state) {
 | 
					var MoreMenu = (0, _reactRedux.connect)(function (state) {
 | 
				
			||||||
  return state.get('moreMenu');
 | 
					  return state.get('moreMenu');
 | 
				
			||||||
})(_componentsMenu2['default']);
 | 
					})(_componentsMenu2['default']);
 | 
				
			||||||
@@ -31223,21 +31233,22 @@ var Root = (function (_Component) {
 | 
				
			|||||||
    value: function render() {
 | 
					    value: function render() {
 | 
				
			||||||
      return _react2['default'].createElement(
 | 
					      return _react2['default'].createElement(
 | 
				
			||||||
        'div',
 | 
					        '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(_componentsHeader2['default'], null),
 | 
				
			||||||
        _react2['default'].createElement(_componentsBreadcrumb2['default'], null),
 | 
					        _react2['default'].createElement(_componentsBreadcrumb2['default'], null),
 | 
				
			||||||
        _react2['default'].createElement(_componentsNavigation2['default'], null),
 | 
					        _react2['default'].createElement(_componentsNavigation2['default'], null),
 | 
				
			||||||
        _react2['default'].createElement(_componentsFileList2['default'], null),
 | 
					        _react2['default'].createElement(_componentsFileList2['default'], null),
 | 
				
			||||||
        _react2['default'].createElement(_componentsToolbar2['default'], null),
 | 
					        _react2['default'].createElement(_componentsToolbar2['default'], null),
 | 
				
			||||||
        _react2['default'].createElement(FileMenu, null),
 | 
					        _react2['default'].createElement(FileMenu, { id: 'fileMenu' }),
 | 
				
			||||||
        _react2['default'].createElement(DirectoryMenu, null),
 | 
					        _react2['default'].createElement(MoreMenu, { id: 'moreMenu' }),
 | 
				
			||||||
        _react2['default'].createElement(MoreMenu, null),
 | 
					 | 
				
			||||||
        _react2['default'].createElement(RenameDialog, null),
 | 
					        _react2['default'].createElement(RenameDialog, null),
 | 
				
			||||||
        _react2['default'].createElement(DeleteDialog, null),
 | 
					        _react2['default'].createElement(DeleteDialog, null),
 | 
				
			||||||
        _react2['default'].createElement(ErrorDialog, null),
 | 
					        _react2['default'].createElement(ErrorDialog, null),
 | 
				
			||||||
        _react2['default'].createElement(CreateDialog, null),
 | 
					        _react2['default'].createElement(CreateDialog, null),
 | 
				
			||||||
        _react2['default'].createElement(SearchDialog, null),
 | 
					        _react2['default'].createElement(SearchDialog, null),
 | 
				
			||||||
        _react2['default'].createElement(_componentsSpinner2['default'], null),
 | 
					        _react2['default'].createElement(_componentsSpinner2['default'], null),
 | 
				
			||||||
 | 
					        _react2['default'].createElement('div', { className: 'swipe-instruction tour-item' }),
 | 
				
			||||||
        _react2['default'].createElement(
 | 
					        _react2['default'].createElement(
 | 
				
			||||||
          'div',
 | 
					          'div',
 | 
				
			||||||
          { className: 'tour-dialog' },
 | 
					          { className: 'tour-dialog' },
 | 
				
			||||||
@@ -31621,7 +31632,7 @@ var entryMenu = {
 | 
				
			|||||||
    action: function action() {
 | 
					    action: function action() {
 | 
				
			||||||
      var files = _store2['default'].getState().get('files');
 | 
					      var files = _store2['default'].getState().get('files');
 | 
				
			||||||
      var active = _store2['default'].getState().get('activeFile');
 | 
					      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, _actionsMenu.hideAll)());
 | 
				
			||||||
      _store2['default'].dispatch((0, _actionsDialog.show)('renameDialog', { description: description }));
 | 
					      _store2['default'].dispatch((0, _actionsDialog.show)('renameDialog', { description: description }));
 | 
				
			||||||
@@ -31973,7 +31984,8 @@ exports['default'] = function (state, action) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (action.type === _actionsTypes.RENAME_FILE) {
 | 
					  if (action.type === _actionsTypes.RENAME_FILE) {
 | 
				
			||||||
    var all = Promise.all(action.file.map(function (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);
 | 
					    all.then(boundRefresh, _utils.reportError);
 | 
				
			||||||
@@ -32214,7 +32226,11 @@ exports['default'] = function (state, action) {
 | 
				
			|||||||
  if (state === undefined) state = DEFAULT;
 | 
					  if (state === undefined) state = DEFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (action.type === _actionsTypes.SETTINGS) {
 | 
					  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;
 | 
					  return state;
 | 
				
			||||||
@@ -32245,6 +32261,13 @@ exports['default'] = function (state, action) {
 | 
				
			|||||||
  switch (action.type) {
 | 
					  switch (action.type) {
 | 
				
			||||||
    case _actionsTypes.CHANGE_DIRECTORY:
 | 
					    case _actionsTypes.CHANGE_DIRECTORY:
 | 
				
			||||||
    case _actionsTypes.REFRESH:
 | 
					    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;
 | 
					      return true;
 | 
				
			||||||
    case _actionsTypes.LIST_FILES:
 | 
					    case _actionsTypes.LIST_FILES:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
@@ -32288,7 +32311,8 @@ var _dialogs = require('./dialogs');
 | 
				
			|||||||
var _dialogs2 = _interopRequireDefault(_dialogs);
 | 
					var _dialogs2 = _interopRequireDefault(_dialogs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var DEFAULT = new _immutable2['default'].Map(Object.assign({
 | 
					var DEFAULT = new _immutable2['default'].Map(Object.assign({
 | 
				
			||||||
  dir: ''
 | 
					  dir: '',
 | 
				
			||||||
 | 
					  settings: JSON.parse(localStorage.getItem('settings') || '{}')
 | 
				
			||||||
}, _dialogs2['default'], _menus2['default']));
 | 
					}, _dialogs2['default'], _menus2['default']));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var store = (0, _redux.createStore)(_reducersAll2['default'], DEFAULT);
 | 
					var store = (0, _redux.createStore)(_reducersAll2['default'], DEFAULT);
 | 
				
			||||||
@@ -32318,7 +32342,8 @@ var MESSAGES = {
 | 
				
			|||||||
  'icon-select': 'Select files for batch actions',
 | 
					  'icon-select': 'Select files for batch actions',
 | 
				
			||||||
  'icon-more': 'Actions used on selected files such as Copy, Delete, Move, …',
 | 
					  'icon-more': 'Actions used on selected files such as Copy, Delete, Move, …',
 | 
				
			||||||
  'drawer': 'Extra options, tools and links are here',
 | 
					  '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;
 | 
					var DIALOG_HIDE_DELAY = 2000;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "version": "0.1.0",
 | 
					  "version": "0.1.0",
 | 
				
			||||||
  "name": "Hawk",
 | 
					  "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",
 | 
					  "launch_path": "/index.html",
 | 
				
			||||||
  "icons": {
 | 
					  "icons": {
 | 
				
			||||||
    "16": "/icon/Icon-16.png",
 | 
					    "16": "/icon/Icon-16.png",
 | 
				
			||||||
@@ -15,17 +15,26 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "type": "privileged",
 | 
					  "type": "privileged",
 | 
				
			||||||
  "permissions": {
 | 
					  "permissions": {
 | 
				
			||||||
    "device-storage:sdcard": {"access": "readwrite"},
 | 
					    "device-storage:sdcard": {
 | 
				
			||||||
    "device-storage:videos": {"access": "readwrite"},
 | 
					      "access": "readwrite",
 | 
				
			||||||
    "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"},
 | 
					    },
 | 
				
			||||||
    "device-storage:apps": {"access": "readwrite"},
 | 
					    "device-storage:videos": {
 | 
				
			||||||
    "webapps-manage": {}
 | 
					      "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": [
 | 
					  "installs_allowed_from": [
 | 
				
			||||||
    "*"
 | 
					    "https://marketplace.firefox.com",
 | 
				
			||||||
 | 
					    "https://marketplace-dev.allizom.org"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "locales": {
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  "default_locale": "en"
 | 
					  "default_locale": "en"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -172,6 +172,28 @@ input:checked + label::after {
 | 
				
			|||||||
  transform: scale(0);
 | 
					  transform: scale(0);
 | 
				
			||||||
  animation: pulse 2s ease-out infinite;
 | 
					  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 {
 | 
					.tour .tour-dialog {
 | 
				
			||||||
  display: block;
 | 
					  display: block;
 | 
				
			||||||
  box-sizing: border-box;
 | 
					  box-sizing: border-box;
 | 
				
			||||||
@@ -196,6 +218,24 @@ input:checked + label::after {
 | 
				
			|||||||
    transform: scale(5);
 | 
					    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 {
 | 
					.coming-soon::after {
 | 
				
			||||||
  content: 'soon...';
 | 
					  content: 'soon...';
 | 
				
			||||||
  background: #f7c59f;
 | 
					  background: #f7c59f;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,7 @@
 | 
				
			|||||||
    "grunt-contrib-watch": "^0.6.1",
 | 
					    "grunt-contrib-watch": "^0.6.1",
 | 
				
			||||||
    "grunt-fxos": "^0.1.2",
 | 
					    "grunt-fxos": "^0.1.2",
 | 
				
			||||||
    "grunt-task-loader": "^0.6.0",
 | 
					    "grunt-task-loader": "^0.6.0",
 | 
				
			||||||
 | 
					    "grunt-zip": "^0.17.0",
 | 
				
			||||||
    "hammerjs": "^2.0.4",
 | 
					    "hammerjs": "^2.0.4",
 | 
				
			||||||
    "immutable": "^3.7.5",
 | 
					    "immutable": "^3.7.5",
 | 
				
			||||||
    "less-plugin-clean-css": "^1.5.1",
 | 
					    "less-plugin-clean-css": "^1.5.1",
 | 
				
			||||||
 
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@@ -32,17 +32,19 @@ export async function children(dir, gatherInfo) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (gatherInfo) {
 | 
					  if (gatherInfo) {
 | 
				
			||||||
    for (let child of childs) {
 | 
					    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;
 | 
					        child.children = subchildren.length;
 | 
				
			||||||
      try {
 | 
					      } else {
 | 
				
			||||||
        subchildren = await child.getFilesAndDirectories();
 | 
					        child.path = dir + '/';
 | 
				
			||||||
      } catch(e) {
 | 
					 | 
				
			||||||
        subchildren = [];
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
      child.children = subchildren.length;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return childs;
 | 
					  return childs;
 | 
				
			||||||
@@ -108,7 +110,7 @@ export async function copy(file, newPath) {
 | 
				
			|||||||
        child.path = oldPath + '/';
 | 
					        child.path = oldPath + '/';
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await move(child, newPath + '/' + child.name);
 | 
					      await copy(child, newPath + '/' + child.name);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,8 @@
 | 
				
			|||||||
import React, { Component } from 'react';
 | 
					import React, { Component } from 'react';
 | 
				
			||||||
import changedir from 'actions/changedir';
 | 
					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 store from 'store';
 | 
				
			||||||
import entry from './mixins/entry';
 | 
					import entry from './mixins/entry';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MENU_TOP_SPACE = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class Directory extends Component {
 | 
					export default class Directory extends Component {
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    super();
 | 
					    super();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ export default class FileList extends Component {
 | 
				
			|||||||
    let settings = store.getState().get('settings');
 | 
					    let settings = store.getState().get('settings');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let els = files.map((file, index) => {
 | 
					    let els = files.map((file, index) => {
 | 
				
			||||||
      let selected = activeFile.length && activeFile.indexOf(file) > -1;
 | 
					      let selected = activeFile.indexOf(file) > -1;
 | 
				
			||||||
      if (type(file) === 'File') {
 | 
					      if (type(file) === 'File') {
 | 
				
			||||||
        return <File selectView={selectView} selected={selected} key={index} index={index} name={file.name} size={file.size} />;
 | 
					        return <File selectView={selectView} selected={selected} key={index} index={index} name={file.name} size={file.size} />;
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,8 @@
 | 
				
			|||||||
import React, { Component } from 'react';
 | 
					import React, { Component } from 'react';
 | 
				
			||||||
import { show } from 'actions/menu';
 | 
					 | 
				
			||||||
import { active } from 'actions/file';
 | 
					 | 
				
			||||||
import { MENU_WIDTH } from './menu';
 | 
					 | 
				
			||||||
import store from 'store';
 | 
					import store from 'store';
 | 
				
			||||||
import { humanSize } from 'utils';
 | 
					import { humanSize } from 'utils';
 | 
				
			||||||
import entry from './mixins/entry';
 | 
					import entry from './mixins/entry';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MENU_TOP_SPACE = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class File extends Component {
 | 
					export default class File extends Component {
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    super();
 | 
					    super();
 | 
				
			||||||
@@ -19,7 +14,7 @@ export default class File extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let input, label;
 | 
					    let input, label;
 | 
				
			||||||
    if (this.props.selectView) {
 | 
					    if (this.props.selectView) {
 | 
				
			||||||
      input = <input type='checkbox' id={checkId} defaultChecked={this.props.selected} readOnly />;
 | 
					      input = <input type='checkbox' id={checkId} checked={this.props.selected} readOnly />;
 | 
				
			||||||
      label = <label htmlFor={checkId}></label>;
 | 
					      label = <label htmlFor={checkId}></label>;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ export const MENU_WIDTH = 245;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default class Menu extends Component {
 | 
					export default class Menu extends Component {
 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    let { items, active, style } = this.props;
 | 
					    let { items, active, style, id } = this.props;
 | 
				
			||||||
    items = items || [];
 | 
					    items = items || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let els = items.map((item, index) => {
 | 
					    let els = items.map((item, index) => {
 | 
				
			||||||
@@ -16,7 +16,7 @@ export default class Menu extends Component {
 | 
				
			|||||||
    let className = 'menu ' + (active ? 'active' : '');
 | 
					    let className = 'menu ' + (active ? 'active' : '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className={className} style={style}>
 | 
					      <div className={className} style={style} id={id}>
 | 
				
			||||||
        <ul>{els}</ul>
 | 
					        <ul>{els}</ul>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,10 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { active } from 'actions/file';
 | 
				
			||||||
 | 
					import { show } from 'actions/menu';
 | 
				
			||||||
 | 
					import { MENU_WIDTH } from 'components/menu';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const MENU_TOP_SPACE = 20;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  contextMenu(e) {
 | 
					  contextMenu(e) {
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
@@ -8,12 +15,20 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let left = x + width / 2 - MENU_WIDTH / 2,
 | 
					    let left = x + width / 2 - MENU_WIDTH / 2,
 | 
				
			||||||
        top  = y + height / 2 + MENU_TOP_SPACE;
 | 
					        top  = y + height / 2 + MENU_TOP_SPACE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let dialogHeight = document.getElementById('fileMenu').offsetHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let diff = window.innerHeight - (dialogHeight + top);
 | 
				
			||||||
 | 
					    if (diff <= 0) {
 | 
				
			||||||
 | 
					      top -= Math.abs(diff);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    store.dispatch(show('fileMenu', {style: {left, top}}));
 | 
					    store.dispatch(show('fileMenu', {style: {left, top}}));
 | 
				
			||||||
    store.dispatch(active([file]));
 | 
					    store.dispatch(active([file]));
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  select() {
 | 
					  select() {
 | 
				
			||||||
    let current = store.getState().get('activeFile').slice(0);
 | 
					    let current = (store.getState().get('activeFile') || []).slice(0);
 | 
				
			||||||
    let file = store.getState().get('files')[this.props.index];
 | 
					    let file = store.getState().get('files')[this.props.index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (current.indexOf(file) > -1) {
 | 
					    if (current.indexOf(file) > -1) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,19 +17,19 @@ export default class Navigation extends Component {
 | 
				
			|||||||
        <p>Filter</p>
 | 
					        <p>Filter</p>
 | 
				
			||||||
        <ul>
 | 
					        <ul>
 | 
				
			||||||
          <li>
 | 
					          <li>
 | 
				
			||||||
            <input id='filter-all' name='filter' value='' type='radio' defaultChecked={!settings.filter} />
 | 
					            <input id='filter-all' name='filter' data-value='' type='radio' defaultChecked={!settings.filter} />
 | 
				
			||||||
            <label htmlFor='filter-all'>All</label>
 | 
					            <label htmlFor='filter-all'>All</label>
 | 
				
			||||||
          </li>
 | 
					          </li>
 | 
				
			||||||
          <li>
 | 
					          <li>
 | 
				
			||||||
            <input id='filter-image' name='filter' value='image' type='radio' defaultChecked={settings.filter === 'image'} />
 | 
					            <input id='filter-image' name='filter' data-value='image' type='radio' defaultChecked={settings.filter === 'image'} />
 | 
				
			||||||
            <label htmlFor='filter-image'>Image</label>
 | 
					            <label htmlFor='filter-image'>Image</label>
 | 
				
			||||||
          </li>
 | 
					          </li>
 | 
				
			||||||
          <li>
 | 
					          <li>
 | 
				
			||||||
            <input id='filter-video' name='filter' value='video' type='radio' defaultChecked={settings.filter === 'video'} />
 | 
					            <input id='filter-video' name='filter' data-value='video' type='radio' defaultChecked={settings.filter === 'video'} />
 | 
				
			||||||
            <label htmlFor='filter-video'>Video</label>
 | 
					            <label htmlFor='filter-video'>Video</label>
 | 
				
			||||||
          </li>
 | 
					          </li>
 | 
				
			||||||
          <li>
 | 
					          <li>
 | 
				
			||||||
            <input id='filter-audio' name='filter' value='audio' type='radio' defaultChecked={settings.filter === 'audio'} />
 | 
					            <input id='filter-audio' name='filter' data-value='audio' type='radio' defaultChecked={settings.filter === 'audio'} />
 | 
				
			||||||
            <label htmlFor='filter-audio'>Audio</label>
 | 
					            <label htmlFor='filter-audio'>Audio</label>
 | 
				
			||||||
          </li>
 | 
					          </li>
 | 
				
			||||||
        </ul>
 | 
					        </ul>
 | 
				
			||||||
@@ -77,7 +77,7 @@ export default class Navigation extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  onChange(e) {
 | 
					  onChange(e) {
 | 
				
			||||||
    let key = e.target.name || e.target.id;
 | 
					    let key = e.target.name || e.target.id;
 | 
				
			||||||
    let value = e.target.value === undefined ? e.target.checked : e.target.value;
 | 
					    let value = typeof e.target.dataset.value !== 'undefined' ? e.target.dataset.value : e.target.checked;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let action = updateSettings({
 | 
					    let action = updateSettings({
 | 
				
			||||||
      [key]: value
 | 
					      [key]: value
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ window.store = store;
 | 
				
			|||||||
window.changedir = changedir;
 | 
					window.changedir = changedir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let FileMenu = connect(state => state.get('fileMenu'))(Menu);
 | 
					let FileMenu = connect(state => state.get('fileMenu'))(Menu);
 | 
				
			||||||
let DirectoryMenu = connect(state => state.get('directoryMenu'))(Menu);
 | 
					// let DirectoryMenu = connect(state => state.get('directoryMenu'))(Menu);
 | 
				
			||||||
let MoreMenu = connect(state => state.get('moreMenu'))(Menu);
 | 
					let MoreMenu = connect(state => state.get('moreMenu'))(Menu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let RenameDialog = connect(state => state.get('renameDialog'))(Dialog);
 | 
					let RenameDialog = connect(state => state.get('renameDialog'))(Dialog);
 | 
				
			||||||
@@ -31,16 +31,16 @@ let SearchDialog = connect(state => state.get('searchDialog'))(Dialog);
 | 
				
			|||||||
export default class Root extends Component {
 | 
					export default class Root extends Component {
 | 
				
			||||||
  render() {
 | 
					  render() {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div onTouchStart={this.touchStart.bind(this)} onClick={this.onClick.bind(this)}>
 | 
					      <div onTouchStart={this.touchStart.bind(this)}
 | 
				
			||||||
 | 
					           onClick={this.onClick.bind(this)}>
 | 
				
			||||||
        <Header />
 | 
					        <Header />
 | 
				
			||||||
        <Breadcrumb />
 | 
					        <Breadcrumb />
 | 
				
			||||||
        <Navigation />
 | 
					        <Navigation />
 | 
				
			||||||
        <FileList />
 | 
					        <FileList />
 | 
				
			||||||
        <Toolbar />
 | 
					        <Toolbar />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <FileMenu />
 | 
					        <FileMenu id='fileMenu' />
 | 
				
			||||||
        <DirectoryMenu />
 | 
					        <MoreMenu id='moreMenu' />
 | 
				
			||||||
        <MoreMenu />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <RenameDialog />
 | 
					        <RenameDialog />
 | 
				
			||||||
        <DeleteDialog />
 | 
					        <DeleteDialog />
 | 
				
			||||||
@@ -50,6 +50,8 @@ export default class Root extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <Spinner />
 | 
					        <Spinner />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div className='swipe-instruction tour-item'></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div className='tour-dialog'>
 | 
					        <div className='tour-dialog'>
 | 
				
			||||||
          Hello! Tap each highlighted button to get an understanding of how they work.
 | 
					          Hello! Tap each highlighted button to get an understanding of how they work.
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ const entryMenu = {
 | 
				
			|||||||
      action() {
 | 
					      action() {
 | 
				
			||||||
        let files = store.getState().get('files');
 | 
					        let files = store.getState().get('files');
 | 
				
			||||||
        let active = store.getState().get('activeFile');
 | 
					        let active = store.getState().get('activeFile');
 | 
				
			||||||
        const description = `Enter the new name for ${active[0].name}?`;
 | 
					        const description = `Enter the new name for ${active[0].name}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        store.dispatch(hideAll());
 | 
					        store.dispatch(hideAll());
 | 
				
			||||||
        store.dispatch(show('renameDialog', {description}));
 | 
					        store.dispatch(show('renameDialog', {description}));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,7 +46,8 @@ export default function(state = [], action) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  if (action.type === RENAME_FILE) {
 | 
					  if (action.type === RENAME_FILE) {
 | 
				
			||||||
    let all = Promise.all(action.file.map(file => {
 | 
					    let all = Promise.all(action.file.map(file => {
 | 
				
			||||||
      return move(file, (file.path || '') + action.name);
 | 
					      let cwd = store.getState().get('cwd');
 | 
				
			||||||
 | 
					      return move(file, cwd + '/' + action.name);
 | 
				
			||||||
    }));
 | 
					    }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    all.then(boundRefresh, reportError);
 | 
					    all.then(boundRefresh, reportError);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,11 @@ const DEFAULT = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default function(state = DEFAULT, action) {
 | 
					export default function(state = DEFAULT, action) {
 | 
				
			||||||
  if (action.type === SETTINGS) {
 | 
					  if (action.type === SETTINGS) {
 | 
				
			||||||
    return Object.assign({}, state, omit(action, 'type'));
 | 
					    let newSettings = Object.assign({}, state, omit(action, 'type'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    localStorage.setItem('settings', JSON.stringify(newSettings));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return newSettings;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return state;
 | 
					  return state;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { SPINNER, CHANGE_DIRECTORY, LIST_FILES, REFRESH, DIALOG, CREATE_FILE, DELETE_FILE } from 'actions/types';
 | 
					import { SPINNER, CHANGE_DIRECTORY, LIST_FILES, REFRESH, DIALOG, SETTINGS,
 | 
				
			||||||
 | 
					         CREATE_FILE, DELETE_FILE, RENAME_FILE, MOVE_FILE, COPY_FILE, SEARCH} from 'actions/types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function(state = false, action) {
 | 
					export default function(state = false, action) {
 | 
				
			||||||
  if (action.type === SPINNER) {
 | 
					  if (action.type === SPINNER) {
 | 
				
			||||||
@@ -12,6 +13,13 @@ export default function(state = false, action) {
 | 
				
			|||||||
  switch (action.type) {
 | 
					  switch (action.type) {
 | 
				
			||||||
    case CHANGE_DIRECTORY:
 | 
					    case CHANGE_DIRECTORY:
 | 
				
			||||||
    case REFRESH:
 | 
					    case REFRESH:
 | 
				
			||||||
 | 
					    case SETTINGS:
 | 
				
			||||||
 | 
					    case CREATE_FILE:
 | 
				
			||||||
 | 
					    case MOVE_FILE:
 | 
				
			||||||
 | 
					    case DELETE_FILE:
 | 
				
			||||||
 | 
					    case RENAME_FILE:
 | 
				
			||||||
 | 
					    case COPY_FILE:
 | 
				
			||||||
 | 
					    case SEARCH:
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    case LIST_FILES:
 | 
					    case LIST_FILES:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import dialogs from './dialogs';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const DEFAULT = new Immutable.Map(Object.assign({
 | 
					const DEFAULT = new Immutable.Map(Object.assign({
 | 
				
			||||||
  dir: '',
 | 
					  dir: '',
 | 
				
			||||||
 | 
					  settings: JSON.parse(localStorage.getItem('settings') || '{}')
 | 
				
			||||||
}, dialogs, menus));
 | 
					}, dialogs, menus));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let store = createStore(reducers, DEFAULT);
 | 
					let store = createStore(reducers, DEFAULT);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,8 @@ const MESSAGES = {
 | 
				
			|||||||
  'icon-select': 'Select files for batch actions',
 | 
					  'icon-select': 'Select files for batch actions',
 | 
				
			||||||
  'icon-more': 'Actions used on selected files such as Copy, Delete, Move, …',
 | 
					  'icon-more': 'Actions used on selected files such as Copy, Delete, Move, …',
 | 
				
			||||||
  'drawer': 'Extra options, tools and links are here',
 | 
					  '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'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const DIALOG_HIDE_DELAY = 2000;
 | 
					const DIALOG_HIDE_DELAY = 2000;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,6 +49,40 @@
 | 
				
			|||||||
    animation: pulse 2s ease-out infinite;
 | 
					    animation: pulse 2s ease-out infinite;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .swipe-instruction {
 | 
				
			||||||
 | 
					    position: fixed;
 | 
				
			||||||
 | 
					    left: 50%;
 | 
				
			||||||
 | 
					    top: 20%;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    width: 70vw;
 | 
				
			||||||
 | 
					    height: 5rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    margin-left: -35vw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    z-index: 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    background: white;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    border-radius: 3rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &::before {
 | 
				
			||||||
 | 
					      content: '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      position: absolute;
 | 
				
			||||||
 | 
					      left: 0;
 | 
				
			||||||
 | 
					      top: 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      width: 5rem;
 | 
				
			||||||
 | 
					      height: 5rem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      background: darken(white, 15);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      border-radius: 50%;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      animation: swipe 3s ease infinite;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .tour-dialog {
 | 
					  .tour-dialog {
 | 
				
			||||||
    display: block;
 | 
					    display: block;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -86,3 +120,22 @@
 | 
				
			|||||||
    transform: scale(5);
 | 
					    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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "version": "0.1.0",
 | 
					  "version": "0.1.0",
 | 
				
			||||||
  "name": "Hawk",
 | 
					  "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",
 | 
					  "launch_path": "/index.html",
 | 
				
			||||||
  "icons": {
 | 
					  "icons": {
 | 
				
			||||||
    "16": "/icon/Icon-16.png",
 | 
					    "16": "/icon/Icon-16.png",
 | 
				
			||||||
@@ -15,13 +15,26 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "type": "privileged",
 | 
					  "type": "privileged",
 | 
				
			||||||
  "permissions": {
 | 
					  "permissions": {
 | 
				
			||||||
    "device-storage:sdcard": {"access": "readwrite"},
 | 
					    "device-storage:sdcard": {
 | 
				
			||||||
    "device-storage:videos": {"access": "readwrite"},
 | 
					      "access": "readwrite",
 | 
				
			||||||
    "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"}
 | 
					    },
 | 
				
			||||||
 | 
					    "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": [
 | 
					  "installs_allowed_from": [
 | 
				
			||||||
    "marketplace.firefox.com"
 | 
					    "https://marketplace.firefox.com",
 | 
				
			||||||
 | 
					    "https://marketplace-dev.allizom.org"
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "default_locale": "en"
 | 
					  "default_locale": "en"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user