<commit_before>
/*!
 * Ext JS Connect
 * Copyright(c) 2010 Ext JS, Inc.
 * MIT Licensed
 */

/**
 * Framework version.
 */

exports.version = '0.0.6';

/**
 * Module dependencies.
 */

var sys = require('sys'),
    fs = require('fs'),
    Url = require('url'),
    Path = require('path'),
    http = require('http'),
    assert = require('assert'),
    utils = require('./utils');

/**
 * Default env for those who do not use _bin/connect_.
 *
 * @type Object
 */

process.connectEnv = process.connectEnv || {
    name: process.env.CONNECT_ENV || 'development',
    post: 3000,
    host: null
};

/**
 * Auto-require response extensions.
 */

require('./response');

/**
 * TODO: remove?
 * @ignore
 */

if (typeof Object.prototype.forEach !== 'function') {
    Object.defineProperty(Object.prototype, 'forEach', {
        value: function(fn, scope){
            var keys = Object.keys(this);
            for (var i = 0, len = keys.length; i < len; ++i) {
                fn.call(scope, this[keys[i]], keys[i], this);
            }
        }
    });
}

/**
 * TODO: remove?
 * @ignore
 */

if (typeof Object.prototype.map !== 'function') {
  Object.defineProperty(Object.prototype, "map", {value: function (callback, thisObject) {
    var accum = [];
    var keys = Object.keys(this);
    var length = keys.length;
    for (var i = 0; i < length; i++) {
      var key = keys[i];
      accum[i] = callback.call(thisObject, this[key], key, this);
    }
    return accum;
  }});
}

/**
 * Setup the given middleware layer callbacks.
 *
 * Examples:
 *
 *     connect.createServer(
 *        connect.log(),
 *        connect.static(__dirname + '/public')
 *     );
 *
 *     var server = connect.createServer();
 *     server.use('/', connect.log());
 *     server.use('/', connect.static(__dirname + '/public'));
 *
 * @params {Function} ...
 * @return {Server}
 * @api public
 */

var Server = exports.Server = function Server(layers) {
    this.stack = [];

    // Stack layers
    layers.forEach(function(layer){
        this.use("/", layer);
    }, this);

    // Set up the request handler using the parent's constructor
    http.Server.call(this, this.handle);
};

// Server is a subclass of http.Server
sys.inherits(Server, http.Server);

/**
 * Stack the given middleware `handle` to the given `route`.
 *
 * @param {String} route
 * @param {Function} handle
 * @return {Server}
 * @api public
 */

Server.prototype.use = function(route, handle){

    // Allow for more than one handle function to be added at a time
    if (arguments.length > 2) {
        Array.prototype.slice.call(arguments, 1).forEach(function (handle) {
            this.use(route, handle);
        }, this);
        return this;
    }

    // Wrap sub-apps
    if (handle instanceof Server) {
        var server = handle;
        handle = function(req, res, next) {
            server.handle(req, res, next);
        };
    }

    // Validate the input
    if (!(typeof route === 'string' && typeof handle === 'function')) {
        throw new Error("Each layer must have a route and a handle function");
    }

    // Normalize route
    if (route[route.length - 1] !== '/') {
        route += '/';
    }

    // Add the route, handle pair to the stack
    this.stack.push({ route: route, handle: handle });

    // Allow chaining
    return this;
};

/**
 * Listen on the given `port` number,
 * defaults to the current environment's port,
 * or _3000_ when neither are present.
 *
 * @param  {Number} port
 * @return {Server}
 * @api public
 */

Server.prototype.listen = function(port) {
    http.Server.prototype.listen.call(this, port || process.connectEnv.port || 3000);
    return this;
};

/**
 * Handle server requests, punting them down
 * the middleware stack.
 *
 * @api private
 */

Server.prototype.handle = function(req, res, outerNext) {
    var pathname = Url.parse(req.url).pathname + '/',
        stack = this.stack,
        index = 0;

    function next(err) {
        var layer = stack[index++];

        // If we reached the end of the chain...
        if (!layer) {
            // ... and we're part of a bigger chain then forward to it.
            if (outerNext) {
                outerNext();
                return;
            }

            // Otherwise send a proper error message to the browser.
            if (err) {
                var msg = process.connectEnv.name === 'production'
                    ? 'Internal Server Error'
                    : err.stack || err.toString();
                res.writeHead(500, { 'Content-Type': 'text/plain' });
                res.end(msg);
            } else {
                res.writeHead(404, { 'Content-Type': 'text/plain' });
                res.end('Cannot find ' + req.url);
            }
            return;
        }

        // Skip this layer if the route doesn't match.
        if (pathname.indexOf(layer.route) !== 0) {
            next();
            return;
        }

        // Call the layer handler
        try {
            // Trim off the part of the url that matches the route
            req.url = req.url.substr(layer.route.length - 1) || '/';

            if (err) {
                if (layer.handle.length === 4) {
                    layer.handle(err, req, res, next);
                } else {
                    next(err);
                }
            } else {
                layer.handle(req, res, next);
            }
        } catch (e) {
            if (e instanceof assert.AssertionError) {
                throw e;
            } else {
                next(e);
            }
        }
    }
    next();
};

/**
 * Shortcut for `new connect.Server([ ... ])`.
 *
 * @param  {Function} ...
 * @param  {Object} env
 * @return {Server}
 * @api public
 */

exports.createServer = function() {
    return new Server(Array.prototype.slice.call(arguments));
};

/**
 * Auto-load bundled middleware with getters.
 */

fs.readdirSync(__dirname + '/middleware').forEach(function(filename){
    if (/\.js$/.test(filename)) {
        var name = filename.substr(0, filename.lastIndexOf('.'));
        Object.defineProperty(exports, name, { get: function(){
            return require('./middleware/' + name);
        }});
    }
});
<commit_msg>Routes need to always end in a slash.<commit_after>
/*!
 * Ext JS Connect
 * Copyright(c) 2010 Ext JS, Inc.
 * MIT Licensed
 */

/**
 * Framework version.
 */

exports.version = '0.0.6';

/**
 * Module dependencies.
 */

var sys = require('sys'),
    fs = require('fs'),
    Url = require('url'),
    Path = require('path'),
    http = require('http'),
    assert = require('assert'),
    utils = require('./utils');

/**
 * Default env for those who do not use _bin/connect_.
 *
 * @type Object
 */

process.connectEnv = process.connectEnv || {
    name: process.env.CONNECT_ENV || 'development',
    post: 3000,
    host: null
};

/**
 * Auto-require response extensions.
 */

require('./response');

/**
 * TODO: remove?
 * @ignore
 */

if (typeof Object.prototype.forEach !== 'function') {
    Object.defineProperty(Object.prototype, 'forEach', {
        value: function(fn, scope){
            var keys = Object.keys(this);
            for (var i = 0, len = keys.length; i < len; ++i) {
                fn.call(scope, this[keys[i]], keys[i], this);
            }
        }
    });
}

/**
 * TODO: remove?
 * @ignore
 */

if (typeof Object.prototype.map !== 'function') {
  Object.defineProperty(Object.prototype, "map", {value: function (callback, thisObject) {
    var accum = [];
    var keys = Object.keys(this);
    var length = keys.length;
    for (var i = 0; i < length; i++) {
      var key = keys[i];
      accum[i] = callback.call(thisObject, this[key], key, this);
    }
    return accum;
  }});
}

/**
 * Setup the given middleware layer callbacks.
 *
 * Examples:
 *
 *     connect.createServer(
 *        connect.log(),
 *        connect.static(__dirname + '/public')
 *     );
 *
 *     var server = connect.createServer();
 *     server.use('/', connect.log());
 *     server.use('/', connect.static(__dirname + '/public'));
 *
 * @params {Function} ...
 * @return {Server}
 * @api public
 */

var Server = exports.Server = function Server(layers) {
    this.stack = [];

    // Stack layers
    layers.forEach(function(layer){
        this.use("/", layer);
    }, this);

    // Set up the request handler using the parent's constructor
    http.Server.call(this, this.handle);
};

// Server is a subclass of http.Server
sys.inherits(Server, http.Server);

/**
 * Stack the given middleware `handle` to the given `route`.
 *
 * @param {String} route
 * @param {Function} handle
 * @return {Server}
 * @api public
 */

Server.prototype.use = function(route, handle){

    // Allow for more than one handle function to be added at a time
    if (arguments.length > 2) {
        Array.prototype.slice.call(arguments, 1).forEach(function (handle) {
            this.use(route, handle);
        }, this);
        return this;
    }

    // Make sure the route always ends in a slash
    if (route[route.length - 1] !== "/") {
        route += "/";
    }

    // Wrap sub-apps
    if (handle instanceof Server) {
        var server = handle;
        handle = function(req, res, next) {
            server.handle(req, res, next);
        };
    }

    // Validate the input
    if (!(typeof route === 'string' && typeof handle === 'function')) {
        throw new Error("Each layer must have a route and a handle function");
    }

    // Normalize route
    if (route[route.length - 1] !== '/') {
        route += '/';
    }

    // Add the route, handle pair to the stack
    this.stack.push({ route: route, handle: handle });

    // Allow chaining
    return this;
};

/**
 * Listen on the given `port` number,
 * defaults to the current environment's port,
 * or _3000_ when neither are present.
 *
 * @param  {Number} port
 * @return {Server}
 * @api public
 */

Server.prototype.listen = function(port) {
    http.Server.prototype.listen.call(this, port || process.connectEnv.port || 3000);
    return this;
};

/**
 * Handle server requests, punting them down
 * the middleware stack.
 *
 * @api private
 */

Server.prototype.handle = function(req, res, outerNext) {
    var pathname = Url.parse(req.url).pathname + '/',
        stack = this.stack,
        index = 0;

    function next(err) {
        var layer = stack[index++];

        // If we reached the end of the chain...
        if (!layer) {
            // ... and we're part of a bigger chain then forward to it.
            if (outerNext) {
                outerNext();
                return;
            }

            // Otherwise send a proper error message to the browser.
            if (err) {
                var msg = process.connectEnv.name === 'production'
                    ? 'Internal Server Error'
                    : err.stack || err.toString();
                res.writeHead(500, { 'Content-Type': 'text/plain' });
                res.end(msg);
            } else {
                res.writeHead(404, { 'Content-Type': 'text/plain' });
                res.end('Cannot find ' + req.url);
            }
            return;
        }

        // Skip this layer if the route doesn't match.
        if (pathname.indexOf(layer.route) !== 0) {
            next();
            return;
        }

        // Call the layer handler
        try {
            // Trim off the part of the url that matches the route
            req.url = req.url.substr(layer.route.length - 1) || '/';

            if (err) {
                if (layer.handle.length === 4) {
                    layer.handle(err, req, res, next);
                } else {
                    next(err);
                }
            } else {
                layer.handle(req, res, next);
            }
        } catch (e) {
            if (e instanceof assert.AssertionError) {
                throw e;
            } else {
                next(e);
            }
        }
    }
    next();
};

/**
 * Shortcut for `new connect.Server([ ... ])`.
 *
 * @param  {Function} ...
 * @param  {Object} env
 * @return {Server}
 * @api public
 */

exports.createServer = function() {
    return new Server(Array.prototype.slice.call(arguments));
};

/**
 * Auto-load bundled middleware with getters.
 */

fs.readdirSync(__dirname + '/middleware').forEach(function(filename){
    if (/\.js$/.test(filename)) {
        var name = filename.substr(0, filename.lastIndexOf('.'));
        Object.defineProperty(exports, name, { get: function(){
            return require('./middleware/' + name);
        }});
    }
});
<|endoftext|>


<commit_before>const { FileModel } = require('./model');

class ThumbnailService {
	constructor() {
		this.app = null;
	}

	patch(id, data) {
		return FileModel.updateOne(
			{ thumbnailRequestToken: id },
			{
				$set: {
					thumbnailRequestToken: null,
					thumbnail: data.thumbnail,
				},
			},
		).exec();
	}

	setup(app) {
		this.app = app;
	}
}

module.exports = function () {
	const app = this;

	// Initialize our service with any options it requires
	app.use('/fileStorage/thumbnail', new ThumbnailService());
};
<commit_msg>satisfy linter<commit_after>const { FileModel } = require('./model');

class ThumbnailService {
	constructor() {
		this.app = null;
	}

	patch(id, data) {
		return FileModel.updateOne(
			{ thumbnailRequestToken: id },
			{
				$set: {
					thumbnailRequestToken: null,
					thumbnail: data.thumbnail,
				},
			},
		).exec();
	}

	setup(app) {
		this.app = app;
	}
}

module.exports = function service() {
	const app = this;

	// Initialize our service with any options it requires
	app.use('/fileStorage/thumbnail', new ThumbnailService());
};
<|endoftext|>


<commit_before>'use strict';

var async = require('async');

var LocationsCreateController = function ($scope, $routeParams, apiService, authService, $q, $location, mapService,
                                          responseHandler, locationFormDefaults, regionService, GeolocationService) {
    var changeTimer, lockUpdate;
    $scope.location = {};
    $scope.assets = {
        file: null
    };
    $scope.marker = {
        lat: null,
        lng: null
    };
    $scope.selectedIndex = 0;
    $scope._sys = locationFormDefaults;

    GeolocationService.getCurrentPosition().then(
        function(position) { //
            console.log('Position',position);
            $scope.marker.lat = position.coords.latitude;
            $scope.marker.lng = position.coords.longitude;
            $scope.updateLocation({ 'lat' : position.coords.latitude , 'lng': position.coords.longitude});
        },
        function(errorCode) {
            if(errorCode === false) {
                alert('GeoLocation is not supported by browser.');
            }
            else if(errorCode === 1) {
                alert('User either denied GeoLocation or waited for long to respond.');
            }
        }
    );

    /*
        Update the map to a new geolocation
        Also retrieves the new address
    */
    $scope.updateLocation = function (latlon) {
        $scope.marker = latlon;

        mapService.reverseGeoCode(latlon.lat,latlon.lng,function(err, result){
            if (err) {
                throw err;
            }
            lockUpdate = true;
            if (!result.error) {
                console.log('updateLocation',result.data);

                var address = mapService.createAddressFromGeo(result.data.address);
                if (address) {
                    $scope.location.street = address.street;
                    $scope.location.city = address.city;
                    $scope.location.postcode = address.postcode;
                    $scope.location.country = address.country;
                }
            }
        });
        $scope.showLatLng = false;
    };

    /*
        Watches the address fields for updates
        Updates the map on address changes
     */
    $scope.$watchGroup(['location.street', 'location.city', 'location.postcode'], function (newValues, oldValues) {
        var changed = false,
            keys = Object.keys(newValues);

        if (lockUpdate) {
            lockUpdate = false;
            return;
        }

        for (var i = 0; i < keys.length; i += 1) {
            if (newValues[keys[i]] !== oldValues[keys[i]]) {
                changed = true;
            }
        }

        if (changed) {
            if (changeTimer) {
                window.clearTimeout(changeTimer);
                changeTimer = null;
            }
            changeTimer = window.setTimeout(function () {
                mapService.geoCode({
                    street: $scope.location.street,
                    city: $scope.location.city,
                    postcode: $scope.location.postcode,
                    country: $scope.location.country
                }, function (err, result) {
                    console.log('found locations',result.data);
                    if (Array.isArray(result.data) && result.data.length > 0) {
                        $scope.marker.lat = result.data[0].lat;
                        $scope.marker.lng = result.data[0].lon;
                        $scope.location.display_name = result.data[0].display_name || "";
                        $scope.center = [result.data[0].lat,result.data[0].lon];

                    }
                });
            },1000);
        }
    });

    /*
        Submit the new location
     */
    $scope.submit = function () {
        var deferred = $q.defer(),
            payload = $scope.location;
        $scope.promise = deferred.promise;
        payload.lonlat = [$scope.marker.lng, $scope.marker.lat];
        var isUpdate = false;
        async.waterfall([
            function (cb) {
                /*
                    Fetch the nearest region
                 */
                // TODO: this must be limited through region admins!
                apiService('regions?lat=' + $scope.marker.lat + '&lon=' + $scope.marker.lng).actions.all(cb);
            },
            function (region, cb) {
                region = region.results || region;
                if (region && region.length > 0) {
                    if (!payload.hasOwnProperty('region_uuid')) {
                        payload.region_uuid = region[0].uuid;
                    }
                    /*
                        Are we updating or creating?
                     */
                    if (payload.uuid) {
                        isUpdate = true;
                        apiService('locations').actions.update(payload.uuid, payload, cb);
                    } else {
                        apiService('locations').actions.create(payload, cb);
                    }
                } else {
                    cb(new Error('errors.regions.fetch_failed'),null);
                }
            },
            function (location, cb) {
                if (!isUpdate) {
                    $scope.location.uuid = location.uuid;
                }
                $scope.location.slug = location.slug;
                /*
                    Upload the associated photos
                 */
                if ($scope.files && $scope.files.length) {
                    async.mapSeries($scope.files, function (file, cb) {
                        apiService('photos').actions.upload({
                            file: file,
                            fields: {location_uuid: location.uuid}
                        }, cb);
                    }, cb);
                } else {
                    cb();
                }
            }
        ], function (err) {
            var msgs = {
                success: $routeParams.uuid ? 'messages.locations.update_success' : 'messages.locations.create_success'
            };
            if (responseHandler.handleResponse(err, deferred, msgs)) {
                $location.path('/' + ($scope.location.region_slug || $scope.location.region_uuid) + '/' +
                    $scope.location.slug);
            }
        });
    };

    /*
        Load an existing location to update
     */
    if ($routeParams.uuid) {
        var deferred = $q.defer();
        $scope.promise = deferred.promise;
        async.waterfall([
            function (cb) {
                apiService('locations').actions.find($routeParams.uuid, cb);
            },
            function (location, cb) {
                if (!location.hasOwnProperty('hidden')) {
                    location.hidden = false;
                }
                $scope.location = location;
                $scope.marker = {
                    lng: location.lonlat[0],
                    lat: location.lonlat[1]
                };
                $scope.formTitle = 'Edit "' + location.title + '"';
                apiService('locations/' + location.uuid + '/photos').actions.all(cb);
            },
            function (photos, cb) {
                $scope.photos =  photos.result || photos;
                cb();
            },
            function (cb) {
                regionService.setCurrentRegion($scope.location.region_uuid, cb);
            },
            function (cb) {
                $scope.currentRegion = regionService.currentRegion.title;
                $scope.isAdmin = authService.hasScopes(['admin', 'region-' + $scope.location.region_uuid]);
                cb();
            }
        ], function (err) {
            responseHandler.handleResponse(err, deferred);
        });
    }
};

LocationsCreateController.$inject = ['$scope', '$routeParams', 'apiService', 'authService', '$q', '$location',
    'mapService', 'responseHandler', 'locationFormDefaults', 'regionService','GeolocationService'];

module.exports = LocationsCreateController;<commit_msg>Fix handling of geolocation provider; only ask for new created location<commit_after>'use strict';

var async = require('async');

var LocationsCreateController = function ($scope, $routeParams, apiService, authService, $q, $location, mapService,
                                          responseHandler, locationFormDefaults, regionService, GeolocationService) {
    var changeTimer, lockUpdate;
    $scope.location = {};
    $scope.assets = {
        file: null
    };
    $scope.marker = {
        lat: null,
        lng: null
    };
    $scope.selectedIndex = 0;
    $scope._sys = locationFormDefaults;

    /*
        Update the map to a new geolocation
        Also retrieves the new address
    */
    $scope.updateLocation = function (latlon) {
        $scope.marker = latlon;

        mapService.reverseGeoCode(latlon.lat,latlon.lng,function(err, result){
            if (err) {
                throw err;
            }
            lockUpdate = true;
            if (!result.error) {
                console.log('updateLocation',result.data);

                var address = mapService.createAddressFromGeo(result.data.address);
                if (address) {
                    $scope.location.street = address.street;
                    $scope.location.city = address.city;
                    $scope.location.postcode = address.postcode;
                    $scope.location.country = address.country;
                }
            }
        });
        $scope.showLatLng = false;
    };

    /*
        Watches the address fields for updates
        Updates the map on address changes
     */
    $scope.$watchGroup(['location.street', 'location.city', 'location.postcode'], function (newValues, oldValues) {
        var changed = false,
            keys = Object.keys(newValues);

        if (lockUpdate) {
            lockUpdate = false;
            return;
        }

        for (var i = 0; i < keys.length; i += 1) {
            if (newValues[keys[i]] !== oldValues[keys[i]]) {
                changed = true;
            }
        }

        if (changed) {
            if (changeTimer) {
                window.clearTimeout(changeTimer);
                changeTimer = null;
            }
            changeTimer = window.setTimeout(function () {
                mapService.geoCode({
                    street: $scope.location.street,
                    city: $scope.location.city,
                    postcode: $scope.location.postcode,
                    country: $scope.location.country
                }, function (err, result) {
                    console.log('found locations',result.data);
                    if (Array.isArray(result.data) && result.data.length > 0) {
                        $scope.marker.lat = result.data[0].lat;
                        $scope.marker.lng = result.data[0].lon;
                        $scope.location.display_name = result.data[0].display_name || "";
                        $scope.center = [result.data[0].lat,result.data[0].lon];

                    }
                });
            },1000);
        }
    });

    /*
        Submit the new location
     */
    $scope.submit = function () {
        var deferred = $q.defer(),
            payload = $scope.location;
        $scope.promise = deferred.promise;
        payload.lonlat = [$scope.marker.lng, $scope.marker.lat];
        var isUpdate = false;
        async.waterfall([
            function (cb) {
                /*
                    Fetch the nearest region
                 */
                // TODO: this must be limited through region admins!
                apiService('regions?lat=' + $scope.marker.lat + '&lon=' + $scope.marker.lng).actions.all(cb);
            },
            function (region, cb) {
                region = region.results || region;
                if (region && region.length > 0) {
                    if (!payload.hasOwnProperty('region_uuid')) {
                        payload.region_uuid = region[0].uuid;
                    }
                    /*
                        Are we updating or creating?
                     */
                    if (payload.uuid) {
                        isUpdate = true;
                        apiService('locations').actions.update(payload.uuid, payload, cb);
                    } else {
                        apiService('locations').actions.create(payload, cb);
                    }
                } else {
                    cb(new Error('errors.regions.fetch_failed'),null);
                }
            },
            function (location, cb) {
                if (!isUpdate) {
                    $scope.location.uuid = location.uuid;
                }
                $scope.location.slug = location.slug;
                /*
                    Upload the associated photos
                 */
                if ($scope.files && $scope.files.length) {
                    async.mapSeries($scope.files, function (file, cb) {
                        apiService('photos').actions.upload({
                            file: file,
                            fields: {location_uuid: location.uuid}
                        }, cb);
                    }, cb);
                } else {
                    cb();
                }
            }
        ], function (err) {
            var msgs = {
                success: $routeParams.uuid ? 'messages.locations.update_success' : 'messages.locations.create_success'
            };
            if (responseHandler.handleResponse(err, deferred, msgs)) {
                $location.path('/' + ($scope.location.region_slug || $scope.location.region_uuid) + '/' +
                    $scope.location.slug);
            }
        });
    };

    /*
        Load an existing location to update
     */
    if ($routeParams.uuid) {
        var deferred = $q.defer();
        $scope.promise = deferred.promise;
        async.waterfall([
            function (cb) {
                apiService('locations').actions.find($routeParams.uuid, cb);
            },
            function (location, cb) {
                if (!location.hasOwnProperty('hidden')) {
                    location.hidden = false;
                }
                $scope.location = location;
                $scope.marker = {
                    lng: location.lonlat[0],
                    lat: location.lonlat[1]
                };
                $scope.formTitle = 'Edit "' + location.title + '"';
                apiService('locations/' + location.uuid + '/photos').actions.all(cb);
            },
            function (photos, cb) {
                $scope.photos =  photos.result || photos;
                cb();
            },
            function (cb) {
                regionService.setCurrentRegion($scope.location.region_uuid, cb);
            },
            function (cb) {
                $scope.currentRegion = regionService.currentRegion.title;
                $scope.isAdmin = authService.hasScopes(['admin', 'region-' + $scope.location.region_uuid]);
                cb();
            }
        ], function (err) {
            responseHandler.handleResponse(err, deferred);
        });
    } else {
        //create a new location: ask user to use his geoloaction
        GeolocationService.getCurrentPosition().then(
            function (position) { //
                console.log('Position', position);
                $scope.marker.lat = position.coords.latitude;
                $scope.marker.lng = position.coords.longitude;
                $scope.updateLocation({'lat': position.coords.latitude, 'lng': position.coords.longitude});
            },
            function (errorCode) {
                if (errorCode === false) {
                    alert('GeoLocation is not supported by browser.');
                }
                else if (errorCode === 1) {
                    alert('User either denied GeoLocation or waited for long to respond.');
                }
            }
        );
    }
};

LocationsCreateController.$inject = ['$scope', '$routeParams', 'apiService', 'authService', '$q', '$location',
    'mapService', 'responseHandler', 'locationFormDefaults', 'regionService','GeolocationService'];

module.exports = LocationsCreateController;<|endoftext|>


<commit_before>/*! HTML5 Notification - v3.0.0 - 2016-09-19

Copyright 2016 Tsvetan Tsvetkov

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.
*/

/** @namespace window */
/** @namespace window.webkitNotifications */
/** @namespace window.external */
(function() {
    /*
     Safari native methods required for Notifications do NOT run in strict mode.
     */
    //'use strict';
    // local variables
    var PERMISSION_DEFAULT = "default";
    // The user decision is unknown; in this case the application will act as if permission was denied.
    var PERMISSION_GRANTED = "granted";
    // The user has explicitly granted permission for the current origin to display system notifications.
    var PERMISSION_DENIED = "denied";
    // The user has explicitly denied permission for the current origin to display system notifications.
    var PERMISSION_NOTSUPPORTED = "notsupported";
    // The Notification API is not supported on current environment
    // map for the old permission values
    var PERMISSIONS = [ PERMISSION_GRANTED, PERMISSION_DEFAULT, PERMISSION_DENIED, PERMISSION_NOTSUPPORTED ];
    var DIRESCTIONS = [ "auto", "ltr", "rtl" ];
    /*
        IE does not support Notifications in the same meaning as other modern browsers.
        On the other side, IE9+(except MS Edge) implement flashing pinned site taskbar buttons.
        Each time new IE Notification is create, previous flashing and icon overlay is cleared.
        So, we need to keep track of the notification that calls close method.
     */
    var IENotificationIndex = -1;
    var IECloseNotificationEvents = [ "click", "scroll", "focus" ];
    var getIco = function(icon) {
        var lastIndex = icon.lastIndexOf(".");
        return (lastIndex !== -1 ? icon.substr(0, lastIndex) : icon) + ".ico";
    };
    /*
     * Internal Notificaiton constructor. Keeps the original Notification
     * constructor if any or use empty function constructor for browsers
     * that do not support Notifications
     */
    var _Notification = window.Notification || /* Opera Mobile/Android Browser */
    window.webkitNotifications && WebKitNotification || /* IE9+ pinned site */
    "external" in window && "msIsSiteMode" in window.external && window.external.msIsSiteMode() !== undefined && IENotification || /* Notifications Not supported. Return dummy constructor */
    DummyNotification;
    /**
     * @constructor DummyNotification
     */
    function DummyNotification() {
        var dummyElement = document.createElement("div");
        this.addEventListener = function(eventName, callback) {
            dummyElement.addEventListener(eventName, callback.bind(this));
        };
        this.removeEventListener = function(eventName, callback) {
            dummyElement.removeEventListener(eventName, callback.bind(this));
        };
        this.dispatchEvent = function(eventName) {
            if (typeof eventName !== "string") {
                return;
            }
            try {
                dummyElement.dispatchEvent(new Event(eventName));
            } catch (e) {
                var event = document.createEvent("Event");
                event.initEvent(eventName, false, true);
                dummyElement.dispatchEvent(event);
            }
        };
    }
    Object.defineProperty(DummyNotification, "permission", {
        enumerable: true,
        get: function() {
            return PERMISSION_NOTSUPPORTED;
        }
    });
    Object.defineProperty(DummyNotification, "requestPermission", {
        enumerable: true,
        writable: true,
        value: function(callback) {
            callback(this.permission);
        }
    });
    /**
     * @constructor IENotification
     */
    function IENotification(title, options) {
        DummyNotification.call(this);
        var notificationIndex = IENotificationIndex;
        Object.defineProperties(this, {
            close: {
                value: function(event) {
                    if (notificationIndex === IENotificationIndex) {
                        window.external.msSiteModeClearIconOverlay();
                        // Remove close events
                        IECloseNotificationEvents.forEach(function(event) {
                            window.removeEventListener(event, this.close);
                        }.bind(this));
                        this.dispatchEvent("click");
                        this.dispatchEvent("close");
                        notificationIndex = null;
                    }
                }.bind(this)
            }
        });
        // Clear any previous icon overlay
        this.close();
        // Set icon
        if (this.icon) {
            window.external.msSiteModeSetIconOverlay(getIco(this.icon), this.description || this.title);
        }
        // Blink icon
        window.external.msSiteModeActivate();
        // Trigger show event
        this.dispatchEvent("show");
        // Attach close event to window
        IECloseNotificationEvents.forEach(function(event) {
            window.addEventListener(event, this.close);
        }.bind(this));
        // Increment notification index
        notificationIndex = ++IENotificationIndex;
    }
    Object.defineProperty(IENotification, "permission", {
        enumerable: true,
        get: function() {
            var isTabPinned = window.external.msIsSiteMode();
            return isTabPinned ? PERMISSION_GRANTED : PERMISSION_DENIED;
        }
    });
    Object.defineProperty(IENotification, "requestPermission", {
        enumerable: true,
        writable: true,
        value: function(callback) {
            return new Promise(function(resolve, reject) {
                if (this.permission === PERMISSION_DENIED) {
                    alert(this.PERMISSION_REQUEST_MESSAGE);
                }
                resolve(this.permission);
            }.bind(this));
        }
    });
    Object.defineProperty(IENotification, "PERMISSION_REQUEST_MESSAGE", {
        writable: true,
        value: "IE supports notifications in pinned mode only. Pin this page on your taskbar to receive notifications."
    });
    /**
     * @constructor WebKitNotification
     */
    function WebKitNotification() {}
    Object.defineProperty(WebKitNotification, "permission", {
        enumerable: true,
        get: function() {
            return PERMISSIONS[window.webkitNotifications.checkPermission()];
        }
    });
    Object.defineProperty(WebKitNotification, "requestPermission", {
        enumerable: true,
        writable: true,
        value: function(callback) {
            return new Promise(function(resolve, reject) {
                window.webkitNotifications.requestPermission(function(permission) {
                    resolve(permission);
                });
            });
        }
    });
    /*
        [Safari] Safari6 do not support Notification.permission.
        Instead, it support Notification.permissionLevel()
     */
    if (!_Notification.permission) {
        Object.defineProperty(_Notification, "permission", {
            enumerable: true,
            get: function() {
                return _Notification.permissionLevel && _Notification.permissionLevel();
            }
        });
    }
    /**
     * @constructor Notification
     */
    function Notification(title, options) {
        var dir;
        var notification;
        if (!arguments.length) {
            throw TypeError('Failed to construct "Notification": 1 argument required, but only 0 present.');
        }
        /*
            Chrome display notifications when title is empty screen, but
            Safari do NOT.

            Set title to non-display characted in order to display notifications
            in Safari as well when title is empty.
         */
        if (title === "") {
            title = "\b";
        }
        if (arguments.length > 1 && "object" !== typeof options) {
            throw TypeError('Failed to construct "Notification": parameter 2 ("options") is not an object.');
        }
        dir = Object(options).dir;
        if (dir !== undefined && DIRESCTIONS.indexOf(dir) === -1) {
            throw TypeError('Failed to construct "Notification": The provided value "' + dir + '" is not a valid enum value of type NotificationDirection.');
        }
        options = Object(options);
        notification = new _Notification(title, options);
        /* TODO: actions property */
        /* TODO: badge property */
        if (!notification.body) {
            Object.defineProperty(notification, "body", {
                value: String(options.body || "")
            });
        }
        if (!notification.data) {
            Object.defineProperty(notification, "data", {
                value: options.data || null
            });
        }
        if (!notification.dir) {
            Object.defineProperty(notification, "dir", {
                value: dir || DIRESCTIONS[0]
            });
        }
        if (!notification.icon) {
            Object.defineProperty(notification, "icon", {
                value: String(options.icon || "")
            });
        }
        if (!notification.lang) {
            Object.defineProperty(notification, "lang", {
                value: String(options.lang || "")
            });
        }
        /* TODO: noscreen property */
        /* TODO: renotify property */
        if (!notification.requireInteraction) {
            Object.defineProperty(notification, "requireInteraction", {
                value: Boolean(options.requireInteraction)
            });
        }
        /* TODO: sound property */
        if (!notification.silent) {
            Object.defineProperty(notification, "silent", {
                value: Boolean(options.silent)
            });
        }
        if (!notification.tag) {
            Object.defineProperty(notification, "tag", {
                value: String(options.tag || "")
            });
        }
        if (!notification.title) {
            Object.defineProperty(notification, "title", {
                value: String(title)
            });
        }
        if (!notification.timestamp) {
            Object.defineProperty(notification, "timestamp", {
                value: new Date().getTime()
            });
        }
        /* TODO: vibrate property */
        return notification;
    }
    Object.defineProperty(Notification, "permission", {
        enumerable: true,
        get: function() {
            return _Notification.permission;
        }
    });
    /*
        Notification.requestPermission should return a Promise(by spec).
        Keep the original method and replace it with a custom one that
        checks if the call of Notification.requestPermission returns a promise
        and if not, then return a custom object that simulates the Promise object.

        Specification:
        Notification.requestPermission().then(callback);

        Old Spec:
        Notification.requestPermission(callback);
     */
    Object.defineProperty(Notification, "requestPermission", {
        enumerable: true,
        value: function() {
            return new Promise(function(resolve, reject) {
                var promise = _Notification.requestPermission(function(permission) {
                    resolve(permission);
                });
                if (!(promise instanceof Promise)) {
                    return;
                }
                resolve(promise);
            });
        }
    });
    window.Notification = Notification;
})();

/**
 * @name      ElkArte Forum
 * @copyright ElkArte Forum contributors
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
 *
 * @version 1.1 Release Candidate 2
 *
 * This bits acts as middle-man between the notify (above) and the ElkNotifications
 * providing the interface required by the latter.
 */

(function() {
	var ElkDesktop = (function(opt) {
		'use strict';
		opt = (opt) ? opt : {};
		var notif,
			alreadyChecked = false,
			alreadyAsked = false,
			permission_granted = false;

		var init = function(opt) {

			notif = Notification;
		};

		var send = function(request) {
			if (request.desktop_notifications.new_from_last > 0)
			{
				if (!hasPermissions())
					return;

				var myNotification = new Notification(request.desktop_notifications.title, {
					body: request.desktop_notifications.message,
					icon: opt.icon
				});
			}
		};

		var hasPermissions = function() {
			if (alreadyChecked)
				return permission_granted;

			if (alreadyAsked === false && notif.permission === "default" && notif.permission !== "notsupported") {
				notif.requestPermission();
				alreadyAsked = true;
			}

			permission_granted = notif.permission === "granted";
			alreadyChecked = notif.permission !== "default";

			return permission_granted;
		};

		init(opt);
		return {
			send: send
		};
	});

	// AMD / RequireJS
	if ( typeof define !== 'undefined' && define.amd) {
		define([], function() {
			return ElkDesktop;
		});
	}
	// CommonJS
	else if ( typeof module !== 'undefined' && module.exports) {
		module.exports = ElkDesktop;
	}
	// included directly via <script> tag
	else {
		this.ElkDesktop = ElkDesktop;
	}

})();

<commit_msg>! remove whitespace + some formatting.<commit_after>/*! HTML5 Notification - v3.0.0 - 2016-09-19

Copyright 2016 Tsvetan Tsvetkov

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.
*/

/** @namespace window */
/** @namespace window.webkitNotifications */
/** @namespace window.external */
(function() {
    /*
     Safari native methods required for Notifications do NOT run in strict mode.
     */
    //'use strict';
    // local variables
    var PERMISSION_DEFAULT = "default";
    // The user decision is unknown; in this case the application will act as if permission was denied.
    var PERMISSION_GRANTED = "granted";
    // The user has explicitly granted permission for the current origin to display system notifications.
    var PERMISSION_DENIED = "denied";
    // The user has explicitly denied permission for the current origin to display system notifications.
    var PERMISSION_NOTSUPPORTED = "notsupported";
    // The Notification API is not supported on current environment
    // map for the old permission values
    var PERMISSIONS = [ PERMISSION_GRANTED, PERMISSION_DEFAULT, PERMISSION_DENIED, PERMISSION_NOTSUPPORTED ];
    var DIRESCTIONS = [ "auto", "ltr", "rtl" ];
    /*
        IE does not support Notifications in the same meaning as other modern browsers.
        On the other side, IE9+(except MS Edge) implement flashing pinned site taskbar buttons.
        Each time new IE Notification is create, previous flashing and icon overlay is cleared.
        So, we need to keep track of the notification that calls close method.
     */
    var IENotificationIndex = -1;
    var IECloseNotificationEvents = [ "click", "scroll", "focus" ];
    var getIco = function(icon) {
        var lastIndex = icon.lastIndexOf(".");
        return (lastIndex !== -1 ? icon.substr(0, lastIndex) : icon) + ".ico";
    };
    /*
     * Internal Notificaiton constructor. Keeps the original Notification
     * constructor if any or use empty function constructor for browsers
     * that do not support Notifications
     */
    var _Notification = window.Notification || /* Opera Mobile/Android Browser */
    window.webkitNotifications && WebKitNotification || /* IE9+ pinned site */
    "external" in window && "msIsSiteMode" in window.external && window.external.msIsSiteMode() !== undefined && IENotification || /* Notifications Not supported. Return dummy constructor */
    DummyNotification;
    /**
     * @constructor DummyNotification
     */
    function DummyNotification() {
        var dummyElement = document.createElement("div");
        this.addEventListener = function(eventName, callback) {
            dummyElement.addEventListener(eventName, callback.bind(this));
        };
        this.removeEventListener = function(eventName, callback) {
            dummyElement.removeEventListener(eventName, callback.bind(this));
        };
        this.dispatchEvent = function(eventName) {
            if (typeof eventName !== "string") {
                return;
            }
            try {
                dummyElement.dispatchEvent(new Event(eventName));
            } catch (e) {
                var event = document.createEvent("Event");
                event.initEvent(eventName, false, true);
                dummyElement.dispatchEvent(event);
            }
        };
    }
    Object.defineProperty(DummyNotification, "permission", {
        enumerable: true,
        get: function() {
            return PERMISSION_NOTSUPPORTED;
        }
    });
    Object.defineProperty(DummyNotification, "requestPermission", {
        enumerable: true,
        writable: true,
        value: function(callback) {
            callback(this.permission);
        }
    });
    /**
     * @constructor IENotification
     */
    function IENotification(title, options) {
        DummyNotification.call(this);
        var notificationIndex = IENotificationIndex;
        Object.defineProperties(this, {
            close: {
                value: function(event) {
                    if (notificationIndex === IENotificationIndex) {
                        window.external.msSiteModeClearIconOverlay();
                        // Remove close events
                        IECloseNotificationEvents.forEach(function(event) {
                            window.removeEventListener(event, this.close);
                        }.bind(this));
                        this.dispatchEvent("click");
                        this.dispatchEvent("close");
                        notificationIndex = null;
                    }
                }.bind(this)
            }
        });
        // Clear any previous icon overlay
        this.close();
        // Set icon
        if (this.icon) {
            window.external.msSiteModeSetIconOverlay(getIco(this.icon), this.description || this.title);
        }
        // Blink icon
        window.external.msSiteModeActivate();
        // Trigger show event
        this.dispatchEvent("show");
        // Attach close event to window
        IECloseNotificationEvents.forEach(function(event) {
            window.addEventListener(event, this.close);
        }.bind(this));
        // Increment notification index
        notificationIndex = ++IENotificationIndex;
    }
    Object.defineProperty(IENotification, "permission", {
        enumerable: true,
        get: function() {
            var isTabPinned = window.external.msIsSiteMode();
            return isTabPinned ? PERMISSION_GRANTED : PERMISSION_DENIED;
        }
    });
    Object.defineProperty(IENotification, "requestPermission", {
        enumerable: true,
        writable: true,
        value: function(callback) {
            return new Promise(function(resolve, reject) {
                if (this.permission === PERMISSION_DENIED) {
                    alert(this.PERMISSION_REQUEST_MESSAGE);
                }
                resolve(this.permission);
            }.bind(this));
        }
    });
    Object.defineProperty(IENotification, "PERMISSION_REQUEST_MESSAGE", {
        writable: true,
        value: "IE supports notifications in pinned mode only. Pin this page on your taskbar to receive notifications."
    });
    /**
     * @constructor WebKitNotification
     */
    function WebKitNotification() {}
    Object.defineProperty(WebKitNotification, "permission", {
        enumerable: true,
        get: function() {
            return PERMISSIONS[window.webkitNotifications.checkPermission()];
        }
    });
    Object.defineProperty(WebKitNotification, "requestPermission", {
        enumerable: true,
        writable: true,
        value: function(callback) {
            return new Promise(function(resolve, reject) {
                window.webkitNotifications.requestPermission(function(permission) {
                    resolve(permission);
                });
            });
        }
    });
    /*
        [Safari] Safari6 do not support Notification.permission.
        Instead, it support Notification.permissionLevel()
     */
    if (!_Notification.permission) {
        Object.defineProperty(_Notification, "permission", {
            enumerable: true,
            get: function() {
                return _Notification.permissionLevel && _Notification.permissionLevel();
            }
        });
    }
    /**
     * @constructor Notification
     */
    function Notification(title, options) {
        var dir;
        var notification;
        if (!arguments.length) {
            throw TypeError('Failed to construct "Notification": 1 argument required, but only 0 present.');
        }
        /*
            Chrome display notifications when title is empty screen, but
            Safari do NOT.

            Set title to non-display characted in order to display notifications
            in Safari as well when title is empty.
         */
        if (title === "") {
            title = "\b";
        }
        if (arguments.length > 1 && "object" !== typeof options) {
            throw TypeError('Failed to construct "Notification": parameter 2 ("options") is not an object.');
        }
        dir = Object(options).dir;
        if (dir !== undefined && DIRESCTIONS.indexOf(dir) === -1) {
            throw TypeError('Failed to construct "Notification": The provided value "' + dir + '" is not a valid enum value of type NotificationDirection.');
        }
        options = Object(options);
        notification = new _Notification(title, options);
        /* TODO: actions property */
        /* TODO: badge property */
        if (!notification.body) {
            Object.defineProperty(notification, "body", {
                value: String(options.body || "")
            });
        }
        if (!notification.data) {
            Object.defineProperty(notification, "data", {
                value: options.data || null
            });
        }
        if (!notification.dir) {
            Object.defineProperty(notification, "dir", {
                value: dir || DIRESCTIONS[0]
            });
        }
        if (!notification.icon) {
            Object.defineProperty(notification, "icon", {
                value: String(options.icon || "")
            });
        }
        if (!notification.lang) {
            Object.defineProperty(notification, "lang", {
                value: String(options.lang || "")
            });
        }
        /* TODO: noscreen property */
        /* TODO: renotify property */
        if (!notification.requireInteraction) {
            Object.defineProperty(notification, "requireInteraction", {
                value: Boolean(options.requireInteraction)
            });
        }
        /* TODO: sound property */
        if (!notification.silent) {
            Object.defineProperty(notification, "silent", {
                value: Boolean(options.silent)
            });
        }
        if (!notification.tag) {
            Object.defineProperty(notification, "tag", {
                value: String(options.tag || "")
            });
        }
        if (!notification.title) {
            Object.defineProperty(notification, "title", {
                value: String(title)
            });
        }
        if (!notification.timestamp) {
            Object.defineProperty(notification, "timestamp", {
                value: new Date().getTime()
            });
        }
        /* TODO: vibrate property */
        return notification;
    }
    Object.defineProperty(Notification, "permission", {
        enumerable: true,
        get: function() {
            return _Notification.permission;
        }
    });
    /*
        Notification.requestPermission should return a Promise(by spec).
        Keep the original method and replace it with a custom one that
        checks if the call of Notification.requestPermission returns a promise
        and if not, then return a custom object that simulates the Promise object.

        Specification:
        Notification.requestPermission().then(callback);

        Old Spec:
        Notification.requestPermission(callback);
     */
    Object.defineProperty(Notification, "requestPermission", {
        enumerable: true,
        value: function() {
            return new Promise(function(resolve, reject) {
                var promise = _Notification.requestPermission(function(permission) {
                    resolve(permission);
                });
                if (!(promise instanceof Promise)) {
                    return;
                }
                resolve(promise);
            });
        }
    });
    window.Notification = Notification;
})();

/*!
 * @name      ElkArte Forum
 * @copyright ElkArte Forum contributors
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
 *
 * @version 1.1 Release Candidate 2
 *
 * This bits acts as middle-man between the notify (above) and the ElkNotifications
 * providing the interface required by the latter.
 */

(function () {
	var ElkDesktop = (function (opt) {
		'use strict';
		opt = (opt) ? opt : {};
		var notif,
			alreadyChecked = false,
			alreadyAsked = false,
			permission_granted = false;

		var init = function (opt) {
			notif = Notification;
		};

		var send = function (request) {
			if (request.desktop_notifications.new_from_last > 0) {
				if (!hasPermissions())
					return;

				var myNotification = new Notification(request.desktop_notifications.title, {
					body: request.desktop_notifications.message,
					icon: opt.icon
				});
			}
		};

		var hasPermissions = function () {
			if (alreadyChecked)
				return permission_granted;

			if (alreadyAsked === false && notif.permission === "default" && notif.permission !== "notsupported") {
				notif.requestPermission();
				alreadyAsked = true;
			}

			permission_granted = notif.permission === "granted";
			alreadyChecked = notif.permission !== "default";

			return permission_granted;
		};

		init(opt);
		return {
			send: send
		};
	});

	// AMD / RequireJS
	if (typeof define !== 'undefined' && define.amd) {
		define([], function () {
			return ElkDesktop;
		});
	}
	// CommonJS
	else if (typeof module !== 'undefined' && module.exports) {
		module.exports = ElkDesktop;
	}
	// included directly via <script> tag
	else {
		this.ElkDesktop = ElkDesktop;
	}
})();
<|endoftext|>


<commit_before>'use strict';

var async = require('async');

var LocationsCreateController = function ($scope, $routeParams, apiService, authService, $q, $location, mapService,
                                          responseHandler, locationFormDefaults, regionService) {
    var changeTimer, lockUpdate;
    $scope.location = {};
    $scope.assets = {
        file: null
    };
    $scope.marker = {
        lat: null,
        lng: null
    };
    $scope._sys = locationFormDefaults;

    $scope.updateLocation = function (latlon) {
        $scope.marker = latlon;

        mapService.reverseGeoCode(latlon.lat,latlon.lng,function(err, data){
            if (err) {
                throw err;
            }
            lockUpdate = true;
            if (!data.error) {
                var address = mapService.createAddressFromGeo(data.address);
                if (address) {
                    $scope.location.street = address.street;
                    $scope.location.city = address.city;
                    $scope.location.postcode = address.postcode;
                    $scope.location.country = address.country;
                }
            }
        });
        $scope.showLatLng = false;
    };

    $scope.$watchGroup(['location.street', 'location.city', 'location.postcode'], function (newValues, oldValues) {
        var changed = false,
            keys = Object.keys(newValues);

        if (lockUpdate) {
            lockUpdate = false;
            return;
        }

        for (var i = 0; i < keys.length; i += 1) {
            if (newValues[keys[i]] !== oldValues[keys[i]]) {
                changed = true;
            }
        }

        if (changed) {
            if (changeTimer) {
                window.clearTimeout(changeTimer);
                changeTimer = null;
            }
            changeTimer = window.setTimeout(function () {
                mapService.geoCode({
                    street: $scope.location.street,
                    city: $scope.location.city,
                    postcode: $scope.location.postcode,
                    country: $scope.location.country
                }, function (err, data) {
                    if (Array.isArray(data) && data.length > 0) {
                        $scope.marker.lat = data[0].lat;
                        $scope.marker.lng = data[0].lon;
                        $scope.location.display_name = data[0].display_name || "";
                    }
                });
            },1000);
        }
    });

    $scope.submit = function () {
        var deferred = $q.defer(),
            payload = $scope.location;
        $scope.promise = deferred.promise;
        payload.lonlat = [$scope.marker.lng, $scope.marker.lat];
        var isUpdate = false;
        async.waterfall([
            function (cb) {
                apiService('regions?lat=' + $scope.marker.lat + '&lon=' + $scope.marker.lng).actions.all(cb);
            },
            function (region, cb) {
                if (region && region.length > 0) {
                    if (!payload.hasOwnProperty('region_uuid')) {
                        payload.region_uuid = region[0].uuid;
                    }
                    if (payload.uuid) {
                        isUpdate = true;
                        apiService('locations').actions.update(payload.uuid, payload, cb);
                    } else {
                        apiService('locations').actions.create(payload, cb);
                    }
                } else {
                    cb(new Error('errors.regions.fetch_failed'),null);
                }
            },
            function (location, cb) {
                if (!isUpdate) {
                    $scope.location.uuid = location.uuid;
                }
                $scope.location.slug = location.slug;
                if ($scope.files && $scope.files.length) {
                    async.mapSeries($scope.files, function (file, cb) {
                        apiService('photos').actions.upload({
                            file: file,
                            fields: {location_uuid: location.uuid}
                        }, cb);
                    }, cb);
                } else {
                    cb();
                }
            }
        ], function (err) {
            var msgs = {
                success: $routeParams.uuid ? 'messages.locations.update_success' : 'messages.locations.create_success'
            };
            if (responseHandler.handleResponse(err, deferred, msgs)) {
                $location.path('/' + ($scope.location.region_slug || $scope.location.region_uuid) + '/' +
                    $scope.location.slug);
            }
        });
    };

    if ($routeParams.uuid) {
        var deferred = $q.defer();
        $scope.promise = deferred.promise;
        async.waterfall([
            function (cb) {
                apiService('locations').actions.find($routeParams.uuid, cb);
            },
            function (location, cb) {
                if (!location.hasOwnProperty('hidden')) {
                    location.hidden = false;
                }
                $scope.location = location;
                $scope.marker = {
                    lng: location.lonlat[0],
                    lat: location.lonlat[1]
                };
                $scope.formTitle = 'Edit "' + location.title + '"';
                apiService('locations/' + location.uuid + '/photos').actions.all(cb);
            },
            function (photos, cb) {
                $scope.photos = photos.results;
                cb();
            },
            function (cb) {
                regionService.setCurrentRegion($scope.location.region_uuid, cb);
            },
            function (cb) {
                $scope.currentRegion = regionService.currentRegion.title;
                $scope.isAdmin = authService.hasScopes(['admin', 'region-' + $scope.location.region_uuid]);
                cb();
            }
        ], function (err) {
            responseHandler.handleResponse(err, deferred);
        });
    }
};

LocationsCreateController.$inject = ['$scope', '$routeParams', 'apiService', 'authService', '$q', '$location',
    'mapService', 'responseHandler', 'locationFormDefaults', 'regionService',];

module.exports = LocationsCreateController;<commit_msg>add some comments<commit_after>'use strict';

var async = require('async');

var LocationsCreateController = function ($scope, $routeParams, apiService, authService, $q, $location, mapService,
                                          responseHandler, locationFormDefaults, regionService) {
    var changeTimer, lockUpdate;
    $scope.location = {};
    $scope.assets = {
        file: null
    };
    $scope.marker = {
        lat: null,
        lng: null
    };
    $scope._sys = locationFormDefaults;

    /*
        Update the map to a new geolocation
        Also retrieves the new address
    */
    $scope.updateLocation = function (latlon) {
        $scope.marker = latlon;

        mapService.reverseGeoCode(latlon.lat,latlon.lng,function(err, data){
            if (err) {
                throw err;
            }
            lockUpdate = true;
            if (!data.error) {
                var address = mapService.createAddressFromGeo(data.address);
                if (address) {
                    $scope.location.street = address.street;
                    $scope.location.city = address.city;
                    $scope.location.postcode = address.postcode;
                    $scope.location.country = address.country;
                }
            }
        });
        $scope.showLatLng = false;
    };

    /*
        Watches the address fields for updates
        Updates the map on address changes
     */
    $scope.$watchGroup(['location.street', 'location.city', 'location.postcode'], function (newValues, oldValues) {
        var changed = false,
            keys = Object.keys(newValues);

        if (lockUpdate) {
            lockUpdate = false;
            return;
        }

        for (var i = 0; i < keys.length; i += 1) {
            if (newValues[keys[i]] !== oldValues[keys[i]]) {
                changed = true;
            }
        }

        if (changed) {
            if (changeTimer) {
                window.clearTimeout(changeTimer);
                changeTimer = null;
            }
            changeTimer = window.setTimeout(function () {
                mapService.geoCode({
                    street: $scope.location.street,
                    city: $scope.location.city,
                    postcode: $scope.location.postcode,
                    country: $scope.location.country
                }, function (err, data) {
                    if (Array.isArray(data) && data.length > 0) {
                        $scope.marker.lat = data[0].lat;
                        $scope.marker.lng = data[0].lon;
                        $scope.location.display_name = data[0].display_name || "";
                    }
                });
            },1000);
        }
    });

    /*
        Submit the new location
     */
    $scope.submit = function () {
        var deferred = $q.defer(),
            payload = $scope.location;
        $scope.promise = deferred.promise;
        payload.lonlat = [$scope.marker.lng, $scope.marker.lat];
        var isUpdate = false;
        async.waterfall([
            function (cb) {
                /*
                    Fetch the nearest region
                 */
                // TODO: this must be limited through region admins!
                apiService('regions?lat=' + $scope.marker.lat + '&lon=' + $scope.marker.lng).actions.all(cb);
            },
            function (region, cb) {
                if (region && region.length > 0) {
                    if (!payload.hasOwnProperty('region_uuid')) {
                        payload.region_uuid = region[0].uuid;
                    }
                    /*
                        Are we updating or creating?
                     */
                    if (payload.uuid) {
                        isUpdate = true;
                        apiService('locations').actions.update(payload.uuid, payload, cb);
                    } else {
                        apiService('locations').actions.create(payload, cb);
                    }
                } else {
                    cb(new Error('errors.regions.fetch_failed'),null);
                }
            },
            function (location, cb) {
                if (!isUpdate) {
                    $scope.location.uuid = location.uuid;
                }
                $scope.location.slug = location.slug;
                /*
                    Upload the associated photos
                 */
                if ($scope.files && $scope.files.length) {
                    async.mapSeries($scope.files, function (file, cb) {
                        apiService('photos').actions.upload({
                            file: file,
                            fields: {location_uuid: location.uuid}
                        }, cb);
                    }, cb);
                } else {
                    cb();
                }
            }
        ], function (err) {
            var msgs = {
                success: $routeParams.uuid ? 'messages.locations.update_success' : 'messages.locations.create_success'
            };
            if (responseHandler.handleResponse(err, deferred, msgs)) {
                $location.path('/' + ($scope.location.region_slug || $scope.location.region_uuid) + '/' +
                    $scope.location.slug);
            }
        });
    };

    /*
        Load an existing location to update
     */
    if ($routeParams.uuid) {
        var deferred = $q.defer();
        $scope.promise = deferred.promise;
        async.waterfall([
            function (cb) {
                apiService('locations').actions.find($routeParams.uuid, cb);
            },
            function (location, cb) {
                if (!location.hasOwnProperty('hidden')) {
                    location.hidden = false;
                }
                $scope.location = location;
                $scope.marker = {
                    lng: location.lonlat[0],
                    lat: location.lonlat[1]
                };
                $scope.formTitle = 'Edit "' + location.title + '"';
                apiService('locations/' + location.uuid + '/photos').actions.all(cb);
            },
            function (photos, cb) {
                $scope.photos = photos.results;
                cb();
            },
            function (cb) {
                regionService.setCurrentRegion($scope.location.region_uuid, cb);
            },
            function (cb) {
                $scope.currentRegion = regionService.currentRegion.title;
                $scope.isAdmin = authService.hasScopes(['admin', 'region-' + $scope.location.region_uuid]);
                cb();
            }
        ], function (err) {
            responseHandler.handleResponse(err, deferred);
        });
    }
};

LocationsCreateController.$inject = ['$scope', '$routeParams', 'apiService', 'authService', '$q', '$location',
    'mapService', 'responseHandler', 'locationFormDefaults', 'regionService',];

module.exports = LocationsCreateController;<|endoftext|>


<commit_before>// Load in dependencies
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var url = require('url');
var util = require('util');

var fwd = require('fwd');
var FormData = require('form-data');
var extend = require('obj-extend');
var request = require('request');
var sax = require('sax');
var readableStream = require('readable-stream');

// TODO: Move to duplex where we emit `data` containing aneverything just before complete
var Writable = readableStream.Writable;
var Readable = readableStream.Readable;
var WritableEvents = [
  'drain',
  'close',
  'finish',
  'pipe',
  'unpipe'
];

// Create XmlToErrorStream
function XmlToErrorStream(options) {
  Writable.call(this, options);
  var xmlParser = sax.createStream();
  this.xmlParser = xmlParser;

  // Set up listeners for validity OR error info
  var that = this;
  xmlParser.on('text', function (text) {
    var node = this._parser.tag;
    var nameNS = node.name.toLowerCase();
    if (nameNS === 'm:validity') {
      that.emit('validity', text === 'true');
    } else if (that.validationErr) {
      var name = nameNS.replace('m:', '');
      that.validationErr[name] = text;
    }
  });

  // Set up listeners to open/close new errors/warnings
  xmlParser.on('opentag', function (node) {
    switch (node.name.toLowerCase()) {
      case 'm:error':
      case 'm:warning':
        that.validationErr = {};
        break;
    }
  });
  xmlParser.on('closetag', function (name) {
    var node = this._parser.tag;
    switch (node.name.toLowerCase()) {
      case 'm:error':
        that.emit('validation-error', that.validationErr);
        that.validationErr = null;
        break;
      case 'm:warning':
        that.emit('validation-warning', that.validationErr);
        that.validationErr = null;
        break;
      case 'env:envelope':
        // DEV: We could move this to a duplex stream but there is no collected data to push...
        // TODO: Move it to one anyway. With no data O_O
        that.emit('end');
        break;
    }
  });

  // Forward errors
  xmlParser.on('error', function (err) {
    that.emit('error', err);
  });
}
util.inherits(XmlToErrorStream, Writable);
extend(XmlToErrorStream.prototype, {
  _write: function (chunk, encoding, callback) {
    this.xmlParser.write(chunk);
    callback();
  }
});

function CssValidator(options) {
  // If options is a string, upcast it to an object
  if (typeof options === 'string') {
    var css = options;
    options = {text: css};
  }

  // Clone the data to prevent mutation
  options = extend({}, options);

  // Set a very high default highWaterMark (1MB) and inhherit from Writable
  var hwm = options.highWaterMark;
  options.highWaterMark = (hwm || hwm === 0) ? hwm : 1000 * 1024;
  Writable.call(this, options);

  // Grab the URL we are going to POST to
  var w3cUrl = options.w3cUrl || validateCss.w3cUrl;

  // Open the request
  var urlParts = url.parse(w3cUrl);
  var form = new FormData();
  urlParts.method = 'POST';
  urlParts.headers = form.getHeaders();
  var req = http.request(urlParts);

  // http://jigsaw.w3.org/css-validator/api.html
  form.append('output', 'soap12');
  Object.getOwnPropertyNames(options).forEach(function sendOption (key) {
    if (key !== 'output' || key !== 'text') {
      form.append(key, options[key]);
    }
  });

  // Create internal placeholder for CSS
  this._css = [];

  // If there was a uri, end the request
  if (options.uri) {
    this._usedUri = true;
    this.end();
  // Otherwise if there was text, write it and end us
  } else if (options.text) {
    this.write(options.text);
    this.end();
  }

  // When we have received all of our data
  // TODO: Use a streaming form library (unfortunately, there were none)
  var that = this;
  this.on('finish', function () {
    // If we did not use a URI, create our text for the form
    if (!that._usedUri) {
      form.append('text', that._css.join(''));
    }

    // Pipe the form and close the request
    form.pipe(req);
    req.end();
  });

  // Listen for errors from the request
  // TODO: Move this to a Readable with collection functionality from `validateCss`
  // Example: this.emit('data', result);
  req.on('error', function (err) {
    that.emit('error', err);
  });

  // Listen for the response
  req.on('response', function (response) {
    // Create a validator and pipe in the response
    var validator = new XmlToErrorStream();
    response.pipe(validator);

    // Forward events back to the that
    fwd(validator, that, function forwardEvents (event, data) {
      // If the message was anything Writable, drop it
      if (WritableEvents.indexOf(event) !== -1) {
        return null;
      }

      // Otherwise, forward normally
      return {
        event: event,
        data: data
      };
    });
  });
}
util.inherits(CssValidator, Writable);
extend(CssValidator.prototype, {
  // DEV: Nuke `pipe` method so `fwd` does not feature detect error-throwing `Writable.pipe`
  pipe: null,
  _write: function (chunk, encoding, callback) {
    this._css.push(chunk);
    callback();
  }
});

// Define our CSS validator
function validateCss(options, cb) {
  // Create an emitter
  var validator = new CssValidator(options);

  if (cb) {
    // Callback with any errors
    validator.on('error', cb);

    // Create placeholder for data
    var validationErrors = [];
    var validationWarnings = [];
    var result = {
      validity: false,
      errors: validationErrors,
      warnings: validationWarnings
    };

    // Collect information
    validator.on('validity', function (validity) {
      result.validity = validity;
    });
    validator.on('validation-error', function (err) {
      validationErrors.push(err);
    });
    validator.on('validation-warning', function (warning) {
      validationWarnings.push(warning);
    });

    // Callback when done
    validator.on('end', function () {
      cb(null, result);
    });
  } else {
    return validator;
  }
}

// Set up reference for default end point
// http://jigsaw.w3.org/css-validator/manual.html#api
validateCss.w3cUrl = 'http://jigsaw.w3.org/css-validator/validator';

// Expose XmlToErrorStream and CssValidator
validateCss.XmlToErrorStream = XmlToErrorStream;
validateCss.CssValidator = CssValidator;

// Export validateCss
module.exports = validateCss;<commit_msg>Starting to debug a lack of callback but it looks like rate limiting from W3C<commit_after>// Load in dependencies
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var url = require('url');
var util = require('util');

var fwd = require('fwd');
var FormData = require('form-data');
var extend = require('obj-extend');
var request = require('request');
var sax = require('sax');
var readableStream = require('readable-stream');

// TODO: Move to duplex where we emit `data` containing aneverything just before complete
var Writable = readableStream.Writable;
var Readable = readableStream.Readable;
var WritableEvents = [
  'drain',
  'close',
  'finish',
  'pipe',
  'unpipe'
];

// Create XmlToErrorStream
function XmlToErrorStream(options) {
  Writable.call(this, options);
  var xmlParser = sax.createStream();
  this.xmlParser = xmlParser;

  // Set up listeners for validity OR error info
  var that = this;
  xmlParser.on('text', function (text) {
    var node = this._parser.tag;
    var nameNS = node.name.toLowerCase();
    if (nameNS === 'm:validity') {
      that.emit('validity', text === 'true');
    } else if (that.validationErr) {
      var name = nameNS.replace('m:', '');
      that.validationErr[name] = text;
    }
  });

  // Set up listeners to open/close new errors/warnings
  xmlParser.on('opentag', function (node) {
    switch (node.name.toLowerCase()) {
      case 'm:error':
      case 'm:warning':
        that.validationErr = {};
        break;
    }
  });
  xmlParser.on('closetag', function (name) {
    var node = this._parser.tag;
    switch (node.name.toLowerCase()) {
      case 'm:error':
        that.emit('validation-error', that.validationErr);
        that.validationErr = null;
        break;
      case 'm:warning':
        that.emit('validation-warning', that.validationErr);
        that.validationErr = null;
        break;
      case 'env:envelope':
        console.log('3');
        // DEV: We could move this to a duplex stream but there is no collected data to push...
        // TODO: Move it to one anyway. With no data O_O
        that.emit('end');
        break;
    }
  });

  // Forward errors
  xmlParser.on('error', function (err) {
    that.emit('error', err);
  });
}
util.inherits(XmlToErrorStream, Writable);
extend(XmlToErrorStream.prototype, {
  _write: function (chunk, encoding, callback) {
    this.xmlParser.write(chunk);
    callback();
  }
});

// TODO: Break this function down...
function CssValidator(options) {
  // If options is a string, upcast it to an object
  if (typeof options === 'string') {
    var css = options;
    options = {text: css};
  }

  // Clone the data to prevent mutation
  options = extend({}, options);

  // Set a very high default highWaterMark (1MB) and inhherit from Writable
  var hwm = options.highWaterMark;
  options.highWaterMark = (hwm || hwm === 0) ? hwm : 1000 * 1024;
  Writable.call(this, options);

  // Grab the URL we are going to POST to
  var w3cUrl = options.w3cUrl || validateCss.w3cUrl;

  // Open the request
  var urlParts = url.parse(w3cUrl);
  var form = new FormData();
  urlParts.method = 'POST';
  urlParts.headers = form.getHeaders();
  var req = http.request(urlParts);

  // http://jigsaw.w3.org/css-validator/api.html
  form.append('output', 'soap12');
  Object.getOwnPropertyNames(options).forEach(function sendOption (key) {
    if (key !== 'output' || key !== 'text') {
      form.append(key, options[key]);
    }
  });

  // Create internal placeholder for CSS
  this._css = [];

  // If there was a uri, end the request
  if (options.uri) {
    this._usedUri = true;
    this.end();
  // Otherwise if there was text, write it and end us
  } else if (options.text) {
    this.write(options.text);
    this.end();
  }

  // When we have received all of our data
  // TODO: Use a streaming form library (unfortunately, there were none)
  var that = this;
  this.on('finish', function () {
    // If we did not use a URI, create our text for the form
    if (!that._usedUri) {
      form.append('text', that._css.join(''));
    }

    // Pipe the form and close the request
    console.log('1');
    form.pipe(req);
    req.end();
  });

  // Listen for errors from the request
  // TODO: Move this to a Readable with collection functionality from `validateCss`
  // Example: this.emit('data', result);
  req.on('error', function (err) {
    that.emit('error', err);
  });

  // Listen for the response
  req.on('response', function (response) {
    console.log('2');
    // Create a validator and pipe in the response
    var validator = new XmlToErrorStream();
    response.pipe(validator);

    // Forward events back to the that
    fwd(validator, that, function forwardEvents (event, data) {
      // If the message was anything Writable, drop it
      if (WritableEvents.indexOf(event) !== -1) {
        return null;
      }

      // Otherwise, forward normally
      return {
        event: event,
        data: data
      };
    });
  });
}
util.inherits(CssValidator, Writable);
extend(CssValidator.prototype, {
  // DEV: Nuke `pipe` method so `fwd` does not feature detect error-throwing `Writable.pipe`
  pipe: null,
  _write: function (chunk, encoding, callback) {
    this._css.push(chunk);
    callback();
  }
});

// Define our CSS validator
function validateCss(options, cb) {
  // Create an emitter
  var validator = new CssValidator(options);

  if (cb) {
    // Callback with any errors
    validator.on('error', cb);

    // Create placeholder for data
    var validationErrors = [];
    var validationWarnings = [];
    var result = {
      validity: false,
      errors: validationErrors,
      warnings: validationWarnings
    };

    // Collect information
    validator.on('validity', function (validity) {
      result.validity = validity;
    });
    validator.on('validation-error', function (err) {
      validationErrors.push(err);
    });
    validator.on('validation-warning', function (warning) {
      validationWarnings.push(warning);
    });

    // Callback when done
    validator.on('end', function () {
      cb(null, result);
    });
  } else {
    return validator;
  }
}

// Set up reference for default end point
// http://jigsaw.w3.org/css-validator/manual.html#api
validateCss.w3cUrl = 'http://jigsaw.w3.org/css-validator/validator';

// Expose XmlToErrorStream and CssValidator
validateCss.XmlToErrorStream = XmlToErrorStream;
validateCss.CssValidator = CssValidator;

// Export validateCss
module.exports = validateCss;<|endoftext|>