<commit_before>//{namespace name=backend/bepado/view/main}

//{block name="backend/bepado/view/export/list"}
Ext.define('Shopware.apps.Bepado.view.export.List', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.bepado-export-list',

    border: false,

    store: 'export.List',

    selModel: {
        selType: 'checkboxmodel',
        mode: 'MULTI'
    },

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            dockedItems: [
                me.getToolbar(),
                me.getPagingToolbar()
            ],
            columns: me.getColumns()
        });

        me.callParent(arguments);

        me.store.load();
    },

    getColumns: function() {
        var me = this;
        return [{
            header: '{s name=export/columns/number}Number{/s}',
            dataIndex: 'number',
            flex: 2
        }, {
            header: '{s name=export/columns/name}Name{/s}',
            dataIndex: 'name',
            flex: 4
        }, {
            header: '{s name=export/columns/supplier}Supplier{/s}',
            dataIndex: 'supplier',
            flex: 3
        }, {
            header: '{s name=export/columns/active}Active{/s}',
            xtype: 'booleancolumn',
            dataIndex: 'active',
            width: 50,
            sortable: false
        }, {
            header: '{s name=export/columns/price}Price{/s}',
            xtype: 'numbercolumn',
            dataIndex: 'price',
            align: 'right',
            width: 55
        }, {
            header: '{s name=export/columns/tax}Tax{/s}',
            xtype: 'numbercolumn',
            dataIndex: 'tax',
            align: 'right',
            flex: 1
        }, {
            header: '{s name=export/columns/stock}Stock{/s}',
            xtype: 'numbercolumn',
            dataIndex: 'inStock',
            format: '0,000',
            align: 'right',
            flex: 1
        }, {
            header: '{s name=export/columns/status}Status{/s}',
            dataIndex: 'exportStatus',
            flex: 2,
            renderer: function(value, metaData, record) {
                if(record.get('exportMessage')) {
                    metaData.tdAttr = 'data-qtip="' +  record.get('exportMessage') + '"';
                }
                return value;
            }
        }, {
            xtype: 'actioncolumn',
            width: 26,
            items: [{
                action: 'edit',
                cls: 'editBtn',
                iconCls: 'sprite-pencil',
                handler: function(view, rowIndex, colIndex, item, opts, record) {
                    Shopware.app.Application.addSubApplication({
                        name: 'Shopware.apps.Article',
                        action: 'detail',
                        params: {
                            articleId: record.get('id')
                        }
                    });
                }
            }]
        }];
    },

    getPagingToolbar: function() {
        var me = this;
        return {
            xtype: 'pagingtoolbar',
            displayInfo: true,
            store: me.store,
            dock: 'bottom'
        };
    },

    getToolbar: function() {
        var me = this;
        return {
            xtype: 'toolbar',
            ui: 'shopware-ui',
            dock: 'top',
            border: false,
            items: me.getTopBar()
        };
    },

    getTopBar:function () {
        var me = this;
        var items = [];
        items.push({
            iconCls:'sprite-plus-circle-frame',
            text:'{s name=export/options/insert_text}Insert / update products to the export{/s}',
            action:'add'
        });
        items.push({
            iconCls:'sprite-minus-circle-frame',
            text:'{s name=export/options/delete_text}Remove products from the export{/s}',
            action:'delete'
        });
        //items.push('->', {
        //    xtype:'textfield',
        //    name:'searchfield',
        //    cls:'searchfield',
        //    width:100,
        //    emptyText:'{s name=search/empty_text}Search...{/s}',
        //    enableKeyEvents:true,
        //    checkChangeBuffer:500
        //}, {
        //    xtype:'tbspacer', width:6
        //});
        return items;
    }
});
//{/block}<commit_msg>Adds pagination selector for the bepado export window<commit_after>//{namespace name=backend/bepado/view/main}

//{block name="backend/bepado/view/export/list"}
Ext.define('Shopware.apps.Bepado.view.export.List', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.bepado-export-list',

    border: false,

    store: 'export.List',

    selModel: {
        selType: 'checkboxmodel',
        mode: 'MULTI'
    },

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            dockedItems: [
                me.getToolbar(),
                me.getPagingToolbar()
            ],
            columns: me.getColumns()
        });

        me.callParent(arguments);

        me.store.load();
    },

    getColumns: function() {
        var me = this;
        return [{
            header: '{s name=export/columns/number}Number{/s}',
            dataIndex: 'number',
            flex: 2
        }, {
            header: '{s name=export/columns/name}Name{/s}',
            dataIndex: 'name',
            flex: 4
        }, {
            header: '{s name=export/columns/supplier}Supplier{/s}',
            dataIndex: 'supplier',
            flex: 3
        }, {
            header: '{s name=export/columns/active}Active{/s}',
            xtype: 'booleancolumn',
            dataIndex: 'active',
            width: 50,
            sortable: false
        }, {
            header: '{s name=export/columns/price}Price{/s}',
            xtype: 'numbercolumn',
            dataIndex: 'price',
            align: 'right',
            width: 55
        }, {
            header: '{s name=export/columns/tax}Tax{/s}',
            xtype: 'numbercolumn',
            dataIndex: 'tax',
            align: 'right',
            flex: 1
        }, {
            header: '{s name=export/columns/stock}Stock{/s}',
            xtype: 'numbercolumn',
            dataIndex: 'inStock',
            format: '0,000',
            align: 'right',
            flex: 1
        }, {
            header: '{s name=export/columns/status}Status{/s}',
            dataIndex: 'exportStatus',
            flex: 2,
            renderer: function(value, metaData, record) {
                if(record.get('exportMessage')) {
                    metaData.tdAttr = 'data-qtip="' +  record.get('exportMessage') + '"';
                }
                return value;
            }
        }, {
            xtype: 'actioncolumn',
            width: 26,
            items: [{
                action: 'edit',
                cls: 'editBtn',
                iconCls: 'sprite-pencil',
                handler: function(view, rowIndex, colIndex, item, opts, record) {
                    Shopware.app.Application.addSubApplication({
                        name: 'Shopware.apps.Article',
                        action: 'detail',
                        params: {
                            articleId: record.get('id')
                        }
                    });
                }
            }]
        }];
    },

    /**
     * Creates a paging toolbar with additional page size selector
     *
     * @returns Array
     */
    getPagingToolbar: function() {
        var me = this;
        var pageSize = Ext.create('Ext.form.field.ComboBox', {
            labelWidth: 120,
            cls: Ext.baseCSSPrefix + 'page-size',
            queryMode: 'local',
            width: 180,
            listeners: {
                scope: me,
                select: function(combo, records) {
                    var record = records[0],
                        me = this;

                    me.store.pageSize = record.get('value');
                    me.store.loadPage(1);
                }
            },
            store: Ext.create('Ext.data.Store', {
                fields: [ 'value' ],
                data: [
                    { value: '20' },
                    { value: '40' },
                    { value: '60' },
                    { value: '80' },
                    { value: '100' },
                    { value: '250' },
                    { value: '500' },
                ]
            }),
            displayField: 'value',
            valueField: 'value',
            editable: false,
            emptyText: '20'
        });
        pageSize.setValue(me.store.pageSize);

        var pagingBar = Ext.create('Ext.toolbar.Paging', {
            store: me.store,
            dock:'bottom',
            displayInfo:true
        });

        pagingBar.insert(pagingBar.items.length - 2, [ { xtype: 'tbspacer', width: 6 }, pageSize ]);
        return pagingBar;
    },

    getToolbar: function() {
        var me = this;
        return {
            xtype: 'toolbar',
            ui: 'shopware-ui',
            dock: 'top',
            border: false,
            items: me.getTopBar()
        };
    },

    getTopBar:function () {
        var me = this;
        var items = [];
        items.push({
            iconCls:'sprite-plus-circle-frame',
            text:'{s name=export/options/insert_text}Insert / update products to the export{/s}',
            action:'add'
        });
        items.push({
            iconCls:'sprite-minus-circle-frame',
            text:'{s name=export/options/delete_text}Remove products from the export{/s}',
            action:'delete'
        });
        //items.push('->', {
        //    xtype:'textfield',
        //    name:'searchfield',
        //    cls:'searchfield',
        //    width:100,
        //    emptyText:'{s name=search/empty_text}Search...{/s}',
        //    enableKeyEvents:true,
        //    checkChangeBuffer:500
        //}, {
        //    xtype:'tbspacer', width:6
        //});
        return items;
    }
});
//{/block}<|endoftext|>


<commit_before>/**
 * SurveysController
 *
 * @description :: Server-side logic for managing surveys
 * @help        :: See http://links.sailsjs.org/docs/controllers
 */

module.exports = {

};

<commit_msg>getQuestions: returns surveys without the responses<commit_after>/**
 * SurveysController
 *
 * @description :: Server-side logic for managing surveys
 * @help        :: See http://links.sailsjs.org/docs/controllers
 */

module.exports = {
    getQuestions: function (req, res) {
        var surveyId = req.param('id');
        var jsonQuery = { "id": surveyId };
        //var jsonProjection = {
        //    id: true,
        //    questions: true,
        //    responses: true,
        //    userId: true,
        //    name: true,
        //    description: true,
        //    location: true, shortUrl: true, createdAt: true, updatedAt: true,
        //};
        Surveys.findOne(jsonQuery, function (err, tempSurvey) {
            var survey = {
                id: tempSurvey.id,
                questions: tempSurvey.questions,
                userId: tempSurvey.userid,
                name: tempSurvey.name,
                description: tempSurvey.description,
                location: tempSurvey.location,
                shortUrl: tempSurvey.shortUrl,
                createdAt: tempSurvey.createdAt,
                updatedAt: tempSurvey.updatedAt,
            };
            return res.send(JSON.stringify(survey));
        });
    }
}

<|endoftext|>


<commit_before>import path from 'path';
import {fileUtil, util, DispatherTypes} from '../../../helper';

export function apiHandler(dispatcher) {
    if (dispatcher.handler) {
        return dispatcher.handler(this).then((data) => {
            let json = data;
            if (typeof data === 'string') {
                try {
                    json = util.JSONParse(data);
                } catch (error) {
                    json = {
                        error: error.toString(),
                        data
                    };
                }
            }
            return Promise.resolve({json});
        });
    }

    const dataPath = dispatcher.dataPath;
    if (Array.isArray(dataPath)) {
        return Promise.all(dataPath.map(url => {
            return util.jsonResolver({url})
        })).then(resps => {
            return resps.reduce((bef, aft) => {
                return {
                    json: Object.assign(bef.json, aft.json)
                };
            });
        });
    }
    return util.jsonResolver({url: dataPath});
}

/**
 * default dispatcher
 * @param  {[type]} dispatcher  [description]
 * @param  {[type]} config [description]
 * @param  {[type]} next [description]
 * @return {[type]}         [description]
 */
export function* dirDispatcher(dispatcher, {tplRender}, next) {
    const sortFiles = (list) => {
        return list.sort((a, b) => {
            return a.name.charAt(0).localeCompare(b.name.charAt(0));
        });
    };
    const viewPath = dispatcher.pagePath;
    const files = yield fileUtil.getDirInfo(viewPath);
    const promises = files.map((file) => fileUtil.getFileStat(path.resolve(viewPath, file)));
    let result = (yield Promise.all(promises)).map((item, idx) => {
        return Object.assign(item, {
            name: files[idx],
            isFile: item.isFile(),
            requestPath: [this.request.path, files[idx], item.isFile() ? '' : '/'].join('')
        });
    });

    const fileList = sortFiles(result.filter((item) => {
        return item.isFile;
    }));
    const dirList = sortFiles(result.filter((item) => {
        return !item.isFile;
    }));

    yield this.render('cataLog', {
        title: '查看列表',
        showList: dirList.concat(fileList)
    });
    yield next;
}

/**
 * 同步请求响应
 * @param dispatcher
 * @param config
 * @param next
 * @returns {*}
 */
export function* syncDispatcher(dispatcher, {tplRender}, next) {
    const filePath = dispatcher.pagePath;
    let res = yield apiHandler.call(this, dispatcher);

    if (!res || !res.json) {
        this.type = 500;
        yield this.render('e', {
            title: '出错了', e: {
                code: 500,
                msg: '模拟数据处理'
            }
        });
        return yield next;
    }

    try {
        let result = yield tplRender.parse(filePath, res.json);

        this.type = 'text/html; charset=utf-8';
        this.body = result;
    } catch (error) {
        util.notify({
            title: '模板解析失败',
            msg: error
        });
        yield this.render('e', {
            title: '出错了', e: {
                code: 500,
                msg: error
            }
        });
    }
    return yield next;
}

/**
 * 异步请求响应
 * @param dispatcher
 * @param config
 * @param next
 * @returns {*}
 */
export function* asyncDispather(dispatcher, {tplRender}, next) {
    /**
     * 异步接口处理
     * @type {[type]}
     */
    let res = yield apiHandler.call(this, dispatcher);
    if (res && res.json) {
        this.type = 'application/json; charset=utf-8';
        this.body = res.json;
        return yield next;
    }
    yield this.render('e', {
        title: '出错了', e: {
            code: 500,
            msg: '请求代理服务器异常'
        }
    });
    yield next;
}

export default ({tplRender}) => {
    return function*(next) {
        if (!this.dispatcher) {
            return void 0;
        }

        /**
         * 分配给不同的处理器
         * @type {Object}
         */
        let args = [{tplRender}, next];

        let dispatcherMap = {
            [DispatherTypes.DIR]: dirDispatcher,
            [DispatherTypes.SYNC]: syncDispatcher,
            [DispatherTypes.ASYNC]: asyncDispather
        };

        let dispatcher = dispatcherMap[this.dispatcher.type];
        if (dispatcher) {
            yield dispatcher.call(this, this.dispatcher, ...args);
        }
    };
};
<commit_msg>Delete dispatcher.es6<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c), Log-Normal, Inc.
 */

/**
\file memory.js
Plugin to collect memory metrics when available.
see: http://code.google.com/p/chromium/issues/detail?id=43281
*/

(function() {

// First make sure BOOMR is actually defined.  It's possible that your plugin is loaded before boomerang, in which case
// you'll need this.
BOOMR = BOOMR || {};
BOOMR.plugins = BOOMR.plugins || {};
if (BOOMR.plugins.Memory) {
	return;
}

// A private object to encapsulate all your implementation details
var impl = {
	complete: false,
	done: function() {
		var w  = BOOMR.window,
		    p  = w.performance,
		    c  = w.console,
		    d  = w.document,
		    fn = d.getElementsByTagName,
		    m, f;

		// handle IE6/7 weirdness regarding host objects
		// See: http://stackoverflow.com/questions/7125288/what-is-document-getelementbyid
		f  = (fn.call === undefined ? function(tag) { return fn(tag); } : fn);

		m = (p && p.memory ? p.memory : (c && c.memory ? c.memory : null));

		// If we have resource timing, get number of resources
		if(p && p.getEntries && p.getEntries().length) {
			BOOMR.addVar("dom.res", p.getEntries().length);
		}

		if(m) {
			BOOMR.addVar({
				"mem.total": m.totalJSHeapSize,
				"mem.used" : m.usedJSHeapSize
			});
		}

		BOOMR.addVar({
			"dom.ln": f.call(d, "*").length,
			"dom.sz": f.call(d, "html")[0].innerHTML.length,
			"dom.img": f.call(d, "img").length,
			"dom.script": f.call(d, "script").length
		});

		this.complete = true;
		BOOMR.sendBeacon();
	}
};

BOOMR.plugins.Memory = {
	init: function() {
		// we do this on onload so that we take a memory and dom snapshot after most things have run
		BOOMR.subscribe("page_ready", impl.done, null, impl);
		BOOMR.subscribe("xhr_load", impl.done, null, impl);
		BOOMR.subscribe("page_unload", impl.done, null, impl);
		return this;
	},

	is_complete: function() {
		return impl.complete;
	}
};

}());

<commit_msg>Refactor to be more efficient<commit_after>/*
 * Copyright (c), Log-Normal, Inc.
 */

/**
\file memory.js
Plugin to collect memory metrics when available.
see: http://code.google.com/p/chromium/issues/detail?id=43281
*/

(function() {
var w, p={}, d, m, impl;
// First make sure BOOMR is actually defined.  It's possible that your plugin is loaded before boomerang, in which case
// you'll need this.
BOOMR = BOOMR || {};
BOOMR.plugins = BOOMR.plugins || {};
if (BOOMR.plugins.Memory) {
	return;
}

function nodeList(type) {
	return d.getElementsByTagName(type);
}

// A private object to encapsulate all your implementation details
impl = {
	done: function() {
		var res, doms={}, a;
		// If we have resource timing, get number of resources
		if(p && p.getEntriesByType && p.getEntriesByType("resource").length) {
			res = p.getEntriesByType("resource");
			BOOMR.addVar("dom.res", res.length);

			a = document.createElement("a");

			res.forEach(function(r) {
				a.href=r.name;
				doms[a.hostname] = true;
			});

			BOOMR.addVar("dom.doms", Object.keys(doms).length);
		}
		else {
			BOOMR.removeVar("dom.res");
		}

		if(m) {
			BOOMR.addVar({
				"mem.total": m.totalJSHeapSize,
				"mem.used" : m.usedJSHeapSize
			});
		}

		try {
			BOOMR.addVar({
				"dom.ln": nodeList("*").length,
				"dom.sz": nodeList("html")[0].innerHTML.length,
				"dom.img": nodeList("img").length,
				"dom.script": nodeList("script").length
			});
		}
		catch(err) {
			BOOMR.addError(err, "Memory.done.dom");
		}

		// no need of sendBeacon because we're called when the beacon is being sent
	}
};

BOOMR.plugins.Memory = {
	init: function() {
		var c;

		try {
			w  = BOOMR.window;
			d  = w.document;
			p  = w.performance;
			c  = w.console;
		}
		catch(err) {
			BOOMR.addError(err, "Memory.init");
		}

		m = (p && p.memory ? p.memory : (c && c.memory ? c.memory : null));

		// we do this before sending a beacon to get the snapshot when the beacon is sent
		BOOMR.subscribe("before_beacon", impl.done, null, impl);
		return this;
	},

	is_complete: function() {
		// Always true since we run on before_beacon, which happens after the check
		return true;
	}
};

}());

<|endoftext|>


<commit_before>L.Draw.Polyline = L.Draw.Feature.extend({
	statics: {
		TYPE: 'polyline'
	},

	Poly: L.Polyline,

	options: {
		allowIntersection: true,
		drawError: {
			color: '#b00b00',
			message: L.drawLocal.draw.polyline.error,
			timeout: 2500
		},
		icon: new L.DivIcon({
			iconSize: new L.Point(8, 8),
			className: 'leaflet-div-icon leaflet-editing-icon'
		}),
		guidelineDistance: 20,
		shapeOptions: {
			stroke: true,
			color: '#f06eaa',
			weight: 4,
			opacity: 0.5,
			fill: false,
			clickable: true
		},
		zIndexOffset: 2000 // This should be > than the highest z-index any map layers
	},

	initialize: function (map, options) {
		// Merge default drawError options with custom options
		if (options && options.drawError) {
			options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
		}

		// Save the type so super can fire, need to do this as cannot do this.TYPE :(
		this.type = L.Draw.Polyline.TYPE;

		L.Draw.Feature.prototype.initialize.call(this, map, options);
	},

	addHooks: function () {
		L.Draw.Feature.prototype.addHooks.call(this);
		if (this._map) {
			this._markers = [];

			this._markerGroup = new L.LayerGroup();
			this._map.addLayer(this._markerGroup);

			this._poly = new L.Polyline([], this.options.shapeOptions);

			this._tooltip.updateContent(this._getTooltipText());

			// Make a transparent marker that will used to catch click events. These click
			// events will create the vertices. We need to do this so we can ensure that
			// we can create vertices over other map layers (markers, vector layers). We
			// also do not want to trigger any click handlers of objects we are clicking on
			// while drawing.
			if (!this._mouseMarker) {
				this._mouseMarker = L.marker(this._map.getCenter(), {
					icon: L.divIcon({
						className: 'leaflet-mouse-marker',
						iconAnchor: [20, 20],
						iconSize: [40, 40]
					}),
					opacity: 0,
					zIndexOffset: this.options.zIndexOffset
				});
			}

			this._mouseMarker
				.on('click', this._onClick, this)
				.addTo(this._map);

			this._map
				.on('mousemove', this._onMouseMove, this)
				.on('zoomend', this._onZoomEnd, this);
		}
	},

	removeHooks: function () {
		L.Draw.Feature.prototype.removeHooks.call(this);

		this._clearHideErrorTimeout();

		this._cleanUpShape();

		// remove markers from map
		this._map.removeLayer(this._markerGroup);
		delete this._markerGroup;
		delete this._markers;

		this._map.removeLayer(this._poly);
		delete this._poly;

		this._mouseMarker.off('click', this._onClick, this);
		this._map.removeLayer(this._mouseMarker);
		delete this._mouseMarker;

		// clean up DOM
		this._clearGuides();

		this._map
			.off('mousemove', this._onMouseMove, this)
			.off('zoomend', this._onZoomEnd, this);
	},

	_finishShape: function () {
		var intersects = this._poly.newLatLngIntersects(this._poly.getLatLngs()[0], true);

		if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {
			this._showErrorTooltip();
			return;
		}

		this._fireCreatedEvent();
		this.disable();
	},

	//Called to verify the shape is valid when the user tries to finish it
	//Return false if the shape is not valid
	_shapeIsValid: function () {
		return true;
	},

	_onZoomEnd: function () {
		this._updateGuide();
	},

	_onMouseMove: function (e) {
		var newPos = e.layerPoint,
			latlng = e.latlng;

		// Save latlng
		// should this be moved to _updateGuide() ?
		this._currentLatLng = latlng;

		// Update the label
		this._tooltip.updatePosition(latlng);

		// Update the guide line
		this._updateGuide(newPos);

		// Update the mouse marker position
		this._mouseMarker.setLatLng(latlng);

		L.DomEvent.preventDefault(e.originalEvent);
	},

	_onClick: function (e) {
		var latlng = e.target.getLatLng(),
			markerCount = this._markers.length;

		if (markerCount > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
			this._showErrorTooltip();
			return;
		}
		else if (this._errorShown) {
			this._hideErrorTooltip();
		}

		this._markers.push(this._createMarker(latlng));

		this._poly.addLatLng(latlng);

		if (this._poly.getLatLngs().length === 2) {
			this._map.addLayer(this._poly);
		}

		this._updateFinishHandler();

		this._vertexAdded(latlng);

		this._clearGuides();
	},

	_updateFinishHandler: function () {
		var markerCount = this._markers.length;
		// The last marker should have a click handler to close the polyline
		if (markerCount > 1) {
			this._markers[markerCount - 1].on('click', this._finishShape, this);
		}

		// Remove the old marker click handler (as only the last point should close the polyline)
		if (markerCount > 2) {
			this._markers[markerCount - 2].off('click', this._finishShape, this);
		}
	},

	_createMarker: function (latlng) {
		var marker = new L.Marker(latlng, {
			icon: this.options.icon,
			zIndexOffset: this.options.zIndexOffset * 2
		});

		this._markerGroup.addLayer(marker);

		return marker;
	},

	_updateGuide: function (newPos) {
		newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);

		var markerCount = this._markers.length;

		if (markerCount > 0) {
			// Update the tooltip text, as long it's not showing and error
			if (!this._errorShown) {
				this._tooltip.updateContent(this._getTooltipText());
			}

			// draw the guide line
			this._clearGuides();
			this._drawGuide(
				this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
				newPos
			);
		}
	},

	_drawGuide: function (pointA, pointB) {
		var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
			i,
			fraction,
			dashPoint,
			dash;

		//create the guides container if we haven't yet
		if (!this._guidesContainer) {
			this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
		}

		//draw a dash every GuildeLineDistance
		for (i = this.options.guidelineDistance; i < length; i += this.options.guidelineDistance) {
			//work out fraction along line we are
			fraction = i / length;

			//calculate new x,y point
			dashPoint = {
				x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
				y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
			};

			//add guide dash to guide container
			dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
			dash.style.backgroundColor =
				!this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;

			L.DomUtil.setPosition(dash, dashPoint);
		}
	},

	_updateGuideColor: function (color) {
		if (this._guidesContainer) {
			for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
				this._guidesContainer.childNodes[i].style.backgroundColor = color;
			}
		}
	},

	// removes all child elements (guide dashes) from the guides container
	_clearGuides: function () {
		if (this._guidesContainer) {
			while (this._guidesContainer.firstChild) {
				this._guidesContainer.removeChild(this._guidesContainer.firstChild);
			}
		}
	},

	_getTooltipText: function () {
		var labelText,
			distance,
			distanceStr;

		if (this._markers.length === 0) {
			labelText = {
				text: L.drawLocal.draw.polyline.tooltip.start
			};
		} else {
			// calculate the distance from the last fixed point to the mouse position
			distance = this._measurementRunningTotal + this._currentLatLng.distanceTo(this._markers[this._markers.length - 1].getLatLng());
			// show metres when distance is < 1km, then show km
			distanceStr = distance  > 1000 ? (distance  / 1000).toFixed(2) + ' km' : Math.ceil(distance) + ' m';

			if (this._markers.length === 1) {
				labelText = {
					text: L.drawLocal.draw.polyline.tooltip.cont,
					subtext: distanceStr
				};
			} else {
				labelText = {
					text: L.drawLocal.draw.polyline.tooltip.end,
					subtext: distanceStr
				};
			}
		}
		return labelText;
	},

	_showErrorTooltip: function () {
		this._errorShown = true;

		// Update tooltip
		this._tooltip
			.showAsError()
			.updateContent({ text: this.options.drawError.message });

		// Update shape
		this._updateGuideColor(this.options.drawError.color);
		this._poly.setStyle({ color: this.options.drawError.color });

		// Hide the error after 2 seconds
		this._clearHideErrorTimeout();
		this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);
	},

	_hideErrorTooltip: function () {
		this._errorShown = false;

		this._clearHideErrorTimeout();

		// Revert tooltip
		this._tooltip
			.removeError()
			.updateContent(this._getTooltipText());

		// Revert shape
		this._updateGuideColor(this.options.shapeOptions.color);
		this._poly.setStyle({ color: this.options.shapeOptions.color });
	},

	_clearHideErrorTimeout: function () {
		if (this._hideErrorTimeout) {
			clearTimeout(this._hideErrorTimeout);
			this._hideErrorTimeout = null;
		}
	},

	_vertexAdded: function (latlng) {
		if (this._markers.length === 1) {
			this._measurementRunningTotal = 0;
		}
		else {
			this._measurementRunningTotal +=
				latlng.distanceTo(this._markers[this._markers.length - 2].getLatLng());
		}
	},

	_cleanUpShape: function () {
		if (this._markers.length > 0) {
			this._markers[this._markers.length - 1].off('click', this._finishShape, this);
		}
	},

	_fireCreatedEvent: function () {
		var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);
		L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly);
	}
});<commit_msg>Refactor the tooltip updating for polylines and polygons so the next label is shown right away.<commit_after>L.Draw.Polyline = L.Draw.Feature.extend({
	statics: {
		TYPE: 'polyline'
	},

	Poly: L.Polyline,

	options: {
		allowIntersection: true,
		drawError: {
			color: '#b00b00',
			message: L.drawLocal.draw.polyline.error,
			timeout: 2500
		},
		icon: new L.DivIcon({
			iconSize: new L.Point(8, 8),
			className: 'leaflet-div-icon leaflet-editing-icon'
		}),
		guidelineDistance: 20,
		shapeOptions: {
			stroke: true,
			color: '#f06eaa',
			weight: 4,
			opacity: 0.5,
			fill: false,
			clickable: true
		},
		zIndexOffset: 2000 // This should be > than the highest z-index any map layers
	},

	initialize: function (map, options) {
		// Merge default drawError options with custom options
		if (options && options.drawError) {
			options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
		}

		// Save the type so super can fire, need to do this as cannot do this.TYPE :(
		this.type = L.Draw.Polyline.TYPE;

		L.Draw.Feature.prototype.initialize.call(this, map, options);
	},

	addHooks: function () {
		L.Draw.Feature.prototype.addHooks.call(this);
		if (this._map) {
			this._markers = [];

			this._markerGroup = new L.LayerGroup();
			this._map.addLayer(this._markerGroup);

			this._poly = new L.Polyline([], this.options.shapeOptions);

			this._tooltip.updateContent(this._getTooltipText());

			// Make a transparent marker that will used to catch click events. These click
			// events will create the vertices. We need to do this so we can ensure that
			// we can create vertices over other map layers (markers, vector layers). We
			// also do not want to trigger any click handlers of objects we are clicking on
			// while drawing.
			if (!this._mouseMarker) {
				this._mouseMarker = L.marker(this._map.getCenter(), {
					icon: L.divIcon({
						className: 'leaflet-mouse-marker',
						iconAnchor: [20, 20],
						iconSize: [40, 40]
					}),
					opacity: 0,
					zIndexOffset: this.options.zIndexOffset
				});
			}

			this._mouseMarker
				.on('click', this._onClick, this)
				.addTo(this._map);

			this._map
				.on('mousemove', this._onMouseMove, this)
				.on('zoomend', this._onZoomEnd, this);
		}
	},

	removeHooks: function () {
		L.Draw.Feature.prototype.removeHooks.call(this);

		this._clearHideErrorTimeout();

		this._cleanUpShape();

		// remove markers from map
		this._map.removeLayer(this._markerGroup);
		delete this._markerGroup;
		delete this._markers;

		this._map.removeLayer(this._poly);
		delete this._poly;

		this._mouseMarker.off('click', this._onClick, this);
		this._map.removeLayer(this._mouseMarker);
		delete this._mouseMarker;

		// clean up DOM
		this._clearGuides();

		this._map
			.off('mousemove', this._onMouseMove, this)
			.off('zoomend', this._onZoomEnd, this);
	},

	_finishShape: function () {
		var intersects = this._poly.newLatLngIntersects(this._poly.getLatLngs()[0], true);

		if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {
			this._showErrorTooltip();
			return;
		}

		this._fireCreatedEvent();
		this.disable();
	},

	//Called to verify the shape is valid when the user tries to finish it
	//Return false if the shape is not valid
	_shapeIsValid: function () {
		return true;
	},

	_onZoomEnd: function () {
		this._updateGuide();
	},

	_onMouseMove: function (e) {
		var newPos = e.layerPoint,
			latlng = e.latlng;

		// Save latlng
		// should this be moved to _updateGuide() ?
		this._currentLatLng = latlng;

		this._updateTooltip(latlng);

		// Update the guide line
		this._updateGuide(newPos);

		// Update the mouse marker position
		this._mouseMarker.setLatLng(latlng);

		L.DomEvent.preventDefault(e.originalEvent);
	},

	_onClick: function (e) {
		var latlng = e.target.getLatLng(),
			markerCount = this._markers.length;

		if (markerCount > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
			this._showErrorTooltip();
			return;
		}
		else if (this._errorShown) {
			this._hideErrorTooltip();
		}

		this._markers.push(this._createMarker(latlng));

		this._poly.addLatLng(latlng);

		if (this._poly.getLatLngs().length === 2) {
			this._map.addLayer(this._poly);
		}

		this._updateFinishHandler();

		this._vertexAdded(latlng);

		this._clearGuides();

		this._updateTooltip();
	},

	_updateFinishHandler: function () {
		var markerCount = this._markers.length;
		// The last marker should have a click handler to close the polyline
		if (markerCount > 1) {
			this._markers[markerCount - 1].on('click', this._finishShape, this);
		}

		// Remove the old marker click handler (as only the last point should close the polyline)
		if (markerCount > 2) {
			this._markers[markerCount - 2].off('click', this._finishShape, this);
		}
	},

	_createMarker: function (latlng) {
		var marker = new L.Marker(latlng, {
			icon: this.options.icon,
			zIndexOffset: this.options.zIndexOffset * 2
		});

		this._markerGroup.addLayer(marker);

		return marker;
	},

	_updateGuide: function (newPos) {
		newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);

		var markerCount = this._markers.length;

		if (markerCount > 0) {
			// draw the guide line
			this._clearGuides();
			this._drawGuide(
				this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
				newPos
			);
		}
	},

	_updateTooltip: function (latLng) {
		var text = this._getTooltipText();

		if (latLng) {
			this._tooltip.updatePosition(latLng);
		}

		if (!this._errorShown) {
			this._tooltip.updateContent(text);
		}
	},

	_drawGuide: function (pointA, pointB) {
		var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
			i,
			fraction,
			dashPoint,
			dash;

		//create the guides container if we haven't yet
		if (!this._guidesContainer) {
			this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
		}

		//draw a dash every GuildeLineDistance
		for (i = this.options.guidelineDistance; i < length; i += this.options.guidelineDistance) {
			//work out fraction along line we are
			fraction = i / length;

			//calculate new x,y point
			dashPoint = {
				x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
				y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
			};

			//add guide dash to guide container
			dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
			dash.style.backgroundColor =
				!this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;

			L.DomUtil.setPosition(dash, dashPoint);
		}
	},

	_updateGuideColor: function (color) {
		if (this._guidesContainer) {
			for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
				this._guidesContainer.childNodes[i].style.backgroundColor = color;
			}
		}
	},

	// removes all child elements (guide dashes) from the guides container
	_clearGuides: function () {
		if (this._guidesContainer) {
			while (this._guidesContainer.firstChild) {
				this._guidesContainer.removeChild(this._guidesContainer.firstChild);
			}
		}
	},

	_getTooltipText: function () {
		var labelText,
			distance,
			distanceStr;

		if (this._markers.length === 0) {
			labelText = {
				text: L.drawLocal.draw.polyline.tooltip.start
			};
		} else {
			// calculate the distance from the last fixed point to the mouse position
			distance = this._measurementRunningTotal + this._currentLatLng.distanceTo(this._markers[this._markers.length - 1].getLatLng());
			// show metres when distance is < 1km, then show km
			distanceStr = distance  > 1000 ? (distance  / 1000).toFixed(2) + ' km' : Math.ceil(distance) + ' m';

			if (this._markers.length === 1) {
				labelText = {
					text: L.drawLocal.draw.polyline.tooltip.cont,
					subtext: distanceStr
				};
			} else {
				labelText = {
					text: L.drawLocal.draw.polyline.tooltip.end,
					subtext: distanceStr
				};
			}
		}
		return labelText;
	},

	_showErrorTooltip: function () {
		this._errorShown = true;

		// Update tooltip
		this._tooltip
			.showAsError()
			.updateContent({ text: this.options.drawError.message });

		// Update shape
		this._updateGuideColor(this.options.drawError.color);
		this._poly.setStyle({ color: this.options.drawError.color });

		// Hide the error after 2 seconds
		this._clearHideErrorTimeout();
		this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);
	},

	_hideErrorTooltip: function () {
		this._errorShown = false;

		this._clearHideErrorTimeout();

		// Revert tooltip
		this._tooltip
			.removeError()
			.updateContent(this._getTooltipText());

		// Revert shape
		this._updateGuideColor(this.options.shapeOptions.color);
		this._poly.setStyle({ color: this.options.shapeOptions.color });
	},

	_clearHideErrorTimeout: function () {
		if (this._hideErrorTimeout) {
			clearTimeout(this._hideErrorTimeout);
			this._hideErrorTimeout = null;
		}
	},

	_vertexAdded: function (latlng) {
		if (this._markers.length === 1) {
			this._measurementRunningTotal = 0;
		}
		else {
			this._measurementRunningTotal +=
				latlng.distanceTo(this._markers[this._markers.length - 2].getLatLng());
		}
	},

	_cleanUpShape: function () {
		if (this._markers.length > 0) {
			this._markers[this._markers.length - 1].off('click', this._finishShape, this);
		}
	},

	_fireCreatedEvent: function () {
		var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);
		L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly);
	}
});<|endoftext|>


<commit_before>import {keys} from "ramda"
import {mapObjIndexed} from "ramda"
import {pick} from "ramda"
import {pipe} from "ramda"
import {prop} from "ramda"
import {merge} from "ramda"
import {map} from "ramda"
import {identity} from "ramda"
import {reject} from "ramda"

const applicate = (...argument) => (ƒunction) => ƒunction(...argument)
const coerce = (schema) => (value, key) => prop(key, schema)(value)
const coerced = (schema) => pipe(pick(keys(schema)), mapObjIndexed(coerce(schema)))
const virtualized = (virtuals) => (raw) => map((virtual) => virtual(raw), virtuals)

// {...} -> f()
export default function abstract (configuration) {
  const {name} = configuration
  const {source = identity} = configuration
  const {schema} = configuration
  const {virtuals} = configuration
  const {validations} = configuration
  const coercedFrom = coerced(schema)
  const virtualizedFrom = virtualized(virtuals)

  return (raw) => {
    const data = source(raw)
    const coercedAttributes = coercedFrom(data)
    const virtualizedAttributes = virtualizedFrom(data)
    const attributes = merge(coercedAttributes, virtualizedAttributes)
    const errors = keys(reject(applicate(attributes), validations))

    return {
      name,
      raw,
      errors,
      attributes
    }
  }
}
<commit_msg>Adding more defaults.<commit_after>import {keys} from "ramda"
import {mapObjIndexed} from "ramda"
import {pick} from "ramda"
import {pipe} from "ramda"
import {prop} from "ramda"
import {merge} from "ramda"
import {map} from "ramda"
import {identity} from "ramda"
import {reject} from "ramda"

const applicate = (...argument) => (ƒunction) => ƒunction(...argument)
const coerce = (schema) => (value, key) => prop(key, schema)(value)
const coerced = (schema) => pipe(pick(keys(schema)), mapObjIndexed(coerce(schema)))
const virtualized = (virtuals) => (raw) => map((virtual) => virtual(raw), virtuals)

// {...} -> f()
export default function abstract (configuration) {
  const {name} = configuration
  const {source = identity} = configuration
  const {schema = {}} = configuration
  const {virtuals = {}} = configuration
  const {validations = []} = configuration
  const coercedFrom = coerced(schema)
  const virtualizedFrom = virtualized(virtuals)

  return (raw) => {
    const data = source(raw)
    const coercedAttributes = coercedFrom(data)
    const virtualizedAttributes = virtualizedFrom(data)
    const attributes = merge(coercedAttributes, virtualizedAttributes)
    const errors = keys(reject(applicate(attributes), validations))

    return {
      name,
      raw,
      errors,
      attributes
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Quotes
 * Cassius - https://github.com/sirDonovan/Cassius
 *
 * Manages the quotes system for the bot, allowing the adding,
 * removing, and viewing of added quotes.
 *
 * @author Mystifi
 * @license MIT license
 */

'use strict';

// These are the valid ranks that a Room Owner can set when
// modifying the permissions.
const validRanks = ['+', '%', '@', '#'];

/**
 * Obtains the given room's database. If the quotes database
 * wasn't already initialised, then it is done here.
 * @param {Room | string} room
 * @return {AnyObject}
 */
function getDatabase(room) {
	// In case a Room object was passed:
	if (room instanceof Rooms.Room) room = room.id;
	let database = Storage.getDatabase(room);
	if (!database.quotes) database.quotes = [];
	if (!database.defaultRanks) {
		// This plugin allows the changing of which auth level(s)
		// have the ability to modify the room's quotes database.
		// Along with setting a default quotes rank, this allows
		// other rooms to add default ranks in this object.
		database.defaultRanks = {};
		database.defaultRanks['quotes'] = '+';
	}
	return database;
}

/**@type {{[k: string]: Command | string}} */
let commands = {
	quotesrank: 'setquotesrank',
	setquotesrank: function (target, room, user) {
		if (room instanceof Users.User || !user.hasRank(room, '#')) return;
		let database = getDatabase(room.id);
		target = target.trim();
		if (!target) return this.say("Users of rank " + database.defaultRanks['quotes'] + " and higher can manage room quotes.");
		if (!validRanks.includes(target)) return this.say("Unknown option. Valid ranks: " + validRanks.join(", "));
		database.defaultRanks['quotes'] = target;
		Storage.exportDatabase(room.id);
		this.say("Users of rank " + target + " and above can now manage room quotes.");
	},
	addquote: function (target, room, user) {
		if (room instanceof Users.User) return;
		let database = getDatabase(room.id);
		if (!user.hasRank(room, database.defaultRanks['quotes'])) return;
		target = target.trim();
		if (!target) return this.say("Please use the following format: .addquote quote");
		let quotes = database.quotes;
		let index = quotes.findIndex(/**@param {string} quote */ quote => Tools.toId(quote) === Tools.toId(target));
		if (index >= 0) return this.say("That quote already exists.");
		quotes.push(target);
		Storage.exportDatabase(room.id);
		this.say("Your quote was successfully added.");
	},
	removequote: function (target, room, user) {
		if (room instanceof Users.User) return;
		let database = getDatabase(room.id);
		if (!user.hasRank(room, database.defaultRanks['quotes'])) return;
		target = target.trim();
		if (!target) return this.say("Please use the following format: .removequote quote");
		let quotes = database.quotes;
		let index = quotes.findIndex(/**@param {string} quote */ quote => Tools.toId(quote) === Tools.toId(target));
		if (index < 0) return this.say("Your quote doesn't exist in the database.");
		quotes.splice(index, 1);
		Storage.exportDatabase(room.id);
		this.say("Your quote was successfully removed.");
	},
	randquote: function (target, room, user) {
		if (room instanceof Users.User || !user.hasRank(room, '+')) return;
		let quotes = getDatabase(room.id).quotes;
		if (!quotes.length) return this.say("This room doesn't have any quotes.");
		this.say(Tools.sampleOne(quotes));
	},
	quotes: function (target, room, user) {
		if (room instanceof Users.User || !user.hasRank(room, '+')) return;
		let quotes = getDatabase(room.id).quotes;
		if (!quotes.length) return this.say("This room doesn't have any quotes.");
		let prettifiedQuotes = "Quotes for " + room.id + ":\n\n" + quotes.map(
			/**
			 * @param {string} quote
			 * @param {number} index
			 */
			(quote, index) => (index + 1) + ": " + quote
		).join("\n");
		Tools.uploadToHastebin(prettifiedQuotes, /**@param {string} hastebinUrl */ hastebinUrl => {
			this.say("Room quotes: " + hastebinUrl);
		});
	},
};

exports.commands = commands;<commit_msg>Quotes: Prevent commands from being used (#9)<commit_after>/**
 * Quotes
 * Cassius - https://github.com/sirDonovan/Cassius
 *
 * Manages the quotes system for the bot, allowing the adding,
 * removing, and viewing of added quotes.
 *
 * @author Mystifi
 * @license MIT license
 */

'use strict';

// These are the valid ranks that a Room Owner can set when
// modifying the permissions.
const validRanks = ['+', '%', '@', '#'];

/**
 * Obtains the given room's database. If the quotes database
 * wasn't already initialised, then it is done here.
 * @param {Room | string} room
 * @return {AnyObject}
 */
function getDatabase(room) {
	// In case a Room object was passed:
	if (room instanceof Rooms.Room) room = room.id;
	let database = Storage.getDatabase(room);
	if (!database.quotes) database.quotes = [];
	if (!database.defaultRanks) {
		// This plugin allows the changing of which auth level(s)
		// have the ability to modify the room's quotes database.
		// Along with setting a default quotes rank, this allows
		// other rooms to add default ranks in this object.
		database.defaultRanks = {};
		database.defaultRanks['quotes'] = '+';
	}
	return database;
}

/**@type {{[k: string]: Command | string}} */
let commands = {
	quotesrank: 'setquotesrank',
	setquotesrank: function (target, room, user) {
		if (room instanceof Users.User || !user.hasRank(room, '#')) return;
		let database = getDatabase(room.id);
		target = target.trim();
		if (!target) return this.say("Users of rank " + database.defaultRanks['quotes'] + " and higher can manage room quotes.");
		if (!validRanks.includes(target)) return this.say("Unknown option. Valid ranks: " + validRanks.join(", "));
		database.defaultRanks['quotes'] = target;
		Storage.exportDatabase(room.id);
		this.say("Users of rank " + target + " and above can now manage room quotes.");
	},
	addquote: function (target, room, user) {
		if (room instanceof Users.User) return;
		let database = getDatabase(room.id);
		if (!user.hasRank(room, database.defaultRanks['quotes'])) return;
		target = target.trim();
		if (!target) return this.say("Please use the following format: .addquote quote");
		if (target.startsWith("/") || target.startsWith("!")) return this.say("You can't use a command in your quote.");
		let quotes = database.quotes;
		let index = quotes.findIndex(/**@param {string} quote */ quote => Tools.toId(quote) === Tools.toId(target));
		if (index >= 0) return this.say("That quote already exists.");
		quotes.push(target);
		Storage.exportDatabase(room.id);
		this.say("Your quote was successfully added.");
	},
	removequote: function (target, room, user) {
		if (room instanceof Users.User) return;
		let database = getDatabase(room.id);
		if (!user.hasRank(room, database.defaultRanks['quotes'])) return;
		target = target.trim();
		if (!target) return this.say("Please use the following format: .removequote quote");
		let quotes = database.quotes;
		let index = quotes.findIndex(/**@param {string} quote */ quote => Tools.toId(quote) === Tools.toId(target));
		if (index < 0) return this.say("Your quote doesn't exist in the database.");
		quotes.splice(index, 1);
		Storage.exportDatabase(room.id);
		this.say("Your quote was successfully removed.");
	},
	randquote: function (target, room, user) {
		if (room instanceof Users.User || !user.hasRank(room, '+')) return;
		let quotes = getDatabase(room.id).quotes;
		if (!quotes.length) return this.say("This room doesn't have any quotes.");
		this.say(Tools.sampleOne(quotes));
	},
	quotes: function (target, room, user) {
		if (room instanceof Users.User || !user.hasRank(room, '+')) return;
		let quotes = getDatabase(room.id).quotes;
		if (!quotes.length) return this.say("This room doesn't have any quotes.");
		let prettifiedQuotes = "Quotes for " + room.id + ":\n\n" + quotes.map(
			/**
			 * @param {string} quote
			 * @param {number} index
			 */
			(quote, index) => (index + 1) + ": " + quote
		).join("\n");
		Tools.uploadToHastebin(prettifiedQuotes, /**@param {string} hastebinUrl */ hastebinUrl => {
			this.say("Room quotes: " + hastebinUrl);
		});
	},
};

exports.commands = commands;<|endoftext|>


<commit_before>// # The game controller
// This gets a bit trippy because it can be used both by
// the browser or server

var controller = function(io){

  // utils
  var each = function(arr, fn){
    for (var i=0; i < arr.length; i++) {
      fn.call(arr[i], arr[i]);
    };
  };
  var rest = function(arr){
    return Array.prototype.slice.call(arr,1);
  };
  var equalCoordinates = function (coord1, coord2) {
    return coord1[0] === coord2[0] && coord1[1] === coord2[1];
  };
  var checkCoordinateInArray = function (coord, arr) {
    var isInArray = false;
    each(arr, function (item) {
      if (equalCoordinates(coord, item)) {
        isInArray = true;
      }
    });
    return isInArray;
  };

  var ArenaCounter = 0;
  var PlayerCounter = 0;

  // a list of players and arenas
  var arenas = [];
  var players = [];

  var gameover = function(){

  }

  /*
    Arena & Controller logic
  */

  var Game = function(){
    this.players = [];
    this.arenas = [];

    this.transports = [];

    this.start();

    // start the game loop
    var g = this;
    (function l(){
      g.loop();
      setTimeout(l,50);
    })()
  };

  // Add entities to the game
  Game.prototype.addPlayer = function(player){
    this.players.push(player);
    this.start();
    this.report();
  };
  Game.prototype.addArena = function(arena){
    this.arenas.push(arena);
    this.start();
    this.report();
  };

  //Hack because games get socks added later and multiple transports
  Game.prototype.addTransport = function(trans){
    this.transports.push(trans);

    var game = this;
    // All ON stuff should be added here, and the context won't be `game`
    // trans.on('button')…
    trans.on('restart', function(){
      game.start();
    })

    this.report();
  };

  Game.prototype.emit = function(){
    var _this = this;
    var args = arguments;
    for(var t in this.transports){
      var trans = this.transports[t];
            console.log(t,this.transports)
      trans.emit.apply(trans,args);
    }
  };

  // report the current game state
  Game.prototype.report = function(){
    this.emit('update:player_count',this.players.length);
    this.emit('update:arena_count',this.arenas.length);
  }

  /* start the game, position the players, etc */
  Game.prototype.start = function(){
    if(this.arenas.length == 0){
      console.log("Couldn't start game,  no arenas."); return;
    }

    // clear the arenas
    each(this.arenas, function(){
      this.usedpoints = [];
      this.emit('clear');
    })

    var startArena = this.arenas[0];

    //(player.id % 4) == 0;//0,height/2     d:right
    //(player.id % 4) == 1;//width,height/2 d:left
    //(player.id % 4) == 2;//width/2,0      d:down
    //(player.id % 4) == 3;//width/2,height d:up

    var y = 0;
    var i = 0;

    var firstarena = this.arenas[0];

    each(this.players, function(player){
      player.dead = false;

      switch(i % 4)
      {
        case 0:
          player.x = 0;
          player.y = firstarena.height/2;
          player.nextDirection = 'right';
          break;
        case 1:
          player.x = firstarena.width;
          player.y = firstarena.height/2;
          player.nextDirection = 'left';
          break;
        case 2:
          player.x = firstarena.width/2;
          player.y = 0;
          player.nextDirection = 'down';
          break;
        case 3:
          player.x = firstarena.width/2;
          player.y = firstarena.height;
          player.nextDirection = 'up';
          break;
      }

      if(i>3){y += 10;}

      i++;
      player.arena = startArena;// XXX temporary
      player.emit('removerestart');
    });
  }

  /* The actual game loop*/
  Game.prototype.loop = function(){
    // console.log("game loop")

    var arenas = this.arenas;

    each(this.players, function(player){
      if(player.dead != true)
      {
        player.advance();
        //update the arena with the new player position
        if(player.arena){
          player.arena.draw(player);
        }
      }
    });

  };

  Game.prototype.restart = function(){

    var playersleft = 0;

    each(this.players, function(player){
      if(player.dead === false)
      {
        playersleft++;
      }
    });

    if(playersleft < 2)
    {
      console.log('game restart')

      each(this.arenas, function(arena){
        arena.emit('notice', 'restarting');
      });

      var g=this;
      setTimeout(function(){
        g.start()
      }, 1500);
    }

  }

  // When a link between arenas is requested - we set this variable
  var linkArena;
  var linkEdge;

  // the controller represention of an arena
  var Arena = function(transport,width,height){
    this.id = ArenaCounter++;
    this.transport = transport;

    this.width = width;
    this.height = height;

    this.links = {};

    this.on('requestLink', function(edge){
      console.log("requested link with", edge);

      if(!linkArena){
        linkArena = this;
        linkEdge = edge;
      } else {
        linkArena.links[linkEdge] = this;
        this.links[edge] = linkArena;

        linkArena.emit('linked', linkEdge);
        this.emit('linked', edge);

        // ready for another link
        linkArena = false;
      }

    });

    this.usedpoints = [];
  };

  // this checks to see if a player is still on this
  // arena and moves them to another one (or null)
  // as appropriate
  Arena.prototype.updatePlayer = function(player){
    if(player.x < 0){
      // left
      if(player.arena = this.links.left)
        player.arena.entry(this, player, player.y/this.height);

    } else if(player.x > this.width){
      // right
      if(player.arena = this.links.right)
        player.arena.entry(this, player, player.y/this.height);

    } else if(player.y < 0){
      //top
      if(player.arena = this.links.top)
        player.arena.entry(this, player, player.x/this.width);
    } else if(player.y > this.height){
      //bottom
      if(player.arena = this.links.bottom)
        player.arena.entry(this, player,  player.x/this.width);
    }
  };

  // this repositions the player as they have entered from another
  // arena
  Arena.prototype.entry = function(fromarena, player, position){
    for(link in this.links){
      if(this.links[link] === fromarena){
        // update the direction and position
        if(link == 'left'){
          player.x = 0;
          player.y = position * this.height;
          player.nextDirection = 'right';
        };
        if(link == 'right'){
          player.x = this.width;
          player.y = position * this.height;
          player.nextDirection = 'left';
        };
        if(link == 'top'){
          player.x = position * this.width;
          player.y = 0;
          player.nextDirection = 'down';
        };
        if(link == 'bottom'){
          player.x = position * this.width;
          player.y = this.height;
          player.nextDirection = 'up';
        };

        console.log("Switched player" + player.id + ' from arena' + fromarena.id + ' --> arena' + this.id);
      }
    }
  }

  // update the view of the arena
  Arena.prototype.draw = function(player){
    this.emit('updatePlayer', player.id, player.x, player.y)
  }

  Arena.prototype.addUsedPoint = function(points){
    // [x,y,playerid]
    // console.log(points)

    this.usedpoints.push(points);
  }

  Arena.prototype.checkCollision = function(head){
    // console.log('head:' + head)
    // console.log('used: '+ this.usedpoints);
    // [[x,y,playerid],[x,y,playerid]]
    playerCollision = checkCoordinateInArray(head, this.usedpoints);
    if(playerCollision){
      return true;
    }
  }

  // the controller representation of a player
  var Player = function(transport){
    var id = this.id = PlayerCounter++;

    var dead = this.dead = false;

    this.transport = transport;

    this.on('left', function(){
      console.log("<<<Player"+this.id+" LEFT");
      // increment the direction
      this.nextDirection = {
        'right':'up',
        'up':'left',
        'left':'down',
        'down':'right'
      }[this.nextDirection];
    });

    this.on('right', function(){
      console.log(">>>Player"+id+" RIGHT");
      // increment the direction
      this.nextDirection = {
        'right':'down',
        'down':'left',
        'left':'up',
        'up':'right'
      }[this.nextDirection];
    });

    this.on('restart', function(){
      console.log('>.< Player '+this.id+' says RESTART!!');

      game.restart(); // best way?
    });

  };

  Player.prototype.advance = function(){

    // increment x y appropriately
    this.x += {left:-1, right:1}[this.nextDirection] || 0;
    this.y += {down:1,  up:-1   }[this.nextDirection] || 0;

    // update/transistion the arena based on the position
    if(this.arena){
      this.arena.updatePlayer(this);

      if(!this.arena){
        // we arent on an arena anymore
        console.log("KABOOM player "+this.id);
        this.dead = true;
        this.restart();
      } else {
        this.checkCollision();
        // store the point for collisions
        var nextPosition = [this.x,this.y,this.id];
        this.arena.addUsedPoint(nextPosition);
      }

    }

  }

  Player.prototype.restart = function(){
    //console.log("KABOOM");
    this.emit('gameover', true);
  }

  Player.prototype.checkCollision = function(){

    // actually, we just ask the arena for now
    if(this.arena.checkCollision([this.x,this.y,this.id]) === true)
    {
      console.log('BOOM!');
      this.dead = true;
      this.restart();
    }
  }

  /*
   Link up the models to the transport

   This means that the models don't have to have
   transport code in them,  calling this.on(…) will
   defer to the transport.  Also - the functions
   will be called in the context of the object
   which is handy.
 */
  var transportPrototypes = function(){
    this.prototype.on = function(event,fn){
      var _this = this;
      this.transport.on(event, function(){
        fn.apply(_this,arguments);
      });
    };
    this.prototype.emit = function(){
      this.transport.emit.apply(this.transport,arguments);
    };
  }
  transportPrototypes.call(Player);
  transportPrototypes.call(Arena);

  var game = new Game();

  // Handle connections other windows
  io.sockets.on('connection', function(socket){
    /*
      socket is a local variable, so if they identify themselves
      as a player or an arena - then we can set up handlers for
      to send them the correct information
    */
    var identifed = false;

    console.log("New Socket connection to server!");

    socket.on('arena', function(width,height,oldid){
      if (identifed) {return} identifed = true;

      // this socket comes from an arena
      console.log("This socket comes from AN ARENA!!!");
      var arena;
      if(oldid){
        each(game.arenas, function(a){
          if(a.id == oldid){
            arena = a;
          }
        })
      }
      arena = arena || new Arena(socket,width,height);

      socket.emit('hello', "Hi there Arena " + arena.id);

      socket.emit('id', arena.id);

      game.addArena(arena);

      socket.on('disconnect', function () {
        console.log("Disconnected arena");
        for (var i = game.arenas.length - 1; i >= 0; i--){
          if(game.arenas[i] == arena){
            console.log("found arena to remove", i)
            game.arenas.splice(i,1);
          }
        };
      });

    });

    socket.on('player', function(){
      if (identifed) {return} identifed = true;

      // this socket comes from a player
      console.log("This socket comes from a PLAYER!!!");

      var player = new Player(socket);

      player.emit('id', player.id);
      socket.emit('hello', "Hi there player " + player.id);

      game.addPlayer(player);

      // restart the game
      game.start();

    });

    socket.on('game', function(){
      if (identifed) {return} identifed = true;

      // this socket comes from a game
      console.log("This socket comes from a GAME!!!");

      game.addTransport(socket);

    });

  });

};

// expose for node
if(typeof exports != 'undefined'){
  exports.start = controller;
} else {
  window.controller = controller;
}

<commit_msg>deal with disconnecting players<commit_after>// # The game controller
// This gets a bit trippy because it can be used both by
// the browser or server

var controller = function(io){

  // utils
  var each = function(arr, fn){
    for (var i=0; i < arr.length; i++) {
      fn.call(arr[i], arr[i]);
    };
  };
  var rest = function(arr){
    return Array.prototype.slice.call(arr,1);
  };
  var equalCoordinates = function (coord1, coord2) {
    return coord1[0] === coord2[0] && coord1[1] === coord2[1];
  };
  var checkCoordinateInArray = function (coord, arr) {
    var isInArray = false;
    each(arr, function (item) {
      if (equalCoordinates(coord, item)) {
        isInArray = true;
      }
    });
    return isInArray;
  };

  var ArenaCounter = 0;
  var PlayerCounter = 0;

  // a list of players and arenas
  var arenas = [];
  var players = [];

  var gameover = function(){

  }

  /*
    Arena & Controller logic
  */

  var Game = function(){
    this.players = [];
    this.arenas = [];

    this.transports = [];

    this.start();

    // start the game loop
    var g = this;
    (function l(){
      g.loop();
      setTimeout(l,50);
    })()
  };

  // Add entities to the game
  Game.prototype.addPlayer = function(player){
    this.players.push(player);

    var playerCount = 0;
    //purge any disconnected players
    for (var i = this.players.length - 1; i >= 0; i--){
      if(!this.players[i].disconnected){
        playerCount++;
      }
    };

    // one connnected player
    if(playerCount == 1){
      this.start();
    }
    this.report();
  };
  Game.prototype.addArena = function(arena){
    this.arenas.push(arena);
    this.start();
    this.report();
  };

  //Hack because games get socks added later and multiple transports
  Game.prototype.addTransport = function(trans){
    this.transports.push(trans);

    var game = this;
    // All ON stuff should be added here, and the context won't be `game`
    // trans.on('button')…
    trans.on('restart', function(){
      game.start();
    })

    this.report();
  };

  Game.prototype.emit = function(){
    var _this = this;
    var args = arguments;
    for(var t in this.transports){
      var trans = this.transports[t];
            console.log(t,this.transports)
      trans.emit.apply(trans,args);
    }
  };

  // report the current game state
  Game.prototype.report = function(){
    this.emit('update:player_count',this.players.length);
    this.emit('update:arena_count',this.arenas.length);
  }

  /* start the game, position the players, etc */
  Game.prototype.start = function(){
    if(this.arenas.length == 0){
      console.log("Couldn't start game,  no arenas."); return;
    }

    // clear the arenas
    each(this.arenas, function(){
      this.usedpoints = [];
      this.emit('clear');
    })

    var startArena = this.arenas[0];

    //purge any disconnected players
    for (var i = this.players.length - 1; i >= 0; i--){
      if(this.players[i].disconnected){
        console.log("Removing player" + this.players[i].id)
        this.players.splice(i,1);
      }
    };

    var y = 0;
    var i = 0;

    var firstarena = this.arenas[0];

    each(this.players, function(player){
      player.dead = false;

      switch(i % 4)
      {
        case 0:
          player.x = 0;
          player.y = firstarena.height/2;
          player.nextDirection = 'right';
          break;
        case 1:
          player.x = firstarena.width;
          player.y = firstarena.height/2;
          player.nextDirection = 'left';
          break;
        case 2:
          player.x = firstarena.width/2;
          player.y = 0;
          player.nextDirection = 'down';
          break;
        case 3:
          player.x = firstarena.width/2;
          player.y = firstarena.height;
          player.nextDirection = 'up';
          break;
      }

      if(i>3){y += 10;}

      i++;
      player.arena = startArena;// XXX temporary
      player.emit('removerestart');
    });
  }

  /* The actual game loop*/
  Game.prototype.loop = function(){
    // console.log("game loop")

    var arenas = this.arenas;

    each(this.players, function(player){
      if(player.dead != true)
      {
        player.advance();
        //update the arena with the new player position
        if(player.arena){
          player.arena.draw(player);
        }
      }
    });

  };

  Game.prototype.restart = function(){

    var playersleft = 0;

    each(this.players, function(player){
      if(player.dead === false)
      {
        playersleft++;
      }
    });

    if(playersleft < 2)
    {
      console.log('game restart')

      each(this.arenas, function(arena){
        arena.emit('notice', 'restarting');
      });

      var g=this;
      setTimeout(function(){
        g.start()
      }, 1500);
    }

  }

  // When a link between arenas is requested - we set this variable
  var linkArena;
  var linkEdge;

  // the controller represention of an arena
  var Arena = function(transport,width,height){
    this.id = ArenaCounter++;
    this.transport = transport;

    this.width = width;
    this.height = height;

    this.links = {};

    this.on('requestLink', function(edge){
      console.log("requested link with", edge);

      if(!linkArena){
        linkArena = this;
        linkEdge = edge;
      } else {
        linkArena.links[linkEdge] = this;
        this.links[edge] = linkArena;

        linkArena.emit('linked', linkEdge);
        this.emit('linked', edge);

        // ready for another link
        linkArena = false;
      }

    });

    this.usedpoints = [];
  };

  // this checks to see if a player is still on this
  // arena and moves them to another one (or null)
  // as appropriate
  Arena.prototype.updatePlayer = function(player){
    if(player.x < 0){
      // left
      if(player.arena = this.links.left)
        player.arena.entry(this, player, player.y/this.height);

    } else if(player.x > this.width){
      // right
      if(player.arena = this.links.right)
        player.arena.entry(this, player, player.y/this.height);

    } else if(player.y < 0){
      //top
      if(player.arena = this.links.top)
        player.arena.entry(this, player, player.x/this.width);
    } else if(player.y > this.height){
      //bottom
      if(player.arena = this.links.bottom)
        player.arena.entry(this, player,  player.x/this.width);
    }
  };

  // this repositions the player as they have entered from another
  // arena
  Arena.prototype.entry = function(fromarena, player, position){
    for(link in this.links){
      if(this.links[link] === fromarena){
        // update the direction and position
        if(link == 'left'){
          player.x = 0;
          player.y = position * this.height;
          player.nextDirection = 'right';
        };
        if(link == 'right'){
          player.x = this.width;
          player.y = position * this.height;
          player.nextDirection = 'left';
        };
        if(link == 'top'){
          player.x = position * this.width;
          player.y = 0;
          player.nextDirection = 'down';
        };
        if(link == 'bottom'){
          player.x = position * this.width;
          player.y = this.height;
          player.nextDirection = 'up';
        };

        console.log("Switched player" + player.id + ' from arena' + fromarena.id + ' --> arena' + this.id);
      }
    }
  }

  // update the view of the arena
  Arena.prototype.draw = function(player){
    this.emit('updatePlayer', player.id, player.x, player.y)
  }

  Arena.prototype.addUsedPoint = function(points){
    // [x,y,playerid]
    // console.log(points)

    this.usedpoints.push(points);
  }

  Arena.prototype.checkCollision = function(head){
    // console.log('head:' + head)
    // console.log('used: '+ this.usedpoints);
    // [[x,y,playerid],[x,y,playerid]]
    playerCollision = checkCoordinateInArray(head, this.usedpoints);
    if(playerCollision){
      return true;
    }
  }

  // the controller representation of a player
  var Player = function(transport){
    var id = this.id = PlayerCounter++;

    var dead = this.dead = false;

    this.transport = transport;

    this.on('left', function(){
      console.log("<<<Player"+this.id+" LEFT");
      // increment the direction
      this.nextDirection = {
        'right':'up',
        'up':'left',
        'left':'down',
        'down':'right'
      }[this.nextDirection];
    });

    this.on('right', function(){
      console.log(">>>Player"+id+" RIGHT");
      // increment the direction
      this.nextDirection = {
        'right':'down',
        'down':'left',
        'left':'up',
        'up':'right'
      }[this.nextDirection];
    });

    this.on('restart', function(){
      console.log('>.< Player '+this.id+' says RESTART!!');

      game.restart(); // best way?
    });

  };

  Player.prototype.advance = function(){

    // increment x y appropriately
    this.x += {left:-1, right:1}[this.nextDirection] || 0;
    this.y += {down:1,  up:-1   }[this.nextDirection] || 0;

    // update/transistion the arena based on the position
    if(this.arena){
      this.arena.updatePlayer(this);

      if(!this.arena){
        // we arent on an arena anymore
        console.log("KABOOM player "+this.id);
        this.dead = true;
        this.restart();
      } else {
        this.checkCollision();
        // store the point for collisions
        var nextPosition = [this.x,this.y,this.id];
        this.arena.addUsedPoint(nextPosition);
      }

    }

  }

  Player.prototype.restart = function(){
    //console.log("KABOOM");
    this.emit('gameover', true);
  }

  Player.prototype.checkCollision = function(){

    // actually, we just ask the arena for now
    if(this.arena.checkCollision([this.x,this.y,this.id]) === true)
    {
      console.log('BOOM!');
      this.dead = true;
      this.restart();
    }
  }

  /*
   Link up the models to the transport

   This means that the models don't have to have
   transport code in them,  calling this.on(…) will
   defer to the transport.  Also - the functions
   will be called in the context of the object
   which is handy.
 */
  var transportPrototypes = function(){
    this.prototype.on = function(event,fn){
      var _this = this;
      this.transport.on(event, function(){
        fn.apply(_this,arguments);
      });
    };
    this.prototype.emit = function(){
      this.transport.emit.apply(this.transport,arguments);
    };
  }
  transportPrototypes.call(Player);
  transportPrototypes.call(Arena);

  var game = new Game();

  // Handle connections other windows
  io.sockets.on('connection', function(socket){
    /*
      socket is a local variable, so if they identify themselves
      as a player or an arena - then we can set up handlers for
      to send them the correct information
    */
    var identifed = false;

    console.log("New Socket connection to server!");

    socket.on('arena', function(width,height,oldid){
      if (identifed) {return} identifed = true;

      // this socket comes from an arena
      console.log("This socket comes from AN ARENA!!!");
      var arena;
      if(oldid){
        each(game.arenas, function(a){
          if(a.id == oldid){
            arena = a;
          }
        })
      }
      arena = arena || new Arena(socket,width,height);

      socket.emit('hello', "Hi there Arena " + arena.id);

      socket.emit('id', arena.id);

      game.addArena(arena);

      socket.on('disconnect', function () {
        console.log("Disconnected arena");
        for (var i = game.arenas.length - 1; i >= 0; i--){
          if(game.arenas[i] == arena){
            console.log("found arena to remove", i)
            game.arenas.splice(i,1);
          }
        };

        // dont think this works…
        // each(game.players, function(p){
        //   //kill players on window loss
        //   if(p.arena == arena){
        //     p.dead = true;
        //   }
        // })
      });

    });

    socket.on('player', function(){
      if (identifed) {return} identifed = true;

      // this socket comes from a player
      console.log("This socket comes from a PLAYER!!!");

      var player = new Player(socket);

      player.emit('id', player.id);
      socket.emit('hello', "Hi there player " + player.id);

      game.addPlayer(player);

      socket.on('disconnect', function () {
        player.disconnected = true;
      });

      // restart the game
      // game.start();

    });

    socket.on('game', function(){
      if (identifed) {return} identifed = true;

      // this socket comes from a game
      console.log("This socket comes from a GAME!!!");

      game.addTransport(socket);

    });

  });

};

// expose for node
if(typeof exports != 'undefined'){
  exports.start = controller;
} else {
  window.controller = controller;
}

<|endoftext|>