diff --git a/bower.json b/bower.json index 7fcf61d1..2fa35d9c 100644 --- a/bower.json +++ b/bower.json @@ -2,32 +2,31 @@ "name": "ng-boilerplate", "version": "0.0.18", "devDependencies": { - "angular": "1.2.x", - "angular-mocks": "~1.2.x", - "angular-route": "~1.2.x", - "angular-resource": "~1.2.x", + "angular": "1.3.x", "angular-bootstrap": "~0.11.0", - "angular-ui-router": "~0.2.x", + "angular-growl-v2": "~0.7.0", + "angular-local-storage": "~0.1.x", + "angular-mocks": "~1.3.x", + "angular-resource": "~1.3.x", + "angular-route": "~1.3.x", + "angular-sanitize": "~1.3.x", + "angular-tags": "git://github.com/boneskull/angular-tags.git#master", "angular-ui-utils": "latest", - "sass-bootstrap": "3.0.2", + "bowser": "0.7.x", + "draggabilly": "~1.1.x", "hint.css": "https://github.com/chinchang/hint.css.git", - "modernizr": "~2.8.x", - "jquery-ui": "~1.10.3", - "momentjs": "~2.7.x", + "humanize-duration": "~2.0.0", "jasmine-expect": "1.x", - "objectdiff": "https://github.com/NV/objectDiff.js.git", + "jquery-ui": "~1.11.x", "lodash": "~2.4.x", - "angular-tags": "git://github.com/boneskull/angular-tags.git#master", - "angular-sanitize": "~1.2.x", - "draggabilly": "~1.1.x", - "bowser": "0.7.x", - "angular-growl-v2": "~0.7.0", - "angular-local-storage": "~0.0.7", - "humanize-duration": "~2.0.0" + "modernizr": "~2.8.x", + "momentjs": "~2.8.x", + "objectdiff": "https://github.com/NV/objectDiff.js.git", + "sass-bootstrap": "3.0.2" }, "dependencies": {}, "private": true, "resolutions": { - "angular": "1.2.24" + "angular": "1.3.0" } -} +} \ No newline at end of file diff --git a/buildConfig/build.config.js b/buildConfig/build.config.js index 8e0ca41f..4880c49a 100644 --- a/buildConfig/build.config.js +++ b/buildConfig/build.config.js @@ -100,7 +100,7 @@ module.exports = { // TODO: THIS IS TERRIBLE! REMOVE UI ASAP... OR AT LEAST ONLY INCLUDE RELEVANT COMPONENTS - 'vendor/jquery-ui/ui/jquery-ui.js', + 'vendor/jquery-ui/jquery-ui.js', // NOTE: bootstrap css imported in application.tpl.scss 'vendor/sass-bootstrap/dist/js/bootstrap.js', @@ -135,7 +135,7 @@ module.exports = { 'vendor/bowser/bowser.js', 'vendor/angular-growl-v2/build/angular-growl.js', - 'vendor/angular-local-storage/angular-local-storage.js', + 'vendor/angular-local-storage/dist/angular-local-storage.js', 'vendor/humanize-duration/humanize-duration.js' ], diff --git a/karma/karma-unit.tpl.js b/karma/karma-unit.tpl.js index 2967db30..5ebee3d8 100644 --- a/karma/karma-unit.tpl.js +++ b/karma/karma-unit.tpl.js @@ -12,7 +12,7 @@ module.exports = function (config) { /** * From where to look for files, starting with the location of this file. */ - configObject.basePath = '../'; + configObject.basePath = '../build'; /** * This is the list of file patterns to load into the browser during testing. @@ -22,7 +22,7 @@ module.exports = function (config) { "vendor/jasmine-expect/dist/jasmine-matchers.js" ].concat(JSON.parse(fileJson).concat([ 'src/**/*.js', - 'src/**/*.spec.js' + '../src/**/*.spec.js' ])); configObject.exclude = [ diff --git a/package.json b/package.json index 5296ac3d..d4ec3440 100644 --- a/package.json +++ b/package.json @@ -17,22 +17,22 @@ ], "devDependencies": { "grunt": "~0.4.1", - "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-clean": "~0.6.0", "grunt-contrib-copy": "0.7.x", "grunt-contrib-jshint": "0.10.x", - "grunt-contrib-concat": "~0.4.0", + "grunt-contrib-concat": "~0.5.0", "grunt-contrib-watch": "~0.6.x", - "grunt-contrib-uglify": "0.5.x", - "grunt-karma": "0.8.x", + "grunt-contrib-uglify": "0.6.x", + "grunt-karma": "0.9.x", "karma-chrome-launcher": "~0.1.1", "grunt-ngmin": "0.0.3", "grunt-html2js": "~0.2.3", "grunt-conventional-changelog": "~1.1", "grunt-changelog": "~0.2.x", - "grunt-bump": "0.0.14", + "grunt-bump": "0.0.16", "grunt-contrib-connect": "0.8.x", "connect-modrewrite": "0.7.x", - "grunt-sass": "0.14.x", + "grunt-sass": "0.16.x", "lodash": "~2.4.1", "karma-phantomjs-launcher": "~0.1.1", "karma-jasmine": "~0.2.x", diff --git a/src/app/listen/listen.js b/src/app/listen/listen.js index 0771c4d6..fe651cc5 100644 --- a/src/app/listen/listen.js +++ b/src/app/listen/listen.js @@ -152,8 +152,8 @@ angular.module('bawApp.listen', ['decipher.tags', 'ui.bootstrap.typeahead']) $scope.model.media = new Media(value.data); var // moment works by reference - need to parse the date twice - sigh - absoluteStartChunk = moment($scope.model.media.recordedDate).add('s', parseFloat($scope.model.media.startOffset)), - absoluteEndChunk = moment($scope.model.media.recordedDate).add('s', parseFloat($scope.model.media.endOffset)); + absoluteStartChunk = moment($scope.model.media.recordedDate).add(parseFloat($scope.model.media.startOffset), 's'), + absoluteEndChunk = moment($scope.model.media.recordedDate).add(parseFloat($scope.model.media.endOffset), 's'); $scope.startOffsetAbsolute = absoluteStartChunk.format("HH:mm:ss"); $scope.endOffsetAbsolute = absoluteEndChunk.format("HH:mm:ss"); @@ -342,7 +342,7 @@ angular.module('bawApp.listen', ['decipher.tags', 'ui.bootstrap.typeahead']) if (!$scope.model.audioRecording) { return undefined; } - return moment($scope.model.audioRecording.recordedDate).add('s', $scope.model.audioRecording.durationSeconds).format("YYYY-MMM-DD, HH:mm:ss"); + return moment($scope.model.audioRecording.recordedDate).add($scope.model.audioRecording.durationSeconds, 's').format("YYYY-MMM-DD, HH:mm:ss"); }; @@ -401,7 +401,7 @@ angular.module('bawApp.listen', ['decipher.tags', 'ui.bootstrap.typeahead']) return undefined; } - return moment($scope.model.media.recordedDate).add('m', $scope.jumpToMinute).format("YYYY-MMM-DD, HH:mm:ss"); + return moment($scope.model.media.recordedDate).add($scope.jumpToMinute, 'm').format("YYYY-MMM-DD, HH:mm:ss"); }; diff --git a/src/common/angular-http-auth.js b/src/common/angular-http-auth.js deleted file mode 100644 index b95b460a..00000000 --- a/src/common/angular-http-auth.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @license HTTP Auth Interceptor Module for AngularJS - * (c) 2012 Witold Szczerba - * License: MIT - * https://github.com/witoldsz/angular-http-auth/tree/gh-pages - */ -angular.module('http-auth-interceptor', []) - - .provider('authService', function () { - /** - * Holds all the requests which failed due to 401 response, - * so they can be re-requested in future, once login is completed. - */ - var buffer = []; - - /** - * Required by HTTP interceptor. - * Function is attached to provider to be invisible for regular users of this service. - */ - this.pushToBuffer = function (config, deferred) { - buffer.push({ - config: config, - deferred: deferred - }); - }; - - this.$get = ['$rootScope', '$injector', function ($rootScope, $injector) { - var $http; // initialized later because of circular dependency problem - function retry(config, deferred) { - $http = $http || $injector.get('$http'); - $http(config).then(function (response) { - deferred.resolve(response); - }); - } - - function retryAll() { - for (var i = 0; i < buffer.length; ++i) { - retry(buffer[i].config, buffer[i].deferred); - } - buffer = []; - } - - return { - loginConfirmed: function () { - console.info("authService::event:auth-loginConfirmed"); - $rootScope.$broadcast('event:auth-loginConfirmed'); - - retryAll(); - } - }; - }]; - }) - -/** - * $http interceptor. - * On 401 response - it stores the request and broadcasts 'event:angular-auth-loginRequired'. - */ - .config(['$httpProvider', 'authServiceProvider', function ($httpProvider, authServiceProvider) { - - var interceptor = ['$rootScope', '$q', function ($rootScope, $q) { - function success(response) { - return response; - } - - function error(response) { - if (response.status === 401) { - var deferred = $q.defer(); - authServiceProvider.pushToBuffer(response.config, deferred); - - console.info("authService::event:auth-loginRequired"); - $rootScope.$broadcast('event:auth-loginRequired'); - return deferred.promise; - } - // otherwise - return $q.reject(response); - } - - return function (promise) { - return promise.then(success, error); - }; - - }]; - $httpProvider.responseInterceptors.push(interceptor); - - // add rails' CSRF token to Post requests - // https://gist.github.com/prognostikos/3698589 - var meta_tag = document.getElementsByTagName('meta'); - for (var meta_tag_index in meta_tag) { - if (meta_tag[meta_tag_index].name == 'csrf-token') { - $httpProvider.defaults.headers.common['X-CSRF-Token'] = meta_tag[meta_tag_index].content; - break; - } - } - - }]); diff --git a/src/components/services/angularHttpAuth.js b/src/components/services/angularHttpAuth.js new file mode 100644 index 00000000..1575eab8 --- /dev/null +++ b/src/components/services/angularHttpAuth.js @@ -0,0 +1,79 @@ +/** + * @license HTTP Auth Interceptor Module for AngularJS + * (c) 2012 Witold Szczerba + * License: MIT + * https://github.com/witoldsz/angular-http-auth/tree/gh-pages + */ +angular.module('http-auth-interceptor', []) + + .provider('authService', function () { + /** + * Holds all the requests which failed due to 401 response, + * so they can be re-requested in future, once login is completed. + */ + var buffer = []; + + /** + * Required by HTTP interceptor. + * Function is attached to provider to be invisible for regular users of this service. + */ + this.pushToBuffer = function (config, deferred) { + buffer.push({ + config: config, + deferred: deferred + }); + }; + + this.$get = ['$rootScope', '$injector', function ($rootScope, $injector) { + var $http; // initialized later because of circular dependency problem + function retry(config, deferred) { + $http = $http || $injector.get('$http'); + $http(config).then(function (response) { + deferred.resolve(response); + }); + } + + function retryAll() { + for (var i = 0; i < buffer.length; ++i) { + retry(buffer[i].config, buffer[i].deferred); + } + buffer = []; + } + + return { + loginConfirmed: function () { + console.info("authService::event:auth-loginConfirmed"); + $rootScope.$broadcast('event:auth-loginConfirmed'); + + retryAll(); + } + }; + }]; + }) +/** + * $http interceptor. + * On 401 response - it stores the request and broadcasts 'event:angular-auth-loginRequired'. + */ + .factory("authHttpInterceptor", + ["authService", "$rootScope", "$q", + function (authServiceProvider, $rootScope, $q) { + return { + 'responseError': function error(response) { + if (response.status === 401) { + var deferred = $q.defer(); + authServiceProvider.pushToBuffer(response.config, deferred); + + console.info("authService::event:auth-loginRequired"); + $rootScope.$broadcast('event:auth-loginRequired'); + return deferred.promise; + } + // otherwise + return $q.reject(response); + } + }; + }]) + + + .config(['$httpProvider', function ($httpProvider) { + $httpProvider.interceptors.push("authHttpInterceptor"); + }]); diff --git a/src/components/services/angularjs-rails-resource.js b/src/components/services/angularjs-rails-resource.js deleted file mode 100644 index 7f72e7ad..00000000 --- a/src/components/services/angularjs-rails-resource.js +++ /dev/null @@ -1,205 +0,0 @@ -// Adapted from https://github.com/tpodom/angularjs-rails-resource/blob/master/vendor/assets/javascripts/angularjs/rails/resource.js - -// Copyright (c) 2012 Tommy Odom -// -// MIT License -// https://github.com/tpodom/angularjs-rails-resource - -(function (undefined) { - angular.module('rails', []) - .constant('casingTransformers', (function() { - /** - * Old function worked via reference - deprecated - * @param data - * @param transform - */ - /*function transformObject(data, transform) { - var newKey; - - if (data && angular.isObject(data)) { - angular.forEach(data, function (value, key) { - newKey = transform(key); - - if (newKey !== key) { - data[newKey] = value; - delete data[key]; - } - - transformObject(value, transform); - }); - } - }*/ - - function transformObject(data, transform) { - if (data && angular.isObject(data)) { - var newData = angular.isArray(data) ? [] : {}; - angular.forEach(data, function (value, key) { - var newKey = transform(key); - - if (angular.isObject(value)) { - newData[newKey] = transformObject(value, transform); - } - else { - newData[newKey] = value; - } - }); - - return newData; - } - } - - var STAMPER_LABEL = "__railsJsonRenamer__"; - - function stampObject(object, value) { - if (angular.isObject(object)) { - try { - // mark this object as having been transformed - - Object.defineProperty(object, STAMPER_LABEL, {configurable: true, value: value}); - } - catch (e) { - console.warn("Object.defineProperty failed in stampObject"); - } - return object; - } - else { - return object; - } - } - - function isStamped(object) { - if (object) { - return object.hasOwnProperty(STAMPER_LABEL); - } - else { - return false; - } - } - - function camelize(key) { - if (!angular.isString(key)) { - return key; - } - - // should this match more than word and digit characters? - return key.replace(/_[\w\d]/g, function (match, index, string) { - return index === 0 ? match : string.charAt(index + 1).toUpperCase(); - }); - } - - function underscore(key) { - if (!angular.isString(key)) { - return key; - } - - return key.replace(/[A-Z]/g, function (match, index) { - return index === 0 ? match : '_' + match.toLowerCase(); - }); - } - - return { - camelize: camelize, - underscore: underscore, - isStamped: isStamped, - stampObject: stampObject, - STAMP_LABEL: STAMPER_LABEL, - transformObject: transformObject - }; - }())) - .factory('railsFieldRenamingTransformerRequest', ['casingTransformers', function (casingTransformers) { - return function railsFieldRenamingTransformerRequest(data, headers) { - // TODO: add conditions - // probs only want to do this if headers contains app/json - // and only if object has a __railsJsonRenamer__ - // or if request is going to our server? - if ((headers()["Accept"] || "").indexOf("application/json") >= 0) { - - if (data === undefined || data === null) { - return; - } - - if (!angular.isObject(data)) { - return data; - } - - var result = casingTransformers.transformObject(data, casingTransformers.underscore); - casingTransformers.stampObject(result, "camelCased->underscored"); - return result; - } - - return data; - }; - }]) - .factory('railsFieldRenamingTransformerResponse', ['casingTransformers', function(casingTransformers) { - return function railsFieldRenamingTransformerResponse(data, headers) { - - if (data === undefined || data === null) { - return; - } - - if (!angular.isObject(data)) { - return data; - } - - if ((headers()["content-type"] || "").indexOf("application/json") >= 0) { - var result = casingTransformers.transformObject(data, casingTransformers.camelize); - casingTransformers.stampObject(result, "underscored->camelCased"); - return result; - } - else { - return data; - } - }; - }]) - // GIANT HACK! - .factory('railsCsrfToken', ['$q', '$rootScope', '$location', function ($q, $rootScope, $location) { - return { - request: function(config) { - if (!$rootScope.csrfToken) { - var token = ""; - - token = $location.search().csrf; - - if (!token) { - console.warn("No temporary CSRF token has been found!"); - } - $rootScope.csrfToken = token; - } - - config.headers["X-CSRF-Token"] = $rootScope.csrfToken; - - return config; - } - }; - }]) - - /** - * Configure the default $httpRequest - */ - .config([ - '$httpProvider', - /* We are in the config phase - traditional services are not available yet - * Thus we must make a reference to the factory's provider. - * This is also why the $gets are necessary below! - */ - 'casingTransformers', - 'railsFieldRenamingTransformerRequestProvider', - 'railsFieldRenamingTransformerResponseProvider', - 'railsCsrfTokenProvider', - function ( - $httpProvider, - casingTransformers, - railsFieldRenamingTransformerRequest, - railsFieldRenamingTransformerResponse, - railsCsrfToken) { - - ////$httpProvider.responseInterceptors.push(railsFieldRenamingInterceptor.$get()().promise); - - //HACK:! - $httpProvider.defaults.transformResponse.push(railsFieldRenamingTransformerResponse.$get[1](casingTransformers)); - $httpProvider.interceptors.push('railsCsrfToken'); - $httpProvider.defaults.transformRequest.unshift(railsFieldRenamingTransformerRequest.$get[1](casingTransformers)); - }]); - -})(); - diff --git a/src/components/services/angularjsRailsResource.js b/src/components/services/angularjsRailsResource.js new file mode 100644 index 00000000..356ccc66 --- /dev/null +++ b/src/components/services/angularjsRailsResource.js @@ -0,0 +1,199 @@ +// Adapted from https://github.com/tpodom/angularjs-rails-resource/blob/master/vendor/assets/javascripts/angularjs/rails/resource.js + +// Copyright (c) 2012 Tommy Odom +// +// MIT License +// https://github.com/tpodom/angularjs-rails-resource + +angular.module('rails', []) + .constant('casingTransformers', (function () { + /** + * Old function worked via reference - deprecated + * @param data + * @param transform + */ + /*function transformObject(data, transform) { + var newKey; + + if (data && angular.isObject(data)) { + angular.forEach(data, function (value, key) { + newKey = transform(key); + + if (newKey !== key) { + data[newKey] = value; + delete data[key]; + } + + transformObject(value, transform); + }); + } + }*/ + + function transformObject(data, transform) { + if (data && angular.isObject(data)) { + var newData = angular.isArray(data) ? [] : {}; + angular.forEach(data, function (value, key) { + var newKey = transform(key); + + if (angular.isObject(value)) { + newData[newKey] = transformObject(value, transform); + } + else { + newData[newKey] = value; + } + }); + + return newData; + } + } + + var STAMPER_LABEL = "__railsJsonRenamer__"; + + function stampObject(object, value) { + if (angular.isObject(object)) { + try { + // mark this object as having been transformed + + Object.defineProperty(object, STAMPER_LABEL, {configurable: true, value: value}); + } + catch (e) { + console.warn("Object.defineProperty failed in stampObject"); + } + return object; + } + else { + return object; + } + } + + function isStamped(object) { + if (object) { + return object.hasOwnProperty(STAMPER_LABEL); + } + else { + return false; + } + } + + function camelize(key) { + if (!angular.isString(key)) { + return key; + } + + // should this match more than word and digit characters? + return key.replace(/_[\w\d]/g, function (match, index, string) { + return index === 0 ? match : string.charAt(index + 1).toUpperCase(); + }); + } + + function underscore(key) { + if (!angular.isString(key)) { + return key; + } + + return key.replace(/[A-Z]/g, function (match, index) { + return index === 0 ? match : '_' + match.toLowerCase(); + }); + } + + return { + camelize: camelize, + underscore: underscore, + isStamped: isStamped, + stampObject: stampObject, + STAMP_LABEL: STAMPER_LABEL, + transformObject: transformObject + }; + }())) + .factory('railsFieldRenamingTransformer', ['casingTransformers', function (casingTransformers) { + return { + "request": function railsFieldRenamingTransformerRequest(data, headers) { + // TODO: add conditions + // probs only want to do this if headers contains app/json + // and only if object has a __railsJsonRenamer__ + // or if request is going to our server? + if ((headers()["Accept"] || "").indexOf("application/json") >= 0) { + + if (data === undefined || data === null) { + return; + } + + if (!angular.isObject(data)) { + return data; + } + + var result = casingTransformers.transformObject(data, casingTransformers.underscore); + casingTransformers.stampObject(result, "camelCased->underscored"); + return result; + } + + return data; + }, + "response": function railsFieldRenamingTransformerResponse(data, headers) { + + if (data === undefined || data === null) { + return; + } + + if (!angular.isObject(data)) { + return data; + } + + if ((headers()["content-type"] || "").indexOf("application/json") >= 0) { + var result = casingTransformers.transformObject(data, casingTransformers.camelize); + casingTransformers.stampObject(result, "underscored->camelCased"); + return result; + } + else { + return data; + } + } + }; + }]) + // GIANT HACK! + .factory('railsCsrfTokenInterceptor', ['$q', '$rootScope', '$location', function ($q, $rootScope, $location) { + return { + request: function (config) { + if (!$rootScope.csrfToken) { + var token = ""; + + token = $location.search().csrf; + + if (!token) { + console.warn("No temporary CSRF token has been found!"); + } + $rootScope.csrfToken = token; + } + + config.headers["X-CSRF-Token"] = $rootScope.csrfToken; + + return config; + } + }; + }]) + +/** + * Configure the default $httpRequest + */ + .config([ + '$httpProvider', + /* We are in the config phase - traditional services are not available yet + * Thus we must make a reference to the factory's provider. + * This is also why the $gets are necessary below! + */ + 'casingTransformers', + 'railsFieldRenamingTransformerProvider', + function ($httpProvider, + casingTransformers, + railsFieldRenamingTransformerProvider) { + + ////$httpProvider.responseInterceptors.push(railsFieldRenamingInterceptor.$get()().promise); + + //HACK:! + var hackedDiTransformers = railsFieldRenamingTransformerProvider.$get(); + $httpProvider.defaults.transformResponse.push(hackedDiTransformers.response); + $httpProvider.interceptors.push('railsCsrfTokenInterceptor'); + $httpProvider.defaults.transformRequest.unshift(hackedDiTransformers.request); + }]); + + diff --git a/src/components/services/bookmark.js b/src/components/services/bookmark.js index ebc1b73f..eac36c94 100644 --- a/src/components/services/bookmark.js +++ b/src/components/services/bookmark.js @@ -1,4 +1,4 @@ -var bawss = bawss || angular.module("bawApp.services", ['bawApp.services.resource', 'bawApp.configuration']); +var bawss = bawss || angular.module("bawApp.services", ['bawApp.services.resource', "bawApp.vendorServices", 'bawApp.configuration']); bawss.factory('Bookmark', [ diff --git a/src/components/services/services.js b/src/components/services/services.js index a83d05e3..b00ce670 100644 --- a/src/components/services/services.js +++ b/src/components/services/services.js @@ -23,7 +23,7 @@ return uri.replace(/(\{([^{}]*)\})/g, ":$2"); } - var bawss = bawss || angular.module("bawApp.services", ["ngResource", "bawApp.configuration", "bawApp.services.core", "bawApp.services.queryBuilder"]); + var bawss = bawss || angular.module("bawApp.services", ["ngResource", "bawApp.configuration", "bawApp.vendorServices", "bawApp.services.queryBuilder"]); bawss.factory('Project', [ '$resource', "$http", 'conf.paths', "QueryBuilder", function ($resource, $http, paths, QueryBuilder) { var resource = resourcePut($resource, uriConvert(paths.api.routes.projectAbsolute), {projectId: "@projectId"}); diff --git a/src/components/services/userProfile.js b/src/components/services/userProfile.js index 5ee4c519..3f29338f 100644 --- a/src/components/services/userProfile.js +++ b/src/components/services/userProfile.js @@ -1,4 +1,4 @@ -var bawss = bawss || angular.module("bawApp.services", ["ngResource", "bawApp.configuration"]); +var bawss = bawss || angular.module("bawApp.services", ["ngResource", "bawApp.vendorServices", "bawApp.configuration"]); bawss .constant("UserProfileEvents", { diff --git a/src/index.html b/src/index.html index d4d6d89e..e02d253a 100644 --- a/src/index.html +++ b/src/index.html @@ -1,10 +1,13 @@ + Bioacoustics Workbench + + <% styles.forEach( function ( file ) { %>