<commit_before>window.iD = function () {
    window.locale.en = iD.data.en;
    window.locale.current('en');

    var context = {},
        storage;

    // https://github.com/openstreetmap/iD/issues/772
    // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
    try { storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
    storage = storage || (function() {
        var s = {};
        return {
            getItem: function(k) { return s[k]; },
            setItem: function(k, v) { s[k] = v; },
            removeItem: function(k) { delete s[k]; }
        };
    })();

    context.storage = function(k, v) {
        try {
            if (arguments.length === 1) return storage.getItem(k);
            else if (v === null) storage.removeItem(k);
            else storage.setItem(k, v);
        } catch(e) {
            // localstorage quota exceeded
            /* eslint-disable no-console */
            if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
            /* eslint-enable no-console */
        }
    };

    /* Accessor for setting minimum zoom for editing features. */

    var minEditableZoom = 16;
    context.minEditableZoom = function(_) {
        if (!arguments.length) return minEditableZoom;
        minEditableZoom = _;
        connection.tileZoom(_);
        return context;
    };

    var history = iD.History(context),
        dispatch = d3.dispatch('enter', 'exit'),
        mode,
        container,
        ui = iD.ui(context),
        connection = iD.Connection(),
        locale = iD.detect().locale,
        localePath;

    if (locale && iD.data.locales.indexOf(locale) === -1) {
        locale = locale.split('-')[0];
    }

    context.preauth = function(options) {
        connection.switch(options);
        return context;
    };

    context.locale = function(loc, path) {
        locale = loc;
        localePath = path;

        // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
        if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
            iD.detect().locale = loc;
        }

        return context;
    };

    context.loadLocale = function(cb) {
        if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
            localePath = localePath || context.assetPath() + 'locales/' + locale + '.json';
            d3.json(localePath, function(err, result) {
                window.locale[locale] = result;
                window.locale.current(locale);
                cb();
            });
        } else {
            cb();
        }
    };

    /* Straight accessors. Avoid using these if you can. */
    context.ui = function() { return ui; };
    context.connection = function() { return connection; };
    context.history = function() { return history; };

    /* Connection */
    function entitiesLoaded(err, result) {
        if (!err) history.merge(result.data, result.extent);
    }

    context.loadTiles = function(projection, dimensions, callback) {
        function done(err, result) {
            entitiesLoaded(err, result);
            if (callback) callback(err, result);
        }
        connection.loadTiles(projection, dimensions, done);
    };

    context.loadEntity = function(id, callback) {
        function done(err, result) {
            entitiesLoaded(err, result);
            if (callback) callback(err, result);
        }
        connection.loadEntity(id, done);
    };

    context.zoomToEntity = function(id, zoomTo) {
        if (zoomTo !== false) {
            this.loadEntity(id, function(err, result) {
                if (err) return;
                var entity = _.find(result.data, function(e) { return e.id === id; });
                if (entity) { map.zoomTo(entity); }
            });
        }

        map.on('drawn.zoomToEntity', function() {
            if (!context.hasEntity(id)) return;
            map.on('drawn.zoomToEntity', null);
            context.on('enter.zoomToEntity', null);
            context.enter(iD.modes.Select(context, [id]));
        });

        context.on('enter.zoomToEntity', function() {
            if (mode.id !== 'browse') {
                map.on('drawn.zoomToEntity', null);
                context.on('enter.zoomToEntity', null);
            }
        });
    };

    /* History */
    context.graph = history.graph;
    context.changes = history.changes;
    context.intersects = history.intersects;

    var inIntro = false;

    context.inIntro = function(_) {
        if (!arguments.length) return inIntro;
        inIntro = _;
        return context;
    };

    context.save = function() {
        if (inIntro || (mode && mode.id === 'save')) return;
        history.save();
        if (history.hasChanges()) return t('save.unsaved_changes');
    };

    context.flush = function() {
        connection.flush();
        features.reset();
        history.reset();
        return context;
    };

    // Debounce save, since it's a synchronous localStorage write,
    // and history changes can happen frequently (e.g. when dragging).
    var debouncedSave = _.debounce(context.save, 350);
    function withDebouncedSave(fn) {
        return function() {
            var result = fn.apply(history, arguments);
            debouncedSave();
            return result;
        };
    }

    context.perform = withDebouncedSave(history.perform);
    context.replace = withDebouncedSave(history.replace);
    context.pop = withDebouncedSave(history.pop);
    context.overwrite = withDebouncedSave(history.overwrite);
    context.undo = withDebouncedSave(history.undo);
    context.redo = withDebouncedSave(history.redo);

    /* Graph */
    context.hasEntity = function(id) {
        return history.graph().hasEntity(id);
    };

    context.entity = function(id) {
        return history.graph().entity(id);
    };

    context.childNodes = function(way) {
        return history.graph().childNodes(way);
    };

    context.geometry = function(id) {
        return context.entity(id).geometry(history.graph());
    };

    /* Modes */
    context.enter = function(newMode) {
        if (mode) {
            mode.exit();
            dispatch.exit(mode);
        }

        mode = newMode;
        mode.enter();
        dispatch.enter(mode);
    };

    context.mode = function() {
        return mode;
    };

    context.selectedIDs = function() {
        if (mode && mode.selectedIDs) {
            return mode.selectedIDs();
        } else {
            return [];
        }
    };

    /* Behaviors */
    context.install = function(behavior) {
        context.surface().call(behavior);
    };

    context.uninstall = function(behavior) {
        context.surface().call(behavior.off);
    };

    /* Copy/Paste */
    var copyIDs = [], copyGraph;
    context.copyGraph = function() { return copyGraph; };
    context.copyIDs = function(_) {
        if (!arguments.length) return copyIDs;
        copyIDs = _;
        copyGraph = history.graph();
        return context;
    };

    /* Projection */
    context.projection = iD.geo.RawMercator();

    /* Background */
    var background = iD.Background(context);
    context.background = function() { return background; };

    /* Features */
    var features = iD.Features(context);
    context.features = function() { return features; };
    context.hasHiddenConnections = function(id) {
        var graph = history.graph(),
            entity = graph.entity(id);
        return features.hasHiddenConnections(entity, graph);
    };

    /* Map */
    var map = iD.Map(context);
    context.map = function() { return map; };
    context.layers = function() { return map.layers; };
    context.surface = function() { return map.surface; };
    context.editable = function() { return map.editable(); };
    context.mouse = map.mouse;
    context.extent = map.extent;
    context.pan = map.pan;
    context.zoomIn = map.zoomIn;
    context.zoomOut = map.zoomOut;
    context.zoomInFurther = map.zoomInFurther;
    context.zoomOutFurther = map.zoomOutFurther;

    context.surfaceRect = function() {
        // Work around a bug in Firefox.
        //   http://stackoverflow.com/questions/18153989/
        //   https://bugzilla.mozilla.org/show_bug.cgi?id=530985
        return context.surface().node().parentNode.getBoundingClientRect();
    };

    /* Presets */
    var presets = iD.presets();

    context.presets = function(_) {
        if (!arguments.length) return presets;
        presets.load(_);
        iD.areaKeys = presets.areaKeys();
        return context;
    };

    context.imagery = function(_) {
        background.load(_);
        return context;
    };

    context.container = function(_) {
        if (!arguments.length) return container;
        container = _;
        container.classed('id-container', true);
        return context;
    };

    /* Taginfo */
    var taginfo;
    context.taginfo = function(_) {
        if (!arguments.length) return taginfo;
        taginfo = _;
        return context;
    };

    var embed = false;
    context.embed = function(_) {
        if (!arguments.length) return embed;
        embed = _;
        return context;
    };

    var assetPath = '';
    context.assetPath = function(_) {
        if (!arguments.length) return assetPath;
        assetPath = _;
        return context;
    };

    var assetMap = {};
    context.assetMap = function(_) {
        if (!arguments.length) return assetMap;
        assetMap = _;
        return context;
    };

    context.imagePath = function(_) {
        var asset = 'img/' + _;
        return assetMap[asset] || assetPath + asset;
    };

    return d3.rebind(context, dispatch, 'on');
};

iD.version = '1.7.4';

(function() {
    var detected = {};

    var ua = navigator.userAgent,
        m = null;

    m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11+
    if (m !== null) {
        detected.browser = 'msie';
        detected.version = m[1];
    }
    if (!detected.browser) {
        m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
        if (m !== null) {
            detected.browser = 'Opera';
            detected.version = m[2];
        }
    }
    if (!detected.browser) {
        m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
        if (m !== null) {
            detected.browser = m[1];
            detected.version = m[2];
            m = ua.match(/version\/([\.\d]+)/i);
            if (m !== null) detected.version = m[1];
        }
    }
    if (!detected.browser) {
        detected.browser = navigator.appName;
        detected.version = navigator.appVersion;
    }

    // keep major.minor version only..
    detected.version = detected.version.split(/\W/).slice(0,2).join('.');

    if (detected.browser.toLowerCase() === 'msie') {
        detected.browser = 'Internet Explorer';
        detected.support = parseFloat(detected.version) > 9;
    } else {
        detected.support = true;
    }

    // Added due to incomplete svg style support. See #715
    detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );

    detected.locale = navigator.language || navigator.userLanguage || 'en-US';

    detected.filedrop = (window.FileReader && 'ondrop' in window);

    function nav(x) {
        return navigator.userAgent.indexOf(x) !== -1;
    }

    if (nav('Win')) {
        detected.os = 'win';
        detected.platform = 'Windows';
    }
    else if (nav('Mac')) {
        detected.os = 'mac';
        detected.platform = 'Macintosh';
    }
    else if (nav('X11') || nav('Linux')) {
        detected.os = 'linux';
        detected.platform = 'Linux';
    }
    else {
        detected.os = 'win';
        detected.platform = 'Unknown';
    }

    iD.detect = function() { return detected; };
})();
<commit_msg>better support for browser language preferences<commit_after>window.iD = function () {
    window.locale.en = iD.data.en;
    window.locale.current('en');

    var context = {},
        storage;

    // https://github.com/openstreetmap/iD/issues/772
    // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
    try { storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
    storage = storage || (function() {
        var s = {};
        return {
            getItem: function(k) { return s[k]; },
            setItem: function(k, v) { s[k] = v; },
            removeItem: function(k) { delete s[k]; }
        };
    })();

    context.storage = function(k, v) {
        try {
            if (arguments.length === 1) return storage.getItem(k);
            else if (v === null) storage.removeItem(k);
            else storage.setItem(k, v);
        } catch(e) {
            // localstorage quota exceeded
            /* eslint-disable no-console */
            if (typeof console !== 'undefined') console.error('localStorage quota exceeded');
            /* eslint-enable no-console */
        }
    };

    /* Accessor for setting minimum zoom for editing features. */

    var minEditableZoom = 16;
    context.minEditableZoom = function(_) {
        if (!arguments.length) return minEditableZoom;
        minEditableZoom = _;
        connection.tileZoom(_);
        return context;
    };

    var history = iD.History(context),
        dispatch = d3.dispatch('enter', 'exit'),
        mode,
        container,
        ui = iD.ui(context),
        connection = iD.Connection(),
        locale = iD.detect().locale,
        localePath;

    if (locale && iD.data.locales.indexOf(locale) === -1) {
        locale = locale.split('-')[0];
    }

    context.preauth = function(options) {
        connection.switch(options);
        return context;
    };

    context.locale = function(loc, path) {
        locale = loc;
        localePath = path;

        // Also set iD.detect().locale (unless we detected 'en-us' and openstreetmap wants 'en')..
        if (!(loc.toLowerCase() === 'en' && iD.detect().locale.toLowerCase() === 'en-us')) {
            iD.detect().locale = loc;
        }

        return context;
    };

    context.loadLocale = function(cb) {
        if (locale && locale !== 'en' && iD.data.locales.indexOf(locale) !== -1) {
            localePath = localePath || context.assetPath() + 'locales/' + locale + '.json';
            d3.json(localePath, function(err, result) {
                window.locale[locale] = result;
                window.locale.current(locale);
                cb();
            });
        } else {
            cb();
        }
    };

    /* Straight accessors. Avoid using these if you can. */
    context.ui = function() { return ui; };
    context.connection = function() { return connection; };
    context.history = function() { return history; };

    /* Connection */
    function entitiesLoaded(err, result) {
        if (!err) history.merge(result.data, result.extent);
    }

    context.loadTiles = function(projection, dimensions, callback) {
        function done(err, result) {
            entitiesLoaded(err, result);
            if (callback) callback(err, result);
        }
        connection.loadTiles(projection, dimensions, done);
    };

    context.loadEntity = function(id, callback) {
        function done(err, result) {
            entitiesLoaded(err, result);
            if (callback) callback(err, result);
        }
        connection.loadEntity(id, done);
    };

    context.zoomToEntity = function(id, zoomTo) {
        if (zoomTo !== false) {
            this.loadEntity(id, function(err, result) {
                if (err) return;
                var entity = _.find(result.data, function(e) { return e.id === id; });
                if (entity) { map.zoomTo(entity); }
            });
        }

        map.on('drawn.zoomToEntity', function() {
            if (!context.hasEntity(id)) return;
            map.on('drawn.zoomToEntity', null);
            context.on('enter.zoomToEntity', null);
            context.enter(iD.modes.Select(context, [id]));
        });

        context.on('enter.zoomToEntity', function() {
            if (mode.id !== 'browse') {
                map.on('drawn.zoomToEntity', null);
                context.on('enter.zoomToEntity', null);
            }
        });
    };

    /* History */
    context.graph = history.graph;
    context.changes = history.changes;
    context.intersects = history.intersects;

    var inIntro = false;

    context.inIntro = function(_) {
        if (!arguments.length) return inIntro;
        inIntro = _;
        return context;
    };

    context.save = function() {
        if (inIntro || (mode && mode.id === 'save')) return;
        history.save();
        if (history.hasChanges()) return t('save.unsaved_changes');
    };

    context.flush = function() {
        connection.flush();
        features.reset();
        history.reset();
        return context;
    };

    // Debounce save, since it's a synchronous localStorage write,
    // and history changes can happen frequently (e.g. when dragging).
    var debouncedSave = _.debounce(context.save, 350);
    function withDebouncedSave(fn) {
        return function() {
            var result = fn.apply(history, arguments);
            debouncedSave();
            return result;
        };
    }

    context.perform = withDebouncedSave(history.perform);
    context.replace = withDebouncedSave(history.replace);
    context.pop = withDebouncedSave(history.pop);
    context.overwrite = withDebouncedSave(history.overwrite);
    context.undo = withDebouncedSave(history.undo);
    context.redo = withDebouncedSave(history.redo);

    /* Graph */
    context.hasEntity = function(id) {
        return history.graph().hasEntity(id);
    };

    context.entity = function(id) {
        return history.graph().entity(id);
    };

    context.childNodes = function(way) {
        return history.graph().childNodes(way);
    };

    context.geometry = function(id) {
        return context.entity(id).geometry(history.graph());
    };

    /* Modes */
    context.enter = function(newMode) {
        if (mode) {
            mode.exit();
            dispatch.exit(mode);
        }

        mode = newMode;
        mode.enter();
        dispatch.enter(mode);
    };

    context.mode = function() {
        return mode;
    };

    context.selectedIDs = function() {
        if (mode && mode.selectedIDs) {
            return mode.selectedIDs();
        } else {
            return [];
        }
    };

    /* Behaviors */
    context.install = function(behavior) {
        context.surface().call(behavior);
    };

    context.uninstall = function(behavior) {
        context.surface().call(behavior.off);
    };

    /* Copy/Paste */
    var copyIDs = [], copyGraph;
    context.copyGraph = function() { return copyGraph; };
    context.copyIDs = function(_) {
        if (!arguments.length) return copyIDs;
        copyIDs = _;
        copyGraph = history.graph();
        return context;
    };

    /* Projection */
    context.projection = iD.geo.RawMercator();

    /* Background */
    var background = iD.Background(context);
    context.background = function() { return background; };

    /* Features */
    var features = iD.Features(context);
    context.features = function() { return features; };
    context.hasHiddenConnections = function(id) {
        var graph = history.graph(),
            entity = graph.entity(id);
        return features.hasHiddenConnections(entity, graph);
    };

    /* Map */
    var map = iD.Map(context);
    context.map = function() { return map; };
    context.layers = function() { return map.layers; };
    context.surface = function() { return map.surface; };
    context.editable = function() { return map.editable(); };
    context.mouse = map.mouse;
    context.extent = map.extent;
    context.pan = map.pan;
    context.zoomIn = map.zoomIn;
    context.zoomOut = map.zoomOut;
    context.zoomInFurther = map.zoomInFurther;
    context.zoomOutFurther = map.zoomOutFurther;

    context.surfaceRect = function() {
        // Work around a bug in Firefox.
        //   http://stackoverflow.com/questions/18153989/
        //   https://bugzilla.mozilla.org/show_bug.cgi?id=530985
        return context.surface().node().parentNode.getBoundingClientRect();
    };

    /* Presets */
    var presets = iD.presets();

    context.presets = function(_) {
        if (!arguments.length) return presets;
        presets.load(_);
        iD.areaKeys = presets.areaKeys();
        return context;
    };

    context.imagery = function(_) {
        background.load(_);
        return context;
    };

    context.container = function(_) {
        if (!arguments.length) return container;
        container = _;
        container.classed('id-container', true);
        return context;
    };

    /* Taginfo */
    var taginfo;
    context.taginfo = function(_) {
        if (!arguments.length) return taginfo;
        taginfo = _;
        return context;
    };

    var embed = false;
    context.embed = function(_) {
        if (!arguments.length) return embed;
        embed = _;
        return context;
    };

    var assetPath = '';
    context.assetPath = function(_) {
        if (!arguments.length) return assetPath;
        assetPath = _;
        return context;
    };

    var assetMap = {};
    context.assetMap = function(_) {
        if (!arguments.length) return assetMap;
        assetMap = _;
        return context;
    };

    context.imagePath = function(_) {
        var asset = 'img/' + _;
        return assetMap[asset] || assetPath + asset;
    };

    return d3.rebind(context, dispatch, 'on');
};

iD.version = '1.7.4';

(function() {
    var detected = {};

    var ua = navigator.userAgent,
        m = null;

    m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11+
    if (m !== null) {
        detected.browser = 'msie';
        detected.version = m[1];
    }
    if (!detected.browser) {
        m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
        if (m !== null) {
            detected.browser = 'Opera';
            detected.version = m[2];
        }
    }
    if (!detected.browser) {
        m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
        if (m !== null) {
            detected.browser = m[1];
            detected.version = m[2];
            m = ua.match(/version\/([\.\d]+)/i);
            if (m !== null) detected.version = m[1];
        }
    }
    if (!detected.browser) {
        detected.browser = navigator.appName;
        detected.version = navigator.appVersion;
    }

    // keep major.minor version only..
    detected.version = detected.version.split(/\W/).slice(0,2).join('.');

    if (detected.browser.toLowerCase() === 'msie') {
        detected.browser = 'Internet Explorer';
        detected.support = parseFloat(detected.version) > 9;
    } else {
        detected.support = true;
    }

    // Added due to incomplete svg style support. See #715
    detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );

    detected.locale = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage || 'en-US');

    detected.filedrop = (window.FileReader && 'ondrop' in window);

    function nav(x) {
        return navigator.userAgent.indexOf(x) !== -1;
    }

    if (nav('Win')) {
        detected.os = 'win';
        detected.platform = 'Windows';
    }
    else if (nav('Mac')) {
        detected.os = 'mac';
        detected.platform = 'Macintosh';
    }
    else if (nav('X11') || nav('Linux')) {
        detected.os = 'linux';
        detected.platform = 'Linux';
    }
    else {
        detected.os = 'win';
        detected.platform = 'Unknown';
    }

    iD.detect = function() { return detected; };
})();
<|endoftext|>


<commit_before>angular.module("3drepo")
	.service("ClientConfigService", ClientConfigService);

function ClientConfigService() {

	var ClientConfig = window.ClientConfig || {};

	ClientConfig.api_algorithm = createRoundRobinAlgorithm();
	ClientConfig.apiUrls = ClientConfig.api_algorithm.apiUrls;
	ClientConfig.apiUrl = ClientConfig.api_algorithm.apiUrl.bind(ClientConfig.api_algorithm);

	var C = ClientConfig.C;

	ClientConfig.GET_API = C.GET_API;
	ClientConfig.POST_API = (ClientConfig.apiUrls[C.POST_API]) ? C.POST_API : ClientConfig.GET_API;
	ClientConfig.MAP_API = (ClientConfig.apiUrls[C.MAP_API]) ? C.MAP_API : ClientConfig.GET_API;

	return ClientConfig;

	/*******************************************************************************
	 * Round robin API configuration
	 * @param {Object} variable - variable to coalesce
	 * @param {Object} value - value to return if object is null or undefined
	 *******************************************************************************/
	function createRoundRobinAlgorithm() {

		var roundRobin = {
			apiUrls : ClientConfig.apiUrls,
			apiUrlCounter: {}
		};

		for (var k in ClientConfig.apiUrls) {
			if(ClientConfig.apiUrls.hasOwnProperty(k)){
				roundRobin.apiUrlCounter[k] = 0;
			}
		}

		// self variable will be filled in by frontend
		roundRobin.apiUrl = function(type, path) {
			var typeFunctions = this.apiUrls[type];
			var functionIndex = this.apiUrlCounter[type] % Object.keys(typeFunctions).length;

			this.apiUrlCounter[type] += 1;

			return this.apiUrls[type][functionIndex](path);
		};

		return roundRobin;

	}

}<commit_msg>ISSUE #394 - ClientConfig scope<commit_after>angular.module("3drepo")
	.service("configService", configService);

function configService() {

	var config = window.ClientConfig || {};

	config.api_algorithm = createRoundRobinAlgorithm();
	config.apiUrls = config.api_algorithm.apiUrls;
	config.apiUrl = config.api_algorithm.apiUrl.bind(config.api_algorithm);

	var C = config.C;

	config.GET_API = C.GET_API;
	config.POST_API = (config.apiUrls[C.POST_API]) ? C.POST_API : config.GET_API;
	config.MAP_API = (config.apiUrls[C.MAP_API]) ? C.MAP_API : config.GET_API;

	return config;

	/*******************************************************************************
	 * Round robin API configuration
	 * @param {Object} variable - variable to coalesce
	 * @param {Object} value - value to return if object is null or undefined
	 *******************************************************************************/
	function createRoundRobinAlgorithm() {

		var roundRobin = {
			apiUrls : config.apiUrls,
			apiUrlCounter: {}
		};

		for (var k in config.apiUrls) {
			if(config.apiUrls.hasOwnProperty(k)){
				roundRobin.apiUrlCounter[k] = 0;
			}
		}

		// self variable will be filled in by frontend
		roundRobin.apiUrl = function(type, path) {
			var typeFunctions = this.apiUrls[type];
			var functionIndex = this.apiUrlCounter[type] % Object.keys(typeFunctions).length;

			this.apiUrlCounter[type] += 1;

			return this.apiUrls[type][functionIndex](path);
		};

		return roundRobin;

	}

}<|endoftext|>


<commit_before>// ========================================================================
// SproutCore -- JavaScript Application Framework
// Copyright ©2006-2008, Sprout Systems, Inc. and contributors.
// Portions copyright ©2008 Apple, Inc.  All rights reserved.
// ========================================================================

/**
  @class

  SelectFieldView displays browser-native popup menu.  To use this view,
  you should either bake into the HTML the preset list of options, or
  you can set the -objects property to an array of items to show.  The
  value is current value of the select.

  @extends SC.FieldView
  @author Charles Jolley
  @author Mike Ball
  @since SproutCore 1.0
*/
SC.SelectFieldView = SC.FieldView.extend(
/** @scope SC.SelectFieldView.prototype */ {

  tagName: 'select',
  classNames: ['sc-select-field-view'],

  /**
    An array of items that will form the menu you want to show.
  */
  objects: [],

  /**
    Binding default for an array of objects
  */
  objectsBindingDefault: SC.Binding.multiple(),

  /**
    If you set this to a non-null value, then the name shown for each
    menu item will be pulled from the object using the named property.
    if this is null, the collection objects themselves will be used.
  */
  nameKey: null,

  /**
   If you set this to a non-null value, then the value of this key will
   be used to sort the objects.  If this is not set, then nameKey will
   be used.
  */
  sortKey: null,

  /**
     Set this to a non-null value to use a key from the passed set of objects
     as the value for the options popup.  If you don't set this, then the
     objects themselves will be used as the value.
  */
  valueKey: null,

  /**
    set this to non-null to place an empty option at the top of the menu.
  */
  emptyName: null,

  /**
    if true, the empty name will be localized.
  */
  localize: false,

  /**
    if true, it means that the nameKey, valueKey or objects changed
  */
  cpDidChange: YES,

  /**
    override this to change the enabled/disabled state of menu items as they
    are built.  Return false if you want the menu item to be disabled.

    @param itemValue the value for the item to validate
    @param itemName the name of the menu item to validate
    @returns YES if the item should be enabled, NO otherwise
  */
  validateMenuItem: function(itemValue, itemName) {
    return true ;
  },

  /**
    override this method to implement your own sorting of the menu. By
    default, menu items are sorted using the value shown or the sortKey

    @param objects the unsorted array of objects to display.
    @returns sorted array of objects
  */
  sortObjects: function(objects) {
    var nameKey = this.get('sortKey') || this.get('nameKey') ;
    objects = objects.sort(function(a,b) {
      if (nameKey) {
        a = a.get ? a.get(nameKey) : a[nameKey] ;
        b = b.get ? b.get(nameKey) : b[nameKey] ;
      }
      return (a<b) ? -1 : ((a>b) ? 1 : 0) ;
    }) ;

    return objects ;
  },

  /**
    call this method to rebuild the menu manually.  Normally you should not
    need to do this since the menu will be rebuilt as its data changes.
  */
  rebuildMenu: function(context, firstTime) {

    // get list of objects.
    var nameKey = this.get('nameKey') ;
    var valueKey = this.get('valueKey') ;
    var objects = this.get('objects') ;
    var fieldValue = this.get('value') ;
    var el, selectElement;

    // get the localization flag.
    var shouldLocalize = this.get('localize');

    // convert fieldValue to guid, if it is an object.
    if (!valueKey && fieldValue) fieldValue = SC.guidFor(fieldValue) ;
    if ((fieldValue === null) || (fieldValue === '')) fieldValue = '***' ;

    if (objects) {
      objects = this.sortObjects(objects) ; // sort'em.
      // var html = [] ;
      if(!firstTime){
        selectElement=this.$input()[0];
        selectElement.innerHTML='';
      }

      var emptyName = this.get('emptyName') ;
      if (emptyName) {
        if (shouldLocalize) emptyName = emptyName.loc() ;
          if(firstTime){
            context.push('<option value="***">%@</option>'.fmt(emptyName)) ;
            context.push('<option disabled="disabled"></option>') ;
          }else{
            el=document.createElement('option');
            el.value="***";
            el.innerHTML=emptyName;
            selectElement.appendChild(el);
            el=document.createElement('option');
            el.disabled="disabled";
            selectElement.appendChild(el);
          }
      }

      // generate option elements.
      objects.forEach(function(object) {
        if (object) {

          // either get the name from the object or convert object to string.
          var name = nameKey ? (object.get ? object.get(nameKey) : object[nameKey]) : object.toString() ;

          // localize name if specified.
          if(shouldLocalize)
          {
            name = name.loc();
          }

          // get the value using the valueKey or the object if no valueKey.
          // then convert to a string or use _guid if one of available.
          var value = (valueKey) ? (object.get ? object.get(valueKey) : object[valueKey]) : object ;
          if (value) value = (SC.guidFor(value)) ? SC.guidFor(value) : value.toString() ;

          // render HTML
          var disable = (this.validateMenuItem && this.validateMenuItem(value, name)) ? '' : 'disabled="disabled" ' ;
          if(firstTime){
            context.push('<option %@value="%@">%@</option>'.fmt(disable,value,name)) ;
          } else{
            el=document.createElement('option');
            el.value=value;
            el.innerHTML=name;
            if(disable.length>0) el.disable="disabled";
            selectElement.appendChild(el);
          }
        // null value means separator.
        } else {
          if(firstTime){
            context.push('<option disabled="disabled"></option>') ;
          }else{
            el=document.createElement('option');
            el.disabled="disabled";
            selectElement.appendChild(el);
          }
        }
      }, this );

      this.setFieldValue(fieldValue);

    } else {
      this.set('value',null);
    }
  },

  // .......................................
  // PRIVATE
  //

  $input: function() { return this.$(); },

  /* @private */
  mouseDown: function(evt) {

    if (!this.get('isEnabled')) {
      evt.stop();
      return YES;
    } else return sc_super();
  },

  // when fetching the raw value, convert back to an object if needed...
  /** @private */
  getFieldValue: function() {
    var value = sc_super(); // get raw value...
    var valueKey = this.get('valueKey') ;
    var objects = this.get('objects') ;

    // Handle empty selection.
    if (value == '***') {
      value = null ;

    // If no value key was set and there are objects then match back to an
    // object.
    } else if (value && objects) {
      // objects = Array.from(objects) ;
      var loc = objects.length ;
      var found = null ; // matching object goes here.
      while(!found && (--loc >= 0)) {
        var object = objects[loc] ;

        // get value using valueKey if there is one or use object
        // map to _guid or toString.
        if (valueKey) object = (object.get) ? object.get(valueKey) : object[valueKey] ;
        var ov = (object) ? (SC.guidFor(object) ? SC.guidFor(object) : object.toString()) : null ;

        // use this object value if it matches.
        if (value == ov) found = object ;
      }
    }

    return valueKey ? value : found ;
  },

  // object changes to the objects array of objects if possible.
  render: function(context, firstTime) {
    // if (this.didChangeFor('_objO','objects','nameKey','valueKey')) {
      var loc ;
      var objects = this.get('objects') ;
      var func = this._objectsItemObserver ;

      // stop observing old objects.
      if (this._objects) {
        loc = this._objects.length ;
        while(--loc >= 0) {
          var object = this._objects[loc] ;
          if (object && object.removeObserver) {
            if (this._nameKey && this._valueKey) {
              object.removeObserver(this._nameKey, this, func) ;
              object.removeObserver(this._valueKey, this, func) ;
            } else {
              object.removeObserver('*', this, func) ;
            } // if (this._nameKey)
          } // if (object &&...)
        } // while(--loc)
      } // if (this._objects)

      // start observing new objects.
      this._objects = objects ;
      this._nameKey = this.get('nameKey') ;
      this._valueKey = this.get('valueKey') ;

      if (this._objects) {
        loc = this._objects.length ;
        while(--loc >= 0) {
          object = this._objects[loc] ;
          if (object && object.addObserver) {
            if (this._nameKey && this._valueKey) {
              object.addObserver(this._nameKey, this, func) ;
              object.addObserver(this._valueKey, this, func) ;
            } else {
              object.addObserver('*', this, func) ;
            } // if (this._nameKey)
          } // if (object &&...)
        } // while(--loc)
      } // if (this._objects)
     this.rebuildMenu(context, firstTime) ;
     //} // if (this.didChangeFor...)
  },

  displayProperties: ['objects','nameKey','valueKey'],

  // this is invoked anytime an item we are interested in in the menu changes
  // rebuild the menu when this happens, but only one time.
  _objectsItemObserver: function(item, key, value) {
    if (item.didChangeFor(SC.guidFor(this), key)) {
      this.rebuildMenu() ;
    }
  },

  fieldDidFocus: function() {
    var isFocused = this.get('isFocused');
    if (!isFocused) this.set('isFocused', true);
  },

  fieldDidBlur: function() {
    var isFocused = this.get('isFocused');
    if (isFocused) this.set('isFocused', false);
  },

  _isFocusedObserver: function() {
    this.$().setClass('focus', this.get('isFocused'));
  }.observes('isFocused'),

  didCreateLayer: function() {
    var input = this.$();
    SC.Event.add(input, 'blur', this, this.fieldDidBlur);
    SC.Event.add(input, 'focus',this, this.fieldDidFocus);
    return sc_super();
  },

  willDestroyLayer: function() {
    var input = this.$input();
    SC.Event.remove(input, 'focus', this, this.fieldDidFocus);
    SC.Event.remove(input, 'blur', this, this.fieldDidBlur);
    return sc_super();
  }

});
<commit_msg>Add option to selectfield sorting, cleanup of code<commit_after>// ========================================================================
// SproutCore -- JavaScript Application Framework
// Copyright ©2006-2008, Sprout Systems, Inc. and contributors.
// Portions copyright ©2008 Apple, Inc.  All rights reserved.
// ========================================================================

/**
  @class

  SelectFieldView displays browser-native popup menu.  To use this view,
  you should either bake into the HTML the preset list of options, or
  you can set the -objects property to an array of items to show.  The
  value is current value of the select.

  @extends SC.FieldView
  @author Charles Jolley
  @author Mike Ball
  @since SproutCore 1.0
*/
SC.SelectFieldView = SC.FieldView.extend(
/** @scope SC.SelectFieldView.prototype */ {

  tagName: 'select',
  classNames: ['sc-select-field-view'],

  /**
    An array of items that will form the menu you want to show.
  */
  objects: [],

  /**
    Binding default for an array of objects
  */
  objectsBindingDefault: SC.Binding.multiple(),

  /**
    If you set this to a non-null value, then the name shown for each
    menu item will be pulled from the object using the named property.
    if this is null, the collection objects themselves will be used.
  */
  nameKey: null,

  /**
   If you set this to a non-null value, then the value of this key will
   be used to sort the objects.  If this is not set, then nameKey will
   be used.
  */
  sortKey: null,

  /**
     Set this to a non-null value to use a key from the passed set of objects
     as the value for the options popup.  If you don't set this, then the
     objects themselves will be used as the value.
  */
  valueKey: null,

  /**
    set this to non-null to place an empty option at the top of the menu.
  */
  emptyName: null,

  /**
    if true, the empty name will be localized.
  */
  localize: false,

  /**
    if true, it means that the nameKey, valueKey or objects changed
  */
  cpDidChange: YES,

  /**
    if true, it means that no sorting will occur, objects will appear
    in the same order as in the array
  */
  disableSort: NO,

  /**
    override this to change the enabled/disabled state of menu items as they
    are built.  Return false if you want the menu item to be disabled.

    @param itemValue the value for the item to validate
    @param itemName the name of the menu item to validate
    @returns YES if the item should be enabled, NO otherwise
  */
  validateMenuItem: function(itemValue, itemName) {
    return true ;
  },

  /**
    override this method to implement your own sorting of the menu. By
    default, menu items are sorted using the value shown or the sortKey

    @param objects the unsorted array of objects to display.
    @returns sorted array of objects
  */
  sortObjects: function(objects) {
    if(!this.get('disableSort')){
      var nameKey = this.get('sortKey') || this.get('nameKey') ;
      objects = objects.sort(function(a,b) {
        if (nameKey) {
          a = a.get ? a.get(nameKey) : a[nameKey] ;
          b = b.get ? b.get(nameKey) : b[nameKey] ;
        }
        return (a<b) ? -1 : ((a>b) ? 1 : 0) ;
      }) ;
    }
    return objects ;
  },

  render: function(context, firstTime) {
    if (this.get('cpDidChange')) {
      this.set('cpDidChange', NO);
      // get list of objects.
      var nameKey = this.get('nameKey') ;
      var valueKey = this.get('valueKey') ;
      var objects = this.get('objects') ;
      var fieldValue = this.get('value') ;
      var el, selectElement;

      // get the localization flag.
      var shouldLocalize = this.get('localize');

      // convert fieldValue to guid, if it is an object.
      if (!valueKey && fieldValue) fieldValue = SC.guidFor(fieldValue) ;
      if ((fieldValue === null) || (fieldValue === '')) fieldValue = '***' ;

      if (objects) {
        objects = this.sortObjects(objects) ; // sort'em.
        // var html = [] ;
        if(!firstTime){
          selectElement=this.$input()[0];
          selectElement.innerHTML='';
        }

        var emptyName = this.get('emptyName') ;
        if (emptyName) {
          if (shouldLocalize) emptyName = emptyName.loc() ;
          if(firstTime){
            context.push('<option value="***">%@</option>'.fmt(emptyName)) ;
            context.push('<option disabled="disabled"></option>') ;
          }else{
            el=document.createElement('option');
            el.value="***";
            el.innerHTML=emptyName;
            selectElement.appendChild(el);
            el=document.createElement('option');
            el.disabled="disabled";
            selectElement.appendChild(el);
          }
        }

          // generate option elements.
        objects.forEach(function(object) {
        if (object) {
          // either get the name from the object or convert object to string.
          var name = nameKey ? (object.get ? object.get(nameKey) : object[nameKey]) : object.toString() ;

          // localize name if specified.
          if(shouldLocalize)
          {
            name = name.loc();
          }

          // get the value using the valueKey or the object if no valueKey.
          // then convert to a string or use _guid if one of available.
          var value = (valueKey) ? (object.get ? object.get(valueKey) : object[valueKey]) : object ;
          if (value) value = (SC.guidFor(value)) ? SC.guidFor(value) : value.toString() ;

          // render HTML
          var disable = (this.validateMenuItem && this.validateMenuItem(value, name)) ? '' : 'disabled="disabled" ' ;
          if(firstTime){
            context.push('<option %@value="%@">%@</option>'.fmt(disable,value,name)) ;
          } else{
            el=document.createElement('option');
            el.value=value;
            el.innerHTML=name;
            if(disable.length>0) el.disable="disabled";
            selectElement.appendChild(el);
          }
        // null value means separator.
        } else {
          if(firstTime){
            context.push('<option disabled="disabled"></option>') ;
          }else{
            el=document.createElement('option');
            el.disabled="disabled";
            selectElement.appendChild(el);
          }
        }
      }, this );

      this.setFieldValue(fieldValue);

      } else {
        this.set('value',null);
      }
    }
  },

  displayProperties: ['objects','nameKey','valueKey'],

  _objectsObserver: function() {
    this.set('cpDidChange', YES);
  }.observes('objects'),

  _nameKeyObserver: function() {
    this.set('cpDidChange', YES);
  }.observes('nameKey'),

  _valueKeyObserver: function() {
    this.set('cpDidChange', YES);
  }.observes('valueKey'),

  // .......................................
  // PRIVATE
  //

  $input: function() { return this.$(); },

  /* @private */
  mouseDown: function(evt) {

    if (!this.get('isEnabled')) {
      evt.stop();
      return YES;
    } else return sc_super();
  },

  // when fetching the raw value, convert back to an object if needed...
  /** @private */
  getFieldValue: function() {
    var value = sc_super(); // get raw value...
    var valueKey = this.get('valueKey') ;
    var objects = this.get('objects') ;

    // Handle empty selection.
    if (value == '***') {
      value = null ;

    // If no value key was set and there are objects then match back to an
    // object.
    } else if (value && objects) {
      // objects = Array.from(objects) ;
      var loc = objects.length ;
      var found = null ; // matching object goes here.
      while(!found && (--loc >= 0)) {
        var object = objects[loc] ;

        // get value using valueKey if there is one or use object
        // map to _guid or toString.
        if (valueKey) object = (object.get) ? object.get(valueKey) : object[valueKey] ;
        var ov = (object) ? (SC.guidFor(object) ? SC.guidFor(object) : object.toString()) : null ;

        // use this object value if it matches.
        if (value == ov) found = object ;
      }
    }

    return valueKey ? found : value;
  },

  setFieldValue: function(newValue) {
    if (SC.none(newValue)) { newValue = '' ; }
    else {
      newValue = ((newValue) ? (SC.guidFor(newValue) ? SC.guidFor(newValue) : newValue.toString()) : null );
    }
    this.$input().val(newValue);
    return this ;
  },

  fieldDidFocus: function() {
    var isFocused = this.get('isFocused');
    if (!isFocused) this.set('isFocused', true);
  },

  fieldDidBlur: function() {
    var isFocused = this.get('isFocused');
    if (isFocused) this.set('isFocused', false);
  },

  _isFocusedObserver: function() {
    this.$().setClass('focus', this.get('isFocused'));
  }.observes('isFocused'),

  didCreateLayer: function() {
    var input = this.$();
    SC.Event.add(input, 'blur', this, this.fieldDidBlur);
    SC.Event.add(input, 'focus',this, this.fieldDidFocus);
    return sc_super();
  },

  willDestroyLayer: function() {
    var input = this.$input();
    SC.Event.remove(input, 'focus', this, this.fieldDidFocus);
    SC.Event.remove(input, 'blur', this, this.fieldDidBlur);
    return sc_super();
  }

});
<|endoftext|>


<commit_before>var fs = require('fs');
var data = JSON.parse(fs.readFileSync(__dirname + '/api.jsca'));
var Emitter = require('eventemitter2').EventEmitter2;
var util = require('util');
var _ = require('underscore');
var md5 = require('MD5');

// Modify Emitter's prototype to conform with what Ti does
Emitter.prototype.addEventListener = Emitter.prototype.on;
Emitter.prototype.removeEventListener = Emitter.prototype.removeListener;
Emitter.prototype.fireEvent = Emitter.prototype.emit;

Titanium = Ti = {};

function retrieve (root, list) {
  var name = list.shift();
  if (!root[name]) {
    var newobj = {};
    newobj.name = name;
    _.extend(newobj, Emitter.prototype);
    Emitter.call(newobj);
    root[name] = newobj;
  }
  var current = root[name];
  if (list.length > 0) return retrieve(current, list);
  else return current;
}

function method (namespace, obj, name) {
  // dont want to overwrite these
  if (['addEventListener', 'removeEventListener', 'fireEvent'].indexOf(name) != -1) return;

  // factory
  if (name.indexOf('create') != -1) {
    obj[name] = function (props) {
      var proto = retrieve(Ti, namespace.concat([name.slice(6)]));
      var ret = {};
      _.extend(ret, proto, props);
      return ret;
    };

    return;
  }

  if (name == 'add') {
    obj.add = function (view) {
      this.children = this.children || [];
      this.children.push(view);
    };
    return;
  }

  // otherwise it's some method so just make it emit that this method was
  // called
  obj[name] = function () {
    if (this.fireEvent) this.fireEvent('function::' + name, {});
  };
}

data.types.forEach(function (item) {
  // Don't worry about stuff that isn't actually in the Titanium namespace for
  // now
  if (item.name.indexOf('Titanium.') == -1) return;

  var name = item.name.slice(9);
  var namespace = name.split('.').slice(-1);
  var obj = retrieve(Ti, name.split('.'));

  item.functions.forEach(function (fn) {
    method(namespace, obj, fn.name);
  });

  item.properties.forEach(function (prop) {
  });
});

// Some manual stuff

Ti.Platform ={
  osname: 'android',
  version: '6',
  model: 'mock',
  id: 'mock-id',
  is24HourTimeFormat: function () { return false; },
  Android: {API_LEVEL: 18},
  displayCaps: {
    platformWidth: 480,
    platformHeight: 800,
    dpi: 160
  }
};

Ti.Network._requestURLs = {};
Ti.Network._requests = [];
var old = Ti.Network.createHTTPClient;
Ti.Network.createHTTPClient = function (spec) {
  var xhr = old(spec);
  xhr.open = function (method, url) {
    xhr.method = method;
    xhr.url = url;
    Ti.Network._requestURLs[url] = xhr;
    Ti.Network._requests.push(xhr);
    xhr.fireEvent('function::open', arguments);
  };

  xhr.send = function (data) {
    xhr.data = data;
    xhr.fireEvent('function::send', arguments);
  };

  return xhr;
};

// Mock some file stuff
Ti.Filesystem._files = {};
// Non-standard, for convenience
Ti.Filesystem.createFile = function () {
  var file = {};
  _.extend(file, Ti.Filesystem.File);
  file.exists = false;
  return file;
};
Ti.Filesystem.getFile = function (name) {
  return Ti.Filesystem._files[name] || Ti.Filesystem.createFile();
};
Ti.Filesystem.getApplicationDataDirectory = function () {
  return '';
};

Ti.Utils.md5HexDigest = function (input) {
  return md5(input);
};

module.exports = Ti;
<commit_msg>using proper prototypal inheritance for create* fns; file fixes<commit_after>var fs = require('fs');
var data = JSON.parse(fs.readFileSync(__dirname + '/api.jsca'));
var Emitter = require('eventemitter2').EventEmitter2;
var util = require('util');
var _ = require('underscore');
var md5 = require('MD5');

// Modify Emitter's prototype to conform with what Ti does
Emitter.prototype.addEventListener = Emitter.prototype.on;
Emitter.prototype.removeEventListener = Emitter.prototype.removeListener;
Emitter.prototype.fireEvent = Emitter.prototype.emit;

Titanium = Ti = {};

function retrieve (root, list) {
  var name = list.shift();
  if (!root[name]) {
    var Newobj = function Newobj () {
      Emitter.call(this);
    };
    util.inherits(Newobj, Emitter);
    Newobj.name = name;
    root[name] = Newobj;
  }
  var current = root[name];
  if (list.length > 0) return retrieve(current, list);
  else return current;
}

function method (namespace, obj, name) {
  // dont want to overwrite these
  if (['addEventListener', 'removeEventListener', 'fireEvent'].indexOf(name) != -1) return;

  // factory
  if (name.indexOf('create') != -1) {
    obj[name] = function (props) {
      var proto = retrieve(Ti, namespace.concat([name.slice(6)]));
      //var ret = {};
      //_.extend(ret, proto, props);
      var ret = new proto();
      _.extend(ret, props);
      return ret;
    };

    return;
  }

  if (name == 'add') {
    obj.add = function (view) {
      this.children = this.children || [];
      this.children.push(view);
    };
    return;
  }

  // otherwise it's some method so just make it emit that this method was
  // called
  obj[name] = function () {
    if (this.fireEvent) this.fireEvent('function::' + name, {});
  };
}

data.types.forEach(function (item) {
  // Don't worry about stuff that isn't actually in the Titanium namespace for
  // now
  if (item.name.indexOf('Titanium.') == -1) return;

  var name = item.name.slice(9);
  var namespace = name.split('.').slice(-1);
  var obj = retrieve(Ti, name.split('.'));

  item.functions.forEach(function (fn) {
    method(namespace, obj, fn.name);
  });

  item.properties.forEach(function (prop) {
  });
});

// Some manual stuff

Ti.Platform ={
  osname: 'android',
  version: '6',
  model: 'mock',
  id: 'mock-id',
  is24HourTimeFormat: function () { return false; },
  Android: {API_LEVEL: 18},
  displayCaps: {
    platformWidth: 480,
    platformHeight: 800,
    dpi: 160
  }
};

Ti.Network._requestURLs = {};
Ti.Network._requests = [];
var old = Ti.Network.createHTTPClient;
Ti.Network.createHTTPClient = function (spec) {
  var xhr = old(spec);
  xhr.open = function (method, url) {
    xhr.method = method;
    xhr.url = url;
    Ti.Network._requestURLs[url] = xhr;
    Ti.Network._requests.push(xhr);
    xhr.fireEvent('function::open', arguments);
  };

  xhr.send = function (data) {
    xhr.data = data;
    xhr.fireEvent('function::send', arguments);
  };

  return xhr;
};

// Mock some file stuff
Ti.Filesystem._files = {};
// Non-standard, for convenience
Ti.Filesystem.createFile = function (spec) {
  var file = {};
  _.extend(file, Ti.Filesystem.File);
  file._exists = false;
  file.exists = function () {
    return file._exists;
  };

  file.read = function () {
    return {text: spec.text};
  };

  return file;
};
Ti.Filesystem.getFile = function (name) {
  return Ti.Filesystem._files[name] || Ti.Filesystem.createFile();
};
Ti.Filesystem.getApplicationDataDirectory = function () {
  return '';
};

Ti.Utils.md5HexDigest = function (input) {
  return md5(input);
};

Ti.include = function () {};
Ti.App.Properties.getString = function (name, def) { return def; };
Ti.App.Properties.getBool = function (name, def) { return def; };
Ti.App.Properties.getList = function (name, def) { return def; };

module.exports = Ti;
<|endoftext|>


<commit_before>/*global window, navigator, console, google */

// django csrf setup
// https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
(function($){
  "use strict";
  // using jQuery
  function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
      var cookies = document.cookie.split(';');
      for (var i = 0; i < cookies.length; i++) {
        var cookie = $.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) == (name + '=')) {
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
          break;
        }
      }
    }
    return cookieValue;
  }

  function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }

  function sameOrigin(url) {
    // test that a given url is a same-origin URL
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
        // or any other URL that isn't scheme relative or absolute i.e relative.
        !(/^(\/\/|http:|https:).*/.test(url));
  }

  var csrftoken = getCookie('csrftoken');
  $.ajaxSetup({
      beforeSend: function(xhr, settings) {
          if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
              // Send the token to same-origin, relative URLs only.
              // Send the token only if the method warrants CSRF protection
              // Using the CSRFToken value acquired earlier
              xhr.setRequestHeader("X-CSRFToken", csrftoken);
          }
      }
  });
})(window.jQuery);

// maps
(function(exports, $){
  "use strict";
  var $container = $('#nav-map-container'),
      map,
      homePin,
      buildingMarkers = [],
      debug = $('body').data('debug');

  var initMap = function(center){
    var mapOptions = {
          zoom: 14,
          mapTypeControl: false,
          streetViewControl: false,
          center: center,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };
    map = new google.maps.Map($container[0], mapOptions);
    $('#recenter').click(function(){
      var center = map.getCenter(),
          position = {coords:{
            latitude: center.lat(),
            longitude: center.lng()
          }};
      window.getClosestBuildings(position);
    });
  };

  var load = function(centerLatLng, buildings){
    var center = new google.maps.LatLng(
          centerLatLng.latitude,
          centerLatLng.longitude);
    var pinColor = "FFFF00";
    var pinImage = new google.maps.MarkerImage(
          "http://chart.apis.google.com/chart?chst=d_map_pin_icon&chld=home|" + pinColor,
          new google.maps.Size(21, 34),
          new google.maps.Point(0,0),
          new google.maps.Point(10, 34));
    var pinShadow = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_shadow",
        new google.maps.Size(40, 37),
        new google.maps.Point(0, 0),
        new google.maps.Point(12, 35));

    if (!map) {
      initMap(center);
    }

    if (!homePin){
      homePin = new google.maps.Marker({
        position: center,
        draggable: true,
        map: map,
        icon: pinImage,
        shadow: pinShadow
      });
      google.maps.event.addDomListener(homePin, 'dragend', function(evt){
        // this: marker
        var newLocation = evt.latLng;
            position = {coords:{
              latitude: newLocation.lat(),
              longitude: newLocation.lng()
            }};
        console.log("drag ended", position.coords);
        window.getClosestBuildings(position);
      });
    } else {
      homePin.setPosition(center);
    }

    if (buildings){
      var building,
          position,
          buildingMarker,
          bounds = new google.maps.LatLngBounds();
      pinColor = "FE7569";
      buildingMarkers.forEach(function(x){ x.setMap(null); });
      buildingMarkers = [];
      $.each(buildings, function(i, building){
        position = new google.maps.LatLng(building.latitude, building.longitude);

        pinImage = new google.maps.MarkerImage(
          "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=" +
            String.fromCharCode(65 + i) + "|" + pinColor,
          new google.maps.Size(21, 34),
          new google.maps.Point(0,0),
          new google.maps.Point(10, 34));

        buildingMarker = new google.maps.Marker({
          position: position,
          map: map,
          icon: pinImage,
          shadow: pinShadow,
          title: building.name_1 + ' '  + building.address_1
        });
        if (debug){
          // enable map pin correction
          buildingMarker.setDraggable(true);
          google.maps.event.addDomListener(buildingMarker, 'dragend', function(evt){
            var newLocation = evt.latLng;
                position = {coords:{
                  latitude: newLocation.lat(),
                  longitude: newLocation.lng()
                }};
            $.post('/building/' + building.elbi + '/', position, function(data){
              console.log(data);
            });
          });
        }
        buildingMarkers.push(buildingMarker);
        bounds.extend(position);
      });
      map.fitBounds(bounds);
    }
  };

  // exports
  exports.loadMap = load;

})(window, window.jQuery);

// app
(function(exports, $){
  "use strict";

  var store;

  var storeData = function(data){
    store = data;
    console.log(store.length);
    exports.store = store;
    $(window).trigger('storeAvailable');
  };

  $.getJSON('/chart/locator/data/', storeData);

  var searchZip = function(zip_code){
    var searchZipFilter = function(x){
      return x.zip_code == zip_code;
    };
    return store.filter(searchZipFilter);
  };

  // distance approximators
  /** Converts numeric degrees to radians */
  if (!Number.prototype.toRad){
    Number.prototype.toRad = function(){
      return this * Math.PI / 180;
    };
  }

  // http://www.movable-type.co.uk/scripts/latlong.html
  var distance = {
    R: 63741,  // kilometers
    haversine: function(lat1, lng1, lat2, lng2){
      var dLat = (lat2-lat1).toRad();
      var dLon = (lng2-lng1).toRad();
      lat1 = lat1.toRad();
      lat2 = lat2.toRad();

      var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
        Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
      var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
      return distance.R * c;
    },
    spherical: function(lat1, lng1, lat2, lng2){
      return Math.acos(Math.sin(lat1)*Math.sin(lat2) +
        Math.cos(lat1) * Math.cos(lat2) *
        Math.cos(lng2 - lng1)) * distance.R;
    },
    pythagorean: function(lat1, lng1, lat2, lng2){
      lat1 = lat1.toRad();
      lng1 = lng1.toRad();
      lat2 = lat2.toRad();
      lng2 = lng2.toRad();
      var x = (lng2 - lng1) * Math.cos((lat1 + lat2) / 2),
          y = lat2 - lat1;
      return Math.sqrt(x * x + y * y) * distance.R;
    },
    magic: function(lat1, lng1, lat2, lng2){
      return (Math.abs(lat1 - lat2) + Math.abs(lng1 - lng2)) * distance.R;
    }
  };

  var closestBuildings = function(lat, lng){
    var metric = distance.spherical;
    // go ahead and sort in place.
    store.sort(function(a, b){
      // TODO make a lookup table?
      return metric(lat, lng, a.latitude, a.longitude) - metric(lat, lng, b.latitude, b.longitude);
    });
    return store.slice(0, 10);
  };

  // hook up UI
  if (!navigator.geolocation){
    $('body').addClass('no-geolocation');
  }

  // Get the url for a building based on server-side url pattern
  var buildingToUrl = function(building){
    return '/building/' + building.elbi + '/';
  };

  var gotPosition = function(position){
    var lat = position.coords.latitude,
        lng = position.coords.longitude;
    var $container = $('#nearest').empty(),
        buildings = closestBuildings(lat, lng);
    $.each(buildings, function(idx, building){
      $container.append('
' +
        '' + building.name_1 + ', ' +
        building.address_1 + ', ' +
        building.city +
        '
');
    });

    window.loadMap(position.coords, buildings);
  };

  $('#locate').on('click', function(){
    navigator.geolocation.getCurrentPosition(gotPosition);
  });

  $(window).on('storeAvailable', function(){
    var data = $('#building-data').data('building');
    if (data && data.latitude){
      gotPosition({
          coords:{
            latitude: data.latitude,
            longitude: data.longitude
          }
      });
    } else {
      navigator.geolocation.getCurrentPosition(gotPosition);
    }
  });

  // exports
  // exports.searchZip = searchZip;
  // exports.d = distance;
  exports.getClosestBuildings = gotPosition;

})(window, window.jQuery);
// "76104"
<commit_msg>fix spherical metric was missing deg to rad conversion<commit_after>/*global window, navigator, console, google */

// django csrf setup
// https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
(function($){
  "use strict";
  // using jQuery
  function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
      var cookies = document.cookie.split(';');
      for (var i = 0; i < cookies.length; i++) {
        var cookie = $.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) == (name + '=')) {
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
          break;
        }
      }
    }
    return cookieValue;
  }

  function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
  }

  function sameOrigin(url) {
    // test that a given url is a same-origin URL
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
        // or any other URL that isn't scheme relative or absolute i.e relative.
        !(/^(\/\/|http:|https:).*/.test(url));
  }

  var csrftoken = getCookie('csrftoken');
  $.ajaxSetup({
      beforeSend: function(xhr, settings) {
          if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
              // Send the token to same-origin, relative URLs only.
              // Send the token only if the method warrants CSRF protection
              // Using the CSRFToken value acquired earlier
              xhr.setRequestHeader("X-CSRFToken", csrftoken);
          }
      }
  });
})(window.jQuery);

// maps
(function(exports, $){
  "use strict";
  var $container = $('#nav-map-container'),
      map,
      homePin,
      buildingMarkers = [],
      debug = $('body').data('debug');

  var initMap = function(center){
    var mapOptions = {
          zoom: 14,
          mapTypeControl: false,
          streetViewControl: false,
          center: center,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };
    map = new google.maps.Map($container[0], mapOptions);
    $('#recenter').click(function(){
      var center = map.getCenter(),
          position = {coords:{
            latitude: center.lat(),
            longitude: center.lng()
          }};
      window.getClosestBuildings(position);
    });
  };

  var load = function(centerLatLng, buildings){
    var center = new google.maps.LatLng(
          centerLatLng.latitude,
          centerLatLng.longitude);
    var pinColor = "FFFF00";
    var pinImage = new google.maps.MarkerImage(
          "http://chart.apis.google.com/chart?chst=d_map_pin_icon&chld=home|" + pinColor,
          new google.maps.Size(21, 34),
          new google.maps.Point(0,0),
          new google.maps.Point(10, 34));
    var pinShadow = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_shadow",
        new google.maps.Size(40, 37),
        new google.maps.Point(0, 0),
        new google.maps.Point(12, 35));

    if (!map) {
      initMap(center);
    }

    if (!homePin){
      homePin = new google.maps.Marker({
        position: center,
        draggable: true,
        map: map,
        icon: pinImage,
        shadow: pinShadow
      });
      google.maps.event.addDomListener(homePin, 'dragend', function(evt){
        // this: marker
        var newLocation = evt.latLng;
            position = {coords:{
              latitude: newLocation.lat(),
              longitude: newLocation.lng()
            }};
        console.log("drag ended", position.coords);
        window.getClosestBuildings(position);
      });
    } else {
      homePin.setPosition(center);
    }

    if (buildings){
      var building,
          position,
          buildingMarker,
          bounds = new google.maps.LatLngBounds();
      pinColor = "FE7569";
      buildingMarkers.forEach(function(x){ x.setMap(null); });
      buildingMarkers = [];
      $.each(buildings, function(i, building){
        position = new google.maps.LatLng(building.latitude, building.longitude);

        pinImage = new google.maps.MarkerImage(
          "http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=" +
            String.fromCharCode(65 + i) + "|" + pinColor,
          new google.maps.Size(21, 34),
          new google.maps.Point(0,0),
          new google.maps.Point(10, 34));

        buildingMarker = new google.maps.Marker({
          position: position,
          map: map,
          icon: pinImage,
          shadow: pinShadow,
          title: building.name_1 + ' '  + building.address_1
        });
        if (debug){
          // enable map pin correction
          buildingMarker.setDraggable(true);
          google.maps.event.addDomListener(buildingMarker, 'dragend', function(evt){
            var newLocation = evt.latLng;
                position = {coords:{
                  latitude: newLocation.lat(),
                  longitude: newLocation.lng()
                }};
            $.post('/building/' + building.elbi + '/', position, function(data){
              console.log(data);
            });
          });
        }
        buildingMarkers.push(buildingMarker);
        bounds.extend(position);
      });
      map.fitBounds(bounds);
    }
  };

  // exports
  exports.loadMap = load;

})(window, window.jQuery);

// app
(function(exports, $){
  "use strict";

  var store;

  var storeData = function(data){
    store = data;
    console.log(store.length);
    exports.store = store;
    $(window).trigger('storeAvailable');
  };

  $.getJSON('/chart/locator/data/', storeData);

  var searchZip = function(zip_code){
    var searchZipFilter = function(x){
      return x.zip_code == zip_code;
    };
    return store.filter(searchZipFilter);
  };

  // distance approximators
  /** Converts numeric degrees to radians */
  if (!Number.prototype.toRad){
    Number.prototype.toRad = function(){
      return this * Math.PI / 180;
    };
  }

  // http://www.movable-type.co.uk/scripts/latlong.html
  var distance = {
    R: 63741,  // kilometers
    haversine: function(lat1, lng1, lat2, lng2){
      var dLat = (lat2-lat1).toRad();
      var dLon = (lng2-lng1).toRad();
      lat1 = lat1.toRad();
      lat2 = lat2.toRad();

      var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
        Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
      var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
      return distance.R * c;
    },
    spherical: function(lat1, lng1, lat2, lng2){
      lat1 = lat1.toRad();
      lat2 = lat2.toRad();
      lng1 = lng1.toRad();
      lng2 = lng2.toRad();
      return Math.acos(Math.sin(lat1)*Math.sin(lat2) +
        Math.cos(lat1) * Math.cos(lat2) *
        Math.cos(lng2 - lng1)) * distance.R;
    },
    pythagorean: function(lat1, lng1, lat2, lng2){
      lat1 = lat1.toRad();
      lng1 = lng1.toRad();
      lat2 = lat2.toRad();
      lng2 = lng2.toRad();
      var x = (lng2 - lng1) * Math.cos((lat1 + lat2) / 2),
          y = lat2 - lat1;
      return Math.sqrt(x * x + y * y) * distance.R;
    },
    magic: function(lat1, lng1, lat2, lng2){
      return (Math.abs(lat1 - lat2) + Math.abs(lng1 - lng2)) * distance.R;
    }
  };

  var closestBuildings = function(lat, lng){
    var metric = distance.spherical;
    // go ahead and sort in place.
    store.sort(function(a, b){
      // TODO make a lookup table?
      return metric(lat, lng, a.latitude, a.longitude) - metric(lat, lng, b.latitude, b.longitude);
    });
    return store.slice(0, 10);
  };

  // hook up UI
  if (!navigator.geolocation){
    $('body').addClass('no-geolocation');
  }

  // Get the url for a building based on server-side url pattern
  var buildingToUrl = function(building){
    return '/building/' + building.elbi + '/';
  };

  var gotPosition = function(position){
    var lat = position.coords.latitude,
        lng = position.coords.longitude;
    var $container = $('#nearest').empty(),
        buildings = closestBuildings(lat, lng);
    $.each(buildings, function(idx, building){
      $container.append('
' +
        '' + building.name_1 + ', ' +
        building.address_1 + ', ' +
        building.city +
        '
');
    });

    window.loadMap(position.coords, buildings);
  };

  $('#locate').on('click', function(){
    navigator.geolocation.getCurrentPosition(gotPosition);
  });

  $(window).on('storeAvailable', function(){
    var data = $('#building-data').data('building');
    if (data && data.latitude){
      gotPosition({
          coords:{
            latitude: data.latitude,
            longitude: data.longitude
          }
      });
    } else {
      navigator.geolocation.getCurrentPosition(gotPosition);
    }
  });

  // exports
  // exports.searchZip = searchZip;
  // exports.d = distance;
  exports.getClosestBuildings = gotPosition;

})(window, window.jQuery);
// "76104"
<|endoftext|>


<commit_before>import _, { messages } from 'intl'
import ActionButton from 'action-button'
import decorate from 'apply-decorators'
import Icon from 'icon'
import React from 'react'
import SortedTable from 'sorted-table'
import StateButton from 'state-button'
import Tooltip from 'tooltip'
import {
  addSubscriptions,
  formatSize,
  formatSpeed,
  generateRandomId,
  noop,
} from 'utils'
import { alert } from 'modal'
import { format, parse } from 'xo-remote-parser'
import { get } from '@xen-orchestra/defined'
import { groupBy, map, isEmpty } from 'lodash'
import { injectIntl } from 'react-intl'
import { injectState, provideState } from 'reaclette'
import { Number, Password, Text } from 'editable'

import {
  deleteRemote,
  deleteRemotes,
  disableRemote,
  editRemote,
  enableRemote,
  subscribeRemotes,
  subscribeRemotesInfo,
  testRemote,
} from 'xo'

import Remote from './remote'

const _changeUrlElement = (value, { remote, element }) =>
  editRemote(remote, {
    url: format({ ...remote, [element]: value === null ? undefined : value }),
  })
const _showError = remote => alert(_('remoteConnectionFailed'), remote.error)
const _editRemoteName = (name, { remote }) => editRemote(remote, { name })
const _editRemoteOptions = (options, { remote }) =>
  editRemote(remote, { options: options !== '' ? options : null })

const COLUMN_NAME = {
  itemRenderer: (remote, { formatMessage }) => (
    <Text
      data-remote={remote}
      onChange={_editRemoteName}
      placeholder={formatMessage(messages.remoteMyNamePlaceHolder)}
      value={remote.name}
    />
  ),
  name: _('remoteName'),
  sortCriteria: 'name',
}
const COLUMN_STATE = {
  itemRenderer: remote => (

      <StateButton
        disabledLabel={_('remoteDisabled')}
        disabledHandler={enableRemote}
        disabledTooltip={_('enableRemote')}
        enabledLabel={_('remoteEnabled')}
        enabledHandler={disableRemote}
        enabledTooltip={_('disableRemote')}
        handlerParam={remote}
        state={remote.enabled}
      />{' '}
      {remote.error && (
        <Tooltip content={_('remoteConnectionFailed')}>
           _showError(remote)}
            style={{ padding: '0px' }}
          >
            <Icon icon='alarm' size='lg' />

        </Tooltip>
      )}

  ),
  name: _('remoteState'),
}
const COLUMN_DISK = {
  itemRenderer: remote =>
    remote.info !== undefined &&
    remote.info.used !== undefined &&
    remote.info.size !== undefined && (

        {`${formatSize(remote.info.used)} / ${formatSize(remote.info.size)}`}

    ),
  name: _('remoteDisk'),
}
const COLUMN_SPEED = {
  itemRenderer: remote => {
    const benchmark = get(() => remote.benchmarks[remote.benchmarks.length - 1])

    return (
      benchmark !== undefined && (
        {`${formatSpeed(benchmark.writeRate, 1e3)} / ${formatSpeed(
          benchmark.readRate,
          1e3
        )}`}
      )
    )
  },
  name: (

      {_('remoteSpeed')}{' '}
      <Tooltip content={_('remoteSpeedInfo')}>
        <Icon icon='info' size='lg' />
      </Tooltip>

  ),
}

const fixRemoteUrl = remote => editRemote(remote, { url: format(remote) })
const COLUMNS_LOCAL_REMOTE = [
  COLUMN_NAME,
  {
    itemRenderer: (remote, { formatMessage }) => (
      <Text
        data-element='path'
        data-remote={remote}
        onChange={_changeUrlElement}
        placeholder={formatMessage(messages.remoteLocalPlaceHolderPath)}
        value={remote.path}
      />
    ),
    name: _('remotePath'),
  },
  COLUMN_STATE,
  COLUMN_DISK,
  COLUMN_SPEED,
]
const COLUMNS_NFS_REMOTE = [
  COLUMN_NAME,
  {
    itemRenderer: (remote, { formatMessage }) => (

        \\
        <Text
          data-element='host'
          data-remote={remote}
          onChange={_changeUrlElement}
          placeholder={formatMessage(messages.remoteNfsPlaceHolderHost)}
          value={remote.host}
        />
        :
        <Number
          data-element='port'
          data-remote={remote}
          nullable
          onChange={_changeUrlElement}
          placeholder={formatMessage(messages.remoteNfsPlaceHolderPort)}
          value={remote.port || ''}
        />
        :
        <Text
          data-element='path'
          data-remote={remote}
          onChange={_changeUrlElement}
          placeholder={formatMessage(messages.remoteNfsPlaceHolderPath)}
          value={remote.path}
        />{' '}
        {remote.invalidUrl && (
          <ActionButton
            btnStyle='danger'
            handler={fixRemoteUrl}
            handlerParam={remote}
            icon='alarm'
            size='small'
            tooltip={_('remoteErrorMessage', {
              url: remote.url,
              newUrl: format(remote),
            })}
          />
        )}

    ),

    name: _('remoteDevice'),
  },
  {
    name: _('remoteOptions'),
    itemRenderer: remote => (
      <Text
        data-remote={remote}
        onChange={_editRemoteOptions}
        value={remote.options || ''}
      />
    ),
  },
  COLUMN_STATE,
  COLUMN_DISK,
  COLUMN_SPEED,
]
const COLUMNS_SMB_REMOTE = [
  COLUMN_NAME,
  {
    itemRenderer: (remote, { formatMessage }) => (

        \\
        <Text
          data-element='host'
          data-remote={remote}
          onChange={_changeUrlElement}
          value={remote.host}
        />
        \

          <Text
            data-element='path'
            data-remote={remote}
            onChange={_changeUrlElement}
            placeholder={formatMessage(messages.remoteSmbPlaceHolderRemotePath)}
            value={remote.path}
          />

    ),
    name: _('remoteShare'),
  },
  COLUMN_STATE,
  {
    itemRenderer: (remote, { formatMessage }) => (

        <Text
          data-element='username'
          data-remote={remote}
          onChange={_changeUrlElement}
          value={remote.username}
        />
        :
        <Password
          data-element='password'
          data-remote={remote}
          onChange={_changeUrlElement}
          placeholder={formatMessage(messages.remotePlaceHolderPassword)}
          value=''
        />
        @
        <Text
          data-element='domain'
          data-remote={remote}
          onChange={_changeUrlElement}
          value={remote.domain}
        />

    ),
    name: _('remoteAuth'),
  },
  COLUMN_SPEED,
]

const GROUPED_ACTIONS = [
  {
    handler: deleteRemotes,
    icon: 'delete',
    label: _('remoteDeleteSelected'),
    level: 'danger',
  },
]

const INDIVIDUAL_ACTIONS = [
  {
    disabled: remote => !remote.enabled,
    handler: remote =>
      testRemote(remote).then(
        answer =>
          answer.success
            ? alert(

                  <Icon icon='success' />{' '}
                  {_('remoteTestSuccess', { name: remote.name })}
                ,
                _('remoteTestSuccessMessage')
              )
            : alert(

                  <Icon icon='error' />{' '}
                  {_('remoteTestFailure', { name: remote.name })}
                ,

{_('remoteTestError')}

{answer.error}

{_('remoteTestStep')}

{answer.step}

              ),
        noop
      ),
    icon: 'diagnosis',
    label: _('remoteTestTip'),
    level: 'primary',
  },
  {
    handler: (remote, { editRemote }) => editRemote(remote),
    icon: 'edit',
    label: _('formEdit'),
    level: 'primary',
  },
  {
    handler: deleteRemote,
    icon: 'delete',
    label: _('remoteDeleteTip'),
    level: 'danger',
  },
]
const FILTERS = {
  filterRemotesOnlyConnected: 'enabled?',
  filterRemotesOnlyDisconnected: '!enabled?',
}

export default decorate([
  addSubscriptions({
    remotes: subscribeRemotes,
    remotesInfo: subscribeRemotesInfo,
  }),
  injectIntl,
  provideState({
    initialState: () => ({
      formKey: generateRandomId(),
      remote: undefined,
    }),
    effects: {
      reset: () => () => ({
        formKey: generateRandomId(),
        remote: undefined,
      }),
      editRemote: (_, remote) => () => ({
        remote,
      }),
    },
    computed: {
      remoteWithInfo: (_, { remotes, remotesInfo }) =>
        groupBy(
          map(remotes, remote => ({
            ...parse(remote.url),
            ...remote,
            info: remotesInfo !== undefined ? remotesInfo[remote.id] : {},
          })),
          'type'
        ),
    },
  }),
  injectState,
  ({ state, effects, remotes = {}, intl: { formatMessage } }) =>
    console.log(remotes) || (

        {!isEmpty(state.remoteWithInfo.file) && (

{_('remoteTypeLocal')}

            <SortedTable
              collection={state.remoteWithInfo.file}
              columns={COLUMNS_LOCAL_REMOTE}
              data-editRemote={effects.editRemote}
              data-formatMessage={formatMessage}
              filters={FILTERS}
              groupedActions={GROUPED_ACTIONS}
              individualActions={INDIVIDUAL_ACTIONS}
              stateUrlParam='l'
            />

        )}

        {!isEmpty(state.remoteWithInfo.nfs) && (

{_('remoteTypeNfs')}

            <SortedTable
              collection={state.remoteWithInfo.nfs}
              columns={COLUMNS_NFS_REMOTE}
              data-editRemote={effects.editRemote}
              data-formatMessage={formatMessage}
              filters={FILTERS}
              groupedActions={GROUPED_ACTIONS}
              individualActions={INDIVIDUAL_ACTIONS}
              stateUrlParam='nfs'
            />

        )}

        {!isEmpty(state.remoteWithInfo.smb) && (

{_('remoteTypeSmb')}

            <SortedTable
              collection={state.remoteWithInfo.smb}
              columns={COLUMNS_SMB_REMOTE}
              data-editRemote={effects.editRemote}
              data-formatMessage={formatMessage}
              filters={FILTERS}
              groupedActions={GROUPED_ACTIONS}
              individualActions={INDIVIDUAL_ACTIONS}
              stateUrlParam='smb'
            />

        )}
        <Remote formatMessage={formatMessage} key={state.formKey} />

    ),
])
<commit_msg>fix(xo-web/remotes): remove console.log (#4065)<commit_after>import _, { messages } from 'intl'
import ActionButton from 'action-button'
import decorate from 'apply-decorators'
import Icon from 'icon'
import React from 'react'
import SortedTable from 'sorted-table'
import StateButton from 'state-button'
import Tooltip from 'tooltip'
import {
  addSubscriptions,
  formatSize,
  formatSpeed,
  generateRandomId,
  noop,
} from 'utils'
import { alert } from 'modal'
import { format, parse } from 'xo-remote-parser'
import { get } from '@xen-orchestra/defined'
import { groupBy, map, isEmpty } from 'lodash'
import { injectIntl } from 'react-intl'
import { injectState, provideState } from 'reaclette'
import { Number, Password, Text } from 'editable'

import {
  deleteRemote,
  deleteRemotes,
  disableRemote,
  editRemote,
  enableRemote,
  subscribeRemotes,
  subscribeRemotesInfo,
  testRemote,
} from 'xo'

import Remote from './remote'

const _changeUrlElement = (value, { remote, element }) =>
  editRemote(remote, {
    url: format({ ...remote, [element]: value === null ? undefined : value }),
  })
const _showError = remote => alert(_('remoteConnectionFailed'), remote.error)
const _editRemoteName = (name, { remote }) => editRemote(remote, { name })
const _editRemoteOptions = (options, { remote }) =>
  editRemote(remote, { options: options !== '' ? options : null })

const COLUMN_NAME = {
  itemRenderer: (remote, { formatMessage }) => (
    <Text
      data-remote={remote}
      onChange={_editRemoteName}
      placeholder={formatMessage(messages.remoteMyNamePlaceHolder)}
      value={remote.name}
    />
  ),
  name: _('remoteName'),
  sortCriteria: 'name',
}
const COLUMN_STATE = {
  itemRenderer: remote => (

      <StateButton
        disabledLabel={_('remoteDisabled')}
        disabledHandler={enableRemote}
        disabledTooltip={_('enableRemote')}
        enabledLabel={_('remoteEnabled')}
        enabledHandler={disableRemote}
        enabledTooltip={_('disableRemote')}
        handlerParam={remote}
        state={remote.enabled}
      />{' '}
      {remote.error && (
        <Tooltip content={_('remoteConnectionFailed')}>
           _showError(remote)}
            style={{ padding: '0px' }}
          >
            <Icon icon='alarm' size='lg' />

        </Tooltip>
      )}

  ),
  name: _('remoteState'),
}
const COLUMN_DISK = {
  itemRenderer: remote =>
    remote.info !== undefined &&
    remote.info.used !== undefined &&
    remote.info.size !== undefined && (

        {`${formatSize(remote.info.used)} / ${formatSize(remote.info.size)}`}

    ),
  name: _('remoteDisk'),
}
const COLUMN_SPEED = {
  itemRenderer: remote => {
    const benchmark = get(() => remote.benchmarks[remote.benchmarks.length - 1])

    return (
      benchmark !== undefined && (
        {`${formatSpeed(benchmark.writeRate, 1e3)} / ${formatSpeed(
          benchmark.readRate,
          1e3
        )}`}
      )
    )
  },
  name: (

      {_('remoteSpeed')}{' '}
      <Tooltip content={_('remoteSpeedInfo')}>
        <Icon icon='info' size='lg' />
      </Tooltip>

  ),
}

const fixRemoteUrl = remote => editRemote(remote, { url: format(remote) })
const COLUMNS_LOCAL_REMOTE = [
  COLUMN_NAME,
  {
    itemRenderer: (remote, { formatMessage }) => (
      <Text
        data-element='path'
        data-remote={remote}
        onChange={_changeUrlElement}
        placeholder={formatMessage(messages.remoteLocalPlaceHolderPath)}
        value={remote.path}
      />
    ),
    name: _('remotePath'),
  },
  COLUMN_STATE,
  COLUMN_DISK,
  COLUMN_SPEED,
]
const COLUMNS_NFS_REMOTE = [
  COLUMN_NAME,
  {
    itemRenderer: (remote, { formatMessage }) => (

        \\
        <Text
          data-element='host'
          data-remote={remote}
          onChange={_changeUrlElement}
          placeholder={formatMessage(messages.remoteNfsPlaceHolderHost)}
          value={remote.host}
        />
        :
        <Number
          data-element='port'
          data-remote={remote}
          nullable
          onChange={_changeUrlElement}
          placeholder={formatMessage(messages.remoteNfsPlaceHolderPort)}
          value={remote.port || ''}
        />
        :
        <Text
          data-element='path'
          data-remote={remote}
          onChange={_changeUrlElement}
          placeholder={formatMessage(messages.remoteNfsPlaceHolderPath)}
          value={remote.path}
        />{' '}
        {remote.invalidUrl && (
          <ActionButton
            btnStyle='danger'
            handler={fixRemoteUrl}
            handlerParam={remote}
            icon='alarm'
            size='small'
            tooltip={_('remoteErrorMessage', {
              url: remote.url,
              newUrl: format(remote),
            })}
          />
        )}

    ),

    name: _('remoteDevice'),
  },
  {
    name: _('remoteOptions'),
    itemRenderer: remote => (
      <Text
        data-remote={remote}
        onChange={_editRemoteOptions}
        value={remote.options || ''}
      />
    ),
  },
  COLUMN_STATE,
  COLUMN_DISK,
  COLUMN_SPEED,
]
const COLUMNS_SMB_REMOTE = [
  COLUMN_NAME,
  {
    itemRenderer: (remote, { formatMessage }) => (

        \\
        <Text
          data-element='host'
          data-remote={remote}
          onChange={_changeUrlElement}
          value={remote.host}
        />
        \

          <Text
            data-element='path'
            data-remote={remote}
            onChange={_changeUrlElement}
            placeholder={formatMessage(messages.remoteSmbPlaceHolderRemotePath)}
            value={remote.path}
          />

    ),
    name: _('remoteShare'),
  },
  COLUMN_STATE,
  {
    itemRenderer: (remote, { formatMessage }) => (

        <Text
          data-element='username'
          data-remote={remote}
          onChange={_changeUrlElement}
          value={remote.username}
        />
        :
        <Password
          data-element='password'
          data-remote={remote}
          onChange={_changeUrlElement}
          placeholder={formatMessage(messages.remotePlaceHolderPassword)}
          value=''
        />
        @
        <Text
          data-element='domain'
          data-remote={remote}
          onChange={_changeUrlElement}
          value={remote.domain}
        />

    ),
    name: _('remoteAuth'),
  },
  COLUMN_SPEED,
]

const GROUPED_ACTIONS = [
  {
    handler: deleteRemotes,
    icon: 'delete',
    label: _('remoteDeleteSelected'),
    level: 'danger',
  },
]

const INDIVIDUAL_ACTIONS = [
  {
    disabled: remote => !remote.enabled,
    handler: remote =>
      testRemote(remote).then(
        answer =>
          answer.success
            ? alert(

                  <Icon icon='success' />{' '}
                  {_('remoteTestSuccess', { name: remote.name })}
                ,
                _('remoteTestSuccessMessage')
              )
            : alert(

                  <Icon icon='error' />{' '}
                  {_('remoteTestFailure', { name: remote.name })}
                ,

{_('remoteTestError')}

{answer.error}

{_('remoteTestStep')}

{answer.step}

              ),
        noop
      ),
    icon: 'diagnosis',
    label: _('remoteTestTip'),
    level: 'primary',
  },
  {
    handler: (remote, { editRemote }) => editRemote(remote),
    icon: 'edit',
    label: _('formEdit'),
    level: 'primary',
  },
  {
    handler: deleteRemote,
    icon: 'delete',
    label: _('remoteDeleteTip'),
    level: 'danger',
  },
]
const FILTERS = {
  filterRemotesOnlyConnected: 'enabled?',
  filterRemotesOnlyDisconnected: '!enabled?',
}

export default decorate([
  addSubscriptions({
    remotes: subscribeRemotes,
    remotesInfo: subscribeRemotesInfo,
  }),
  injectIntl,
  provideState({
    initialState: () => ({
      formKey: generateRandomId(),
      remote: undefined,
    }),
    effects: {
      reset: () => () => ({
        formKey: generateRandomId(),
        remote: undefined,
      }),
      editRemote: (_, remote) => () => ({
        remote,
      }),
    },
    computed: {
      remoteWithInfo: (_, { remotes, remotesInfo }) =>
        groupBy(
          map(remotes, remote => ({
            ...parse(remote.url),
            ...remote,
            info: remotesInfo !== undefined ? remotesInfo[remote.id] : {},
          })),
          'type'
        ),
    },
  }),
  injectState,
  ({ state, effects, remotes = {}, intl: { formatMessage } }) => (

      {!isEmpty(state.remoteWithInfo.file) && (

{_('remoteTypeLocal')}

          <SortedTable
            collection={state.remoteWithInfo.file}
            columns={COLUMNS_LOCAL_REMOTE}
            data-editRemote={effects.editRemote}
            data-formatMessage={formatMessage}
            filters={FILTERS}
            groupedActions={GROUPED_ACTIONS}
            individualActions={INDIVIDUAL_ACTIONS}
            stateUrlParam='l'
          />

      )}

      {!isEmpty(state.remoteWithInfo.nfs) && (

{_('remoteTypeNfs')}

          <SortedTable
            collection={state.remoteWithInfo.nfs}
            columns={COLUMNS_NFS_REMOTE}
            data-editRemote={effects.editRemote}
            data-formatMessage={formatMessage}
            filters={FILTERS}
            groupedActions={GROUPED_ACTIONS}
            individualActions={INDIVIDUAL_ACTIONS}
            stateUrlParam='nfs'
          />

      )}

      {!isEmpty(state.remoteWithInfo.smb) && (

{_('remoteTypeSmb')}

          <SortedTable
            collection={state.remoteWithInfo.smb}
            columns={COLUMNS_SMB_REMOTE}
            data-editRemote={effects.editRemote}
            data-formatMessage={formatMessage}
            filters={FILTERS}
            groupedActions={GROUPED_ACTIONS}
            individualActions={INDIVIDUAL_ACTIONS}
            stateUrlParam='smb'
          />

      )}
      <Remote formatMessage={formatMessage} key={state.formKey} />

  ),
])
<|endoftext|>