<commit_before>/*
    --------------- jSQL ---------------
    a SQL like database using javascript
    website: http://jsql.us
    licence: MIT Licence
    version: 0.4.0 dev

    description: using jSQL to process the data easily.
*/

(function() {
    var slice               = Array.prototype.slice,
        toString            = Object.prototype.toString,
        hasOwnProperty      = Object.prototype.hasOwnProperty;

    var nativeForEach       = Array.prototype.forEach,
        nativeIsArray       = Array.isArray,
        nativeKeys          = Object.keys,
        nativeIndexOf       = Array.prototype.indexOf,
        nativeLastIndexOf   = Array.prototype.lastIndexOf;

    var utils = {};
    var jSQL_KEY_NAME = 'jSQL_Key';
    var jSQL, _jSQL, _jsql, _DB = {}, _DBIndexMap = {}, _protected = {};

    if(typeof(this.jSQL) !== 'undefined') {
        _jSQL = this.jSQL;
    }

    if(typeof(this.jsql) !== 'undefined') {
        _jsql = this.jsql;
    }

    jSQL = function() {
        this.init.apply(this, arguments);
    };

    jSQL.prototype = {
        version: '0.4.0-dev',

        init: function() {
            this._jSQL = _jSQL;
            this._jsql = _jsql;
            this._DB = _DB;
            this._currentDB = null;
            this._buffer = null;
            this._DBIndexMap = _DBIndexMap;
            this._protected = _protected;
            this.utils = utils;
        },

        create: function(dbname, db) {
            var indexList;

            if(this._DB.hasOwnProperty(dbname)) {
                throw('DB Already Exist.');
            }

            if(utils.isArray(db)) {
                indexList = utils.listSlice(arguments, '2:');
                utils.appendKey(db, indexList);
                _DBIndexMap[dbname] = utils.arrayToObject(db);
            }

            if(utils.isPlainObject(db)) {
                _DBIndexMap[dbname] = utils.clone(db);
                db = utils.objectToArray(db);
            }

            this._DB[dbname] = db;
            return this;
        },

        use: function(dbname) {
            if(!this._DB.hasOwnProperty(dbname)) {
                throw('Database Not Exist.');
            }

            this._currentDB = this._DB[dbname];
            this._currentDBName = dbname;
            this.rebase();
            return this;
        },

        drop: function(dbname) {
            if(this._DB.hasOwnProperty(dbname)) {
                delete this._DB[dbname];
            }
        },

        dbs: function() {
            return utils.keys(this._DB);
        },

        db: function() {
            return this._currentDBName;
        },

        select: function(key) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            this._buffer = this._currentDB; //reset the _buffer
            this._protected['field'] = [].slice.call(utils.isArray(key) ? key : arguments);

            if(key === '*') {
                return this;
            }

            return this;
        },

        count: function() {
            var result;
            result = this._buffer.length;
            this.rebase();
            return result;
        },

        total: function(scope) {
            var rs = 0, _tmp;

            for(var _key in this._currentDB) {
                if(this._currentDB.hasOwnProperty(_key)) {
                    _tmp = scope === '*' ?
                        this._currentDB[_key] :
                        typeof(scope) === 'function' ?
                            scope.call(this, this._currentDB[_key], _key) === true ? true : undefined :
                            utils.deep(this._currentDB[_key], scope);

                    if(typeof(_tmp) !== 'undefined') {
                        rs++;
                    }
                }
            }

            return rs;
        },

        orderby: function(field, callback, order) {
            var _array = this._buffer;
            var _this = this;

            if(typeof(callback) !== 'function') {
                callback = [order, order = callback][0];
            }

            _array.sort(function(a, b) {
                a = utils.deep(a, field);
                b = utils.deep(b, field);
                if(callback) {
                    a = callback(a);
                    b = callback(b);
                }
                return  order && order.toLowerCase() === 'asc' ? a - b : b - a;
            });

            this._buffer = _array;
            return this;
        },

        where: function(fn) {
            var _tmp = [], _swap;
            this._buffer = this._buffer || this._currentDB;
            fn = utils.parseFn(fn);

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    if(typeof(fn) === 'function') {
                        _swap = fn.call(this, utils.clone(this._buffer[i]), i);
                    }

                    if(utils.isArray(fn)) {
                        _swap = false;

                        for(var f in fn) {
                            if(fn.hasOwnProperty(f)) {
                                if(fn[f].call(this, utils.clone(this._buffer[i]), i)) {
                                    _swap = true;
                                }
                            }
                        }
                    }

                    if(_swap) {
                        _tmp.push(utils.clone(this._buffer[i]));
                    }
                }
            }
            this._buffer = _tmp;
            return this;
        },

        iterate: function(fn) {
            var _swap;
            this._buffer = this._buffer || this._currentDB;

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    _swap = fn.call(this, this._buffer[i]);

                    if(_swap) {
                        this.update(i, _swap);
                    }
                }
            }
            return this;
        },

        findAll: function() {
            var result;
            result = utils.clone(utils.arrayToObject(this._select()));
            this.rebase();
            return result;
        },

        find: function(key) {
            var result;
            var _tmp = this._DBIndexMap[this._currentDBName];

            if(!key) {
                for(var i in _tmp) {
                    if(_tmp.hasOwnProperty(i)) {
                        if(key) {
                            break;
                        }

                        if(this._buffer.hasOwnProperty(i)) {
                            key = i;
                        }
                    }
                }
            }

            result = utils.clone(_tmp[key]);
            this.rebase();
            return result;
        },

        listAll: function() {
            var result;
            result = utils.clone(this._select());
            this.rebase();
            return result;
        },

        update: function(key, data) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            if(this._currentDB.hasOwnProperty(key)) {
                this._currentDB[key] = data;
            }
        },

        insert: function() {
            //TODO
        },

        limit: function(start, end) {
            var _tmp = this._buffer;
            var limit;

            if(!end) {
                start = [0, end = start][0];
            }

            limit = start + ':' + (start + end);

            this._buffer = utils.listSlice(_tmp, limit);
            return this;
        },

        keys: function() {
            return utils.keys(this.findAll());
        },

        first: function(fn) {
            if(fn) {
                return this.where(fn).first();
            }

            return utils.listSlice(this._select(), ':1');
        },

        last: function(fn) {
            if(fn) {
                return this.where(fn).last();
            }

            return utils.listSlice(this._select(), '-1:');
        },

        rebase: function() {
            this._protected = {};
            this.select('*');
            return this;
        },

        noConflict: function() {
            if(window.jSQL === jSQL) {
                window.jSQL = this._jSQL;
            }

            if(window.jsql === jsql) {
                window.jsql = this._jsql;
            }

            return this;
        },

        _select: function(field) {
            var tmp, result = [];

            field = field || this._protected['field'];

            if(field === '*' || (field.join && field.join('') === '*')) {
                return this._buffer;
            }

            if(typeof(field) === 'string') {
                field = field.split(',');
            }

            utils.each(this._buffer, function(o, i, r) {
                tmp = {};
                tmp[jSQL_KEY_NAME] = utils.deep(o, jSQL_KEY_NAME);

                utils.each(field, function(_o, _i, _r) {
                    if(o.hasOwnProperty(_o)) {
                        tmp[_o.split('.').pop()] = utils.deep(o, _o);
                    }
                });
                result.push(tmp);
            });

            return result;
        }
    };

    utils = {
        deep: function(data, scope) {
            var _tmp = data, scope = scope.split('.');
            for(var i = 0; i < scope.length; i++) {
                _tmp = _tmp[scope[i]];
            }
            return _tmp;
        },

        isArray: nativeIsArray || function(obj) {
            return toString.call(obj) === '[object Array]';
        },

        isObject: function(obj) {
            return obj === Object(obj);
        },

        isPlainObject: function(obj) {
            return this.isObject(obj) && obj.constructor === Object;
        },

        clone: function (obj) {
            if(obj == null || typeof(obj) != 'object') {
                return obj;
            }

            var temp = new obj.constructor();
            for(var key in obj) {
                temp[key] = arguments.callee(obj[key]);
            }

            return temp;
        },

        objectToArray: function(object) {
            var array = [], object = this.clone(object);

            for(var i in object) {
                if(object.hasOwnProperty(i)) {
                    object[i][jSQL_KEY_NAME] = i;
                    array.push(object[i]);
                }
            }

            return array;
        },

        arrayToObject: function(array, key) {
            var object = {};

            for(var i = 0; i < array.length; i++) {
                object[array[i][key || jSQL_KEY_NAME]] = this.clone(array[i]);
                delete object[array[i][key || jSQL_KEY_NAME]][key || jSQL_KEY_NAME];
            };

            return object;
        },

        each: function(list, fn) {
            if(nativeForEach) {
                list.forEach(fn);
                return;
            }

            for(var i = 0; i < list.length; i++) {
                fn(list[i], i, list);
            }
        },

        keygen: function(object, indexList) {
            var that = this;
            var baseRef = [].slice.call(arguments, 1);
            var key = '';

            if(that.isArray(indexList)) {
                baseRef = indexList;
            }

            that.each(baseRef, function(o, i, r) {
                key += utils.deep(object, o);
            });

            return key;
        },

        listSlice: function(list, range) {
            var start, end;

            list = [].slice.call(list);
            range = range.split(':');
            start = range[0] || 0;
            end = range.length > 1 ? range[1] || list.length : list.length;
            return [].slice.call(list, start, end);
        },

        appendKey: function(list, indexList) {
            var that = this;
            that.each(list, function(o, i, r) {
                o[jSQL_KEY_NAME] = that.keygen(o, indexList) || i;
            });
        },

        keys: nativeKeys || (function() {
            var hasDontEnumBug = true,
                dontEnums = [
                    'toString',
                    'toLocaleString',
                    'valueOf',
                    'hasOwnProperty',
                    'isPrototypeOf',
                    'propertyIsEnumerable',
                    'constructor'
                ],
                dontEnumsLength = dontEnums.length;

            for (var key in {'toString': null}) {
                hasDontEnumBug = false;
            }

            return function keys(object) {
                if ((typeof object != 'object' && typeof object != 'function') || object === null) {
                    throw new TypeError('Object.keys called on a non-object');
                }

                var keys = [];
                for (var name in object) {
                    if (object.hasOwnProperty(name)) {
                        keys.push(name);
                    }
                }

                if (hasDontEnumBug) {
                    for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
                        var dontEnum = dontEnums[i];
                        if (object.hasOwnProperty(dontEnum)) {
                            keys.push(dontEnum);
                        }
                    }
                }
                return keys;
            };
        })(),

        parseFn: function(fn) {
            if(typeof(fn) === 'string') {
                fn = new Function('data', 'with(data) { return ' + fn + '; }');
            }

            return fn;
        },

        indexOf: function(list, sought /*, fromIndex */ ) {
            if(nativeIndexOf) {
                return nativeIndexOf.apply(list, this.listSlice(arguments, '1:'));
            }

            var self = list,
                length = self.length >>> 0;

            if (!length) {
                return -1;
            }

            var i = 0;
            if (arguments.length > 2) {
                i = arguments[2];
            }

            // handle negative indices
            i = i >= 0 ? i : Math.max(0, length + i);
            for (; i < length; i++) {
                if (i in self && self[i] === sought) {
                    return i;
                }
            }
            return -1;
        }
    };

    jSQL = new jSQL();

    typeof(module) !== 'undefined' && module.exports ? module.exports = jSQL : this.jsql = this.jSQL = jSQL;
})();
<commit_msg>update jsql.insert method.<commit_after>/*
    --------------- jSQL ---------------
    a SQL like database using javascript
    website: http://jsql.us
    licence: MIT Licence
    version: 0.4.0 dev

    description: using jSQL to process the data easily.
*/

(function() {
    var slice               = Array.prototype.slice,
        toString            = Object.prototype.toString,
        hasOwnProperty      = Object.prototype.hasOwnProperty;

    var nativeForEach       = Array.prototype.forEach,
        nativeIsArray       = Array.isArray,
        nativeKeys          = Object.keys,
        nativeIndexOf       = Array.prototype.indexOf,
        nativeLastIndexOf   = Array.prototype.lastIndexOf;

    var utils = {};
    var jSQL_KEY_NAME = 'jSQL_Key';
    var jSQL, _jSQL, _jsql, _DB = {}, _DBIndexMap = {}, _protected = {};

    if(typeof(this.jSQL) !== 'undefined') {
        _jSQL = this.jSQL;
    }

    if(typeof(this.jsql) !== 'undefined') {
        _jsql = this.jsql;
    }

    jSQL = function() {
        this.init.apply(this, arguments);
    };

    jSQL.prototype = {
        version: '0.4.0-dev',

        init: function() {
            this._jSQL = _jSQL;
            this._jsql = _jsql;
            this._DB = _DB;
            this._currentDB = null;
            this._buffer = null;
            this._indexList = null;
            this._currentDBName = null;
            this._DBIndexMap = _DBIndexMap;
            this._protected = _protected;
            this.utils = utils;
        },

        create: function(dbname, db) {
            var indexList;

            if(this._DB.hasOwnProperty(dbname)) {
                throw('DB Already Exist.');
            }

            if(utils.isArray(db)) {
                indexList = utils.listSlice(arguments, '2:');
                utils.appendKey(db, indexList);
                _DBIndexMap[dbname] = utils.arrayToObject(db);
                this._indexList = indexList;
            }

            if(utils.isPlainObject(db)) {
                _DBIndexMap[dbname] = utils.clone(db);
                db = utils.objectToArray(db);
            }

            this._DB[dbname] = db;
            return this;
        },

        use: function(dbname) {
            if(!this._DB.hasOwnProperty(dbname)) {
                throw('Database Not Exist.');
            }

            this._currentDB = this._DB[dbname];
            this._currentDBName = dbname;
            this.rebase();
            return this;
        },

        drop: function(dbname) {
            if(this._DB.hasOwnProperty(dbname)) {
                delete this._DB[dbname];
            }
        },

        dbs: function() {
            return utils.keys(this._DB);
        },

        db: function() {
            return this._currentDBName;
        },

        select: function(key) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            this._buffer = this._currentDB; //reset the _buffer
            this._protected['field'] = [].slice.call(utils.isArray(key) ? key : arguments);

            if(key === '*') {
                return this;
            }

            return this;
        },

        count: function() {
            var result;
            result = this._buffer.length;
            this.rebase();
            return result;
        },

        total: function(scope) {
            var rs = 0, _tmp;

            for(var _key in this._currentDB) {
                if(this._currentDB.hasOwnProperty(_key)) {
                    _tmp = scope === '*' ?
                        this._currentDB[_key] :
                        typeof(scope) === 'function' ?
                            scope.call(this, this._currentDB[_key], _key) === true ? true : undefined :
                            utils.deep(this._currentDB[_key], scope);

                    if(typeof(_tmp) !== 'undefined') {
                        rs++;
                    }
                }
            }

            return rs;
        },

        orderby: function(field, callback, order) {
            var _array = this._buffer;
            var _this = this;

            if(typeof(callback) !== 'function') {
                callback = [order, order = callback][0];
            }

            _array.sort(function(a, b) {
                a = utils.deep(a, field);
                b = utils.deep(b, field);
                if(callback) {
                    a = callback(a);
                    b = callback(b);
                }
                return  order && order.toLowerCase() === 'asc' ? a - b : b - a;
            });

            this._buffer = _array;
            return this;
        },

        where: function(fn) {
            var _tmp = [], _swap;
            this._buffer = this._buffer || this._currentDB;
            fn = utils.parseFn(fn);

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    if(typeof(fn) === 'function') {
                        _swap = fn.call(this, utils.clone(this._buffer[i]), i);
                    }

                    if(utils.isArray(fn)) {
                        _swap = false;

                        for(var f in fn) {
                            if(fn.hasOwnProperty(f)) {
                                if(fn[f].call(this, utils.clone(this._buffer[i]), i)) {
                                    _swap = true;
                                }
                            }
                        }
                    }

                    if(_swap) {
                        _tmp.push(utils.clone(this._buffer[i]));
                    }
                }
            }
            this._buffer = _tmp;
            return this;
        },

        iterate: function(fn) {
            var _swap;
            this._buffer = this._buffer || this._currentDB;

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    _swap = fn.call(this, this._buffer[i]);

                    if(_swap) {
                        this.update(i, _swap);
                    }
                }
            }
            return this;
        },

        findAll: function() {
            var result;
            result = utils.clone(utils.arrayToObject(this._select()));
            this.rebase();
            return result;
        },

        find: function(key) {
            var result;
            var _tmp = this._DBIndexMap[this._currentDBName];

            if(!key) {
                for(var i in _tmp) {
                    if(_tmp.hasOwnProperty(i)) {
                        if(key) {
                            break;
                        }

                        if(this._buffer.hasOwnProperty(i)) {
                            key = i;
                        }
                    }
                }
            }

            result = utils.clone(_tmp[key]);
            this.rebase();
            return result;
        },

        listAll: function() {
            var result;
            result = utils.clone(this._select());
            this.rebase();
            return result;
        },

        update: function(key, data) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            if(this._currentDB.hasOwnProperty(key)) {
                this._currentDB[key] = data;
            }
        },

        insert: function(item, key /*, fromIndex */) {
            var item = utils.clone(item);
            var fromIndex = arguments[2];

            item[jSQL_KEY_NAME] = item.key || key;
            fromIndex ?
                this._currentDB.splice(fromIndex, 0, item) :
                this._currentDB.push(item);
            this.rebase();
        },

        limit: function(start, end) {
            var _tmp = this._buffer;
            var limit;

            if(!end) {
                start = [0, end = start][0];
            }

            limit = start + ':' + (start + end);

            this._buffer = utils.listSlice(_tmp, limit);
            return this;
        },

        keys: function() {
            return utils.keys(this.findAll());
        },

        first: function(fn) {
            if(fn) {
                return this.where(fn).first();
            }

            return utils.listSlice(this._select(), ':1');
        },

        last: function(fn) {
            if(fn) {
                return this.where(fn).last();
            }

            return utils.listSlice(this._select(), '-1:');
        },

        rebase: function() {
            this._protected = {};
            this.select('*');
            this._updateIndexMap();
            return this;
        },

        noConflict: function() {
            if(window.jSQL === jSQL) {
                window.jSQL = this._jSQL;
            }

            if(window.jsql === jsql) {
                window.jsql = this._jsql;
            }

            return this;
        },

        _select: function(field) {
            var tmp, result = [];

            field = field || this._protected['field'];

            if(field === '*' || (field.join && field.join('') === '*')) {
                return this._buffer;
            }

            if(typeof(field) === 'string') {
                field = field.split(',');
            }

            utils.each(this._buffer, function(o, i, r) {
                tmp = {};
                tmp[jSQL_KEY_NAME] = utils.deep(o, jSQL_KEY_NAME);

                utils.each(field, function(_o, _i, _r) {
                    if(o.hasOwnProperty(_o)) {
                        tmp[_o.split('.').pop()] = utils.deep(o, _o);
                    }
                });
                result.push(tmp);
            });

            return result;
        },

        _updateIndexMap: function() {
            _DBIndexMap[this._currentDBName] = utils.arrayToObject(this._currentDB);
        }
    };

    utils = {
        deep: function(data, scope) {
            var _tmp = data, scope = scope.split('.');
            for(var i = 0; i < scope.length; i++) {
                _tmp = _tmp[scope[i]];
            }
            return _tmp;
        },

        isArray: nativeIsArray || function(obj) {
            return toString.call(obj) === '[object Array]';
        },

        isObject: function(obj) {
            return obj === Object(obj);
        },

        isPlainObject: function(obj) {
            return this.isObject(obj) && obj.constructor === Object;
        },

        clone: function (obj) {
            if(obj == null || typeof(obj) != 'object') {
                return obj;
            }

            var temp = new obj.constructor();
            for(var key in obj) {
                temp[key] = arguments.callee(obj[key]);
            }

            return temp;
        },

        objectToArray: function(object) {
            var array = [], object = this.clone(object);

            for(var i in object) {
                if(object.hasOwnProperty(i)) {
                    object[i][jSQL_KEY_NAME] = i;
                    array.push(object[i]);
                }
            }

            return array;
        },

        arrayToObject: function(array, key) {
            var object = {};

            for(var i = 0; i < array.length; i++) {
                object[array[i][key || jSQL_KEY_NAME]] = this.clone(array[i]);
                delete object[array[i][key || jSQL_KEY_NAME]][key || jSQL_KEY_NAME];
            };

            return object;
        },

        each: function(list, fn) {
            if(nativeForEach) {
                list.forEach(fn);
                return;
            }

            for(var i = 0; i < list.length; i++) {
                fn(list[i], i, list);
            }
        },

        keygen: function(object, indexList) {
            var that = this;
            var baseRef = [].slice.call(arguments, 1);
            var key = '';

            if(that.isArray(indexList)) {
                baseRef = indexList;
            }

            that.each(baseRef, function(o, i, r) {
                key += utils.deep(object, o);
            });

            return key;
        },

        listSlice: function(list, range) {
            var start, end;

            list = [].slice.call(list);
            range = range.split(':');
            start = range[0] || 0;
            end = range.length > 1 ? range[1] || list.length : list.length;
            return [].slice.call(list, start, end);
        },

        appendKey: function(list, indexList) {
            var that = this;
            that.each(list, function(o, i, r) {
                o[jSQL_KEY_NAME] = that.keygen(o, indexList) || i;
            });
        },

        keys: nativeKeys || (function() {
            var hasDontEnumBug = true,
                dontEnums = [
                    'toString',
                    'toLocaleString',
                    'valueOf',
                    'hasOwnProperty',
                    'isPrototypeOf',
                    'propertyIsEnumerable',
                    'constructor'
                ],
                dontEnumsLength = dontEnums.length;

            for (var key in {'toString': null}) {
                hasDontEnumBug = false;
            }

            return function keys(object) {
                if ((typeof object != 'object' && typeof object != 'function') || object === null) {
                    throw new TypeError('Object.keys called on a non-object');
                }

                var keys = [];
                for (var name in object) {
                    if (object.hasOwnProperty(name)) {
                        keys.push(name);
                    }
                }

                if (hasDontEnumBug) {
                    for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
                        var dontEnum = dontEnums[i];
                        if (object.hasOwnProperty(dontEnum)) {
                            keys.push(dontEnum);
                        }
                    }
                }
                return keys;
            };
        })(),

        parseFn: function(fn) {
            if(typeof(fn) === 'string') {
                fn = new Function('data', 'with(data) { return ' + fn + '; }');
            }

            return fn;
        },

        indexOf: function(list, sought /*, fromIndex */ ) {
            if(nativeIndexOf) {
                return nativeIndexOf.apply(list, this.listSlice(arguments, '1:'));
            }

            var self = list,
                length = self.length >>> 0;

            if (!length) {
                return -1;
            }

            var i = 0;
            if (arguments.length > 2) {
                i = arguments[2];
            }

            // handle negative indices
            i = i >= 0 ? i : Math.max(0, length + i);
            for (; i < length; i++) {
                if (i in self && self[i] === sought) {
                    return i;
                }
            }
            return -1;
        }
    };

    jSQL = new jSQL();

    typeof(module) !== 'undefined' && module.exports ? module.exports = jSQL : this.jsql = this.jSQL = jSQL;
})();
<|endoftext|>


<commit_before>
(function(){
	var root = this,
		Backbone = root.Backbone,
		sharejs = root.sharejs,
		_ = root._;
	//TODO: Ensure the above imports are resolved and work in node.js

	var diff = (function() {
		//The following two methods were lifted from http://code.google.com/p/google-diff-match-patch/
		var diff_commonPrefix = function(text1, text2) {
			// Quick check for common null cases.
			if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {
				return 0;
			}
			// Binary search.
			// Performance analysis: http://neil.fraser.name/news/2007/10/09/
			var pointermin = 0;
			var pointermax = Math.min(text1.length, text2.length);
			var pointermid = pointermax;
			var pointerstart = 0;
			while (pointermin < pointermid) {
				if (text1.substring(pointerstart, pointermid) ==
						text2.substring(pointerstart, pointermid)) {
					pointermin = pointermid;
					pointerstart = pointermin;
				} else {
					pointermax = pointermid;
				}
				pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
			}
			return pointermid;
		};

		var diff_commonSuffix = function(text1, text2) {
			// Quick check for common null cases.
			if (!text1 || !text2 ||
					text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {
				return 0;
			}
			// Binary search.
			// Performance analysis: http://neil.fraser.name/news/2007/10/09/
			var pointermin = 0;
			var pointermax = Math.min(text1.length, text2.length);
			var pointermid = pointermax;
			var pointerend = 0;
			while (pointermin < pointermid) {
				if (text1.substring(text1.length - pointermid, text1.length - pointerend) ==
						text2.substring(text2.length - pointermid, text2.length - pointerend)) {
					pointermin = pointermid;
					pointerend = pointermin;
				} else {
					pointermax = pointermid;
				}
				pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
			}
			return pointermid;
		};

		return function(str1, str2) {
			var prefix = diff_commonPrefix(str1, str2),
				suffix = diff_commonSuffix(str1, str2);

			return {
				d: str1.substring(prefix, str1.length - suffix),
				i: str2.substring(prefix, str2.length - suffix),
				p: prefix
			};
		};

	}).call(this);

	//Lifted from jQuery
	var type = (function() {
		var class2type = {};

		_.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(name) {
			class2type[ "[object " + name + "]" ] = name.toLowerCase();
		});

		return function( obj ) {
			return obj == null ?
				String(obj ):
				class2type[Object.prototype.toString.call(obj)] || "object";
		}
	}).call(this);

	var S4 = function() {
		return (((1 + Math.random()) * 65536) | 0).toString(16).substring(1);
	};

	var generateGUID = function() {
		return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
	};

	var isShareModel = function(obj) {
		return obj instanceof Backbone.SharedModel || obj instanceof Backbone.SharedCollection;
	};

	Backbone.SharedModel = Backbone.Model.extend({
		constructor: function(attr, options) {
			Backbone.Model.prototype.constructor.apply(this, arguments);

			var self = this;

			attr = attr || {};
			options = options || {};

			this.set('id', attr.id || generateGUID());

			this.defaults = this.defaults || {};
			this.documentPath = options.documentPath || this.generateDocumentPath();
			this.pendingOperations = [];

			this.parent = options.parent;

			if (!Array.isArray(this.documentPath)) {
				throw new Error('Document path must be an array');
			}

			if (this.parent && this.parent.shareDoc) {
				this.isRoot = false;
				this.initShareDoc(this.parent.shareDoc);
			} else if (this.parent) {
				this.parent.on('share:connected', function(shareDoc) {
					self.isRoot = false;
					self.initShareDoc(shareDoc);
				});
			} else {
				this.isRoot = true;
				this.documentName = options.documentName || this.generateDocumentName();
				sharejs.open(this.documentName, 'json', function(err, doc) {
					if (err) throw err;

					console.log('Opened document "' + self.documentName + '"');
					self.initShareDoc(doc);
				});
			}

			this.on("change", function(model, options) {
				if (!options || !options.local) {
					return self._sendModelChange(options);
				}
			});
		},

		generateDocumentPath: function() {
			return [];
		},

		generateDocumentName: function() {
			return this.get('id');
		},

		initShareDoc: function(shareDoc) {
			var self = this, attributes;

			if (shareDoc.type.name !== 'json') {
				throw new Error('ShareJS document must be of type "json"');
			}

			this.shareDoc = shareDoc;

			if (shareDoc.created) {
				shareDoc.submitOp([{p: this.documentPath, od: null, oi: this.toJSON()}]);
				shareDoc.created = false;
			}

			shareDoc.on('remoteop', function(ops) {
				_.each(ops, function(op, i) {
					if (_.isEqual(op.p, self.documentPath)) {
						this._handleOperation(op);
					}
				});
			});

			if (this.pendingOperations.length) {
				shareDoc.submitOp(this.pendingOperations, function(error) {
					if (!error) self.pendingOperations.length = 0;
					this.trigger('share:connected', this.shareDoc);
				});
			} else {
				this.trigger('share:connected', this.shareDoc);
			}
		},

		_sendModelChange: function() {
			var self = this;

			var ops = [];
			_.each(_.pairs(this.changedAttributes()), function(pair) {
				var k = pair[0], v = pair[1], t = type(v), prev = self.previous(k);
				var result, textDiff;
				result = {p: self.documentPath.concat([k])};
				switch(t) {
					case 'string':
						textDiff = diff(prev, v);
						result.p.push(textDiff.p);

						if (!!textDiff.d) {
							result.sd = textDiff.d;
						}
						if (!!textDiff.i) {
							if (result.sd) {
								ops.push(result);
								result = _.clone(result);
								delete result.sd;
							}
							result.si = textDiff.i;
						}
						break;
					case 'number':
						result.na = v - prev;
						break;
					case 'boolean':
						result.oi = v;
						result.od = !v;
						break;
					case 'object':
					case 'array':
						if (isShareModel(v)) {
							result.oi = v.toJSON();
						} else {
							result.oi = v;
						}

						if (prev) {
							if (isShareModel(prev)) {
								result.od = prev.toJSON();
							} else {
								result.od = prev;
							}
						}
						break;
					case 'null':
					case 'undefined':
						if (isShareModel(prev)) {
							result.od = prev.toJSON();
						} else {
							result.od = prev;
						}
						break;
					default:
						console.log('Ignoring attempt to send change on type ' + t);
						break;
				}

				return ops.push(result);
			});

			if (this.shareDoc) {
				console.log('Sending:', ops);
				this.shareDoc.submitOp(ops, function(error, ops) {
					//self.shareDoc.submitOp(self.shareDoc.type.invert(ops), function() {
						console.log(arguments);
					//});
				});
			} else {
				Array.prototype.push(pendingOperations, ops);
			}
		},

		_handleOperation: function (op) {
			if (this.isRoot && op.p.length <= 2 || _.isEqual(op.p, this.documentPath)) {
				if (op.si || op.sd) this._handleStringOperation(op);
				if (op.oi || op.od) this._handleObjectOperation(op);
				if (op.na) this._handleNumberOperation(op);
			}
		},

		_handleStringOperation: function(op) {
			var pathProp = op.p[op.p.length - 2],
				pathIndex = op.p[op.p.length - 1],
				original = this.get(pathProp),
				modified, deleted;

			if (op.si) {
				this.set(
					pathProp,
					original.slice(0, pathIndex) + op.si + original.slice(pathIndex),
					{local: true}
				);
			}

			if (op.sd) {
				deleted = original.slice(pathIndex, pathIndex + op.sd.length);
				if (op.sd !== deleted) {
					throw new Error('Delete component ' + op.sd + ' does not match deleted text ' + deleted);
				}
				modified = original.slice(0, pathIndex) + original.slice(pathIndex + op.sd.length);
				this.set(pathProp, modified, {local: true});
			}
		},

		_handleObjectOperation: function(op) {
			var pathProp = op.p[op.p.length - 1],
				obj = this.get(pathProp);

			if (op.oi) {
				if (isShareModel(obj)) {
					obj.clear({local: true});
					obj.set(op.oi, {local: true});
				} else {
					this.set(pathProp, op.oi, {local: true});
				}
			} else {
				if (isShareModel(obj)) {
					obj.clear({local: true});
				} else {
					this.unset(pathProp, {local: true});
				}
			}
		},

		_handleNumberOperation: function(op) {
			var pathProp = op.p[op.p.length - 1],
				currentValue = this.get(pathProp);

			this.get(pathProp).set(currentValue + op.na, {local: true});
		}
	});

	// Collection path should be a simple name?
	// Need to deal with non-roots
	// Need to deal with hooking up parents to children
	// Need to come up with undo-redo
	// Check paths on receiving ops
	// Need to validate models
	// Error handling on submitOp
	// Verify re-opening works

	Backbone.SharedCollection = Backbone.Collection.extend({
		constructor: function(models, options) {
			Backbone.Collection.prototype.constructor.apply(this, arguments);

			var self = this;

			options = options || {};

			this.documentPath = options.documentPath || this.generateDocumentPath();

			if (!Array.isArray(this.documentPath)) {
				throw new Error('Document path must be an array');
			}

			if (options.shareDoc) {
				this.isRoot = false;
				this.initShareDoc(options.shareDoc);
			} else {
				this.isRoot = true;
				this.documentName = options.documentName || this.generateDocumentName();
				sharejs.open(this.documentName, 'json', function(err, doc) {
					if (err) throw err;

					console.log('Opened document "' + self.documentName + '"');
					self.initShareDoc(doc);
				});
			}
		},

		generateDocumentPath: function() {
			return [];
		},

		generateDocumentName: function() {
			return generateGUID();
		},

		initShareDoc: function(shareDoc) {
			var self = this;

			if (shareDoc.type.name !== 'json') {
				throw new Error('ShareJS document must be of type "json"');
			}

			this.shareDoc = shareDoc;

			if (shareDoc.created) {
				shareDoc.submitOp([{p: [], oi: this.toJSON()}]);
			}

			shareDoc.on('remoteop', function(ops) {
				_.each(ops, function(op, i) {
					if (_.isEqual(op.p, self.documentPath)) {
						this._handleListOperation(op);
					}
				});
			});

			this.on("add", function(model, options) {
				if (!options || !options.local) {
					return self._sendModelAdd(options);
				}
			});

			this.on("remove", function(model, options) {
				if (!options || !options.local) {
					return self._sendModelRemove(options);
				}
			});
		},

		_sendModelAdd: function(model, options) {
			var ops = [{
				p: this.documentPath.concat(
					[options && options.at ? options.at : this.length - 1]),
				li: model.toJSON()
			}];

			this.shareDoc.submitOp(ops, function(error, ops) {
				//self.shareDoc.submitOp(self.shareDoc.type.invert(ops), function() {
					console.log(arguments);
				//});
			});
		},

		_sendModelRemove: function(model, options) {
			var ops = [{
				p: this.documentPath.concat(
					[options && options.index ? options.index : this.length - 1]),
				ld: model.toJSON()
			}];

			this.shareDoc.submitOp(ops, function(error, ops) {
				//self.shareDoc.submitOp(self.shareDoc.type.invert(ops), function() {
					console.log(arguments);
				//});
			});
		},

		_handleListOperation: function(op) {
			if (op.li) {
				this.create(op.li, {local: true});
			}

			if (op.ld) {
				this.remove(op.ld, {local: true});
			}
		}
	});

}).call(this);<commit_msg>Removed erroneous 'get' call before 'set'<commit_after>
(function(){
	var root = this,
		Backbone = root.Backbone,
		sharejs = root.sharejs,
		_ = root._;
	//TODO: Ensure the above imports are resolved and work in node.js

	var diff = (function() {
		//The following two methods were lifted from http://code.google.com/p/google-diff-match-patch/
		var diff_commonPrefix = function(text1, text2) {
			// Quick check for common null cases.
			if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {
				return 0;
			}
			// Binary search.
			// Performance analysis: http://neil.fraser.name/news/2007/10/09/
			var pointermin = 0;
			var pointermax = Math.min(text1.length, text2.length);
			var pointermid = pointermax;
			var pointerstart = 0;
			while (pointermin < pointermid) {
				if (text1.substring(pointerstart, pointermid) ==
						text2.substring(pointerstart, pointermid)) {
					pointermin = pointermid;
					pointerstart = pointermin;
				} else {
					pointermax = pointermid;
				}
				pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
			}
			return pointermid;
		};

		var diff_commonSuffix = function(text1, text2) {
			// Quick check for common null cases.
			if (!text1 || !text2 ||
					text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {
				return 0;
			}
			// Binary search.
			// Performance analysis: http://neil.fraser.name/news/2007/10/09/
			var pointermin = 0;
			var pointermax = Math.min(text1.length, text2.length);
			var pointermid = pointermax;
			var pointerend = 0;
			while (pointermin < pointermid) {
				if (text1.substring(text1.length - pointermid, text1.length - pointerend) ==
						text2.substring(text2.length - pointermid, text2.length - pointerend)) {
					pointermin = pointermid;
					pointerend = pointermin;
				} else {
					pointermax = pointermid;
				}
				pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
			}
			return pointermid;
		};

		return function(str1, str2) {
			var prefix = diff_commonPrefix(str1, str2),
				suffix = diff_commonSuffix(str1, str2);

			return {
				d: str1.substring(prefix, str1.length - suffix),
				i: str2.substring(prefix, str2.length - suffix),
				p: prefix
			};
		};

	}).call(this);

	//Lifted from jQuery
	var type = (function() {
		var class2type = {};

		_.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(name) {
			class2type[ "[object " + name + "]" ] = name.toLowerCase();
		});

		return function( obj ) {
			return obj == null ?
				String(obj ):
				class2type[Object.prototype.toString.call(obj)] || "object";
		}
	}).call(this);

	var S4 = function() {
		return (((1 + Math.random()) * 65536) | 0).toString(16).substring(1);
	};

	var generateGUID = function() {
		return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
	};

	var isShareModel = function(obj) {
		return obj instanceof Backbone.SharedModel || obj instanceof Backbone.SharedCollection;
	};

	Backbone.SharedModel = Backbone.Model.extend({
		constructor: function(attr, options) {
			Backbone.Model.prototype.constructor.apply(this, arguments);

			var self = this;

			attr = attr || {};
			options = options || {};

			this.set('id', attr.id || generateGUID());

			this.defaults = this.defaults || {};
			this.documentPath = options.documentPath || this.generateDocumentPath();
			this.pendingOperations = [];

			this.parent = options.parent;

			if (!Array.isArray(this.documentPath)) {
				throw new Error('Document path must be an array');
			}

			if (this.parent && this.parent.shareDoc) {
				this.isRoot = false;
				this.initShareDoc(this.parent.shareDoc);
			} else if (this.parent) {
				this.parent.on('share:connected', function(shareDoc) {
					self.isRoot = false;
					self.initShareDoc(shareDoc);
				});
			} else {
				this.isRoot = true;
				this.documentName = options.documentName || this.generateDocumentName();
				sharejs.open(this.documentName, 'json', function(err, doc) {
					if (err) throw err;

					console.log('Opened document "' + self.documentName + '"');
					self.initShareDoc(doc);
				});
			}

			this.on("change", function(model, options) {
				if (!options || !options.local) {
					return self._sendModelChange(options);
				}
			});
		},

		generateDocumentPath: function() {
			return [];
		},

		generateDocumentName: function() {
			return this.get('id');
		},

		initShareDoc: function(shareDoc) {
			var self = this, attributes;

			if (shareDoc.type.name !== 'json') {
				throw new Error('ShareJS document must be of type "json"');
			}

			this.shareDoc = shareDoc;

			if (shareDoc.created) {
				shareDoc.submitOp([{p: this.documentPath, od: null, oi: this.toJSON()}]);
				shareDoc.created = false;
			}

			shareDoc.on('remoteop', function(ops) {
				_.each(ops, function(op, i) {
					if (_.isEqual(op.p, self.documentPath)) {
						this._handleOperation(op);
					}
				});
			});

			if (this.pendingOperations.length) {
				shareDoc.submitOp(this.pendingOperations, function(error) {
					if (!error) self.pendingOperations.length = 0;
					this.trigger('share:connected', this.shareDoc);
				});
			} else {
				this.trigger('share:connected', this.shareDoc);
			}
		},

		_sendModelChange: function() {
			var self = this;

			var ops = [];
			_.each(_.pairs(this.changedAttributes()), function(pair) {
				var k = pair[0], v = pair[1], t = type(v), prev = self.previous(k);
				var result, textDiff;
				result = {p: self.documentPath.concat([k])};
				switch(t) {
					case 'string':
						textDiff = diff(prev, v);
						result.p.push(textDiff.p);

						if (!!textDiff.d) {
							result.sd = textDiff.d;
						}
						if (!!textDiff.i) {
							if (result.sd) {
								ops.push(result);
								result = _.clone(result);
								delete result.sd;
							}
							result.si = textDiff.i;
						}
						break;
					case 'number':
						result.na = v - prev;
						break;
					case 'boolean':
						result.oi = v;
						result.od = !v;
						break;
					case 'object':
					case 'array':
						if (isShareModel(v)) {
							result.oi = v.toJSON();
						} else {
							result.oi = v;
						}

						if (prev) {
							if (isShareModel(prev)) {
								result.od = prev.toJSON();
							} else {
								result.od = prev;
							}
						}
						break;
					case 'null':
					case 'undefined':
						if (isShareModel(prev)) {
							result.od = prev.toJSON();
						} else {
							result.od = prev;
						}
						break;
					default:
						console.log('Ignoring attempt to send change on type ' + t);
						break;
				}

				return ops.push(result);
			});

			if (this.shareDoc) {
				console.log('Sending:', ops);
				this.shareDoc.submitOp(ops, function(error, ops) {
					//self.shareDoc.submitOp(self.shareDoc.type.invert(ops), function() {
						console.log(arguments);
					//});
				});
			} else {
				Array.prototype.push(pendingOperations, ops);
			}
		},

		_handleOperation: function (op) {
			if (this.isRoot && op.p.length <= 2 || _.isEqual(op.p, this.documentPath)) {
				if (op.si || op.sd) this._handleStringOperation(op);
				if (op.oi || op.od) this._handleObjectOperation(op);
				if (op.na) this._handleNumberOperation(op);
			}
		},

		_handleStringOperation: function(op) {
			var pathProp = op.p[op.p.length - 2],
				pathIndex = op.p[op.p.length - 1],
				original = this.get(pathProp),
				modified, deleted;

			if (op.si) {
				this.set(
					pathProp,
					original.slice(0, pathIndex) + op.si + original.slice(pathIndex),
					{local: true}
				);
			}

			if (op.sd) {
				deleted = original.slice(pathIndex, pathIndex + op.sd.length);
				if (op.sd !== deleted) {
					throw new Error('Delete component ' + op.sd + ' does not match deleted text ' + deleted);
				}
				modified = original.slice(0, pathIndex) + original.slice(pathIndex + op.sd.length);

				this.set(pathProp, modified, {local: true});
			}
		},

		_handleObjectOperation: function(op) {
			var pathProp = op.p[op.p.length - 1],
				obj = this.get(pathProp);

			if (op.oi || op.oi === false) {
				if (isShareModel(obj)) {
					obj.clear({local: true});
					obj.set(op.oi, {local: true});
				} else {
					this.set(pathProp, op.oi, {local: true});
				}
			} else {
				if (isShareModel(obj)) {
					obj.clear({local: true});
				} else {
					this.unset(pathProp, {local: true});
				}
			}
		},

		_handleNumberOperation: function(op) {
			var pathProp = op.p[op.p.length - 1],
				currentValue = this.get(pathProp);

			this.set(pathProp, currentValue + op.na, {local: true});
		}
	});

	// Collection path should be a simple name?
	// Need to deal with non-roots
	// Need to deal with hooking up parents to children
	// Need to come up with undo-redo
	// Check paths on receiving ops
	// Need to validate models
	// Error handling on submitOp
	// Verify re-opening works

	Backbone.SharedCollection = Backbone.Collection.extend({
		constructor: function(models, options) {
			Backbone.Collection.prototype.constructor.apply(this, arguments);

			var self = this;

			options = options || {};

			this.documentPath = options.documentPath || this.generateDocumentPath();

			if (!Array.isArray(this.documentPath)) {
				throw new Error('Document path must be an array');
			}

			if (options.shareDoc) {
				this.isRoot = false;
				this.initShareDoc(options.shareDoc);
			} else {
				this.isRoot = true;
				this.documentName = options.documentName || this.generateDocumentName();
				sharejs.open(this.documentName, 'json', function(err, doc) {
					if (err) throw err;

					console.log('Opened document "' + self.documentName + '"');
					self.initShareDoc(doc);
				});
			}
		},

		generateDocumentPath: function() {
			return [];
		},

		generateDocumentName: function() {
			return generateGUID();
		},

		initShareDoc: function(shareDoc) {
			var self = this;

			if (shareDoc.type.name !== 'json') {
				throw new Error('ShareJS document must be of type "json"');
			}

			this.shareDoc = shareDoc;

			if (shareDoc.created) {
				shareDoc.submitOp([{p: [], oi: this.toJSON()}]);
			}

			shareDoc.on('remoteop', function(ops) {
				_.each(ops, function(op, i) {
					if (_.isEqual(op.p, self.documentPath)) {
						this._handleListOperation(op);
					}
				});
			});

			this.on("add", function(model, options) {
				if (!options || !options.local) {
					return self._sendModelAdd(options);
				}
			});

			this.on("remove", function(model, options) {
				if (!options || !options.local) {
					return self._sendModelRemove(options);
				}
			});
		},

		_sendModelAdd: function(model, options) {
			var ops = [{
				p: this.documentPath.concat(
					[options && options.at ? options.at : this.length - 1]),
				li: model.toJSON()
			}];

			this.shareDoc.submitOp(ops, function(error, ops) {
				//self.shareDoc.submitOp(self.shareDoc.type.invert(ops), function() {
					console.log(arguments);
				//});
			});
		},

		_sendModelRemove: function(model, options) {
			var ops = [{
				p: this.documentPath.concat(
					[options && options.index ? options.index : this.length - 1]),
				ld: model.toJSON()
			}];

			this.shareDoc.submitOp(ops, function(error, ops) {
				//self.shareDoc.submitOp(self.shareDoc.type.invert(ops), function() {
					console.log(arguments);
				//});
			});
		},

		_handleListOperation: function(op) {
			if (op.li) {
				this.create(op.li, {local: true});
			}

			if (op.ld) {
				this.remove(op.ld, {local: true});
			}
		}
	});

}).call(this);<|endoftext|>


<commit_before>
/**
 * References:
 *
 *   - http://en.wikipedia.org/wiki/ANSI_escape_code
 *   - http://www.termsys.demon.co.uk/vtansi.htm
 *
 */

/**
 * Module dependencies.
 */

var prefix = '\033[' // For all escape codes
  , suffix = 'm';    // Only for color codes

/**
 * The ANSI escape sequences.
 */

var codes = {
    up: 'A'
  , down: 'B'
  , forward: 'C'
  , back: 'D'
  , nextLine: 'E'
  , previousLine: 'F'
  , horizontalAbsolute: 'G'
  , eraseData: 'J'
  , eraseLine: 'K'
  , scrollUp: 'S'
  , scrollDown: 'T'
  , savePosition: 's'
  , restorePosition: 'u'
  , queryPosition: '6n'
  , hide: '?25l'
  , show: '?25h'
};

/**
 * Rendering ANSI codes.
 */

var styles = {
    bold: 1
  , italic: 3
  , underline: 4
  , inverse: 7
};

/**
 * The negating ANSI code for the rendering modes.
 */

var reset = {
    bold: 22
  , italic: 23
  , underline: 24
  , inverse: 27
  , foreground: 39
  , background: 49
};

/**
 * The standard, styleable ANSI colors.
 */

var colors = {
    white: 37
  , grey: 90
  , black: 30
  , blue: 34
  , cyan: 36
  , green: 32
  , magenta: 35
  , red: 31
  , yellow: 33
};

/**
 * Creates a Cursor instance based off the given `writable stream` instance.
 */

function ansi (stream, options) {
  if (stream._ansicursor) {
    return stream._ansicursor;
  } else {
    return stream._ansicursor = new Cursor(stream, options);
  }
}
module.exports = exports = ansi;

/**
 * The `Cursor` class.
 */

function Cursor (stream, options) {
  this.stream = stream;
  this.fg = this.foreground = new Foreground(this);
  this.bg = this.background = new Background(this);
}
exports.Cursor = Cursor;

/**
 * The `Foreground` class.
 */

function Foreground (cursor) {
  this.cursor = cursor;
}
exports.Foreground = Foreground;

/**
 * The `Background` class.
 */

function Background (cursor) {
  this.cursor = cursor;
}
exports.Background = Background;

/**
 * Helper function that calls `write()` on the underlying Stream.
 * Returns `this` instead of the write() return value to keep
 * the chaining going.
 */

Cursor.prototype.write = function () {
  this.stream.write.apply(this.stream, arguments);
  return this;
}

/**
 * Set up the positional ANSI codes.
 */

Object.keys(codes).forEach(function (name) {
  var code = String(codes[name]);
  Cursor.prototype[name] = function () {
    var c = code;
    if (arguments.length > 0) {
      c = Math.round(arguments[0]) + code;
    }
    this.write(prefix + c);
    return this;
  }
});

/**
 * Set up the functions for the rendering ANSI codes.
 */

Object.keys(styles).forEach(function (style) {
  var name = style[0].toUpperCase() + style.substring(1);

  Cursor.prototype[style] = function () {
    this.write(prefix + styles[style] + suffix);
    return this;
  }

  Cursor.prototype['reset'+name] = function () {
    this.write(prefix + reset[style] + suffix);
    return this;
  }
});

/**
 * Setup the functions for the standard colors.
 */

Object.keys(colors).forEach(function (color) {
  Foreground.prototype[color] = function () {
    this.cursor.write(prefix + colors[color] + suffix);
    return this.cursor;
  }

  var bgCode = colors[color] + 10;
  Background.prototype[color] = function () {
    this.cursor.write(prefix + bgCode + suffix);
    return this.cursor;
  }

  Cursor.prototype[color] = function () {
    return this.foreground[color]();
  }
});

/**
 * Makes a beep sound!
 */

Cursor.prototype.beep = function () {
  this.write('\007');
  return this;
}

/**
 * Moves cursor to specific position
 */

Cursor.prototype.goto = function (x, y) {
  x = ~~x;
  y = ~~y;
  this.write(prefix + y + ';' + x + 'H');
  return this;
}

/**
 * Reset the foreground color.
 */

Foreground.prototype.reset = function () {
  this.cursor.write(prefix + reset.foreground + suffix);
  return this.cursor;
}

/**
 * Reset the background color.
 */

Background.prototype.reset = function () {
  this.cursor.write(prefix + reset.background + suffix);
  return this.cursor;
}

/**
 * Resets all ANSI formatting on the stream.
 */

Cursor.prototype.reset = function () {
  this.write(prefix + '0' + suffix);
  return this;
}

/**
 * Sets the foreground color with the given RGB values.
 * The closest match out of the 216 colors is picked.
 */

Foreground.prototype.rgb = function (r, g, b) {
  this.cursor.write(prefix + '38;5;' + rgb(r, g, b) + suffix);
  return this.cursor;
}

/**
 * Sets the background color with the given RGB values.
 * The closest match out of the 216 colors is picked.
 */

Background.prototype.rgb = function (r, g, b) {
  this.cursor.write(prefix + '48;5;' + rgb(r, g, b) + suffix);
  return this.cursor;
}

/**
 * Same as `cursor.fg.rgb()`.
 */

Cursor.prototype.rgb = function (r, g, b) {
  return this.foreground.rgb(r, g, b);
}

/**
 * Accepts CSS color codes for use with ANSI escape codes.
 * For example: `#FF000` would be bright red.
 */

Foreground.prototype.hex = Background.prototype.hex = function (color) {
  var rgb = hex(color);
  return this.rgb(rgb[0], rgb[1], rgb[2]);
}

/**
 * Same as `cursor.fg.hex()`.
 */

Cursor.prototype.hex = function (color) {
  return this.foreground.hex(color);
}

/**
 * Translates a 255 RGB value to a 0-5 ANSI RGV value,
 * then returns the single ANSI color code to use.
 */

function rgb (r, g, b) {
  var red = r / 255 * 5
    , green = g / 255 * 5
    , blue = b / 255 * 5;
  return rgb5(red, green, blue);
}

/**
 * Turns rgb 0-5 values into a single ANSI color code to use.
 */

function rgb5 (r, g, b) {
  var red = Math.round(r)
    , green = Math.round(g)
    , blue = Math.round(b);
  return 16 + (red*36) + (green*6) + blue;
}

/**
 * Accepts a hex CSS color code string (# is optional) and
 * translates it into an Array of 3 RGB 0-255 values, which
 * can then be used with rgb().
 */

function hex (color) {
  var c = color[0] === '#' ? color.substring(1) : color
    , r = c.substring(0, 2)
    , g = c.substring(2, 4)
    , b = c.substring(4, 6);
  return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)];
}
<commit_msg>Support codes that accept multiple values with a ;<commit_after>
/**
 * References:
 *
 *   - http://en.wikipedia.org/wiki/ANSI_escape_code
 *   - http://www.termsys.demon.co.uk/vtansi.htm
 *
 */

/**
 * Module dependencies.
 */

var prefix = '\033[' // For all escape codes
  , suffix = 'm';    // Only for color codes

/**
 * The ANSI escape sequences.
 */

var codes = {
    up: 'A'
  , down: 'B'
  , forward: 'C'
  , back: 'D'
  , nextLine: 'E'
  , previousLine: 'F'
  , horizontalAbsolute: 'G'
  , eraseData: 'J'
  , eraseLine: 'K'
  , scrollUp: 'S'
  , scrollDown: 'T'
  , savePosition: 's'
  , restorePosition: 'u'
  , queryPosition: '6n'
  , hide: '?25l'
  , show: '?25h'
};

/**
 * Rendering ANSI codes.
 */

var styles = {
    bold: 1
  , italic: 3
  , underline: 4
  , inverse: 7
};

/**
 * The negating ANSI code for the rendering modes.
 */

var reset = {
    bold: 22
  , italic: 23
  , underline: 24
  , inverse: 27
  , foreground: 39
  , background: 49
};

/**
 * The standard, styleable ANSI colors.
 */

var colors = {
    white: 37
  , grey: 90
  , black: 30
  , blue: 34
  , cyan: 36
  , green: 32
  , magenta: 35
  , red: 31
  , yellow: 33
};

/**
 * Creates a Cursor instance based off the given `writable stream` instance.
 */

function ansi (stream, options) {
  if (stream._ansicursor) {
    return stream._ansicursor;
  } else {
    return stream._ansicursor = new Cursor(stream, options);
  }
}
module.exports = exports = ansi;

/**
 * The `Cursor` class.
 */

function Cursor (stream, options) {
  this.stream = stream;
  this.fg = this.foreground = new Foreground(this);
  this.bg = this.background = new Background(this);
}
exports.Cursor = Cursor;

/**
 * The `Foreground` class.
 */

function Foreground (cursor) {
  this.cursor = cursor;
}
exports.Foreground = Foreground;

/**
 * The `Background` class.
 */

function Background (cursor) {
  this.cursor = cursor;
}
exports.Background = Background;

/**
 * Helper function that calls `write()` on the underlying Stream.
 * Returns `this` instead of the write() return value to keep
 * the chaining going.
 */

Cursor.prototype.write = function () {
  this.stream.write.apply(this.stream, arguments);
  return this;
}

/**
 * Set up the positional ANSI codes.
 */

Object.keys(codes).forEach(function (name) {
  var code = String(codes[name]);
  Cursor.prototype[name] = function () {
    var c = code;
    if (arguments.length > 0) {
      c = toArray(arguments).map(Math.round).join(';') + code;
    }
    this.write(prefix + c);
    return this;
  }
});

/**
 * Set up the functions for the rendering ANSI codes.
 */

Object.keys(styles).forEach(function (style) {
  var name = style[0].toUpperCase() + style.substring(1);

  Cursor.prototype[style] = function () {
    this.write(prefix + styles[style] + suffix);
    return this;
  }

  Cursor.prototype['reset'+name] = function () {
    this.write(prefix + reset[style] + suffix);
    return this;
  }
});

/**
 * Setup the functions for the standard colors.
 */

Object.keys(colors).forEach(function (color) {
  Foreground.prototype[color] = function () {
    this.cursor.write(prefix + colors[color] + suffix);
    return this.cursor;
  }

  var bgCode = colors[color] + 10;
  Background.prototype[color] = function () {
    this.cursor.write(prefix + bgCode + suffix);
    return this.cursor;
  }

  Cursor.prototype[color] = function () {
    return this.foreground[color]();
  }
});

/**
 * Makes a beep sound!
 */

Cursor.prototype.beep = function () {
  this.write('\007');
  return this;
}

/**
 * Moves cursor to specific position
 */

Cursor.prototype.goto = function (x, y) {
  x = ~~x;
  y = ~~y;
  this.write(prefix + y + ';' + x + 'H');
  return this;
}

/**
 * Reset the foreground color.
 */

Foreground.prototype.reset = function () {
  this.cursor.write(prefix + reset.foreground + suffix);
  return this.cursor;
}

/**
 * Reset the background color.
 */

Background.prototype.reset = function () {
  this.cursor.write(prefix + reset.background + suffix);
  return this.cursor;
}

/**
 * Resets all ANSI formatting on the stream.
 */

Cursor.prototype.reset = function () {
  this.write(prefix + '0' + suffix);
  return this;
}

/**
 * Sets the foreground color with the given RGB values.
 * The closest match out of the 216 colors is picked.
 */

Foreground.prototype.rgb = function (r, g, b) {
  this.cursor.write(prefix + '38;5;' + rgb(r, g, b) + suffix);
  return this.cursor;
}

/**
 * Sets the background color with the given RGB values.
 * The closest match out of the 216 colors is picked.
 */

Background.prototype.rgb = function (r, g, b) {
  this.cursor.write(prefix + '48;5;' + rgb(r, g, b) + suffix);
  return this.cursor;
}

/**
 * Same as `cursor.fg.rgb()`.
 */

Cursor.prototype.rgb = function (r, g, b) {
  return this.foreground.rgb(r, g, b);
}

/**
 * Accepts CSS color codes for use with ANSI escape codes.
 * For example: `#FF000` would be bright red.
 */

Foreground.prototype.hex = Background.prototype.hex = function (color) {
  var rgb = hex(color);
  return this.rgb(rgb[0], rgb[1], rgb[2]);
}

/**
 * Same as `cursor.fg.hex()`.
 */

Cursor.prototype.hex = function (color) {
  return this.foreground.hex(color);
}

/**
 * Translates a 255 RGB value to a 0-5 ANSI RGV value,
 * then returns the single ANSI color code to use.
 */

function rgb (r, g, b) {
  var red = r / 255 * 5
    , green = g / 255 * 5
    , blue = b / 255 * 5;
  return rgb5(red, green, blue);
}

/**
 * Turns rgb 0-5 values into a single ANSI color code to use.
 */

function rgb5 (r, g, b) {
  var red = Math.round(r)
    , green = Math.round(g)
    , blue = Math.round(b);
  return 16 + (red*36) + (green*6) + blue;
}

/**
 * Accepts a hex CSS color code string (# is optional) and
 * translates it into an Array of 3 RGB 0-255 values, which
 * can then be used with rgb().
 */

function hex (color) {
  var c = color[0] === '#' ? color.substring(1) : color
    , r = c.substring(0, 2)
    , g = c.substring(2, 4)
    , b = c.substring(4, 6);
  return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)];
}

/**
 * Turns an array-like object into a real array.
 */

function toArray (a) {
  var i = 0
    , l = a.length
    , rtn = [];
  for (; i<l; i++) {
    rtn.push(a[i]);
  }
  return rtn;
}
<|endoftext|>


<commit_before>/*
 * directive for dialogs using jQuery UI binding
 */
angular.module("jqDialog",[])
    .directive("jqDialog", function() {
        return {
           /* scope: {
                dlgState : "=",
                ngModel: "@"
            },*/
            link:  function(scope, element, attrs) {

                if(attrs.jqDialog.length > 0) {
                    options = scope.$eval(attrs.jqDialog);
                } else {
                    //default options
                    options= {
                        modal:true
                    };
                }
                options.autoOpen= false;
                options.close = function() {
                    scope.dlgState = false
                    scope.$digest();
                    };
                //scope.hideDlg;

                if(attrs.dlgButtons && attrs.dlgButtons.length > 0) {
                    options.buttons = scope.$eval(attrs.dlgButtons)
                } else {
                    options.buttons =
                        {
                            "Cancel" : options.close,
                            "Save"  :  function() {
                                if(attrs.dlgAction)  {
                                    scope.$eval(attrs.dlgAction)();
                                } else {
                                    scope.submitDlg();
                                }
                            }
                        }
                }

                scope.openDlg=function() {
                    element.dialog("open");
                };

                scope.$watch('dlgState',function(newVal,oldVal) {
                    console.log("dialog status was " + oldVal + " is now " + newVal);
                    if(newVal) {
                        element.dialog("open");
                    } else {
                        element.dialog("close");
                    }

                });

                $(element).dialog(options);
                scope.dlgState=false;
            }
        }
    }
    );

<commit_msg>added safe digest to dialog Signed-off-by:h0ru5 <johannes.hund@gmail.com><commit_after>/*
 * directive for dialogs using jQuery UI binding
 */
angular.module("jqDialog",[])
    .directive("jqDialog", function() {
        return {
           /* scope: {
                dlgState : "=",
                ngModel: "@"
            },*/
            link:  function(scope, element, attrs) {

                if(attrs.jqDialog.length > 0) {
                    options = scope.$eval(attrs.jqDialog);
                } else {
                    //default options
                    options= {
                        modal:true
                    };
                }
                options.autoOpen= false;
                options.close = function() {
                    scope.dlgState = false
                    if(!scope.$$phase) scope.$digest();
                    };
                //scope.hideDlg;

                if(attrs.dlgButtons && attrs.dlgButtons.length > 0) {
                    options.buttons = scope.$eval(attrs.dlgButtons)
                } else {
                    options.buttons =
                        {
                            "Cancel" : options.close,
                            "Save"  :  function() {
                                if(attrs.dlgAction)  {
                                    scope.$eval(attrs.dlgAction)();
                                } else {
                                    scope.submitDlg();
                                }
                            }
                        }
                }

                scope.openDlg=function() {
                    element.dialog("open");
                };

                scope.$watch('dlgState',function(newVal,oldVal) {
                    console.log("dialog status was " + oldVal + " is now " + newVal);
                    if(newVal) {
                        element.dialog("open");
                    } else {
                        element.dialog("close");
                    }

                });

                $(element).dialog(options);
                scope.dlgState=false;
            }
        }
    }
    );

<|endoftext|>


<commit_before>import React from 'react';
import Player from './Player';
import {timestamp, sound} from '../utils';
import {gamepadHandler, queryGamePads, clearDelayed, delayedPressed} from '../gamepad';
import _ from 'lodash';
import {DIR} from '../logic';

class Messages extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            visibility: 'visible',
            text: 'Press X to Play'
        }
    }
    setText(text) {
        this.setState({text: text});
    }
    hide() {
        this.setState({visibility: 'hidden'});
    }
    show() {
        this.setState({visibility: 'visible'});
    }
    render() {
        let messageStyle = {
            visibility: this.state.visibility,
            position: 'absolute',
            fontSize: '2vh'
        };
        return (

                {this.state.text}

        );
    }
}

var GeneralKEYs = {
    ESC: 27,
    SPACE: 32
};

class Main extends React.Component {
    constructor(props) {
        super(props);
        this.playing = false;
        this.players = [];
        this.paused = false;
        this.last = timestamp();
        this.gamePadCallback = this.gamePadCallback.bind(this);
        this.loseCb = this.loseCb.bind(this);
    }

    componentDidMount() {
        this.run();
    }

    gamePadCallback(pad, idx, type) {
        console.log(pad, idx, type);
        var i = pad.index;
        if (type == "pressed") {
            switch (idx) {
                case 13:
                    //arrow down
                    delayedPressed(idx, function () {
                        this.players[i].actions.push(DIR.DOWN);
                        sound("move");
                    }.bind(this));
                    break;
                case 14:
                    //arrow left
                    delayedPressed(idx, function () {
                        this.players[i].actions.push(DIR.LEFT);
                        sound("move");
                    }.bind(this));
                    break;
                case 15:
                    //arrow right
                    delayedPressed(idx, function () {
                        this.players[i].actions.push(DIR.RIGHT);
                        sound("move");
                    }.bind(this));
                    break;
                case 12:
                    //arrow up
                    this.players[i].actions.push(DIR.UP);
                    sound("drop");
                    break;
                case 0:
                    //x
                    this.play();
                    this.players[i].actions.push(DIR.TURNLEFT);
                    sound("rotate");
                    break;
                case 1:
                    //'o'
                    this.players[i].actions.push(DIR.TURNRIGHT);
                    sound("rotate");
                    break;
                case 5:
                    //'r1'
                    this.players[i].actions.push(DIR.HOLD);
                    break;
                case 9:
                    //'options
                    this.togglePause();
                    break;
                case 6:
                //l2
                case 7:
                //r2
                case 2:
                //square
                case 3:
                //'triangle'
                case 4:
                    //l1
                    break;
            }
        } else if (type == "released") {
            clearDelayed(idx);
        }
    }

    keydown(ev) {
        console.log("getting key event", ev.keyCode, ev);
        var handled = false;
        if (this.playing) {
            for (var i = 0; i < this.players.length; i++) {
                switch (ev.keyCode) {
                    case this.players[i].KEYs.LEFT:
                        this.players[i].actions.push(DIR.LEFT);
                        handled = true;
                        break;
                    case this.players[i].KEYs.RIGHT:
                        this.players[i].actions.push(DIR.RIGHT);
                        handled = true;
                        break;
                    case this.players[i].KEYs.UP:
                        this.players[i].actions.push(DIR.UP);
                        handled = true;
                        break;
                    case this.players[i].KEYs.DOWN:
                        this.players[i].actions.push(DIR.DOWN);
                        handled = true;
                        break;
                    case this.players[i].KEYs.TURNLEFT:
                        this.players[i].actions.push(DIR.TURNLEFT);
                        handled = true;
                        break;
                    case this.players[i].KEYs.TURNRIGHT:
                        this.players[i].actions.push(DIR.TURNRIGHT);
                        handled = true;
                        break;
                    case this.players[i].KEYs.HOLD:
                        this.players[i].actions.push(DIR.HOLD);
                        handled = true;
                        break;
                    case GeneralKEYs.ESC:
                        this.players[i].lose();
                        handled = true;
                        break;
                }
            }
        } else if (ev.keyCode == GeneralKEYs.SPACE) {
            play();
            handled = true;
        }
        if (handled) {
            ev.preventDefault(); // prevent arrow keys from scrolling the page (supported in IE9+ and all other browsers)
        }
    }

    loseCb(player) {
        player.setEnd('LOSE');
        for (var i = 0; i < this.players.length; i++) {
            if (this.players[i] != player) {
                this.players[i].setEnd('WIN');
                this.players[i].incrWins();
            }
        }
        this.playing = false;
    }

    initPlayers() {
        //player 1 KEYs: left_arrow, up_arrow, right_arrow, down_arrow, o, p, i
        this.refs.player1.KEYs =
        {
            LEFT: 37,
            UP: 38,
            RIGHT: 39,
            DOWN: 40,
            TURNLEFT: 79,
            TURNRIGHT: 80,
            HOLD: 73
        };

        //player 2 KEYs: w, a, s, d, q, e, r
        this.refs.player2.KEYs =
        {
            LEFT: 65,
            UP: 87,
            RIGHT: 68,
            DOWN: 83,
            TURNLEFT: 81,
            TURNRIGHT: 69,
            HOLD: 82
        };

        this.refs.player1.setOpponent(this.refs.player2);
        this.refs.player2.setOpponent(this.refs.player1);
        this.refs.player1.setLoseCallback(this.loseCb);
        this.refs.player2.setLoseCallback(this.loseCb);

        this.players.push(this.refs.player1);
        this.players.push(this.refs.player2);
    }

    //-------------------------------------------------------------------------
    // GAME LOOP
    //-------------------------------------------------------------------------

    run() {
        this.initPlayers();
        this.addEvents();
        this.players.forEach(p => {
            p.resize();
            p.reset();
        });

        let self = this;

        function frame () {
            queryGamePads(self.gamePadCallback);
            // using requestAnimationFrame have to be able to handle large delta's caused
            // when it 'hibernates' in a background or non-visible tab
            if (!self.paused) {
                let now = timestamp();
                self.update(Math.min(1, (now - self.last) / 1000.0));
                self.last = now;
            }
            requestAnimationFrame(frame);
        }

        // start the first frame
        frame();
    }

    play() {
        if (this.playing) return;
        this.playing = true;
        this.refs.messages.hide();
        this.reset();
    }

    togglePause() {
        this.paused = !this.paused;
        if (this.paused) {
            this.refs.messages.setText("Paused");
            this.refs.messages.show();
        } else
            this.refs.messages.hide();
    }

    resize() {
        if (this.players)
            this.players.forEach(p => {
                p.resize()
            });
    }

    reset() {
        this.players.forEach(p => {
            p.reset()
        });
    }

    addEvents() {
        addEventListener('keydown', this.keydown);
        addEventListener('resize', this.resize);
        addEventListener("gamepadconnected", function (e) {
            gamepadHandler(e, true);
        });
        addEventListener("gamepaddisconnected", function (e) {
            gamepadHandler(e, false);
        });
    }

    update(idt) {
        if (this.playing)
            this.players.forEach(p => {
                p.update(idt)
            });
    }

    render() {
        return (

                    <Messages ref="messages" />
                    <Player number="1" name="Lubosz" background="lubosz.jpg" color="blue" ref="player1" />

                    <Player number="2" name="Jessi" background="porenta.gif" color="purple" ref="player2" />

        );
    }
}

export default Main;<commit_msg>do not print keyboard input<commit_after>import React from 'react';
import Player from './Player';
import {timestamp, sound} from '../utils';
import {gamepadHandler, queryGamePads, clearDelayed, delayedPressed} from '../gamepad';
import _ from 'lodash';
import {DIR} from '../logic';

class Messages extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            visibility: 'visible',
            text: 'Press X to Play'
        }
    }
    setText(text) {
        this.setState({text: text});
    }
    hide() {
        this.setState({visibility: 'hidden'});
    }
    show() {
        this.setState({visibility: 'visible'});
    }
    render() {
        let messageStyle = {
            visibility: this.state.visibility,
            position: 'absolute',
            fontSize: '2vh'
        };
        return (

                {this.state.text}

        );
    }
}

var GeneralKEYs = {
    ESC: 27,
    SPACE: 32
};

class Main extends React.Component {
    constructor(props) {
        super(props);
        this.playing = false;
        this.players = [];
        this.paused = false;
        this.last = timestamp();
        this.gamePadCallback = this.gamePadCallback.bind(this);
        this.loseCb = this.loseCb.bind(this);
    }

    componentDidMount() {
        this.run();
    }

    gamePadCallback(pad, idx, type) {
        //console.log(pad, idx, type);
        var i = pad.index;
        if (type == "pressed") {
            switch (idx) {
                case 13:
                    //arrow down
                    delayedPressed(idx, function () {
                        this.players[i].actions.push(DIR.DOWN);
                        sound("move");
                    }.bind(this));
                    break;
                case 14:
                    //arrow left
                    delayedPressed(idx, function () {
                        this.players[i].actions.push(DIR.LEFT);
                        sound("move");
                    }.bind(this));
                    break;
                case 15:
                    //arrow right
                    delayedPressed(idx, function () {
                        this.players[i].actions.push(DIR.RIGHT);
                        sound("move");
                    }.bind(this));
                    break;
                case 12:
                    //arrow up
                    this.players[i].actions.push(DIR.UP);
                    sound("drop");
                    break;
                case 0:
                    //x
                    this.play();
                    this.players[i].actions.push(DIR.TURNLEFT);
                    sound("rotate");
                    break;
                case 1:
                    //'o'
                    this.players[i].actions.push(DIR.TURNRIGHT);
                    sound("rotate");
                    break;
                case 5:
                    //'r1'
                    this.players[i].actions.push(DIR.HOLD);
                    break;
                case 9:
                    //'options
                    this.togglePause();
                    break;
                case 6:
                //l2
                case 7:
                //r2
                case 2:
                //square
                case 3:
                //'triangle'
                case 4:
                    //l1
                    break;
            }
        } else if (type == "released") {
            clearDelayed(idx);
        }
    }

    keydown(ev) {
        console.log("getting key event", ev.keyCode, ev);
        var handled = false;
        if (this.playing) {
            for (var i = 0; i < this.players.length; i++) {
                switch (ev.keyCode) {
                    case this.players[i].KEYs.LEFT:
                        this.players[i].actions.push(DIR.LEFT);
                        handled = true;
                        break;
                    case this.players[i].KEYs.RIGHT:
                        this.players[i].actions.push(DIR.RIGHT);
                        handled = true;
                        break;
                    case this.players[i].KEYs.UP:
                        this.players[i].actions.push(DIR.UP);
                        handled = true;
                        break;
                    case this.players[i].KEYs.DOWN:
                        this.players[i].actions.push(DIR.DOWN);
                        handled = true;
                        break;
                    case this.players[i].KEYs.TURNLEFT:
                        this.players[i].actions.push(DIR.TURNLEFT);
                        handled = true;
                        break;
                    case this.players[i].KEYs.TURNRIGHT:
                        this.players[i].actions.push(DIR.TURNRIGHT);
                        handled = true;
                        break;
                    case this.players[i].KEYs.HOLD:
                        this.players[i].actions.push(DIR.HOLD);
                        handled = true;
                        break;
                    case GeneralKEYs.ESC:
                        this.players[i].lose();
                        handled = true;
                        break;
                }
            }
        } else if (ev.keyCode == GeneralKEYs.SPACE) {
            play();
            handled = true;
        }
        if (handled) {
            ev.preventDefault(); // prevent arrow keys from scrolling the page (supported in IE9+ and all other browsers)
        }
    }

    loseCb(player) {
        player.setEnd('LOSE');
        for (var i = 0; i < this.players.length; i++) {
            if (this.players[i] != player) {
                this.players[i].setEnd('WIN');
                this.players[i].incrWins();
            }
        }
        this.playing = false;
    }

    initPlayers() {
        //player 1 KEYs: left_arrow, up_arrow, right_arrow, down_arrow, o, p, i
        this.refs.player1.KEYs =
        {
            LEFT: 37,
            UP: 38,
            RIGHT: 39,
            DOWN: 40,
            TURNLEFT: 79,
            TURNRIGHT: 80,
            HOLD: 73
        };

        //player 2 KEYs: w, a, s, d, q, e, r
        this.refs.player2.KEYs =
        {
            LEFT: 65,
            UP: 87,
            RIGHT: 68,
            DOWN: 83,
            TURNLEFT: 81,
            TURNRIGHT: 69,
            HOLD: 82
        };

        this.refs.player1.setOpponent(this.refs.player2);
        this.refs.player2.setOpponent(this.refs.player1);
        this.refs.player1.setLoseCallback(this.loseCb);
        this.refs.player2.setLoseCallback(this.loseCb);

        this.players.push(this.refs.player1);
        this.players.push(this.refs.player2);
    }

    //-------------------------------------------------------------------------
    // GAME LOOP
    //-------------------------------------------------------------------------

    run() {
        this.initPlayers();
        this.addEvents();
        this.players.forEach(p => {
            p.resize();
            p.reset();
        });

        let self = this;

        function frame () {
            queryGamePads(self.gamePadCallback);
            // using requestAnimationFrame have to be able to handle large delta's caused
            // when it 'hibernates' in a background or non-visible tab
            if (!self.paused) {
                let now = timestamp();
                self.update(Math.min(1, (now - self.last) / 1000.0));
                self.last = now;
            }
            requestAnimationFrame(frame);
        }

        // start the first frame
        frame();
    }

    play() {
        if (this.playing) return;
        this.playing = true;
        this.refs.messages.hide();
        this.reset();
    }

    togglePause() {
        this.paused = !this.paused;
        if (this.paused) {
            this.refs.messages.setText("Paused");
            this.refs.messages.show();
        } else
            this.refs.messages.hide();
    }

    resize() {
        if (this.players)
            this.players.forEach(p => {
                p.resize()
            });
    }

    reset() {
        this.players.forEach(p => {
            p.reset()
        });
    }

    addEvents() {
        addEventListener('keydown', this.keydown);
        addEventListener('resize', this.resize);
        addEventListener("gamepadconnected", function (e) {
            gamepadHandler(e, true);
        });
        addEventListener("gamepaddisconnected", function (e) {
            gamepadHandler(e, false);
        });
    }

    update(idt) {
        if (this.playing)
            this.players.forEach(p => {
                p.update(idt)
            });
    }

    render() {
        return (

                    <Messages ref="messages" />
                    <Player number="1" name="Lubosz" background="lubosz.jpg" color="blue" ref="player1" />

                    <Player number="2" name="Jessi" background="porenta.gif" color="purple" ref="player2" />

        );
    }
}

export default Main;<|endoftext|>