diff --git a/lib/browser/app.js b/lib/browser/app.js index 67b68236..5fcf2945 100644 --- a/lib/browser/app.js +++ b/lib/browser/app.js @@ -41,6 +41,7 @@ require('./browser/pages/settings/settings'); require('./browser/utils/window-progress/window-progress'); require('./browser/utils/open-external/open-external'); require('./browser/utils/if-state/if-state'); +require('./browser/utils/dropzone/dropzone'); const app = angular.module('Etcher', [ 'ui.router', @@ -69,7 +70,8 @@ const app = angular.module('Etcher', [ // Utils 'Etcher.Utils.WindowProgress', 'Etcher.Utils.OpenExternal', - 'Etcher.Utils.IfState' + 'Etcher.Utils.IfState', + 'Etcher.Utils.Dropzone' ]); app.config(function($stateProvider, $urlRouterProvider) { @@ -163,7 +165,14 @@ app.controller('AppController', function( // completely up and running. document.querySelector('body').style.display = 'initial'; - this.selectImage = function() { + this.selectImage = function(image) { + self.selection.setImage(image); + AnalyticsService.logEvent('Select image', { + image: image + }); + }; + + this.openImageSelector = function() { return $q.when(dialog.selectImage()).then(function(image) { // Avoid analytics and selection state changes @@ -172,10 +181,7 @@ app.controller('AppController', function( return; } - self.selection.setImage(image); - AnalyticsService.logEvent('Select image', { - image: image - }); + self.selectImage(image); }); }; diff --git a/lib/browser/utils/dropzone/directives/dropzone.js b/lib/browser/utils/dropzone/directives/dropzone.js new file mode 100644 index 00000000..a2bb9350 --- /dev/null +++ b/lib/browser/utils/dropzone/directives/dropzone.js @@ -0,0 +1,71 @@ +/* + * Copyright 2016 Resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const _ = require('lodash'); + +/** + * @summary Dropzone directive + * @function + * @public + * + * @description + * This directive provides an attribute to detect a file + * being dropped into the element. + * + * @param {Object} $timeout - Angular's timeout wrapper + * @returns {Object} directive + * + * @example + *
Drag a file here
+ */ +module.exports = function($timeout) { + return { + restrict: 'A', + scope: { + dropzone: '&' + }, + link: function(scope, element) { + const domElement = element[0]; + + // See https://github.com/electron/electron/blob/master/docs/api/file-object.md + + // We're not interested in these events + domElement.ondragover = _.constant(false); + domElement.ondragleave = _.constant(false); + domElement.ondragend = _.constant(false); + + domElement.ondrop = function(event) { + event.preventDefault(); + const filename = event.dataTransfer.files[0].path; + + // Safely bring this to the word of Angular + $timeout(function() { + scope.dropzone({ + + // Pass the filename as a named + // parameter called `$file` + $file: filename + + }); + }); + + return false; + }; + } + }; +}; diff --git a/lib/browser/utils/dropzone/dropzone.js b/lib/browser/utils/dropzone/dropzone.js new file mode 100644 index 00000000..a6d41600 --- /dev/null +++ b/lib/browser/utils/dropzone/dropzone.js @@ -0,0 +1,25 @@ +/* + * Copyright 2016 Resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +/** + * @module Etcher.Utils.Dropzone + */ + +const angular = require('angular'); +const Dropzone = angular.module('Etcher.Utils.Dropzone', []); +Dropzone.directive('dropzone', require('./directives/dropzone')); diff --git a/lib/partials/main.html b/lib/partials/main.html index 55473551..025db3e4 100644 --- a/lib/partials/main.html +++ b/lib/partials/main.html @@ -1,12 +1,12 @@
-
+
1
- +
diff --git a/tests/browser/utils/dropzone.spec.js b/tests/browser/utils/dropzone.spec.js new file mode 100644 index 00000000..e671e017 --- /dev/null +++ b/tests/browser/utils/dropzone.spec.js @@ -0,0 +1,90 @@ +/* + * Copyright 2016 Resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const m = require('mochainon'); +const angular = require('angular'); +require('angular-mocks'); +require('../../../lib/browser/utils/dropzone/dropzone'); + +describe('Browser: Dropzone', function() { + + beforeEach(angular.mock.module('Etcher.Utils.Dropzone')); + + describe('dropzone', function() { + + let $compile; + let $rootScope; + let $timeout; + + beforeEach(angular.mock.inject(function(_$compile_, _$rootScope_, _$timeout_) { + $compile = _$compile_; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + })); + + it('should pass the file back to the callback as $file', function(done) { + $rootScope.onDropZone = function(file) { + m.chai.expect(file).to.equal('/foo/bar'); + done(); + }; + + const element = $compile('
Drop a file here
')($rootScope); + $rootScope.$digest(); + + element[0].ondrop({ + preventDefault: angular.noop, + dataTransfer: { + files: [ + { + path: '/foo/bar' + } + ] + } + }); + + $rootScope.$digest(); + $timeout.flush(); + }); + + it('should pass undefined to the callback if not passing $file', function(done) { + $rootScope.onDropZone = function(file) { + m.chai.expect(file).to.be.undefined; + done(); + }; + + const element = $compile('
Drop a file here
')($rootScope); + $rootScope.$digest(); + + element[0].ondrop({ + preventDefault: angular.noop, + dataTransfer: { + files: [ + { + path: '/foo/bar' + } + ] + } + }); + + $rootScope.$digest(); + $timeout.flush(); + }); + + }); + +});