diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..1308deb --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "extends": "google", + "installedESLint": true, + "rules": { + "indent": ["error", 4] + }, + "plugins": [ + "angular", + "html", + "jquery", + "security", + "this" + ], + "globals": { + "angular": false, + "moment": false, + "Highcharts": false, + "$": false + } +} diff --git a/Gruntfile.js b/Gruntfile.js index 2ac3285..7999719 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -38,8 +38,7 @@ module.exports = function(grunt) { grunt.registerTask('jsdev', [ 'wiredep', - 'newer:jshint', - 'newer:jscs' + 'newer:eslint' ]); grunt.registerTask('htmldev', [ diff --git a/accountant-ui/js/accounts.js b/accountant-ui/js/accounts.js index a9e38b8..11719cc 100644 --- a/accountant-ui/js/accounts.js +++ b/accountant-ui/js/accounts.js @@ -56,203 +56,193 @@ angular.module('accountant.accounts', [ return Account; }]) -.controller( - 'AccountController', [ - '$scope', '$ngBootbox', 'Account', 'Notification', - function($scope, $ngBootbox, Account, Notification) { - - /* - * Return the class for an account current value compared to authorized - * overdraft. - */ - $scope.rowClass = function(account) { - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - if (!account || !account.authorized_overdraft || !account.current) { - return; - } - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - if (account.current < account.authorized_overdraft) { - return 'danger'; - } else if (account.current < 0) { - return 'warning'; - } - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - }; - - /* - * Return the class for a value compared to account authorized overdraft. - */ - $scope.valueClass = function(account, value) { - if (!account || !value) { - return; - } - - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - if (value < account.authorized_overdraft) { - return 'text-danger'; - } else if (value < 0) { - return 'text-warning'; - } - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - }; - - /* - * Add an empty account. - */ - $scope.add = function() { - var account = new Account({ - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - authorized_overdraft: 0 - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - }); - - // Insert account at the begining of the array. - $scope.accounts.splice(0, 0, account); - }; - - /* - * Cancel account edition. Remove it from array if a new one. - */ - $scope.cancelEdit = function(rowform, account, $index) { - if (!account.id) { - // Account not saved, just remove it from array. - $scope.accounts.splice($index, 1); - } else { - rowform.$cancel(); - } - }; - - /* - * Save account. - */ - $scope.save = function(account) { - //var account = $scope.accounts[$index]; - - //account = angular.merge(account, $data); - - return account.$save().then(function(data) { - Notification.success('Account #' + data.id + ' saved.'); - - // TODO Alexis Lahouze 2016-03-08 Update solds - - return data; - }); - }; - - /* - * Delete an account. - */ - $scope.delete = function(account, $index) { - var id = account.id; - - $ngBootbox.confirm( - 'Voulez-vous supprimer le compte \'' + account.name + '\' ?', - function(result) { - if (result) { - account.$delete().then(function() { - Notification.success('Account #' + id + ' deleted.'); - - // Remove account from array. - $scope.accounts.splice($index, 1); - }); - } +.controller('AccountController', [ + '$scope', '$ngBootbox', 'Account', 'Notification', + function($scope, $ngBootbox, Account, Notification) { + /* + * Return the class for an account current value compared to authorized + * overdraft. + */ + $scope.rowClass = function(account) { + // eslint-disable-next-line camelcase + if (!account || !account.authorized_overdraft || !account.current) { + return; } - ); - }; - // Load accounts. - $scope.accounts = Account.query(); - -}]) - -.directive( - 'accountFormDialog', function(Account, $ngBootbox, Notification) { - return { - restrict: 'A', - scope: { - account: '=ngModel' - }, - link: function(scope, element) { - var title = 'Account'; - - if (scope.account && scope.account.id) { - title = title + ' #' + scope.account.id; - } - - scope.form = {}; - - scope.submitForm = function() { - // check to make sure the form is completely valid - if (!scope.form.$valid) { - return false; - } - - // Authorized overdraft is a positive integer but data is a negative integer. - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - scope.data.authorized_overdraft = -scope.data.authorized_overdraft; - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - - angular.copy(scope.data, scope.account); - - // Save account - console.log(scope.account); - return scope.account.$save().then( - function(data) { - Notification.success('Account #' + data.id + ' saved.'); - - scope.account.getSolds(); - - return data; - }, function(data) { - Notification.error('Error while saving account #' + data.id); - scope.account.getSolds(); - console.log(data); - return false; - } - ); - }; - - element.on('click', function() { - // Create new account if not passed in ng-model. - if (!scope.account) { - scope.account = new Account({ - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - authorized_overdraft: 0 - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - }); - } - - scope.data = {}; - angular.copy(scope.account, scope.data); - - // Authorized overdraft must be positive in form. - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - scope.data.authorized_overdraft = -scope.data.authorized_overdraft; - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - - // Open dialog with form. - $ngBootbox.customDialog({ - scope: scope, - title: title, - templateUrl: 'views/account.form.tmpl.html', - onEscape: true, - buttons: { - save: { - label: 'Save', - className: 'btn-success', - callback: scope.submitForm - }, - cancel: { - label: 'Cancel', - className: 'btn-default', - callback: true - } - } - }); - }); + // eslint-disable-next-line camelcase + if (account.current < account.authorized_overdraft) { + return 'danger'; + } else if (account.current < 0) { + return 'warning'; } }; - } -); + + /* + * Return the class for a value compared to account authorized overdraft. + */ + $scope.valueClass = function(account, value) { + if (!account || !value) { + return; + } + + // eslint-disable-next-line camelcase + if (value < account.authorized_overdraft) { + return 'text-danger'; + } else if (value < 0) { + return 'text-warning'; + } + }; + + /* + * Add an empty account. + */ + $scope.add = function() { + var account = new Account({ + // eslint-disable-next-line camelcase + authorized_overdraft: 0 + }); + + // Insert account at the begining of the array. + $scope.accounts.splice(0, 0, account); + }; + + /* + * Cancel account edition. Remove it from array if a new one. + */ + $scope.cancelEdit = function(rowform, account, $index) { + if (!account.id) { + // Account not saved, just remove it from array. + $scope.accounts.splice($index, 1); + } else { + rowform.$cancel(); + } + }; + + /* + * Save account. + */ + $scope.save = function(account) { + // var account = $scope.accounts[$index]; + + // account = angular.merge(account, $data); + + return account.$save().then(function(data) { + Notification.success('Account #' + data.id + ' saved.'); + + // TODO Alexis Lahouze 2016-03-08 Update solds + + return data; + }); + }; + + /* + * Delete an account. + */ + $scope.delete = function(account, $index) { + var id = account.id; + + $ngBootbox.confirm( + 'Voulez-vous supprimer le compte \'' + account.name + '\' ?', + function(result) { + if (result) { + account.$delete().then(function() { + Notification.success('Account #' + id + ' deleted.'); + + // Remove account from array. + $scope.accounts.splice($index, 1); + }); + } + } + ); + }; + + // Load accounts. + $scope.accounts = Account.query(); + }] +) + +.directive('accountFormDialog', function(Account, $ngBootbox, Notification) { + return { + restrict: 'A', + scope: { + account: '=ngModel' + }, + link: function(scope, element) { + var title = 'Account'; + + if (scope.account && scope.account.id) { + title = title + ' #' + scope.account.id; + } + + scope.form = {}; + + scope.submitForm = function() { + // check to make sure the form is completely valid + if (!scope.form.$valid) { + return false; + } + + // Authorized overdraft is a positive integer but data is a negative integer. + // eslint-disable-next-line camelcase + scope.data.authorized_overdraft = -scope.data.authorized_overdraft; + + angular.copy(scope.data, scope.account); + + // Save account + console.log(scope.account); + return scope.account.$save().then( + function(data) { + Notification.success('Account #' + data.id + ' saved.'); + + scope.account.getSolds(); + + return data; + }, + function(data) { + Notification.error('Error while saving account #' + data.id); + scope.account.getSolds(); + console.log(data); + return false; + } + ); + }; + + element.on('click', function() { + // Create new account if not passed in ng-model. + if (!scope.account) { + scope.account = new Account({ + // eslint-disable-next-line camelcase + authorized_overdraft: 0 + }); + } + + scope.data = {}; + angular.copy(scope.account, scope.data); + + // Authorized overdraft must be positive in form. + // eslint-disable-next-line camelcase + scope.data.authorized_overdraft = -scope.data.authorized_overdraft; + + // Open dialog with form. + $ngBootbox.customDialog({ + scope: scope, + title: title, + templateUrl: 'views/account.form.tmpl.html', + onEscape: true, + buttons: { + save: { + label: 'Save', + className: 'btn-success', + callback: scope.submitForm + }, + cancel: { + label: 'Cancel', + className: 'btn-default', + callback: true + } + } + }); + }); + } + }; +}); diff --git a/accountant-ui/js/app.js b/accountant-ui/js/app.js index 077b7a2..571b018 100644 --- a/accountant-ui/js/app.js +++ b/accountant-ui/js/app.js @@ -92,66 +92,67 @@ angular.module('accountant', [ }) .controller('MainController', [ - '$scope', '$rootScope', '$http', 'authService', '$storage', '$ngBootbox', - function($scope, $rootScope, $http, authService, $storage, $ngBootbox) { - $scope.dialogShown = false; + '$scope', '$rootScope', '$http', 'authService', '$storage', '$ngBootbox', + function($scope, $rootScope, $http, authService, $storage, $ngBootbox) { + $scope.dialogShown = false; - $scope.showLoginForm = function() { - // First, if there are registered credentials, use them - if ($scope.dialogShown) { - return; - } + $scope.showLoginForm = function() { + // First, if there are registered credentials, use them + if ($scope.dialogShown) { + return; + } - $scope.dialogShown = true; + $scope.dialogShown = true; - $storage.clear(); + $storage.clear(); - $ngBootbox.customDialog({ - title: 'Authentification requise', - templateUrl: 'views/login.tmpl.html', - buttons: { - login: { - label: 'Login', - className: 'btn-primary', - callback: function() { - $scope.dialogShown = false; + $ngBootbox.customDialog({ + title: 'Authentification requise', + templateUrl: 'views/login.tmpl.html', + buttons: { + login: { + label: 'Login', + className: 'btn-primary', + callback: function() { + $scope.dialogShown = false; - var email = $('#email').val(); - var password = $('#password').val(); - $http.post( - '/api/user/login', - { - 'email': email, - 'password': password - } - ).success(function(result) { - // TODO Alexis Lahouze 2015-08-28 Handle callback. - // Call to /api/login to retrieve the token - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - $storage.set('token_type', result.token_type); - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - $storage.set('token', result.token); - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - $storage.set('expiration_date', result.expiration_date); - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers + var email = $('#email').val(); + var password = $('#password').val(); + $http.post( + '/api/user/login', + { + email: email, + password: password + } + ).success(function(result) { + // TODO Alexis Lahouze 2015-08-28 Handle callback. + // Call to /api/login to retrieve the token + // jscs:disable requireCamelCaseOrUpperCaseIdentifiers + $storage.set('token_type', result.token_type); + // jscs:enable requireCamelCaseOrUpperCaseIdentifiers + $storage.set('token', result.token); + // jscs:disable requireCamelCaseOrUpperCaseIdentifiers + $storage.set('expiration_date', result.expiration_date); + // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - authService.loginConfirmed(); - }); - } - }, - cancel: { - label: 'Annuler', - className: 'btn-default', - callback: function() { - authService.loginCancelled(null, 'Login cancelled by user action.'); - $scope.dialogShown = false; + authService.loginConfirmed(); + }); + } + }, + cancel: { + label: 'Annuler', + className: 'btn-default', + callback: function() { + authService.loginCancelled(null, 'Login cancelled by user action.'); + $scope.dialogShown = false; + } } } - } - }); - }; + }); + }; - $rootScope.$on('event:auth-loginRequired', $scope.showLoginForm); -}]) + $rootScope.$on('event:auth-loginRequired', $scope.showLoginForm); + } +]) ; diff --git a/accountant-ui/js/operations.js b/accountant-ui/js/operations.js index 3508f5a..a29d9bf 100644 --- a/accountant-ui/js/operations.js +++ b/accountant-ui/js/operations.js @@ -32,7 +32,7 @@ angular.module('accountant.operations', [ $resourceProvider.defaults.stripTrailingSlashes = false; }]) -.factory('Operation', ['$resource', function($resource) { +.factory('Operation', ['$resource', function($resource) { return $resource( '/api/operation/:id', { id: '@id' @@ -40,38 +40,38 @@ angular.module('accountant.operations', [ ); }]) -.factory('OHLC', ['$resource', '$routeParams', - function($resource, $routeParams) { - return $resource( - '/api/account/:account_id/ohlc', { - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - account_id: $routeParams.accountId - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - } - ); -}]) +.factory('OHLC', [ + '$resource', '$routeParams', function($resource, $routeParams) { + return $resource( + '/api/account/:account_id/ohlc', { + // eslint-disable-next-line camelcase + account_id: $routeParams.accountId + } + ); + } +]) -.factory('Category', ['$resource', '$routeParams', - function($resource, $routeParams) { - return $resource( - '/api/account/:account_id/category', { - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - account_id: $routeParams.accountId - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - } - ); -}]) +.factory('Category', [ + '$resource', '$routeParams', function($resource, $routeParams) { + return $resource( + '/api/account/:account_id/category', { + // eslint-disable-next-line camelcase + account_id: $routeParams.accountId + } + ); + } +]) -.factory('Balance', ['$resource', '$routeParams', - function($resource, $routeParams) { - return $resource( - '/api/account/:account_id/balance', { - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers - account_id: $routeParams.accountId - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers - } - ); -}]) +.factory('Balance', [ + '$resource', '$routeParams', function($resource, $routeParams) { + return $resource( + '/api/account/:account_id/balance', { + // eslint-disable-next-line camelcase + account_id: $routeParams.accountId + } + ); + } +]) /* * Controller for category chart. @@ -131,8 +131,8 @@ angular.module('accountant.operations', [ size: '60%', dataLabels: { formatter: function() { - return this.point.name !== null && this.percentage >= 2.5 ? this.point.name : null; - } + return this.point.name !== null && this.percentage >= + 2.5 ? this.point.name : null; } } }] }; @@ -140,6 +140,7 @@ angular.module('accountant.operations', [ $scope.brightenColor = function(color) { var brightness = 0.2; + // eslint-disable-next-line new-cap return Highcharts.Color(color).brighten(brightness).get(); }; @@ -367,9 +368,8 @@ angular.module('accountant.operations', [ */ $scope.add = function() { var operation = new Operation({ - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers + // eslint-disable-next-line camelcase account_id: $routeParams.accountId - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers }); $scope.operations.splice(0, 0, operation); @@ -380,9 +380,8 @@ angular.module('accountant.operations', [ */ $scope.load = function(begin, end) { $scope.operations = Operation.query({ - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers + // eslint-disable-next-line camelcase account_id: $routeParams.accountId, - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers begin: begin.format('YYYY-MM-DD'), end: end.format('YYYY-MM-DD') }); diff --git a/accountant-ui/js/scheduler.js b/accountant-ui/js/scheduler.js index 03b49e7..8c2731c 100644 --- a/accountant-ui/js/scheduler.js +++ b/accountant-ui/js/scheduler.js @@ -49,9 +49,8 @@ angular.module('accountant.scheduler', [ */ $scope.add = function() { var operation = new ScheduledOperation({ - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers + // eslint-disable-next-line camelcase account_id: $routeParams.accountId - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers }); // Insert new operation at the beginning of the array. @@ -63,9 +62,8 @@ angular.module('accountant.scheduler', [ */ $scope.load = function() { $scope.operations = ScheduledOperation.query({ - // jscs:disable requireCamelCaseOrUpperCaseIdentifiers + // eslint-disable-next-line camelcase account_id: $routeParams.accountId - // jscs:enable requireCamelCaseOrUpperCaseIdentifiers }); }; diff --git a/grunt-config/eslint.js b/grunt-config/eslint.js new file mode 100644 index 0000000..2564dd6 --- /dev/null +++ b/grunt-config/eslint.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + frontend: [ + '<%= accountant.frontend.src %>/index.html', + '<%= accountant.frontend.src %>/views/*.html', + '<%= accountant.frontend.src %>/js/*.js' + ], + toolchain: [ + 'Gruntfile.js', + 'grunt-config/*.js' + ] +}; diff --git a/package.json b/package.json index 4862eb5..ec469b7 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,14 @@ "devDependencies": { "connect-logger": "0.0.1", "connect-proxy-layer": "^0.1.2", + "eslint": "^3.7.1", + "eslint-config-google": "~0.6", + "eslint-plugin-angular": "^1.4.1", + "eslint-plugin-html": "^1.5.3", + "eslint-plugin-jquery": "^1.0.1", + "eslint-plugin-promise": "^3.0.0", + "eslint-plugin-security": "^1.2.0", + "eslint-plugin-this": "^0.1.4", "grunt": "~1.0", "grunt-bg-shell": "^2.3.1", "grunt-contrib-clean": "~1.0", @@ -16,6 +24,7 @@ "grunt-contrib-uglify": "~1.0", "grunt-contrib-watch": "~1.0", "grunt-copy": "^0.1.0", + "grunt-eslint": "~19.0", "grunt-filerev": "^2.3.1", "grunt-flake8": "^0.1.3", "grunt-htmllint": "^0.2.7",