diff --git a/tools/Streetview/sv-wizard/.bowerrc b/tools/Streetview/sv-wizard/.bowerrc new file mode 100644 index 0000000..69fad35 --- /dev/null +++ b/tools/Streetview/sv-wizard/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "bower_components" +} diff --git a/tools/Streetview/sv-wizard/.gitattributes b/tools/Streetview/sv-wizard/.gitattributes new file mode 100644 index 0000000..176a458 --- /dev/null +++ b/tools/Streetview/sv-wizard/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/tools/Streetview/sv-wizard/.gitignore b/tools/Streetview/sv-wizard/.gitignore new file mode 100644 index 0000000..4509cc4 --- /dev/null +++ b/tools/Streetview/sv-wizard/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.tmp +bower_components diff --git a/tools/Streetview/sv-wizard/Gruntfile.js b/tools/Streetview/sv-wizard/Gruntfile.js new file mode 100644 index 0000000..665fa05 --- /dev/null +++ b/tools/Streetview/sv-wizard/Gruntfile.js @@ -0,0 +1,460 @@ +// This file has been initially generated using +// generator-webapp 1.1.0 +// but has been modified to fit the build requirements on the project +'use strict'; + +module.exports = function (grunt) { + + // Time how long tasks take. Can help when optimizing build times + require('time-grunt')(grunt); + + // Automatically load required grunt tasks + require('jit-grunt')(grunt, { + useminPrepare: 'grunt-usemin' + }); + + // Configurable paths + var config = { + app: 'app', + dist: 'dist' + }; + + // Define the configuration for all the tasks + grunt.initConfig({ + + // Project settings + config: config, + + // Watches files for changes and runs tasks based on the changed files + watch: { + bower: { + files: ['bower.json'], + tasks: ['wiredep'] + }, + babel: { + files: ['<%= config.app %>/scripts/{,*/}*.js'], + tasks: ['babel:dist'] + }, + babelTest: { + files: ['test/spec/{,*/}*.js'], + tasks: ['babel:test', 'test:watch'] + }, + gruntfile: { + files: ['Gruntfile.js'] + }, + sass: { + files: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'], + tasks: ['sass', 'postcss'] + }, + styles: { + files: ['<%= config.app %>/styles/{,*/}*.css'], + tasks: ['newer:copy:styles', 'postcss'] + } + }, + + browserSync: { + options: { + notify: false, + background: true, + watchOptions: { + ignored: '' + } + }, + livereload: { + options: { + files: [ + '<%= config.app %>/{,*/}*.html', + '.tmp/styles/{,*/}*.css', + '<%= config.app %>/images/{,*/}*', + '.tmp/scripts/{,*/}*.js' + ], + port: 8080, + server: { + baseDir: ['.tmp', config.app], + routes: { + '/bower_components': './bower_components' + } + } + } + }, + test: { + options: { + port: 9001, + open: false, + logLevel: 'silent', + host: 'localhost', + server: { + baseDir: ['.tmp', './test', config.app], + routes: { + '/bower_components': './bower_components' + } + } + } + }, + dist: { + options: { + port: 8080, + background: false, + server: '<%= config.dist %>' + } + } + }, + + // Empties folders to start fresh + clean: { + dist: { + files: [{ + dot: true, + src: [ + '.tmp', + '<%= config.dist %>/*', + '!<%= config.dist %>/.git*' + ] + }] + }, + server: '.tmp' + }, + + // Make sure code styles are up to par and there are no obvious mistakes + eslint: { + options: { + configFile: 'eslint.json' + }, + target: [ + 'Gruntfile.js', + '<%= config.app %>/scripts/{,*/}*.js', + '!<%= config.app %>/scripts/vendor/*', + 'test/spec/{,*/}*.js' + ] + }, + + // Generate JSDoc + jsdoc : { + dist : { + src: ['<%= config.app %>/scripts/{,*/}*.js'], + options: { + destination: '<%= config.dist %>/doc' + } + } + }, + + // Mocha testing framework configuration options + mocha: { + all: { + options: { + run: true, + urls: ['http://<%= browserSync.test.options.host %>:<%= browserSync.test.options.port %>/index.html'] + } + } + }, + + // Compiles ES6 with Babel + babel: { + options: { + sourceMap: true + }, + dist: { + files: [{ + expand: true, + cwd: '<%= config.app %>/scripts', + src: '{,*/}*.js', + dest: '.tmp/scripts', + ext: '.js' + }] + }, + test: { + files: [{ + expand: true, + cwd: 'test/spec', + src: '{,*/}*.js', + dest: '.tmp/spec', + ext: '.js' + }] + } + }, + + // Compiles Sass to CSS and generates necessary files if requested + sass: { + options: { + sourceMap: true, + sourceMapEmbed: true, + sourceMapContents: true, + includePaths: ['.'] + }, + dist: { + files: [{ + expand: true, + cwd: '<%= config.app %>/styles', + src: ['*.{scss,sass}'], + dest: '.tmp/styles', + ext: '.css' + }] + } + }, + + postcss: { + options: { + map: true, + processors: [ + // Add vendor prefixed styles + require('autoprefixer')({ + browsers: ['> 1%', 'last 2 versions', 'Firefox ESR'] + }) + ] + }, + dist: { + files: [{ + expand: true, + cwd: '.tmp/styles/', + src: '{,*/}*.css', + dest: '.tmp/styles/' + }] + } + }, + + // Automatically inject Bower components into the HTML file + wiredep: { + app: { + src: ['<%= config.app %>/index.html'], + ignorePath: /^(\.\.\/)*\.\./ + }, + sass: { + src: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'], + ignorePath: /^(\.\.\/)+/ + } + }, + + // Renames files for browser caching purposes + filerev: { + dist: { + src: [ + '<%= config.dist %>/scripts/{,*/}*.js', + '<%= config.dist %>/styles/{,*/}*.css', + '<%= config.dist %>/images/{,*/}*.*', + '<%= config.dist %>/styles/fonts/{,*/}*.*', + '<%= config.dist %>/*.{ico,png}' + ] + } + }, + + // Reads HTML for usemin blocks to enable smart builds that automatically + // concat, minify and revision files. Creates configurations in memory so + // additional tasks can operate on them + useminPrepare: { + options: { + dest: '<%= config.dist %>', + flow: { + /* Disabled uglify for JS because of + https://github.com/GabiAxel/ng-polymer-elements/pull/57 */ + steps: { js: ['concat'], css: ['concat', 'cssmin'] } + } + }, + html: '<%= config.app %>/index.html' + + }, + + + // Performs rewrites based on rev and the useminPrepare configuration + usemin: { + options: { + assetsDirs: [ + '<%= config.dist %>', + '<%= config.dist %>/images', + '<%= config.dist %>/styles' + ] + }, + html: ['<%= config.dist %>/{,*/}*.html'], + css: ['<%= config.dist %>/styles/{,*/}*.css'] + }, + + // The following *-min tasks produce minified files in the dist folder + imagemin: { + dist: { + files: [{ + expand: true, + cwd: '<%= config.app %>/images', + src: '{,*/}*.{gif,jpeg,jpg,png}', + dest: '<%= config.dist %>/images' + }] + } + }, + + svgmin: { + dist: { + files: [{ + expand: true, + cwd: '<%= config.app %>/images', + src: '{,*/}*.svg', + dest: '<%= config.dist %>/images' + }] + } + }, + + htmlmin: { + dist: { + options: { + collapseBooleanAttributes: true, + collapseWhitespace: true, + conservativeCollapse: true, + removeAttributeQuotes: true, + removeCommentsFromCDATA: true, + removeEmptyAttributes: true, + removeOptionalTags: true, + // true would impact styles with attribute selectors + removeRedundantAttributes: false, + useShortDoctype: true + }, + files: [{ + expand: true, + cwd: '<%= config.dist %>', + src: '{,*/}*.html', + dest: '<%= config.dist %>' + }] + } + }, + + // By default, your `index.html`'s will take care + // of minification. These next options are pre-configured if you do not + // wish to use the Usemin blocks. + // cssmin: { + // dist: { + // files: { + // '<%= config.dist %>/styles/main.css': [ + // '.tmp/styles/{,*/}*.css', + // '<%= config.app %>/styles/{,*/}*.css' + // ] + // } + // } + // }, + // uglify: { + // dist: { + // files: { + // '<%= config.dist %>/scripts/scripts.js': [ + // '<%= config.dist %>/scripts/scripts.js' + // ] + // } + // } + // }, + // concat: { + // dist: {} + // }, + + // Copies remaining files to places other tasks can use + copy: { + dist: { + files: [{ + expand: true, + dot: true, + cwd: '<%= config.app %>', + dest: '<%= config.dist %>', + src: [ + '*.{ico,png,txt}', + 'images/{,*/}*.*', + '{,*/}*.html', + 'styles/fonts/{,*/}*.*' + ] + }, { + expand: true, + dot: true, + dest: '<%= config.dist %>', + src: [ + // Copy bower components to fix polymer dependencies as they are + // not well integrated with the Grunt build tools. + 'bower_components/**/*.*' + ] + }] + } + }, + + // Generates a custom Modernizr build that includes only the tests you + // reference in your app + modernizr: { + dist: { + devFile: 'bower_components/modernizr/modernizr.js', + outputFile: '<%= config.dist %>/scripts/vendor/modernizr.js', + files: { + src: [ + '<%= config.dist %>/scripts/{,*/}*.js', + '<%= config.dist %>/styles/{,*/}*.css', + '!<%= config.dist %>/scripts/vendor/*' + ] + }, + uglify: true + } + }, + + // Run some tasks in parallel to speed up build process + concurrent: { + server: [ + 'babel:dist', + 'sass' + ], + test: [ + 'babel' + ], + dist: [ + 'babel', + 'sass', + 'svgmin' + ] + } + }); + + + grunt.registerTask('serve', 'start the server and preview your app', function (target) { + + if (target === 'dist') { + return grunt.task.run(['build', 'browserSync:dist']); + } + + grunt.task.run([ + 'clean:server', + 'wiredep', + 'concurrent:server', + 'postcss', + 'browserSync:livereload', + 'watch' + ]); + }); + + grunt.registerTask('server', function (target) { + grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); + grunt.task.run([target ? ('serve:' + target) : 'serve']); + }); + + grunt.registerTask('test', function (target) { + if (target !== 'watch') { + grunt.task.run([ + 'clean:server', + 'concurrent:test', + 'postcss' + ]); + } + + grunt.task.run([ + 'browserSync:test', + 'mocha' + ]); + }); + + grunt.registerTask('build', [ + 'clean:dist', + 'wiredep', + 'useminPrepare', + 'concurrent:dist', + 'postcss', + 'concat', + 'cssmin', + 'copy:dist', + 'modernizr', + 'filerev', + 'usemin', + 'htmlmin' + ]); + + grunt.registerTask('default', [ + 'eslint', + 'build', + 'jsdoc' + ]); +}; diff --git a/tools/Streetview/sv-wizard/README.md b/tools/Streetview/sv-wizard/README.md new file mode 100644 index 0000000..57cbde1 --- /dev/null +++ b/tools/Streetview/sv-wizard/README.md @@ -0,0 +1,14 @@ +# sv-wizard +[Google Maps Street View API](https://developers.google.com/maps/documentation/streetview/intro) interactive request generator. + +## How to build and serve +To build the tool you need to have `node`, `npm` and `bower` installed. Once you have that, you'll have to get the `node` and `bower` dependencies. To do so, move inside the `sv-wizard` directory and execute the following commands: + + npm install + bower install + +Once that's completed, you can execute grunt tasks to build and serve the application. + +* `grunt build`: Builds the tool in to the `dist/` folder. The build consists in concatenating and minifying the source code and copying it and its dependencies in to the target folder. +* `grunt serve:dist`: Builds and serves the tool in port `8080`. +* `grunt serve`: Serves the application under the `app/` folder, without building it, in port `8080`. diff --git a/tools/Streetview/sv-wizard/app/favicon.ico b/tools/Streetview/sv-wizard/app/favicon.ico new file mode 100644 index 0000000..49e316a Binary files /dev/null and b/tools/Streetview/sv-wizard/app/favicon.ico differ diff --git a/tools/Streetview/sv-wizard/app/images/blur.png b/tools/Streetview/sv-wizard/app/images/blur.png new file mode 100644 index 0000000..8bef8eb Binary files /dev/null and b/tools/Streetview/sv-wizard/app/images/blur.png differ diff --git a/tools/Streetview/sv-wizard/app/index.html b/tools/Streetview/sv-wizard/app/index.html new file mode 100644 index 0000000..0ff8703 --- /dev/null +++ b/tools/Streetview/sv-wizard/app/index.html @@ -0,0 +1,95 @@ + + + + + SV Wizard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + diff --git a/tools/Streetview/sv-wizard/app/scripts/controllers/confirmCtrl.js b/tools/Streetview/sv-wizard/app/scripts/controllers/confirmCtrl.js new file mode 100644 index 0000000..7b836f4 --- /dev/null +++ b/tools/Streetview/sv-wizard/app/scripts/controllers/confirmCtrl.js @@ -0,0 +1,40 @@ +var svWizard = svWizard || {}; + +svWizard.controllers = svWizard.controllers || {}; + +svWizard.controllers.ConfirmCtrl = (function(){ + /** + * CofirmController controls a simple dialog to confirm or decline an + * action. Note that this controller can only be used in a dialog opened using + * ngDialog as it relies + * on the properties and functions inserted by this utility on the scope. + *

+ * The message to display has to be pased upon the dialog creation in the data + * object with the message key + * @constructor + * @param {Scope} $scope Injected + * @memberOf svWizard.controllers + */ + var ConfirmCtrl = function($scope) { + /** + * Closes the dialog confirming the action. + * @method + */ + this.confirm = $scope.confirm; + /** + * Closes the dialog canceling the action. + * @method + */ + this.cancel = $scope.closeThisDialog; + /** + * The message to display in the dialog + * @type {string} + */ + this.message = $scope.ngDialogData.message; + }; + + return ConfirmCtrl; +})(); + +angular.module('svWizardApp').controller( 'ConfirmCtrl',['$scope', + svWizard.controllers.ConfirmCtrl]); diff --git a/tools/Streetview/sv-wizard/app/scripts/controllers/generatedUrlCtrl.js b/tools/Streetview/sv-wizard/app/scripts/controllers/generatedUrlCtrl.js new file mode 100644 index 0000000..b54edb7 --- /dev/null +++ b/tools/Streetview/sv-wizard/app/scripts/controllers/generatedUrlCtrl.js @@ -0,0 +1,44 @@ +var svWizard = svWizard || {}; +svWizard.controllers = svWizard.controllers || {}; + +svWizard.controllers.GeneratedUrlCtrl = (function(){ + + /** + * GeneratedUrlCtrl controls a simple dialog to show and open the generated + * StreetView request. Note that this controller can only be used in a dialog + * opened using ngDialog + * as it relies on the properties and functions inserted by this utility on + * the scope. + *

+ * The request URL has to be pased upon the dialog creation in the data + * object with the url key + * @constructor + * @param {Scope} $scope Injected + * @param {window} $window Injected: Angular wrapper around the + * browser's window object + * @memberOf svWizard.controllers + */ + var GeneratedUrlCtrl = function($scope, $window) { + + /** + * Street View request URL + * @type {string} + */ + this.url = $scope.ngDialogData.url; + + this.window_ = $window; + }; + + /** + * Opens the Street View request in a new browser window + * @method + */ + GeneratedUrlCtrl.prototype.open = function() { + this.window_.open(this.url); + }; + + return GeneratedUrlCtrl; +})(); + +angular.module('svWizardApp').controller( 'GeneratedUrlCtrl',['$scope', + '$window', svWizard.controllers.GeneratedUrlCtrl]); diff --git a/tools/Streetview/sv-wizard/app/scripts/controllers/mainCtrl.js b/tools/Streetview/sv-wizard/app/scripts/controllers/mainCtrl.js new file mode 100644 index 0000000..87075fc --- /dev/null +++ b/tools/Streetview/sv-wizard/app/scripts/controllers/mainCtrl.js @@ -0,0 +1,255 @@ +var svWizard = svWizard || {}; +svWizard.controllers = svWizard.controllers || {}; + +svWizard.controllers.MainCtrl = (function(){ + /** + * MainCtrl controls the main view of the application, where the user can edit + * the different parameters using the form and also using the Street View + * panorama viewer. The parameters modified by any of these ways will be + * reflected in the other, as they both modify the current request. + * When this parameters are modified, the State service is used to + * save the changes into local storage. + *

+ * This controller also handles generating the request URL and displaying it + * to the user in a dialog, saving the requests and creating new ones. + * @constructor + * @param {Scope} scope Injected + * @param {svWizard.services.State} state Injected + * @param {svWizard.services.Settings} settings Injected + * @param {svWizard.services.Menu} menu Injected + * @param {svWizard.services.Utils} utils Injected + * @param {ngDialog} ngDialog Injected + * @memberOf svWizard.controllers + */ + var MainCtrl = function(scope, state, settings, menu, utils, ngDialog) { + + /** + * The application's state + * @type {svWizard.services.State} + */ + this.state = state; + + /** + * The application's settings + * @type {svWizard.services.Settings} + */ + this.settings = settings; + + /** + * The Menu service used to open and close the side menu + * @type {svWizard.services.Menu} + */ + this.menu = menu; + + /** + * @type {svWizard.models.AuthenticationMode} + */ + this.AuthenticationMode = svWizard.models.AuthenticationMode; + + /** + * List of messages needed in the view + * @type {Object} + */ + this.messages = { + /** + * Message to display in the new-request confirmation dialog + * @type {string} + * @default + */ + confirmNew: 'Do you really want to create a new request? All unsaved ' + + 'changes will be discarded.' + }; + + /** + * Flag that indicates whether the user is missing to provide the needed + * authentication credentials for the selected authentication method. It is + * used in the view to display an error message. + * @type {boolean} + */ + this.authenticationMissing = false; + + /** + * The request generator + * @private + * @type {svWizard.services.utils.generator} + **/ + this.generator_ = utils.generator; + + /** + * ngDialog service to create pop-up dialogs + * @private + * @type {ngDialog} + **/ + this.ngDialog_ = ngDialog; + + var self = this; + + // If any of the properties of the current request changes, onRequestChange + // is called, which saves the changes into local storage and validates the + // authentication + scope.$watch( function() { + return self.state.current; + }, function(){ + self.onRequestChange(); + }, true); + + // If the authentication settings change, the authentication is checked + // to check if there is anything missing and display a message accordingly + scope.$watch( function() { + return { + apiKey: self.settings.apiKey, + cryptoKey: self.settings.cryptoKey, + clientId: self.settings.clientId + }; + }, function(){ + self.validateAuth(); + }, true); + } + + /** + * It updates the current request location to the one of the provided address. + *

+ * This function has to be used within the autocomplete directive, so it is + * called when the user selects and address. + * @param {google.maps.places.PlaceResult} address the selected address on + * the autocomplete input + * @method + */ + MainCtrl.prototype.addressSelected = function(address) { + if( address !== undefined && address !== null) { + var location = address.geometry.location; + this.state.current.location.lat = location.lat(); + this.state.current.location.lng = location.lng(); + } + }; + + /** + * Generates a Street View request URL based on the current request parameters + * and opens the GeneratedDialog to display it + * @method + */ + MainCtrl.prototype.generate = function() { + var url = this.generator_.generate(this.state.current, this.settings); + this.ngDialog_.open({ + template: 'templates/generated.html', + className: 'ngdialog-theme-default ngdialog-theme-custom', + controller: 'GeneratedUrlCtrl', + controllerAs: 'generated', + data: { + url: url + } + }); + }; + + + /** + * Checks whether the current request is configured to use for Work or + * Premium Plan, which means using client id and crypto key to authenticate it + * @returns {boolean} + * @method + */ + MainCtrl.prototype.isForWork = function() { + return this.state.current.authenticationMode + === this.AuthenticationMode.CLIENT_AND_CRYPTO; + } + + /** + * Checks whether the current request is configured to use the free version, + * which means not using any authentication method + * @returns {boolean} + * @method + */ + MainCtrl.prototype.isFree = function() { + return this.state.current.authenticationMode + === this.AuthenticationMode.NONE; + } + + /** + * Checks whether the current request is configured to use an API key to + * authenticate the request + * @returns {boolean} + * @method + */ + MainCtrl.prototype.isApiKey = function() { + return this.state.current.authenticationMode + === this.AuthenticationMode.API_KEY; + } + + /** + * Checks if the user has provided the needed authentication credentials in + * the settings page for the selected authentication method, and updates the + * authenticationMissing flag accodingly. Should be called everytime + * the current request authentication method or the authentication settings + * change. + * @method + */ + MainCtrl.prototype.validateAuth = function() { + var mode = this.state.current.authenticationMode; + this.authenticationMissing = true; + + if(mode === this.AuthenticationMode.NONE){ + this.authenticationMissing = false; + }else if(mode === this.AuthenticationMode.API_KEY && this.settings.apiKey + && this.settings.apiKey.length > 0) { + this.authenticationMissing = false; + }else if(mode === this.AuthenticationMode.CLIENT_AND_CRYPTO + && this.settings.cryptoKey && this.settings.cryptoKey.length > 0 + && this.settings.clientId && this.settings.clientId.length > 0) { + this.authenticationMissing = false; + } + }; + + /** + * Saves the current request in localstorage using the State service + * and validates the authentication using validateAuth. + *

+ * It should be called everytime some parameter on the current request + * chages. + * @method + */ + MainCtrl.prototype.onRequestChange = function(){ + this.validateAuth(); + this.state.saveCurrentRequest(); + } + + /** + * Opens the save dialog so the user can change the name of the request or + * save it as a new request. It will update the current request with the saved + * one, which will contain a new id if it's been newly created and a new + * timestamp. + * @method + */ + MainCtrl.prototype.save = function() { + var self = this; + this.ngDialog_.openConfirm({ + template: 'templates/save.html', + className: 'ngdialog-theme-default ngdialog-theme-custom', + controller: 'SaveCtrl', + controllerAs: 'save', + data: { + request: this.state.current + } + }).then( function(saved) { + self.state.current = angular.copy(saved); + }); + } + + /** + * Creates a new request. Note that in the View, this function is called using + * the confirmAction directive, which means that it won't be executed + * when the user clicks the new button, but rather when the users confirms + * the action in the dialog that will be prompted. The parameters of the + * new request will remain the same as they were, but it will have no id nor + * name. + * @method + */ + MainCtrl.prototype.newRequest = function() { + this.state.current.id = null; + this.state.current.name = ''; + }; + return MainCtrl; +})(); + +angular.module('svWizardApp').controller( 'MainCtrl', ['$scope', 'State', +'Settings', 'Menu', 'Utils', 'ngDialog', + svWizard.controllers.MainCtrl]); diff --git a/tools/Streetview/sv-wizard/app/scripts/controllers/menuCtrl.js b/tools/Streetview/sv-wizard/app/scripts/controllers/menuCtrl.js new file mode 100644 index 0000000..b7ccdb5 --- /dev/null +++ b/tools/Streetview/sv-wizard/app/scripts/controllers/menuCtrl.js @@ -0,0 +1,100 @@ +var svWizard = svWizard || {}; +svWizard.controllers = svWizard.controllers || {}; + +svWizard.controllers.MenuCtrl = (function(){ + /** + * MenuCtrl controls the side menu on the application. There are two sections + * in the menu: + *
+ * + * @constructor + * @param {svWizard.services.Settings} settings Injected + * @param {svWizard.services.RequestProvider} requestProvier Injected + * @param {svWizard.services.State} State Injected + * @param {svWizard.services.Menu} Menu Injected + * @memberOf svWizard.controllers + */ + var MenuCtrl = function(settings, requestProvider, + state, menu) { + + /** + * @type {svWizard.services.RequestProvider} + */ + this.provider = requestProvider; + /** + * @type {svWizard.services.Settings} + */ + this.settings = settings; + /** + * @type {svWizard.services.State} + */ + this.state = state; + + /** + * @type {svWizard.services.Menu} + */ + this.menu = menu; + + /** + * String to filter the saved URLs with + * @type {string} + */ + this.search = ''; + + /** + * List of messages needed in the view + * @type {Object} + */ + this.messages = { + /** + * Message to display in the delete-request confirmation dialog + * @type {string} + * @default + */ + confirmDelete: 'Do you really want to delete this request? Note that ' + + 'this action cannot be undone' + } + }; + + /** + * Opens a saved request so it is displayed on the main view of the + * application. It does it by just copying the saved request into the current + * request. + * @method + * @param {svWizard.models.Request} request the request to open + */ + MenuCtrl.prototype.openRequest = function(request) { + this.state.current = angular.copy(request); + this.menu.close(); + }; + + /** + * Deletes a saved request from localstorage and, in case it is the current + * request, it makes it new by removing its id and name. This is only called + * after user's confirmation. + * @method + * @param {svWizard.models.Request} request the request to open + */ + MenuCtrl.prototype.deleteRequest = function(request){ + this.provider.deleteRequest(request.id); + if(this.state.current.id === request.id) { + this.state.current.id = null; + this.state.current.name = ''; + } + } + return MenuCtrl; +})(); + + +angular.module('svWizardApp').controller( 'MenuCtrl', ['Settings', +'RequestProvider', 'State', 'Menu', svWizard.controllers.MenuCtrl]); + \ No newline at end of file diff --git a/tools/Streetview/sv-wizard/app/scripts/controllers/saveCtrl.js b/tools/Streetview/sv-wizard/app/scripts/controllers/saveCtrl.js new file mode 100644 index 0000000..f238b1b --- /dev/null +++ b/tools/Streetview/sv-wizard/app/scripts/controllers/saveCtrl.js @@ -0,0 +1,72 @@ +var svWizard = svWizard || {}; +svWizard.controllers = svWizard.controllers || {}; + +svWizard.controllers.SaveCtrl = (function(){ + + /** + * SaveCtrl controls the dialog used to save the current request. It shows an + * input field so the user can enter or modify the name of the requests. If + * the request is new and has never been saved before (it doesn't have an id), + * the user will only be able to "Save As" the request, which will create a + * new one with a new id. However, if the request is already been saved before + * (have an id) the user will be able to choose between "Save", which may + * change the request name but keep the same id, or "Save As", which will + * create a new request with a new id. + *

+ * Note that this controller can only be used in a dialog opened using + * ngDialog as it relies + * on the properties and functions inserted by this utility on the scope. + *

+ * The request to save has to be pased upon the dialog creation in the data + * object with the request key. Once the request is saved, it will + * be returned in the close dialog promise. + * @constructor + * @param {Scope} $scope Injected + * @param {svWizard.services.RequestProvider} requestProvider Injected + * @memberOf svWizard.controllers + */ + var SaveCtrl = function($scope, requestProvider) { + /** + * Closes the dialog when the request is saved + * @method + */ + this.confirm = $scope.confirm; + + /** + * @type {svWizard.services.RequestProvider} + */ + this.requestProvider = requestProvider; + + /** + * A copy of the current request provided in the dialog creation + * @type {svWizard.models.Request} + */ + this.request = angular.copy($scope.ngDialogData.request); + }; + + /** + * Saves the request and closes the dialog returning the saved request to the + * promise. It doesn't create a new request but rather updates an already + * existing one + * @method + */ + SaveCtrl.prototype.saveRequest = function() { + this.request = this.requestProvider. + updateRequest(this.request.id, this.request); + this.confirm(this.request); + }; + + /** + * Creates a new request and closes the dialog returning the saved request to + * the promise. + * @method + */ + SaveCtrl.prototype.saveRequestAs = function() { + this.request = this.requestProvider.createRequest(this.request); + this.confirm(this.request); + }; + return SaveCtrl; +})(); + +angular.module('svWizardApp').controller( 'SaveCtrl',['$scope', + 'RequestProvider', svWizard.controllers.SaveCtrl]); diff --git a/tools/Streetview/sv-wizard/app/scripts/directives/autocomplete.js b/tools/Streetview/sv-wizard/app/scripts/directives/autocomplete.js new file mode 100644 index 0000000..9a3fb88 --- /dev/null +++ b/tools/Streetview/sv-wizard/app/scripts/directives/autocomplete.js @@ -0,0 +1,35 @@ +var svWizard = svWizard || {}; +svWizard.directives = svWizard.directives || {}; + +svWizard.directives.mapAutocomplete = (function(){ + /** + * The mapAutocomplete (map-autocomplete) directive has to be applied in an + * input method. It will provide the Google Maps autocomplete feature to + * this input element. Once the user selects a place, the function provided + * in the directive value will be called with the selected result. + * @member {Directive} mapAutocomplete + * @memberOf svWizard.directives + */ + var mapAutocomplete = function($timeout) { + return { + restrict: 'A', + scope: { + mapAutocomplete: '&' + }, + link: function(scope, element, attrs) { + var autocomplete = new google.maps.places.Autocomplete(element[0]); + autocomplete.addListener('place_changed', function(){ + scope.$apply(); + $timeout(function() { + scope.mapAutocomplete({address: autocomplete.getPlace()}); + }); + }); + } + }; + }; + return mapAutocomplete; +})(); + +angular.module('svWizardApp').directive( 'mapAutocomplete', [ '$timeout', + svWizard.directives.mapAutocomplete]); + \ No newline at end of file diff --git a/tools/Streetview/sv-wizard/app/scripts/directives/confirmAction.js b/tools/Streetview/sv-wizard/app/scripts/directives/confirmAction.js new file mode 100644 index 0000000..dd21f23 --- /dev/null +++ b/tools/Streetview/sv-wizard/app/scripts/directives/confirmAction.js @@ -0,0 +1,51 @@ +var svWizard = svWizard || {}; +svWizard.directives = svWizard.directives || {}; + +svWizard.directives.confirmAction = (function(){ + /** + * The confirmAction (confirm-action) displays a confirmation dialog one the + * user click into the element it is inserted in. It only executes the action + * if the user click in the Accept or Yes button. + *

+ * The action to execute upon confirmation is provided in the attribute + * confirm-action and the message to display to the user in the + * confirm-action-message. + * @member {Directive} confirmAction + * @memberOf svWizard.directives + */ + var confirmAction = function(ngDialog) { + return { + restrict: 'A', + scope: { + confirmAction: '&', + confirmActionMessage: '@' + }, + link: function(scope, element, attrs) { + var action = scope.confirmAction; + element.bind('click', function(){ + scope.$apply( function(){ + //Display message + var message = scope.confirmActionMessage; + ngDialog.openConfirm({ + template: 'templates/confirm.html', + className: 'ngdialog-theme-default ngdialog-theme-custom', + controller: 'ConfirmCtrl', + controllerAs: 'dialog', + data: { + message: message + } + }).then(function() { + //if confirmed by user, execute action. Do nothing otherwise + action(); + }); + }); + }); + } + } + } + return confirmAction; +})(); + +angular.module('svWizardApp').directive( 'confirmAction', [ 'ngDialog', + svWizard.directives.confirmAction]); + \ No newline at end of file diff --git a/tools/Streetview/sv-wizard/app/scripts/directives/mapPreview.js b/tools/Streetview/sv-wizard/app/scripts/directives/mapPreview.js new file mode 100644 index 0000000..ca40e3b --- /dev/null +++ b/tools/Streetview/sv-wizard/app/scripts/directives/mapPreview.js @@ -0,0 +1,63 @@ +var svWizard = svWizard || {}; +svWizard.directives = svWizard.directives || {}; + +svWizard.directives.mapPreview = (function(){ + + /** + * The mapPreview (map-preview) directive inserts a Google Map into the + * view linked to a Street View panorama so it updates its position when the + * panorama does and also, it changes the panorama location when the pegman + * is dropped some where. + *

+ * The following attributes are included in the scope + *