<commit_before>openerp.web.data_import = function(openerp) {
var QWeb = openerp.web.qweb;
/**
 * Safari does not deal well at all with raw JSON data being returned. As a
 * result, we're going to cheat by using a pseudo-jsonp: instead of getting
 * JSON data in the iframe, we're getting a ``script`` tag which consists of a
 * function call and the returned data (the json dump).
 *
 * The function is an auto-generated name bound to ``window``, which calls
 * back into the callback provided here.
 *
 * @param {Object} form the form element (DOM or jQuery) to use in the call
 * @param {Object} attributes jquery.form attributes object
 * @param {Function} callback function to call with the returned data
 */
function jsonp(form, attributes, callback) {
    var options = {jsonp: _.uniqueId('import_callback_')};
    window[options.jsonp] = function () {
        delete window[options.jsonp];
        callback.apply(null, arguments);
    };
    $(form).ajaxSubmit(_.extend({
        data: options
    }, attributes));
}
openerp.web.DataImport = openerp.web.Dialog.extend({
    template: 'ImportDataView',
    dialog_title: "Import Data",
    init: function(parent, dataset){
        this._super(parent, {});
    },
    start: function() {
        var self = this;
        this._super();
        this.open({
            modal:true,
            width:'70%',
            height:'auto',
            position:'top',
            buttons:{
                "Close":function() {
                    self.stop();
                },
                "Import File":function() {
                    self.do_import();
                }
            },
            close:function(event, ui) {
                self.stop();
            }
        });
        this.$element.find('#csvfile').change(this.on_autodetect_data);
        this.$element.find('fieldset').change(this.on_autodetect_data);
        this.$element.find('fieldset legend').click(function () {
                $(this).next().toggle();
        });
    },
    do_import: function() {
            var self = this;
            if(!this.$element.find('#csvfile').val()) { return; }
            jsonp(this.$element.find('#import_data'), {
                url: '/web/import/import_data'
            }, this.on_import_results);
    },
    on_autodetect_data: function() {
            var self = this;
            if(this.$element.find("#res td")){
                this.$element.find("#res td").remove();
                this.$element.find("#imported_success").css('display','none');
            }
            if(!this.$element.find('#csvfile').val()) { return; }
            jsonp(this.$element.find('#import_data'), {
                url: '/web/import/detect_data'
            }, this.on_import_results);
    },
    on_import_results:function(results){
        var self = this;
        this.$element.find('#result, #success').empty();
        var result_node = $("#result");
        var records = {};

        if (results['records']){
            records = {'header':results['header'],'sel':results['all_fields'],'row':results['records']};
            result_node.append(QWeb.render('ImportView-result',{'records':records}));
        }else if(results['error']){
            result_node.append(QWeb.render('ImportView-result',{'error': results['error']}));
        }else if(results['success']){
            self.stop();
            if (this.widget_parent.widget_parent.active_view == "list"){
                this.widget_parent.reload_content();
            }
        }
        this.do_check_req_field(results['req_field']);
        this.on_change_check(results['req_field']);
        this.$element.find("td #sel_field").change(function(){
            self.on_change_check(results['req_field']);
        });
    },
    on_change_check:function(req_field){
            var self = this;
            self.$element.find("#message, #msg").remove();
            var selected_flds = self.$element.find("td #sel_field option:selected");
            _.each(selected_flds,function(fld){
                if (fld.index != 0){
                    var res = self.$element.find("td #sel_field option:selected[value='"+ fld.value +"']");
                    if (res.length == 1){
                        res.parent().removeClass("duplicate_fld").addClass("select_fld");
                    }else if(res.length > 1){
                        res.parent().removeClass("select_fld").addClass("duplicate_fld");
                        res.parent().focus();
                    }
                }else{
                    var elem = self.$element.find("td #sel_field option:selected[value='"+ fld.value +"']");
                    elem.parent().removeClass("duplicate_fld").addClass("select_fld");
                }
            });
            if(self.$element.find(".duplicate_fld").length){
                $("#result").before('
*Selected column should not be same.
');
                $(".ui-button-text:contains('Import File')").parent().attr("disabled",true);
            }else{
                self.$element.find("#msg").remove();
                $(".ui-button-text:contains('Import File')").parent().attr("disabled",false);
            }
            self.do_check_req_field(req_field);

    },
    do_check_req_field: function(req_fld){
        var self = this;
        if (req_fld.length){
            self.$element.find("#message").remove();
            var sel_fields = _.map(this.$element.find("td #sel_field option:selected"), function(fld){
                return fld['text']
            });
            var required_fields = _.filter(req_fld, function(fld){
                return !_.contains(sel_fields, fld)
            });
            if (required_fields.length){
                $("#result").before('
*Required Fields are not selected : '+required_fields+'.
');
                $(".ui-button-text:contains('Import File')").parent().attr("disabled",true);
            }else{
                self.$element.find("#message").remove();
                $(".ui-button-text:contains('Import File')").parent().attr("disabled",false);
            }
        }
    },
    stop: function() {
        $(this.$dialog).remove();
        this._super();
    }
});
};
<commit_msg>[IMP] style fixes<commit_after>openerp.web.data_import = function(openerp) {
var QWeb = openerp.web.qweb;
/**
 * Safari does not deal well at all with raw JSON data being returned. As a
 * result, we're going to cheat by using a pseudo-jsonp: instead of getting
 * JSON data in the iframe, we're getting a ``script`` tag which consists of a
 * function call and the returned data (the json dump).
 *
 * The function is an auto-generated name bound to ``window``, which calls
 * back into the callback provided here.
 *
 * @param {Object} form the form element (DOM or jQuery) to use in the call
 * @param {Object} attributes jquery.form attributes object
 * @param {Function} callback function to call with the returned data
 */
function jsonp(form, attributes, callback) {
    var options = {jsonp: _.uniqueId('import_callback_')};
    window[options.jsonp] = function () {
        delete window[options.jsonp];
        callback.apply(null, arguments);
    };
    $(form).ajaxSubmit(_.extend({
        data: options
    }, attributes));
}
openerp.web.DataImport = openerp.web.Dialog.extend({
    template: 'ImportDataView',
    dialog_title: "Import Data",
    init: function(parent, dataset){
        this._super(parent, {});
    },
    start: function() {
        var self = this;
        this._super();
        this.open({
            modal: true,
            width: '70%',
            height: 'auto',
            position: 'top',
            buttons: {
                "Close": function() {
                    self.stop();
                },
                "Import File": function() {
                    self.do_import();
                }
            },
            close: function(event, ui) {
                self.stop();
            }
        });
        this.$element.find('#csvfile').change(this.on_autodetect_data);
        this.$element.find('fieldset').change(this.on_autodetect_data);
        this.$element.find('fieldset legend').click(function() {
            $(this).next().toggle();
        });
    },
    do_import: function() {
        if(!this.$element.find('#csvfile').val()) { return; }
        jsonp(this.$element.find('#import_data'), {
            url: '/web/import/import_data'
        }, this.on_import_results);
    },
    on_autodetect_data: function() {
        if(this.$element.find("#res td")){
            this.$element.find("#res td").remove();
            this.$element.find("#imported_success").css('display','none');
        }
        if(!this.$element.find('#csvfile').val()) { return; }
        jsonp(this.$element.find('#import_data'), {
            url: '/web/import/detect_data'
        }, this.on_import_results);
    },
    on_import_results: function(results) {
        this.$element.find('#result, #success').empty();
        var result_node = $("#result");
        var records = {};

        if (results['records']) {
            records = {'header': results['header'], 'sel': results['all_fields'], 'row': results['records']};
            result_node.append(QWeb.render('ImportView-result', {'records': records}));
        } else if (results['error']) {
            result_node.append(QWeb.render('ImportView-result', {'error': results['error']}));
        } else if (results['success']) {
            self.stop();
            if (this.widget_parent.widget_parent.active_view == "list") {
                this.widget_parent.reload_content();
            }
        }
        this.do_check_req_field(results['req_field']);
        this.on_change_check(results['req_field']);
        var self = this;
        this.$element.find("td #sel_field").change(function() {
            self.on_change_check(results['req_field']);
        });
    },
    on_change_check: function (req_field){
        var self = this;
        self.$element.find("#message, #msg").remove();
        var selected_flds = self.$element.find("td #sel_field option:selected");
        _.each(selected_flds, function(fld) {
            if (fld.index != 0) {
                var res = self.$element.find("td #sel_field option:selected[value='" + fld.value + "']");
                if (res.length == 1) {
                    res.parent().removeClass("duplicate_fld").addClass("select_fld");
                } else if (res.length > 1) {
                    res.parent().removeClass("select_fld").addClass("duplicate_fld");
                    res.parent().focus();
                }
            } else {
                var elem = self.$element.find("td #sel_field option:selected[value='" + fld.value + "']");
                elem.parent().removeClass("duplicate_fld").addClass("select_fld");
            }
        });
        if (self.$element.find(".duplicate_fld").length) {
            $("#result").before('
*Selected column should not be same.
');
            $(".ui-button-text:contains('Import File')").parent().attr("disabled", true);
        } else {
            self.$element.find("#msg").remove();
            $(".ui-button-text:contains('Import File')").parent().attr("disabled", false);
        }
        self.do_check_req_field(req_field);
    },
    do_check_req_field: function(req_fld) {
        var self = this;
        if (req_fld.length) {
            self.$element.find("#message").remove();
            var sel_fields = _.map(this.$element.find("td #sel_field option:selected"), function(fld) {
                return fld['text']
            });
            var required_fields = _.filter(req_fld, function(fld) {
                return !_.contains(sel_fields, fld)
            });
            if (required_fields.length) {
                $("#result").before('
*Required Fields are not selected : ' + required_fields + '.
');
                $(".ui-button-text:contains('Import File')").parent().attr("disabled", true);
            } else {
                self.$element.find("#message").remove();
                $(".ui-button-text:contains('Import File')").parent().attr("disabled", false);
            }
        }
    },
    stop: function() {
        $(this.$dialog).remove();
        this._super();
    }
});
};
<|endoftext|>


<commit_before>'use strict';

import createHistory from 'history/lib/createBrowserHistory';
import useQueries from 'history/lib/useQueries';
import Store from 'store-prototype';
import List from '../lib/List';

let history = useQueries(createHistory)();

let _location = null;
let _ready = false;
let _loading = false;
let _items = {};

const _list = new List(Keystone.list);

const active = {
	columns: _list.expandColumns(Keystone.list.defaultColumns),
	filters: [],
	search: '',
	sort: _list.expandSort(Keystone.list.defaultSort)
};

const page = {
	size: 100,
	index: 1
};

function updateQueryParams (params) {
	if (!_location) return;
	let newParams = Object.assign({}, _location.query);
	Object.keys(params).forEach(i => {
		if (params[i]) newParams[i] = params[i];
		else delete newParams[i];
	});
	history.pushState(null, _location.pathname, newParams);
}

const CurrentListStore = new Store({
	getList () {
		return _list;
	},
	getAvailableColumns () {
		return _list.columns;
	},
	getActiveColumns () {
		return active.columns;
	},
	setActiveColumns (cols) {
		active.columns = _list.expandColumns(cols);
		this.loadItems();
	},
	getActiveSearch () {
		return active.search;
	},
	setActiveSearch (str) {
		updateQueryParams({ search: str });
	},
	getActiveSort () {
		return active.sort;
	},
	setActiveSort (sort) {
		active.sort = _list.expandSort(sort || _list.defaultSort);
		this.loadItems();
		this.notifyChange();
	},
	getAvailableFilters () {
		return _list.columns.filter(col => col.field && col.field.hasFilterMethod);
	},
	getActiveFilters () {
		return active.filters;
	},
	getFilter (path) {
		return active.filters.filter(i => i.field.path === path)[0];
	},
	setFilter (path, value) {
		let filter = active.filters.filter(i => i.field.path === path)[0];
		if (filter) {
			filter.value = value;
		} else {
			let field = _list.fields[path];
			if (!field) {
				console.warn('Invalid Filter path specified:', path);
				return;
			}
			filter = { field, value };
			active.filters.push(filter);
		}
		this.setCurrentPage(1);
		this.notifyChange();
	},
	clearFilter (path) {
		var filter = active.filters.filter(i => i.field.path === path)[0];
		if (!filter) return;
		active.filters.splice(active.filters.indexOf(filter), 1);
		this.loadItems();
		this.notifyChange();
	},
	clearAllFilters () {
		active.filters = [];
		this.loadItems();
		this.notifyChange();
	},
	getPageSize () {
		return page.size;
	},
	getCurrentPage () {
		return page.index;
	},
	setCurrentPage (i) {
		page.index = i;
		this.loadItems();
	},
	isLoading () {
		return _loading;
	},
	isReady () {
		return _ready;
	},
	loadItems () {
		_loading = true;
		_list.loadItems({
			search: active.search,
			filters: active.filters,
			sort: active.sort,
			columns: active.columns,
			page: page
		}, (err, items) => {
			_loading = false;
			// TODO: graceful error handling
			if (items) {
				_ready = true;
				_items = items;
			}
			this.notifyChange();
		});
	},
	getItems () {
		return _items;
	},
	deleteItem (itemId) {
		_list.deleteItem(itemId, (err, data) => {
			// TODO: graceful error handling
			this.loadItems();
		});
	},
	deleteItems (itemIds) {
		_list.deleteItems(itemIds, (err, data) => {
			// TODO: graceful error handling
			this.loadItems();
		});
	},
	downloadItems (format, columns) {
		var url = _list.getDownloadURL({
			search: active.search,
			filters: active.filters,
			sort: active.sort,
			columns: columns ? _list.expandColumns(columns) : active.columns,
			format: format
		});
		window.open(url);
	}
});

history.listen(function (location) {
	_location = location;
	let querySearch = location.query.search || '';
	if (active.search !== querySearch) {
		active.search = querySearch;
		CurrentListStore.loadItems();
		CurrentListStore.notifyChange();
	}
});

module.exports = CurrentListStore;
<commit_msg>Using replaceState for nicer history w/ search-as-you-type<commit_after>'use strict';

import createHistory from 'history/lib/createBrowserHistory';
import useQueries from 'history/lib/useQueries';
import Store from 'store-prototype';
import List from '../lib/List';

let history = useQueries(createHistory)();

let _location = null;
let _ready = false;
let _loading = false;
let _items = {};

const _list = new List(Keystone.list);

const active = {
	columns: _list.expandColumns(Keystone.list.defaultColumns),
	filters: [],
	search: '',
	sort: _list.expandSort(Keystone.list.defaultSort)
};

const page = {
	size: 100,
	index: 1
};

function updateQueryParams (params, replace) {
	if (!_location) return;
	let newParams = Object.assign({}, _location.query);
	Object.keys(params).forEach(i => {
		if (params[i]) newParams[i] = params[i];
		else delete newParams[i];
	});
	history[replace ? 'replaceState' : 'pushState'](null, _location.pathname, newParams);
}

const CurrentListStore = new Store({
	getList () {
		return _list;
	},
	getAvailableColumns () {
		return _list.columns;
	},
	getActiveColumns () {
		return active.columns;
	},
	setActiveColumns (cols) {
		active.columns = _list.expandColumns(cols);
		this.loadItems();
	},
	getActiveSearch () {
		return active.search;
	},
	setActiveSearch (str) {
		// starting or clearing a search pushes a new history state, but updating
		// the current search replaces it for nicer history navigation support
		let replace = (str && this.getActiveSearch());
		updateQueryParams({ search: str }, replace);
	},
	getActiveSort () {
		return active.sort;
	},
	setActiveSort (sort) {
		active.sort = _list.expandSort(sort || _list.defaultSort);
		this.loadItems();
		this.notifyChange();
	},
	getAvailableFilters () {
		return _list.columns.filter(col => col.field && col.field.hasFilterMethod);
	},
	getActiveFilters () {
		return active.filters;
	},
	getFilter (path) {
		return active.filters.filter(i => i.field.path === path)[0];
	},
	setFilter (path, value) {
		let filter = active.filters.filter(i => i.field.path === path)[0];
		if (filter) {
			filter.value = value;
		} else {
			let field = _list.fields[path];
			if (!field) {
				console.warn('Invalid Filter path specified:', path);
				return;
			}
			filter = { field, value };
			active.filters.push(filter);
		}
		this.setCurrentPage(1);
		this.notifyChange();
	},
	clearFilter (path) {
		var filter = active.filters.filter(i => i.field.path === path)[0];
		if (!filter) return;
		active.filters.splice(active.filters.indexOf(filter), 1);
		this.loadItems();
		this.notifyChange();
	},
	clearAllFilters () {
		active.filters = [];
		this.loadItems();
		this.notifyChange();
	},
	getPageSize () {
		return page.size;
	},
	getCurrentPage () {
		return page.index;
	},
	setCurrentPage (i) {
		page.index = i;
		this.loadItems();
	},
	isLoading () {
		return _loading;
	},
	isReady () {
		return _ready;
	},
	loadItems () {
		_loading = true;
		_list.loadItems({
			search: active.search,
			filters: active.filters,
			sort: active.sort,
			columns: active.columns,
			page: page
		}, (err, items) => {
			_loading = false;
			// TODO: graceful error handling
			if (items) {
				_ready = true;
				_items = items;
			}
			this.notifyChange();
		});
	},
	getItems () {
		return _items;
	},
	deleteItem (itemId) {
		_list.deleteItem(itemId, (err, data) => {
			// TODO: graceful error handling
			this.loadItems();
		});
	},
	deleteItems (itemIds) {
		_list.deleteItems(itemIds, (err, data) => {
			// TODO: graceful error handling
			this.loadItems();
		});
	},
	downloadItems (format, columns) {
		var url = _list.getDownloadURL({
			search: active.search,
			filters: active.filters,
			sort: active.sort,
			columns: columns ? _list.expandColumns(columns) : active.columns,
			format: format
		});
		window.open(url);
	}
});

history.listen(function (location) {
	_location = location;
	let querySearch = location.query.search || '';
	if (active.search !== querySearch) {
		active.search = querySearch;
		CurrentListStore.loadItems();
		CurrentListStore.notifyChange();
	}
});

module.exports = CurrentListStore;
<|endoftext|>


<commit_before><commit_msg>Added feedback command<commit_after><|endoftext|>


<commit_before>var RBTree = require('bintrees').RBTree
var fnv = require('hash.fnv')
var assert = require('assert')

function Aplomb (options) {
    this.compare = options.compare
    this.extract = options.extract
    this.bucketCount = options.bucketCount
    this.delegations = new RBTree(compare)
    this.connections = new RBTree(compare)

    function compare (a, b) { return options.compare(a.key, b.key) }
}

Aplomb.prototype.max = function () {
    return this.delegations.max()
}

Aplomb.prototype.getIndex = function (connection) {
    var key = this.extract(connection)
    var hash = fnv(0, new Buffer(key), 0, Buffer.byteLength(key))
    return hash % this.bucketCount
}

Aplomb.prototype.getDelegates = function (connection) {
    var key = this.extract(connection)
    var hash = fnv(0, new Buffer(key), 0, Buffer.byteLength(key))
    var index = hash % this.bucketCount
    var delegates = []
    this.delegations.reach(function (delegation) {
        if (delegation.delegates.length != 0) {
            delegates.push(delegation.buckets[index])
        }
    })
    return delegates.filter(function (del, i, set) {
            return (set.indexOf(del) == i)
        })
}

Aplomb.prototype.getDelegate = function (connection) {
    var delegations = this.delegations.iterator(), delegation
        while (delegation = delegations.prev()) {
            if (delegation.enacted) {
                break
            }
        }
    if (delegation == null || delegation.delegates.length == 0) {
        return null
    }
    var index = this.getIndex(connection)
        return delegation.buckets[index]
}

Aplomb.prototype.addDelegate = function (key, delegate) {
    if (this.delegations.size) {
        var delegation = this.delegations.max()
        var delegates = delegation.delegates
        if (~delegates.indexOf(delegate)) {
            return {
                key: key,
                enacted: false,
                buckets: delegation.buckets,
                delegates: delegates
            }
        }
        if (delegates.length) {
            var buckets = delegation.buckets.slice()
            var redist = Array.apply(null, Array(delegates.length)).map(Number.prototype.valueOf, 0)

            delegates.push(delegate)

            var total = Math.ceil(buckets.length / delegates.length)
            var each = Math.ceil(total / (delegates.length - 1))

            for (var b = 0, I = buckets.length; b < I && total; b++) {
                if (redist[delegates.indexOf(buckets[b])] == each) {
                    continue
                }
                redist[delegates.indexOf(buckets[b])]++
                buckets[b] = delegate
                total--
            }

            return { key: key, enacted: false, buckets: buckets, delegates: delegates }
        }
    }

    return {
        key: key,
        enacted: false,
        buckets: Array.apply(null, Array(this.bucketCount)).map(String.prototype.toString, delegate),
        delegates: [ delegate ]
    }
}

Aplomb.prototype.removeDelegate = function (key, delegate) {
    assert(this.delegations.size)

    var delegation = this.delegations.max()
    if (delegation.delegates.length > 1) {
        if (!~delegation.delegates.indexOf(delegate)) {
            return {
                key: key,
                enacted: false,
                buckets: delegation.buckets,
                delegates: delegation.delegates
            }
        }
        var delegates = delegation.delegates.slice()
        var buckets = delegation.buckets.slice()

        var index = delegates.indexOf(delegate)
        assert(~index, 'index not found')
        delegates.splice(index, 1)

        for (var i = 0, b = 0, B = buckets.length; b < B; b++) {
            if (buckets[b] == delegate) {
                buckets[b] = delegates[i++ % delegates.length]
            }
        }

        return { key: key, enacted: false, buckets: buckets, delegates: delegates }
    }

    return { key: key, enacted: false, buckets: null, delegates: [] }
}

Aplomb.prototype.replaceDelegate = function (key, oldDelegate, newDelegate) {
    var delegation = this.delegations.max()
    var buckets = delegation.buckets.slice()

    var delegates = delegation.delegates.map(function (delegate) {
        return delegate == oldDelegate ? newDelegate : delegate
    })

    for (var b = 0, I = buckets.length; b < I; b++) {
        if (delegation.buckets[b] == oldDelegate) {
            buckets[b] = newDelegate
        }
    }

    return { key: key, enacted: false, buckets: buckets, delegates: delegates }
}

Aplomb.prototype.getDelegations = function () {
    var delegations = []
    this.delegations.reach(function (delegation) {
        delegations.push(delegation)
    })
    return delegations
}

Aplomb.prototype.addDelegation = function (delegation) {
    this.delegations.insert(delegation)
}

Aplomb.prototype.removeDelegation = function (key) {
    return this.delegations.remove({ key: key })
}

Aplomb.prototype.addConnection = function (key, connection) {
    var tree = this.connections.find({ key: key })
        if (tree == null) {
            tree = {
                key: key,
                connections: new RBTree(function (a, b) {
                    a = this.extract(a)
                    b = this.extract(b)
                    return a < b ? -1 : a > b ? 1 : 0
                }.bind(this))
            }
            this.connections.insert(tree)
        }
    tree.connections.insert(connection)
}

Aplomb.prototype.getConnectionCount = function (key) {
    var version = this.connections.find({ key: key })
    return version ? version.connections.size : 0
}

Aplomb.prototype.removeConnectionSet = function (key) {
    return !! this.connections.remove({ key: key })
}

Aplomb.prototype.removeConnection = function (connection) {
    var tree, iterator = this.connections.iterator(), found = false

    while (tree = iterator.prev()) {
        found = !! tree.connections.remove(connection) || found
    }

    return found
}

Aplomb.prototype.getConnection = function (connection) {
    var delegate, tree, iterator = this.connections.iterator()

        while (tree = iterator.prev()) {
            if (delegate = tree.connections.find(connection)) {
                return delegate
            }
        }

    return null
}

Aplomb.prototype.evictable = function (delegate) {
    var tree

        var compare = this.compare
        var latest = this.delegations.max()
        var iterator = this.connections.iterator()
        if (((tree = iterator.next()) != null) && compare(tree.key, latest.key) != 0) {
            while (tree.connections.size != 0) {
                var connection = tree.connections.min()
                if (this.getDelegate(connection) == delegate) {
                    tree.connections.remove(connection)
                    this.addConnection(latest.key, connection)
                } else {
                    return { type: 'connection', connection: connection }
                }
            }
            return { type: 'delegation', key: tree.key }
        }

    return null
}

module.exports = Aplomb
<commit_msg>Styling and cleanup.<commit_after>var RBTree = require('bintrees').RBTree
var fnv = require('hash.fnv')
var assert = require('assert')

function Aplomb (options) {
    this.compare = options.compare
    this.extract = options.extract
    this.bucketCount = options.bucketCount
    this.delegations = new RBTree(compare)
    this.connections = new RBTree(compare)

    function compare (a, b) { return options.compare(a.key, b.key) }
}

Aplomb.prototype.max = function () {
    return this.delegations.max()
}

Aplomb.prototype.getIndex = function (connection) {
    var key = this.extract(connection)
    var hash = fnv(0, new Buffer(key), 0, Buffer.byteLength(key))

    return hash % this.bucketCount
}

Aplomb.prototype.getDelegates = function (connection) {
    var key = this.extract(connection)
    var hash = fnv(0, new Buffer(key), 0, Buffer.byteLength(key))
    var index = hash % this.bucketCount
    var delegates = []

    this.delegations.reach(function (delegation) {
        if (delegation.delegates.length != 0) {
            delegates.push(delegation.buckets[index])
        }
    })

    return delegates.filter(function (del, i, set) {
        return (set.indexOf(del) == i)
    })
}

Aplomb.prototype.getDelegate = function (connection) {
    var delegations = this.delegations.iterator(), delegation

    while (delegation = delegations.prev()) {
        if (delegation.enacted) {
            break
        }
    }

    if (delegation == null || delegation.delegates.length == 0) {
        return null
    }

    var index = this.getIndex(connection)

    return delegation.buckets[index]
}

Aplomb.prototype.addDelegate = function (key, delegate) {
    if (this.delegations.size) {
        var delegation = this.delegations.max()
        var delegates = delegation.delegates

        if (~delegates.indexOf(delegate)) {
            return {
                key: key,
                enacted: false,
                buckets: delegation.buckets,
                delegates: delegates
            }
        }

        if (delegates.length) {
            var buckets = delegation.buckets.slice()
            var redist = Array.apply(null, Array(delegates.length)).map(Number.prototype.valueOf, 0)

            delegates.push(delegate)

            var total = Math.ceil(buckets.length / delegates.length)
            var each = Math.ceil(total / (delegates.length - 1))

            for (var b = 0, I = buckets.length; b < I && total; b++) {
                if (redist[delegates.indexOf(buckets[b])] == each) {
                    continue
                }
                redist[delegates.indexOf(buckets[b])]++
                buckets[b] = delegate
                total--
            }

            return { key: key, enacted: false, buckets: buckets, delegates: delegates }
        }
    }

    return {
        key: key,
        enacted: false,
        buckets: Array.apply(null, Array(this.bucketCount)).map(String.prototype.toString, delegate),
        delegates: [ delegate ]
    }
}

Aplomb.prototype.removeDelegate = function (key, delegate) {
    assert(this.delegations.size)

    var delegation = this.delegations.max()

    if (delegation.delegates.length > 1) {
        if (!~delegation.delegates.indexOf(delegate)) {
            return {
                key: key,
                enacted: false,
                buckets: delegation.buckets,
                delegates: delegation.delegates
            }
        }

        var delegates = delegation.delegates.slice()
        var buckets = delegation.buckets.slice()
        var index = delegates.indexOf(delegate)

        assert(~index, 'index not found')

        delegates.splice(index, 1)

        for (var i = 0, b = 0, B = buckets.length; b < B; b++) {
            if (buckets[b] == delegate) {
                buckets[b] = delegates[i++ % delegates.length]
            }
        }

        return { key: key, enacted: false, buckets: buckets, delegates: delegates }
    }

    return { key: key, enacted: false, buckets: null, delegates: [] }
}

Aplomb.prototype.replaceDelegate = function (key, oldDelegate, newDelegate) {
    var delegation = this.delegations.max()
    var buckets = delegation.buckets.slice()

    var delegates = delegation.delegates.map(function (delegate) {
        return delegate == oldDelegate ? newDelegate : delegate
    })

    for (var b = 0, I = buckets.length; b < I; b++) {
        if (delegation.buckets[b] == oldDelegate) {
            buckets[b] = newDelegate
        }
    }

    return { key: key, enacted: false, buckets: buckets, delegates: delegates }
}

Aplomb.prototype.getDelegations = function () {
    var delegations = []
    this.delegations.reach(function (delegation) {
        delegations.push(delegation)
    })
    return delegations
}

Aplomb.prototype.addDelegation = function (delegation) {
    this.delegations.insert(delegation)
}

Aplomb.prototype.removeDelegation = function (key) {
    return this.delegations.remove({ key: key })
}

Aplomb.prototype.addConnection = function (key, connection) {
    var tree = this.connections.find({ key: key })
        if (tree == null) {
            tree = {
                key: key,
                connections: new RBTree(function (a, b) {
                    a = this.extract(a)
                    b = this.extract(b)
                    return a < b ? -1 : a > b ? 1 : 0
                }.bind(this))
            }
            this.connections.insert(tree)
        }
    tree.connections.insert(connection)
}

Aplomb.prototype.getConnectionCount = function (key) {
    var version = this.connections.find({ key: key })
    return version ? version.connections.size : 0
}

Aplomb.prototype.removeConnectionSet = function (key) {
    return !! this.connections.remove({ key: key })
}

Aplomb.prototype.removeConnection = function (connection) {
    var tree, iterator = this.connections.iterator(), found = false

    while (tree = iterator.prev()) {
        found = !! tree.connections.remove(connection) || found
    }

    return found
}

Aplomb.prototype.getConnection = function (connection) {
    var delegate, tree, iterator = this.connections.iterator()

    while (tree = iterator.prev()) {
        if (delegate = tree.connections.find(connection)) {
            return delegate
        }
    }

    return null
}

Aplomb.prototype.evictable = function (delegate) {
    var tree

    var compare = this.compare
    var latest = this.delegations.max()
    var iterator = this.connections.iterator()

    if (((tree = iterator.next()) != null) && compare(tree.key, latest.key) != 0) {
        while (tree.connections.size != 0) {
            var connection = tree.connections.min()

            if (this.getDelegate(connection) == delegate) {
                tree.connections.remove(connection)
                this.addConnection(latest.key, connection)
            } else {
                return { type: 'connection', connection: connection }
            }
        }
        return { type: 'delegation', key: tree.key }
    }

    return null
}

module.exports = Aplomb
<|endoftext|>


<commit_before>// MapPreview
// ----------
// A web map client for displaying tile-based maps.
view = Backbone.View.extend({
    className: 'MapClient',
    id: 'map',
    initialize: function(options) {
        _.bindAll(this, 'ready');
    },
    ready: function() {
        var mm = com.modestmaps;

        function mmNav() {
            if (!$('.zoom').size()) return;
            $('.zoom.active').removeClass('active');
            $('.zoom-' + map.getZoom()).addClass('active');
        }

        var center = this.model.get('center');
        var tj = this.model.toJSON();
        var map = new mm.Map(this.el,
            new wax.mm.connector(tj)
        ).setCenterZoom(
            new mm.Location(center[1], center[0]
        ), center[2]);

        wax.mm.zoomer(map).appendTo(map.parent);
        wax.mm.zoombox(map);
        wax.mm.attribution(map).appendTo(map.parent);

        if (tj.grids) {
            wax.mm.interaction(map, tj);
        }

        map.addCallback('zoomed', mmNav);
        map.addCallback('extentset', mmNav);
        mmNav();

        this.map = map;
    }
});
<commit_msg>fix #35<commit_after>// MapPreview
// ----------
// A web map client for displaying tile-based maps.
view = Backbone.View.extend({
    className: 'MapClient',
    id: 'map',
    initialize: function(options) {
        _.bindAll(this, 'ready');
    },
    ready: function() {
        var mm = com.modestmaps;

        function mmNav() {
            if (!$('.zoom').size()) return;
            $('.zoom.active').removeClass('active');
            $('.zoom-' + map.getZoom()).addClass('active');
        }

        var center = this.model.get('center');
        var tj = this.model.toJSON();
        var map = new mm.Map(this.el,
            new wax.mm.connector(tj)
        ).setCenterZoom(
            new mm.Location(center[1], center[0]
        ), center[2]);
        var layer = map.getLayerAt(0);
        layer.provider.options.tiles = this.model.get('tiles');
        layer.provider.options.minzoom = this.model.get('minzoom');
        layer.provider.options.maxzoom = this.model.get('maxzoom');
        layer.setProvider(layer.provider);

        layer.provider.setZoomRange(layer.provider.options.minzoom,
                              layer.provider.options.maxzoom)

        map.setZoomRange(layer.provider.options.minzoom,
                              layer.provider.options.maxzoom)
        wax.mm.zoomer(map).appendTo(map.parent);
        wax.mm.zoombox(map);
        wax.mm.attribution(map).appendTo(map.parent);

        if (tj.grids) {
            wax.mm.interaction(map, tj);
        }

        map.addCallback('zoomed', mmNav);
        map.addCallback('extentset', mmNav);
        mmNav();

        this.map = map;
    }
});
<|endoftext|>


<commit_before>var mongoose    = require('mongoose');
var _           = require('lodash');
var objectHash  = require('object-hash');
var SimpleEvent = mongoose.model('SimpleEvent');
var DataEvent   = mongoose.model('DataEvent');

// This file contains code for some database operations.
// I've decided to abstract them in a single file so other files
// don't have to worry about db logic.

// project, category and action are namespace fragments.
// This function ensures the fragment is trimmed and doesn't contain slashes.
function normalizeNamespaceFragment(fragment) {
    return fragment.trim().replace(/[\/\\]/g, '');
}

// Records a simple event, i.e., one without a data object.
// A callback(error) can be passed.
exports.registerSimpleEvent = function(project, category, action, callback) {
    var normalized = {
        project : normalizeNamespaceFragment(project),
        category: normalizeNamespaceFragment(category),
        action  : normalizeNamespaceFragment(action)
    };
    var update = {
        $setOnInsert: normalized,
        $inc: {count: 1}
    };
    var options = {upsert: true, select: {}};
    SimpleEvent.findOneAndUpdate(normalized, update, options, function(e) {
        if (callback) callback(e);
    });
};

// Registers a data event. This is more involved than a simple event, and
// involves hashing. Flags are part of the data event logic, but should be
// handled prior to this step (in other words, the data object passed
// is expected to be final).
exports.registerDataEvent = function(project, category, action, data, callback) {
    var hash = {hash: objectHash.MD5(data)};
    var normalized = {
        project : normalizeNamespaceFragment(project),
        category: normalizeNamespaceFragment(category),
        action  : normalizeNamespaceFragment(action)
    };
    var update = {
        $setOnInsert: _.merge({}, normalized, hash, {data: data}),
        $inc: {count: 1}
    };
    var query = _.merge({}, hash, normalized);
    var options = {upsert: true, select: {}};
    DataEvent.findOneAndUpdate(query, update, options, function(e) {
        if (callback) callback(e);
    });
};

// Simplified event registering. Pass an options object with properties
// project, category, action, and optionally, data (which controls whether or not
// the event is a data event), and a callback(error) like usual.
exports.registerEvent = function(options, callback) {
    var o = options;
    if (options.data) {
        exports.registerDataEvent(o.project, o.category, o.action, o.data, callback);
    } else {
        exports.registerSimpleEvent(o.project, o.category, o.action, callback);
    }
}

// Gets projects, and gives them to a callback that does something with them.
// Will call the callback with an array of project names, or null on error.
exports.getProjects = function(callback) {
    SimpleEvent.distinct('project', function(err1, result1) {
        DataEvent.distinct('project', function(err2, result2) {
            if (err1 || err2) return callback(null);
            callback(_.uniq(result1.concat(result2)).sort());
        });
    });
};

// Gets a project's categories (if any was found), and passes them to a callback.
// Will call the callback with an array of category names, or null on error/no project.
exports.getCategories = function(project, callback) {
    SimpleEvent.distinct('category', {project: project}, function(err1, result1) {
        DataEvent.distinct('category', {project: project}, function(err2, result2) {
            if (err1 || err2) return callback(null);
            var categories = _.uniq(result1.concat(result2)).sort();
            callback(categories.length ? categories : null);
        });
    });
};
<commit_msg>.getActions db helper.<commit_after>var mongoose    = require('mongoose');
var _           = require('lodash');
var objectHash  = require('object-hash');
var SimpleEvent = mongoose.model('SimpleEvent');
var DataEvent   = mongoose.model('DataEvent');

// This file contains code for some database operations.
// I've decided to abstract them in a single file so other files
// don't have to worry about db logic.

// project, category and action are namespace fragments.
// This function ensures the fragment is trimmed and doesn't contain slashes.
function normalizeNamespaceFragment(fragment) {
    return fragment.trim().replace(/[\/\\]/g, '');
}

// Records a simple event, i.e., one without a data object.
// A callback(error) can be passed.
exports.registerSimpleEvent = function(project, category, action, callback) {
    var normalized = {
        project : normalizeNamespaceFragment(project),
        category: normalizeNamespaceFragment(category),
        action  : normalizeNamespaceFragment(action)
    };
    var update = {
        $setOnInsert: normalized,
        $inc: {count: 1}
    };
    var options = {upsert: true, select: {}};
    SimpleEvent.findOneAndUpdate(normalized, update, options, function(e) {
        if (callback) callback(e);
    });
};

// Registers a data event. This is more involved than a simple event, and
// involves hashing. Flags are part of the data event logic, but should be
// handled prior to this step (in other words, the data object passed
// is expected to be final).
exports.registerDataEvent = function(project, category, action, data, callback) {
    var hash = {hash: objectHash.MD5(data)};
    var normalized = {
        project : normalizeNamespaceFragment(project),
        category: normalizeNamespaceFragment(category),
        action  : normalizeNamespaceFragment(action)
    };
    var update = {
        $setOnInsert: _.merge({}, normalized, hash, {data: data}),
        $inc: {count: 1}
    };
    var query = _.merge({}, hash, normalized);
    var options = {upsert: true, select: {}};
    DataEvent.findOneAndUpdate(query, update, options, function(e) {
        if (callback) callback(e);
    });
};

// Simplified event registering. Pass an options object with properties
// project, category, action, and optionally, data (which controls whether or not
// the event is a data event), and a callback(error) like usual.
exports.registerEvent = function(options, callback) {
    var o = options;
    if (options.data) {
        exports.registerDataEvent(o.project, o.category, o.action, o.data, callback);
    } else {
        exports.registerSimpleEvent(o.project, o.category, o.action, callback);
    }
}

// Gets projects, and gives them to a callback that does something with them.
// Will call the callback with an array of project names, or null on error.
exports.getProjects = function(callback) {
    SimpleEvent.distinct('project', function(err1, result1) {
        DataEvent.distinct('project', function(err2, result2) {
            if (err1 || err2) return callback(null);
            callback(_.uniq(result1.concat(result2)).sort());
        });
    });
};

// Gets a project's categories (if any was found), and passes them to a callback.
// Will call the callback with an array of category names, or null on error/no project.
exports.getCategories = function(project, callback) {
    SimpleEvent.distinct('category', {project: project}, function(err1, result1) {
        DataEvent.distinct('category', {project: project}, function(err2, result2) {
            if (err1 || err2) return callback(null);
            var categories = _.uniq(result1.concat(result2)).sort();
            callback(categories.length ? categories : null);
        });
    });
};

// Same as above, for actions.
exports.getActions = function(project, category, callback) {
    var rules = {project: project, category: category};
    SimpleEvent.distinct('action', rules, function(err1, result1) {
        DataEvent.distinct('action', rules, function(err2, result2) {
            if (err1 || err2) return callback(null);
            var actions = _.uniq(result1.concat(result2)).sort();
            callback(actions.length ? actions : null);
        });
    });
};
<|endoftext|>


<commit_before>'use strict';

const ds = require('../services/elasticsearch');
const config = require('../../../shared/config');

// Autosuggest for search field
exports.suggest = function suggest(req, res, next) {
  const text = req.query.text;
  const query = {
    index: [config.es.assetIndex, config.es.seriesIndex],
    body: {
      suggest: {
        text,
        completion: {
          field: 'suggest',
          fuzzy: {fuzziness: 1},
        },
      },
    },
  };

  ds.suggest(query).then(function(resp) {
    res.header('Content-type', 'application/json; charset=utf-8');
    if (resp.suggest && resp.suggest.length >= 1) {
      var suggestions = resp.suggest[0].options;
      res.json(suggestions);
    } else {
      next(new Error('Empty suggestion result from Elasticsearch.'));
    }
  }, function(err) {
    next(new Error('Error getting suggestions from Elasticsearch: ' + err));
  });
};
<commit_msg>Responses from new elasticsearch package are objects with body as a field<commit_after>'use strict';

const ds = require('../services/elasticsearch');
const config = require('../../../shared/config');

// Autosuggest for search field
exports.suggest = function suggest(req, res, next) {
  const text = req.query.text;
  const query = {
    index: [config.es.assetIndex, config.es.seriesIndex],
    body: {
      suggest: {
        text,
        completion: {
          field: 'suggest',
          fuzzy: {fuzziness: 1},
        },
      },
    },
  };

  //TODO: suggest does not exist in most recent elasticsearch - where's it gone mate?
  ds.suggest(query).then(function({body: resp}) {
    res.header('Content-type', 'application/json; charset=utf-8');
    if (resp.suggest && resp.suggest.length >= 1) {
      var suggestions = resp.suggest[0].options;
      res.json(suggestions);
    } else {
      next(new Error('Empty suggestion result from Elasticsearch.'));
    }
  }, function(err) {
    next(new Error('Error getting suggestions from Elasticsearch: ' + err));
  });
};
<|endoftext|>


<commit_before>const dataStoreVersion = "0.1.4";
importScripts('/scripts/sw/router.js');
importScripts('/scripts/sw/fileManifest.js');

/*
  Escape hatch. ABORT ABORT. Any URL with a kill-sw=true at the end of the query string.
*/
router.get(/\?kill-sw=true/, function() {
  self.registration.unregister();

  caches.keys().then(cacheKeys => Promise.all(cacheKeys.map(key => caches.delete(key))));
}, {urlMatchProperty: "search"});

/*
  Manage all the request for this origin in a cache only manner.
*/
router.get(`${self.location.origin}`, e => {
  const request = e.request;
  const url = new URL(e.request.url);

  e.respondWith(
    caches.match(e.request).then(response => {
      return response || fetch(e.request);
    })
  );
}, {urlMatchProperty: "origin"});

/*
  Handle requests to Google Analytics seperately
*/
router.get(/http[s]*:\/\/www.google-analytics.com/, (e)=>{
  console.log('Analytics request', e);
}, {urlMatchProperty: "origin"});

router.get(/.*/, e => {
  /* this just shows that the origin filter above works and all other requests
     are handled by this */
  console.log("Foreign Request", e.request)
});

self.addEventListener('activate', function(event) {
  event.waitUntil(self.clients.claim());
});

self.addEventListener('install', function(e) {
  e.waitUntil(
    caches.open(dataStoreVersion).then(function(cache) {
      return cache.addAll(requiredFiles);
    })
  );
});<commit_msg>Create sw.js<commit_after>const dataStoreVersion = "0.1.5";
importScripts('/scripts/sw/router.js');
importScripts('/scripts/sw/fileManifest.js');

/*
  Escape hatch. ABORT ABORT. Any URL with a kill-sw=true at the end of the query string.
*/
router.get(/\?kill-sw=true/, function() {
  self.registration.unregister();

  caches.keys().then(cacheKeys => Promise.all(cacheKeys.map(key => caches.delete(key))));
}, {urlMatchProperty: "search"});

/*
  Manage all the request for this origin in a cache only manner.
*/
router.get(`${self.location.origin}`, e => {
  const request = e.request;
  const url = new URL(e.request.url);

  e.respondWith(
    caches.match(e.request).then(response => {
      return response || fetch(e.request);
    })
  );
}, {urlMatchProperty: "origin"});

/*
  Handle requests to Google Analytics seperately
*/
router.get(/http[s]*:\/\/www.google-analytics.com/, (e)=>{
  console.log('Analytics request', e);
}, {urlMatchProperty: "origin"});

router.get(/.*/, e => {
  /* this just shows that the origin filter above works and all other requests
     are handled by this */
  console.log("Foreign Request", e.request)
});

self.addEventListener('activate', function(event) {
  event.waitUntil(self.clients.claim());
});

self.addEventListener('install', function(e) {
  e.waitUntil(
    caches.open(dataStoreVersion).then(function(cache) {
      return cache.addAll(requiredFiles);
    })
  );
});
<|endoftext|>


<commit_before>/**
 * @module appenv
 */
'use strict';

const os = require('os');
const path = require('path');

exports.music_root = process.env.MUSIC_ROOT || path.join(os.homedir(), 'music');
exports.load_concurrency = process.env.LOAD_CONCURRENCY || 32;
exports.port = process.env.PORT || 3000;
exports.db_path = process.env.DB_PATH || path.join(__dirname, 'db');
<commit_msg>Strict<commit_after>/**
 * @module appenv
 */
' use strict';

const os = require('os');
const path = require('path');

exports.music_root = process.env.MUSIC_ROOT || path.join(os.homedir(), 'music');
exports.load_concurrency = process.env.LOAD_CONCURRENCY || 32;
exports.port = process.env.PORT || 3000;
exports.db_path = process.env.DB_PATH || path.join(__dirname, 'db');
<|endoftext|>


<commit_before>'use strict';

module.exports = {
	db: 'mongodb://localhost/enrtama-dev',
	app: {
		title: 'enrtama - Development Environment'
	},
	facebook: {
		clientID: process.env.FACEBOOK_ID || 'APP_ID',
		clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
		callbackURL: '/auth/facebook/callback'
	},
	twitter: {
		clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
		clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
		callbackURL: '/auth/twitter/callback'
	},
	google: {
		clientID: process.env.GOOGLE_ID || 'APP_ID',
		clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
		callbackURL: '/auth/google/callback'
	},
	linkedin: {
		clientID: process.env.LINKEDIN_ID || 'APP_ID',
		clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
		callbackURL: '/auth/linkedin/callback'
	},
	github: {
		clientID: process.env.GITHUB_ID || 'APP_ID',
		clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
		callbackURL: '/auth/github/callback'
	},
	mailer: {
		from: process.env.MAILER_FROM || 'MAILER_FROM',
		options: {
			service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
			auth: {
				user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
				pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
			}
		}
	}
};
<commit_msg>Adapt mongodb to Heroku + MongoLab<commit_after>'use strict';

module.exports = {
	db: process.env.MONGOLAB_URI ||
		process.env.MONGOHQ_URL ||
		'mongodb://localhost/enrtama-dev',
	app: {
		title: 'enrtama - Development Environment'
	},
	facebook: {
		clientID: process.env.FACEBOOK_ID || 'APP_ID',
		clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET',
		callbackURL: '/auth/facebook/callback'
	},
	twitter: {
		clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY',
		clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET',
		callbackURL: '/auth/twitter/callback'
	},
	google: {
		clientID: process.env.GOOGLE_ID || 'APP_ID',
		clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET',
		callbackURL: '/auth/google/callback'
	},
	linkedin: {
		clientID: process.env.LINKEDIN_ID || 'APP_ID',
		clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET',
		callbackURL: '/auth/linkedin/callback'
	},
	github: {
		clientID: process.env.GITHUB_ID || 'APP_ID',
		clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET',
		callbackURL: '/auth/github/callback'
	},
	mailer: {
		from: process.env.MAILER_FROM || 'MAILER_FROM',
		options: {
			service: process.env.MAILER_SERVICE_PROVIDER || 'MAILER_SERVICE_PROVIDER',
			auth: {
				user: process.env.MAILER_EMAIL_ID || 'MAILER_EMAIL_ID',
				pass: process.env.MAILER_PASSWORD || 'MAILER_PASSWORD'
			}
		}
	}
};
<|endoftext|>


<commit_before>// https://docs.gitlab.com/ce/ci/variables/README.html

module.exports = {

  detect : function(){
    return !!process.env.GITLAB_CI;
  },

  configuration : function(){
    console.log('    Gitlab CI Detected');
    return {
      service : 'gitlab',
      build :  process.env.CI_BUILD_ID,
      commit : process.env.CI_BUILD_REF,
      branch : process.env.CI_BUILD_REF_NAME,
      root : process.env.CI_PROJECT_DIR,
      slug : process.env.CI_BUILD_REPO.split('/').slice(3, 5).join('/').replace('.git', '')
    };
  }

};
<commit_msg>add CI_REPOSITORY_URL to gitlab detection, fix #60<commit_after>// https://docs.gitlab.com/ce/ci/variables/README.html

module.exports = {

  detect : function(){
    return !!process.env.GITLAB_CI;
  },

  configuration : function(){
    console.log('    Gitlab CI Detected');
    var remote = process.env.CI_BUILD_REPO || process.env.CI_REPOSITORY_URL || '';
    return {
      service : 'gitlab',
      build :  process.env.CI_BUILD_ID,
      commit : process.env.CI_BUILD_REF,
      branch : process.env.CI_BUILD_REF_NAME,
      root : process.env.CI_PROJECT_DIR,
      slug : remote.split('/').slice(3, 5).join('/').replace('.git', '')
    };
  }

};
<|endoftext|>


<commit_before>// Copyright 2015, EMC, Inc.

'use strict';

module.exports = lookupServiceFactory;

lookupServiceFactory.$provide = 'Services.Lookup';
lookupServiceFactory.$inject = [
    'Promise',
    'Services.Waterline',
    'Services.Configuration',
    'Assert',
    'Errors',
    '_',
    'lru-cache',
    'ARPCache'
];

/**
 * Provides a LookupService singleton.
 * @private
 * @param  {Promise} Promise
 * @param  {Waterline} waterline
 * @param  {Assert} assert
 * @param  {Errors} errors
 * @param  {lodash} _
 * @param  {lru-cache} LRU Cache Package
 *
 * @return {LookupService} An instance of the LookupService.
 */
function lookupServiceFactory(
    Promise,
    waterline,
    configuration,
    assert,
    Errors,
    _,
    LRU,
    arpCache
) {
    // TODO: stolen from http-service could stand to be a re-usable helper.
    /**
     * Get remote address of the client.
     * @private
     * @param {req} req from express
     * @returns {String|Undefined} either the ip of requester or undefined
     *                             if unavailable
     */
    function remoteAddress(req) {
        assert.ok(req, 'req');

        if(req.get("X-Real-IP")) {
            return req.get("X-Real-IP");
        }

        if (req.ip) {
            return req.ip;
        }

        if (req._remoteAddress) {
            return req._remoteAddress;
        }

        if (req.connection) {
            return req.connection.remoteAddress;
        }

        // TODO: This doesn't appear to ever get hit and causes an assertion
        // in the IP lookup due to undefined not being an IP so we need to investigate
        // what to do in this particular case (which again doesn't seem to have ever
        // actually occurred)
        return undefined;
    }

    /**
     * Provides functionality to correlate various identifiers
     * to their appropriate counterparts.
     * @constructor
     */
    function LookupService () {
        this.resetNodeIdCache();
    }

    /**
     * Reset all cached Node IDs.
     */
    LookupService.prototype.resetNodeIdCache = function () {
        if (this.nodeIdCache) {
            this.nodeIdCache.reset();
            return;
        }

        this.nodeIdCache = LRU({
            max: 500,
            maxAge: 30000
        });
    };

    /**
     * Reset the cache for a specific IP or MAC address.
     * @param  {String} cache key - IP or MAC address.
     */
    LookupService.prototype.clearNodeIdCache = function (key) {
        this.assignNodeIdCache(key, null);
    };

    LookupService.prototype.clearNodeIdCacheAndThrow = function (key, error) {
        this.clearNodeIdCache(key);

        throw error;
    };

    /**
     * Check the cache for a Node ID by IP or MAC addres.
     * @param  {String} cache key - IP or MAC address.
     * @return {Promise|null} Returns a promise if the Node ID is cached,
                              otherwise null.
     */
    LookupService.prototype.checkNodeIdCache = function (key) {
        var current = this.nodeIdCache.get(key);

        if (current === null || typeof current === 'string') {
            return new Promise(function (resolve, reject) {
                var responder = {resolve: resolve, reject: reject};

                this.handleCachePromise(responder, current);
            }.bind(this));
        }

        if (Array.isArray(current)) {
            return new Promise(function (resolve, reject) {
                var buffer = this.nodeIdCache.get(key),
                    responder = {resolve: resolve, reject: reject};

                if (Array.isArray(buffer)) {
                    buffer.push(responder);
                }

                else {
                    this.handleCachePromise(responder);
                }
            }.bind(this));
        }

        this.nodeIdCache.set(key, []);

        return null;
    };

    /**
     * Cache Node ID by either an IP or MAC address.
     * @param  {String} cache key - IP or MAC address.
     * @param  {String} cache value - Node ID.
     * @return {String} value converted to a string.
     */
    LookupService.prototype.assignNodeIdCache = function(key, value) {
        value = value && value.toString();

        var buffer = this.nodeIdCache.peek(key);

        this.nodeIdCache.set(key, value);

        if (Array.isArray(buffer)) {
            buffer.forEach(function (responder) {
                this.handleCachePromise(responder, value);
            }.bind(this));
        }

        return value;
    };

    LookupService.prototype.handleCachePromise = function (responder, value) {
      if (!value) {
          return responder.reject(
              new Errors.NotFoundError(
                  'Lookup Record Not Found (handleCachePromise)',
                  null
              )
          );
      }

      responder.resolve(value);
    };

    /**
     * Provides a middleware function suitable for
     * looking up the request IP via express and adding a req.macaddress and
     * req.macAddress value representing the MAC address used for the HTTP
     * request.
     * @return {function} An express middleware function.
     */
    LookupService.prototype.ipAddressToMacAddressMiddleware = function () {
        var self = this;

        return function(req, res, next) {
            self.ipAddressToMacAddress(remoteAddress(req)).then(function (macAddress) {
                // Provide both lower case and camel case versions.
                req.macaddress = macAddress;
                req.macAddress = macAddress;
            }).finally(function() {
                next();
            });
        };
    };

    /**
     * Converts the given IP address into a MAC address
     * via a lookup in the leaseCache.
     * @param  {String} ip IP Address to correlate to a MAC Address.
     * @return {Promise.<String>}    A promise fulfilled to the MAC address which
     * correlates to the provided IP.
     */
    LookupService.prototype.ipAddressToMacAddress = function (ip) {
        assert.isIP(ip);
        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(ip).then(function (record) {
                return record.macAddress;
            });
        });
    };

    /**
     * macAddressToNode converts the given MAC address into a Node document via
     * a lookup in the DomainService.
     * @param  {String} macAddress MAC Address to correlate to a Node.
     * @return {Promise.<NodeDocument>} A promise fulfilled to the Node which
     * correlates to the provided MAC Address.
     */
    LookupService.prototype.macAddressToNode = function (macAddress) {
        return waterline.lookups.findOneByTerm(macAddress).then(function (record) {
            if (record.node) {
                return waterline.nodes.needOneById(record.node);
            } else {
                throw new Errors.NotFoundError(
                    'Lookup Record Not Found (macAddressToNode)',
                    record
                );
            }
        });
    };

    /**
     * Returns the IP Address associated with the given MAC address.
     * @param {String} macAddress MAC Address to correlate with an IP.
     * @return {Promise.<String>} A promise fulfilled with the IP Address which
     * correlates to the provided MAC Address.
     */
    LookupService.prototype.macAddressToIp = function (macAddress) {
        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(macAddress).then(function (record) {
                if (record.ipAddress) {
                    return record.ipAddress;
                } else {
                    throw new Errors.NotFoundError(
                        'Lookup Record Not Found (macAddressToIp)',
                        record
                    );
                }
            });
        });
    };

    /**
     * Converts the given MAC address into the BSON ID of the node document via
     * a lookup in the DomainService. This function will cache values to avoid
     * excessive round trips to the DB, so its use is recommended when only the
     * BSON ID is needed.
     * @param  {String} macAddress MAC Address to correlate to a Node.
     * @return {Promise.<ObjectId>} A promise fulfilled to the Node ID which
     * correlates to the provided MAC Address.
     */
    LookupService.prototype.macAddressToNodeId = function (macAddress) {
        var self = this;
        var cachePromise = self.checkNodeIdCache(macAddress);

        if (cachePromise) {
            return cachePromise;
        }

        return waterline.lookups.findOneByTerm(macAddress)
        .then(function (record) {
            if (record.node) {
                return self.assignNodeIdCache(macAddress, record.node);
            }
            else {
                self.clearNodeIdCache(macAddress);
                throw new Errors.NotFoundError(
                    'Lookup Record Not Found (macAddressToNodeId)',
                    record
                );
            }
        })
        .catch(self.clearNodeIdCacheAndThrow.bind(self, macAddress));
    };

    /**
     * Converts the given IP Address into a Node document via
     * a lookup using the leaseCache & DomainService.
     * @param  {String} ip IP Address to correlate to a Node.
     * @return {Promise.<NodeDocument>} A promise fulfilled to the Node which
     * correlates to the provided IP Address.
     */
    LookupService.prototype.ipAddressToNode = function (ip) {
        assert.isIP(ip);

        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(ip).then(function (record) {
                if (record.node) {
                    return waterline.nodes.needOneById(record.node);
                } else {
                    throw new Errors.NotFoundError(
                        'Lookup Record Not Found (ipAddressToNode)',
                        record
                    );
                }
            });
        });
    };

    /**
     * Converts the given IP Address into the BSON ID of the Node via
     * a lookup using the leaseCache & DomainService. This function will cache
     * values to avoid excessive round trips to the DB, so its use is
     * recommended when only the BSON ID is needed.
     * @param  {String} ip IP Address to correlate to a Node.
     * @return {Promise.<ObjectId>} A promise fulfilled to the Node ID which
     * correlates to the provided IP Address.
     */
    LookupService.prototype.ipAddressToNodeId = function (ip) {
        assert.isIP(ip);

        var self = this;
        var cachePromise = self.checkNodeIdCache(ip);
        if (cachePromise) {
            return cachePromise;
        }

        return self.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(ip).then(function (record) {
                if (record.node) {
                    return self.assignNodeIdCache(ip, record.node);
                }
                else {
                    self.clearNodeIdCache(ip);
                    throw new Errors.NotFoundError(
                        'Lookup Record Not Found (ipAddressToNodeId)',
                        record
                    );
                }
            })
            .catch(self.clearNodeIdCacheAndThrow.bind(self, ip));
        });
    };

    /**
     * Returns a list of IP Addresses for the given Node ID.
     * @param  {String} id Node ID
     * @return {Promise.<Array>} A promise fulfilled with an array of
     * IP Addresses associated with the Node.
     */
    LookupService.prototype.nodeIdToIpAddresses = function (id) {
        var self = this;
        assert.isMongoId(id);

        return self.validateArpCache()
        .then(function() {
            return waterline.lookups.findByTerm(id).then(function (records) {
                return _.reduce(records, function (ipAddresses, record) {
                    if (record.ipAddress) {
                        ipAddresses.push(record.ipAddress);
                    }

                    return ipAddresses;
                }, []);
            });
        });
    };

    /**
     * Returns the proxy server for the given Node ID.
     * @param  {String} id Node ID
     * @return {Promise.<String>} A promise fulfilled to the Proxy server URL
     * that corresponds to this node
     */
    LookupService.prototype.nodeIdToProxy = function (id) {

        return waterline.lookups.findOneByTerm(id).then(function (record) {
            if (record && record.proxy) {
                return record.proxy;
            } else {
                return undefined;
            }
        });
    };

    LookupService.prototype.setIpAddress = function (ip, mac) {
        return waterline.lookups.setIp(ip, mac);
    };

    LookupService.prototype.validateArpCache = function () {
        var self = this;
        if(configuration.get('arpCacheEnabled', true)) {
            return arpCache.getCurrent()
            .map(function(entry) {
                return self.setIpAddress(entry.ip, entry.mac);
            });
        }
    };

    LookupService.prototype.start = function () {
        return waterline.lookups.setIndexes();
    };

    LookupService.prototype.stop = function () {
        return Promise.resolve();
    };

    return new LookupService();
}
<commit_msg>Always return promise when validating arp cache (#160)<commit_after>// Copyright 2015, EMC, Inc.

'use strict';

module.exports = lookupServiceFactory;

lookupServiceFactory.$provide = 'Services.Lookup';
lookupServiceFactory.$inject = [
    'Promise',
    'Services.Waterline',
    'Services.Configuration',
    'Assert',
    'Errors',
    '_',
    'lru-cache',
    'ARPCache'
];

/**
 * Provides a LookupService singleton.
 * @private
 * @param  {Promise} Promise
 * @param  {Waterline} waterline
 * @param  {Assert} assert
 * @param  {Errors} errors
 * @param  {lodash} _
 * @param  {lru-cache} LRU Cache Package
 *
 * @return {LookupService} An instance of the LookupService.
 */
function lookupServiceFactory(
    Promise,
    waterline,
    configuration,
    assert,
    Errors,
    _,
    LRU,
    arpCache
) {
    // TODO: stolen from http-service could stand to be a re-usable helper.
    /**
     * Get remote address of the client.
     * @private
     * @param {req} req from express
     * @returns {String|Undefined} either the ip of requester or undefined
     *                             if unavailable
     */
    function remoteAddress(req) {
        assert.ok(req, 'req');

        if(req.get("X-Real-IP")) {
            return req.get("X-Real-IP");
        }

        if (req.ip) {
            return req.ip;
        }

        if (req._remoteAddress) {
            return req._remoteAddress;
        }

        if (req.connection) {
            return req.connection.remoteAddress;
        }

        // TODO: This doesn't appear to ever get hit and causes an assertion
        // in the IP lookup due to undefined not being an IP so we need to investigate
        // what to do in this particular case (which again doesn't seem to have ever
        // actually occurred)
        return undefined;
    }

    /**
     * Provides functionality to correlate various identifiers
     * to their appropriate counterparts.
     * @constructor
     */
    function LookupService () {
        this.resetNodeIdCache();
    }

    /**
     * Reset all cached Node IDs.
     */
    LookupService.prototype.resetNodeIdCache = function () {
        if (this.nodeIdCache) {
            this.nodeIdCache.reset();
            return;
        }

        this.nodeIdCache = LRU({
            max: 500,
            maxAge: 30000
        });
    };

    /**
     * Reset the cache for a specific IP or MAC address.
     * @param  {String} cache key - IP or MAC address.
     */
    LookupService.prototype.clearNodeIdCache = function (key) {
        this.assignNodeIdCache(key, null);
    };

    LookupService.prototype.clearNodeIdCacheAndThrow = function (key, error) {
        this.clearNodeIdCache(key);

        throw error;
    };

    /**
     * Check the cache for a Node ID by IP or MAC addres.
     * @param  {String} cache key - IP or MAC address.
     * @return {Promise|null} Returns a promise if the Node ID is cached,
                              otherwise null.
     */
    LookupService.prototype.checkNodeIdCache = function (key) {
        var current = this.nodeIdCache.get(key);

        if (current === null || typeof current === 'string') {
            return new Promise(function (resolve, reject) {
                var responder = {resolve: resolve, reject: reject};

                this.handleCachePromise(responder, current);
            }.bind(this));
        }

        if (Array.isArray(current)) {
            return new Promise(function (resolve, reject) {
                var buffer = this.nodeIdCache.get(key),
                    responder = {resolve: resolve, reject: reject};

                if (Array.isArray(buffer)) {
                    buffer.push(responder);
                }

                else {
                    this.handleCachePromise(responder);
                }
            }.bind(this));
        }

        this.nodeIdCache.set(key, []);

        return null;
    };

    /**
     * Cache Node ID by either an IP or MAC address.
     * @param  {String} cache key - IP or MAC address.
     * @param  {String} cache value - Node ID.
     * @return {String} value converted to a string.
     */
    LookupService.prototype.assignNodeIdCache = function(key, value) {
        value = value && value.toString();

        var buffer = this.nodeIdCache.peek(key);

        this.nodeIdCache.set(key, value);

        if (Array.isArray(buffer)) {
            buffer.forEach(function (responder) {
                this.handleCachePromise(responder, value);
            }.bind(this));
        }

        return value;
    };

    LookupService.prototype.handleCachePromise = function (responder, value) {
      if (!value) {
          return responder.reject(
              new Errors.NotFoundError(
                  'Lookup Record Not Found (handleCachePromise)',
                  null
              )
          );
      }

      responder.resolve(value);
    };

    /**
     * Provides a middleware function suitable for
     * looking up the request IP via express and adding a req.macaddress and
     * req.macAddress value representing the MAC address used for the HTTP
     * request.
     * @return {function} An express middleware function.
     */
    LookupService.prototype.ipAddressToMacAddressMiddleware = function () {
        var self = this;

        return function(req, res, next) {
            self.ipAddressToMacAddress(remoteAddress(req)).then(function (macAddress) {
                // Provide both lower case and camel case versions.
                req.macaddress = macAddress;
                req.macAddress = macAddress;
            }).finally(function() {
                next();
            });
        };
    };

    /**
     * Converts the given IP address into a MAC address
     * via a lookup in the leaseCache.
     * @param  {String} ip IP Address to correlate to a MAC Address.
     * @return {Promise.<String>}    A promise fulfilled to the MAC address which
     * correlates to the provided IP.
     */
    LookupService.prototype.ipAddressToMacAddress = function (ip) {
        assert.isIP(ip);
        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(ip).then(function (record) {
                return record.macAddress;
            });
        });
    };

    /**
     * macAddressToNode converts the given MAC address into a Node document via
     * a lookup in the DomainService.
     * @param  {String} macAddress MAC Address to correlate to a Node.
     * @return {Promise.<NodeDocument>} A promise fulfilled to the Node which
     * correlates to the provided MAC Address.
     */
    LookupService.prototype.macAddressToNode = function (macAddress) {
        return waterline.lookups.findOneByTerm(macAddress).then(function (record) {
            if (record.node) {
                return waterline.nodes.needOneById(record.node);
            } else {
                throw new Errors.NotFoundError(
                    'Lookup Record Not Found (macAddressToNode)',
                    record
                );
            }
        });
    };

    /**
     * Returns the IP Address associated with the given MAC address.
     * @param {String} macAddress MAC Address to correlate with an IP.
     * @return {Promise.<String>} A promise fulfilled with the IP Address which
     * correlates to the provided MAC Address.
     */
    LookupService.prototype.macAddressToIp = function (macAddress) {
        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(macAddress).then(function (record) {
                if (record.ipAddress) {
                    return record.ipAddress;
                } else {
                    throw new Errors.NotFoundError(
                        'Lookup Record Not Found (macAddressToIp)',
                        record
                    );
                }
            });
        });
    };

    /**
     * Converts the given MAC address into the BSON ID of the node document via
     * a lookup in the DomainService. This function will cache values to avoid
     * excessive round trips to the DB, so its use is recommended when only the
     * BSON ID is needed.
     * @param  {String} macAddress MAC Address to correlate to a Node.
     * @return {Promise.<ObjectId>} A promise fulfilled to the Node ID which
     * correlates to the provided MAC Address.
     */
    LookupService.prototype.macAddressToNodeId = function (macAddress) {
        var self = this;
        var cachePromise = self.checkNodeIdCache(macAddress);

        if (cachePromise) {
            return cachePromise;
        }

        return waterline.lookups.findOneByTerm(macAddress)
        .then(function (record) {
            if (record.node) {
                return self.assignNodeIdCache(macAddress, record.node);
            }
            else {
                self.clearNodeIdCache(macAddress);
                throw new Errors.NotFoundError(
                    'Lookup Record Not Found (macAddressToNodeId)',
                    record
                );
            }
        })
        .catch(self.clearNodeIdCacheAndThrow.bind(self, macAddress));
    };

    /**
     * Converts the given IP Address into a Node document via
     * a lookup using the leaseCache & DomainService.
     * @param  {String} ip IP Address to correlate to a Node.
     * @return {Promise.<NodeDocument>} A promise fulfilled to the Node which
     * correlates to the provided IP Address.
     */
    LookupService.prototype.ipAddressToNode = function (ip) {
        assert.isIP(ip);

        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(ip).then(function (record) {
                if (record.node) {
                    return waterline.nodes.needOneById(record.node);
                } else {
                    throw new Errors.NotFoundError(
                        'Lookup Record Not Found (ipAddressToNode)',
                        record
                    );
                }
            });
        });
    };

    /**
     * Converts the given IP Address into the BSON ID of the Node via
     * a lookup using the leaseCache & DomainService. This function will cache
     * values to avoid excessive round trips to the DB, so its use is
     * recommended when only the BSON ID is needed.
     * @param  {String} ip IP Address to correlate to a Node.
     * @return {Promise.<ObjectId>} A promise fulfilled to the Node ID which
     * correlates to the provided IP Address.
     */
    LookupService.prototype.ipAddressToNodeId = function (ip) {
        assert.isIP(ip);

        var self = this;
        var cachePromise = self.checkNodeIdCache(ip);
        if (cachePromise) {
            return cachePromise;
        }

        return self.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(ip).then(function (record) {
                if (record.node) {
                    return self.assignNodeIdCache(ip, record.node);
                }
                else {
                    self.clearNodeIdCache(ip);
                    throw new Errors.NotFoundError(
                        'Lookup Record Not Found (ipAddressToNodeId)',
                        record
                    );
                }
            })
            .catch(self.clearNodeIdCacheAndThrow.bind(self, ip));
        });
    };

    /**
     * Returns a list of IP Addresses for the given Node ID.
     * @param  {String} id Node ID
     * @return {Promise.<Array>} A promise fulfilled with an array of
     * IP Addresses associated with the Node.
     */
    LookupService.prototype.nodeIdToIpAddresses = function (id) {
        var self = this;
        assert.isMongoId(id);

        return self.validateArpCache()
        .then(function() {
            return waterline.lookups.findByTerm(id).then(function (records) {
                return _.reduce(records, function (ipAddresses, record) {
                    if (record.ipAddress) {
                        ipAddresses.push(record.ipAddress);
                    }

                    return ipAddresses;
                }, []);
            });
        });
    };

    /**
     * Returns the proxy server for the given Node ID.
     * @param  {String} id Node ID
     * @return {Promise.<String>} A promise fulfilled to the Proxy server URL
     * that corresponds to this node
     */
    LookupService.prototype.nodeIdToProxy = function (id) {

        return waterline.lookups.findOneByTerm(id).then(function (record) {
            if (record && record.proxy) {
                return record.proxy;
            } else {
                return undefined;
            }
        });
    };

    LookupService.prototype.setIpAddress = function (ip, mac) {
        return waterline.lookups.setIp(ip, mac);
    };

    LookupService.prototype.validateArpCache = function () {
        var self = this;
        return Promise.resolve().then(function() {
            if(configuration.get('arpCacheEnabled', true)) {
                return arpCache.getCurrent()
                .map(function(entry) {
                    return self.setIpAddress(entry.ip, entry.mac);
                });
            }
        });

    };

    LookupService.prototype.start = function () {
        return waterline.lookups.setIndexes();
    };

    LookupService.prototype.stop = function () {
        return Promise.resolve();
    };

    return new LookupService();
}
<|endoftext|>


<commit_before>var request = require('request'),
    log     = require('./log');

/**
 * SessionManager Constructor
 *
 * @param {Object} options
 * @api public
 */
function SessionManager (options) {

  log.debug('[chimp][session-manager] options are', options);

  if (!options) {
    throw new Error('options is required');
  }

  if (!options.port) {
    throw new Error('options.port is required');
  }

  if (!options.browser && !options.deviceName) {
    throw new Error('[chimp][session-manager] options.browser or options.deviceName is required');
  }

  this.options = options;

  this.maxRetries = 30;
  this.retryDelay = 3000;
  this.retry = 0;

  log.debug('[chimp][session-manager] created a new SessionManager', options);

}

SessionManager.prototype.webdriver = require('webdriverio');

/**
 * Wraps the webdriver remote method and allows reuse options
 *
 * @api public
 */
SessionManager.prototype.remote = function (webdriverOptions, callback) {

  var self = this;

  log.debug('[chimp][session-manager] creating webdriver remote ');
  var browser = this.webdriver.remote(webdriverOptions);

  function decideReuse () {

    if (self.options.browser === 'phantomjs') {
      log.debug('[chimp][session-manager] browser is phantomjs, not reusing a session');
      callback(null, browser);
      return;
    }

    if (process.env['chimp.noSessionReuse'] && process.env['chimp.noSessionReuse'] !== 'false') {
      log.debug('[chimp][session-manager] noSessionReuse is true, not reusing a session');
      callback(null, browser);
      return;
    }

    if (process.env['chimp.watch'] === 'false' && process.env['chimp.server'] === 'false') {
      log.debug('[chimp][session-manager] watch mode is false, not reusing a session');
      callback(null, browser);
      return;
    }

    self._getWebdriverSessions(function (err, sessions) {
      if (sessions.length !== 0) {
        log.debug('[chimp][session-manager] Found an open selenium sessions, reusing session', sessions[0].id);
        browser.requestHandler.sessionID = sessions[0].id;
      }

      log.debug('[chimp][session-manager] Did not find any open selenium sessions, not reusing a session');
      browser = self._monkeyPatchBrowserSessionManagement(browser, sessions);
      callback(null, browser);
    });

  }

  this._waitForConnection(browser, decideReuse);

};

SessionManager.prototype._waitForConnection = function(browser, callback) {
  log.debug('[chimp][session-manager] checking connection to selenium server');
  var self = this;
  browser.status(function (err) {
    if (err && /ECONNREFUSED/.test(err.message)) {
      if (++self.retry === self.maxRetries) {
        callback('[chimp][session-manager] timed out retrying to connect to selenium server');
      }
      log.debug('[chimp][session-manager] could not connect to the server, retrying', '(' + self.retry + '/' + self.maxRetries + ')');
      setTimeout(function() {
        self._waitForConnection(browser, callback);
      }, self.retryDelay);
    } else {
      callback();
    }
  });
};

SessionManager.prototype._monkeyPatchBrowserSessionManagement = function (browser, sessions) {

  log.debug('[chimp][session-manager]', 'monkey patching the browser object');

  var callbacker = function () {
    var cb = arguments[arguments.length - 1];
    if (cb && typeof cb === 'function') {
      cb();
    }
    return {
      then: function (c) {
        c();
      }
    };
  };

  browser._init = browser.init;
  browser.init = function () {
    if (sessions.length !== 0) {
      log.debug('[chimp][session-manager]', 'browser already initialized');
      return callbacker.apply(this, arguments);
    }
    log.debug('[chimp][session-manager]', 'initializing browser');
    return browser._init.apply(this, arguments);
  };

  browser._end = browser.end;
  browser.end = callbacker.bind(browser);

  browser._endAll = browser.end;
  browser.endAll = callbacker.bind(browser);

  return browser;

};

/**
 * Gets a list of sessions from the localhost selenium server
 *
 * @api private
 */
SessionManager.prototype._getWebdriverSessions = function (callback) {

  var wdHubSessions = 'http://localhost:' + this.options.port + '/wd/hub/sessions';

  log.debug('[chimp][session-manager]', 'requesting sessions from', wdHubSessions);

  request(wdHubSessions, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      log.debug('[chimp][session-manager]', 'received data', body);
      callback(null, JSON.parse(body).value);
    } else {
      log.error('[chimp][session-manager]', 'received error', error);
      callback(error);
    }
  });

};

/**
 * Kills the 1st session found running on selenium server
 *
 * @api public
 */
SessionManager.prototype.killCurrentSession = function (callback) {

  if (this.options.browser === 'phantomjs') {
    log.debug('[chimp][session-manager] browser is phantomjs, not killing session');
    callback();
    return;
  }

  if (!process.env['chimp.noSessionReuse']) {
    log.debug('[chimp][session-manager] noSessionReuse is true, , not killing session');
    callback();
    return;
  }

  if (process.env['chimp.watch'] === 'true' || process.env['chimp.server'] === 'true') {
    log.debug('[chimp][session-manager] watch / server mode are true, not killing session');
    callback();
    return;
  }

  var wdHubSession = 'http://localhost:' + this.options.port + '/wd/hub/session';

  this._getWebdriverSessions(function(err, sessions) {

    // XXX this currently only works for one open session at a time
    var sessionId = sessions[0].id;

    log.debug('[chimp][session-manager]', 'deleting wd session', sessionId);

    request.del(wdHubSession + '/' + sessionId, function (error, response, body) {
      if (!error && response.statusCode == 200) {
        log.debug('[chimp][session-manager]', 'received data', body);
        callback();
      } else {
        log.error('[chimp][session-manager]', 'received error', error);
        callback(error);
      }
    });

  });

};

module.exports = SessionManager;
<commit_msg>improved logging<commit_after>var request = require('request'),
    log     = require('./log');

/**
 * SessionManager Constructor
 *
 * @param {Object} options
 * @api public
 */
function SessionManager (options) {

  log.debug('[chimp][session-manager] options are', options);

  if (!options) {
    throw new Error('options is required');
  }

  if (!options.port) {
    throw new Error('options.port is required');
  }

  if (!options.browser && !options.deviceName) {
    throw new Error('[chimp][session-manager] options.browser or options.deviceName is required');
  }

  this.options = options;

  this.maxRetries = 30;
  this.retryDelay = 3000;
  this.retry = 0;

  log.debug('[chimp][session-manager] created a new SessionManager', options);

}

SessionManager.prototype.webdriver = require('webdriverio');

/**
 * Wraps the webdriver remote method and allows reuse options
 *
 * @api public
 */
SessionManager.prototype.remote = function (webdriverOptions, callback) {

  var self = this;

  log.debug('[chimp][session-manager] creating webdriver remote ');
  var browser = this.webdriver.remote(webdriverOptions);

  function decideReuse () {

    if (self.options.browser === 'phantomjs') {
      log.debug('[chimp][session-manager] browser is phantomjs, not reusing a session');
      callback(null, browser);
      return;
    }

    if (process.env['chimp.noSessionReuse'] && process.env['chimp.noSessionReuse'] !== 'false') {
      log.debug('[chimp][session-manager] noSessionReuse is true, not reusing a session');
      callback(null, browser);
      return;
    }

    if (process.env['chimp.watch'] === 'false' && process.env['chimp.server'] === 'false') {
      log.debug('[chimp][session-manager] watch mode is false, not reusing a session');
      callback(null, browser);
      return;
    }

    self._getWebdriverSessions(function (err, sessions) {
      if (sessions.length !== 0) {
        log.debug('[chimp][session-manager] Found an open selenium sessions, reusing session', sessions[0].id);
        browser.requestHandler.sessionID = sessions[0].id;
      } else {
        log.debug('[chimp][session-manager] Did not find any open selenium sessions, not reusing a session');
      }

      browser = self._monkeyPatchBrowserSessionManagement(browser, sessions);
      callback(null, browser);
    });

  }

  this._waitForConnection(browser, decideReuse);

};

SessionManager.prototype._waitForConnection = function(browser, callback) {
  log.debug('[chimp][session-manager] checking connection to selenium server');
  var self = this;
  browser.status(function (err) {
    if (err && /ECONNREFUSED/.test(err.message)) {
      if (++self.retry === self.maxRetries) {
        callback('[chimp][session-manager] timed out retrying to connect to selenium server');
      }
      log.debug('[chimp][session-manager] could not connect to the server, retrying', '(' + self.retry + '/' + self.maxRetries + ')');
      setTimeout(function() {
        self._waitForConnection(browser, callback);
      }, self.retryDelay);
    } else {
      callback();
    }
  });
};

SessionManager.prototype._monkeyPatchBrowserSessionManagement = function (browser, sessions) {

  log.debug('[chimp][session-manager]', 'monkey patching the browser object');

  var callbacker = function () {
    var cb = arguments[arguments.length - 1];
    if (cb && typeof cb === 'function') {
      cb();
    }
    return {
      then: function (c) {
        c();
      }
    };
  };

  browser._init = browser.init;
  browser.init = function () {
    if (sessions.length !== 0) {
      log.debug('[chimp][session-manager]', 'browser already initialized');
      return callbacker.apply(this, arguments);
    }
    log.debug('[chimp][session-manager]', 'initializing browser');
    return browser._init.apply(this, arguments);
  };

  browser._end = browser.end;
  browser.end = callbacker.bind(browser);

  browser._endAll = browser.end;
  browser.endAll = callbacker.bind(browser);

  return browser;

};

/**
 * Gets a list of sessions from the localhost selenium server
 *
 * @api private
 */
SessionManager.prototype._getWebdriverSessions = function (callback) {

  var wdHubSessions = 'http://localhost:' + this.options.port + '/wd/hub/sessions';

  log.debug('[chimp][session-manager]', 'requesting sessions from', wdHubSessions);

  request(wdHubSessions, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      log.debug('[chimp][session-manager]', 'received data', body);
      callback(null, JSON.parse(body).value);
    } else {
      log.error('[chimp][session-manager]', 'received error', error);
      callback(error);
    }
  });

};

/**
 * Kills the 1st session found running on selenium server
 *
 * @api public
 */
SessionManager.prototype.killCurrentSession = function (callback) {

  if (this.options.browser === 'phantomjs') {
    log.debug('[chimp][session-manager] browser is phantomjs, not killing session');
    callback();
    return;
  }

  if (!process.env['chimp.noSessionReuse']) {
    log.debug('[chimp][session-manager] noSessionReuse is true, , not killing session');
    callback();
    return;
  }

  if (process.env['chimp.watch'] === 'true' || process.env['chimp.server'] === 'true') {
    log.debug('[chimp][session-manager] watch / server mode are true, not killing session');
    callback();
    return;
  }

  var wdHubSession = 'http://localhost:' + this.options.port + '/wd/hub/session';

  this._getWebdriverSessions(function(err, sessions) {

    // XXX this currently only works for one open session at a time
    var sessionId = sessions[0].id;

    log.debug('[chimp][session-manager]', 'deleting wd session', sessionId);

    request.del(wdHubSession + '/' + sessionId, function (error, response, body) {
      if (!error && response.statusCode == 200) {
        log.debug('[chimp][session-manager]', 'received data', body);
        callback();
      } else {
        log.error('[chimp][session-manager]', 'received error', error);
        callback(error);
      }
    });

  });

};

module.exports = SessionManager;
<|endoftext|>