<commit_before>'use strict';

var fnTrue = () => true;
var fnSelf = x => x;

let enumerableSymbols = {
    arr: Symbol(),
    index: Symbol()
};

class Enumerable {
    constructor(arr = []) {
        this[enumerableSymbols.arr] = arr;
        this[enumerableSymbols.index] = 0;
    }

    [Symbol.iterator]() {
        return this;
    }

    next() {
        if (this[enumerableSymbols.index] < this[enumerableSymbols.arr].length) {
            return {
                value: this[enumerableSymbols.arr][this[enumerableSymbols.index]++],
                done: false
            };
        }

        return {
            done: true
        };
    }

    first(selector = fnTrue) {
        for (let item of this) {
            if (selector(item)) {
                return item;
            }
        }

        throw new Error('Sequence contains no matching elements');
    }

    firstOrDefault(selector = fnTrue) {
        try {
            return this.first(selector);
        } catch (e) {
            return undefined;
        }
    }

    single(selector = fnTrue) {
        var matched;
        for (let item of this) {
            if (selector(item)) {
                if (matched) {
                    throw Error('Sequence contains more than one matching element');
                } else {
                    matched = item;
                }
            }
        }
        if (matched) {
            return matched;
        }

        throw Error('Sequence contains no matching element');
    }

    singleOrDefault(selector = fnTrue) {
        try {
            return this.single(selector);
        } catch (e) {
            if (e.message === 'Sequence contains no matching element') {
                return undefined;
            }
            throw e;
        }
    }

    all(fn = fnTrue) {
        for (let x of this) {
            if (!fn(x)) {
                return false;
            }
        }
        return true;
    }

    any(fn = fnTrue) {
        for (let x of this) {
            if (fn(x)) {
                return true;
            }
        }
        return false;
    }

    count(fn = fnTrue) {
        var count = 0;
        for (let i of this) {
            if (fn(i)) {
                count++;
            }
        }
        return count;
    }

    aggregate(seed, fn, selector) {
        var it = this;

        switch (arguments.length) {
            case 1:
                fn = seed;
                seed = it.next().value;
                break;

            case 2:
                if (typeof seed === 'function') {
                    selector = fn;
                    fn = seed;
                    seed = it.next().value;
                }
                break;
        }
        for (let item of it) {
            seed = fn(seed, item);
        }

        if (selector) {
            return selector(seed);
        }
        return seed;
    }

    average(fn = fnSelf) {
        var total = 0;
        var count = 0;

        for (let item of this) {
            total += fn(item);
            count++;
        }

        if (count) {
            return total / count;
        }

        throw Error('Sequence contains no elements');
    }

    concat(col) {
        return new ConcatEnumerable(this, col);
    }

    contains(value, tester = x => x === value) {
        var it = this;

        for (let item of it) {
            if (tester(item, value)) {
                return true;
            }
        }
        return false;
    }

    where(selector) {
        return new WhereEnumerable(this, selector);
    }

    filter(selector) {
        return this.where(selector);
    }

    select(fn = fnSelf) {
        return new SelectEnumerable(this, fn);
    }

    map(fn) {
        return this.select(fn);
    }

    selectMany(fn = fnSelf) {
        return new SelectManyEnumerable(this, fn);
    }

    take(count = 0) {
        return new TakeEnumerable(this, count);
    }

    takeWhile(fn = 0) {
        if (fn === 0) {
            return this.take();
        }
        return new TakeEnumerable(this, fn);
    }

    skip(count = 0) {
        return new SkipEnumerable(this, count);
    }

    skipWhile(fn = 0) {
        if (fn === 0) {
            return this.skip();
        }
        return new SkipEnumerable(this, fn);
    }

    toArray() {
        var arr = [];
        for (let i of this) {
            arr.push(i);
        }
        return arr;
    }

    static range(start = 0, end = 0) {
        return new RangeEnumerable(start, end);
    }

    static repeat(item, count = 0) {
        return new RepeatEnumerable(item, count);
    }
}

class ConcatEnumerable extends Enumerable {
    constructor(arr, col) {
        super();
        this.arr = arr;
        this.col = Array.isArray(col) ? col.asEnumerable() : col;
    }

    next() {
        var value = this.arr.next();
        if (!value.done) {
            return value;
        }

        value = this.col.next();
        return value;
    }
}

class WhereEnumerable extends Enumerable {
    constructor(arr, selector) {
        super();
        this.arr = arr;
        this.selector = selector;
    }

    next() {
        var val = this.arr.next();
        if (val.done) {
            return val;
        }

        if (this.selector(val.value)) {
            return val;
        }

        return this.next();
    }
}

class SelectEnumerable extends Enumerable {
    constructor(arr, fn) {
        super();
        this.arr = arr;
        this.fn = fn;
        this._index = 0;
    }

    next() {
        var value = this.arr.next();
        if (value.done) {
            return value;
        }
        return {
            value: this.fn(value.value, this._index++),
            done: false
        };
    }
}

class SelectManyEnumerable extends Enumerable {
    constructor(arr, colSelector, resultSelector) {
        super();
        this.arr = arr;
        this.colSelector = colSelector;
        this.resultSelector = resultSelector || ((_, x) => x);
        this._index = 0;
    }

    next() {
        var value = this.arr.next();
        if (value.done) {
            return value;
        }
        let arr = this.colSelector(value.value, this._index++);
        return {
            value: this.resultSelector(arr, arr[0]),
            done: false
        };
    }
}

class RangeEnumerable extends Enumerable {
    constructor(start, count) {
        super();
        this.count = count;
        this.index = 0;
        this.start = start;
    }

    next() {
        if (this.index === this.count) {
            return {
                done: true
            };
        }

        var value = this.start;
        this.start++;
        this.index++;
        return {
            value: value,
            done: false
        };
    }
}

class TakeEnumerable extends Enumerable {
    constructor(arr, selector) {
        super();
        this.arr = arr;
        this.selector = selector;
        this.index = 0;
    }

    next() {
        var item = this.arr.next();
        if (item.done) {
            return item;
        }

        if (typeof this.selector === 'number') {
            if (this.index < this.selector) {
                this.index++;
                return item;
            } else {
                return {
                    done: true
                };
            }
        } else {
            if (this.selector(item.value, this.index)) {
                this.index++;
                return item;
            } else {
                return {
                    done: true
                };
            }
        }
    }
}

class SkipEnumerable extends Enumerable {
    constructor(arr, selector) {
        super();
        this.arr = arr;
        this.selector = selector;
        this.index = 0;
        this.flag = false;
    }

    next() {
        var item = this.arr.next();
        if (item.done) {
            return item;
        }

        var i = this.index;
        this.index++;
        if (typeof this.selector === 'number') {
            if (i >= this.selector) {
                return item;
            } else {
                return this.next();
            }
        } else {
            if (!this.flag && !this.selector(item.value, i)) {
                this.flag = true;
            }
            if (this.flag) {
                return item;
            }
            return this.next();
        }
    }
}

class RepeatEnumerable extends Enumerable {
    constructor(item, count) {
        super();
        this.item = item;
        this.count = count;
        this.index = 0;
    }

    next() {
        if (this.index === this.count) {
            return {
                done: true
            };
        }

        var stripped = JSON.stringify(this.item);
        this.index++;
        return {
            value: JSON.parse(stripped),
            done: false
        };
    }
}

// extension methods
Array.prototype.asEnumerable = function() {
    return new Enumerable(this);
};

export default Enumerable;<commit_msg>Forgot to pass index to where callback<commit_after>'use strict';

var fnTrue = () => true;
var fnSelf = x => x;

let enumerableSymbols = {
    arr: Symbol(),
    index: Symbol()
};

class Enumerable {
    constructor(arr = []) {
        this[enumerableSymbols.arr] = arr;
        this[enumerableSymbols.index] = 0;
    }

    [Symbol.iterator]() {
        return this;
    }

    next() {
        if (this[enumerableSymbols.index] < this[enumerableSymbols.arr].length) {
            return {
                value: this[enumerableSymbols.arr][this[enumerableSymbols.index]++],
                done: false
            };
        }

        return {
            done: true
        };
    }

    first(selector = fnTrue) {
        for (let item of this) {
            if (selector(item)) {
                return item;
            }
        }

        throw new Error('Sequence contains no matching elements');
    }

    firstOrDefault(selector = fnTrue) {
        try {
            return this.first(selector);
        } catch (e) {
            return undefined;
        }
    }

    single(selector = fnTrue) {
        var matched;
        for (let item of this) {
            if (selector(item)) {
                if (matched) {
                    throw Error('Sequence contains more than one matching element');
                } else {
                    matched = item;
                }
            }
        }
        if (matched) {
            return matched;
        }

        throw Error('Sequence contains no matching element');
    }

    singleOrDefault(selector = fnTrue) {
        try {
            return this.single(selector);
        } catch (e) {
            if (e.message === 'Sequence contains no matching element') {
                return undefined;
            }
            throw e;
        }
    }

    all(fn = fnTrue) {
        for (let x of this) {
            if (!fn(x)) {
                return false;
            }
        }
        return true;
    }

    any(fn = fnTrue) {
        for (let x of this) {
            if (fn(x)) {
                return true;
            }
        }
        return false;
    }

    count(fn = fnTrue) {
        var count = 0;
        for (let i of this) {
            if (fn(i)) {
                count++;
            }
        }
        return count;
    }

    aggregate(seed, fn, selector) {
        var it = this;

        switch (arguments.length) {
            case 1:
                fn = seed;
                seed = it.next().value;
                break;

            case 2:
                if (typeof seed === 'function') {
                    selector = fn;
                    fn = seed;
                    seed = it.next().value;
                }
                break;
        }
        for (let item of it) {
            seed = fn(seed, item);
        }

        if (selector) {
            return selector(seed);
        }
        return seed;
    }

    average(fn = fnSelf) {
        var total = 0;
        var count = 0;

        for (let item of this) {
            total += fn(item);
            count++;
        }

        if (count) {
            return total / count;
        }

        throw Error('Sequence contains no elements');
    }

    concat(col) {
        return new ConcatEnumerable(this, col);
    }

    contains(value, tester = x => x === value) {
        var it = this;

        for (let item of it) {
            if (tester(item, value)) {
                return true;
            }
        }
        return false;
    }

    where(selector) {
        return new WhereEnumerable(this, selector);
    }

    filter(selector) {
        return this.where(selector);
    }

    select(fn = fnSelf) {
        return new SelectEnumerable(this, fn);
    }

    map(fn) {
        return this.select(fn);
    }

    selectMany(fn = fnSelf) {
        return new SelectManyEnumerable(this, fn);
    }

    take(count = 0) {
        return new TakeEnumerable(this, count);
    }

    takeWhile(fn = 0) {
        if (fn === 0) {
            return this.take();
        }
        return new TakeEnumerable(this, fn);
    }

    skip(count = 0) {
        return new SkipEnumerable(this, count);
    }

    skipWhile(fn = 0) {
        if (fn === 0) {
            return this.skip();
        }
        return new SkipEnumerable(this, fn);
    }

    toArray() {
        var arr = [];
        for (let i of this) {
            arr.push(i);
        }
        return arr;
    }

    static range(start = 0, end = 0) {
        return new RangeEnumerable(start, end);
    }

    static repeat(item, count = 0) {
        return new RepeatEnumerable(item, count);
    }
}

class ConcatEnumerable extends Enumerable {
    constructor(arr, col) {
        super();
        this.arr = arr;
        this.col = Array.isArray(col) ? col.asEnumerable() : col;
    }

    next() {
        var value = this.arr.next();
        if (!value.done) {
            return value;
        }

        value = this.col.next();
        return value;
    }
}

class WhereEnumerable extends Enumerable {
    constructor(arr, selector) {
        super();
        this.arr = arr;
        this.selector = selector;
        this.index = 0;
    }

    next() {
        var val = this.arr.next();
        if (val.done) {
            return val;
        }

        if (this.selector(val.value, this.index++)) {
            return val;
        }

        return this.next();
    }
}

class SelectEnumerable extends Enumerable {
    constructor(arr, fn) {
        super();
        this.arr = arr;
        this.fn = fn;
        this._index = 0;
    }

    next() {
        var value = this.arr.next();
        if (value.done) {
            return value;
        }
        return {
            value: this.fn(value.value, this._index++),
            done: false
        };
    }
}

class SelectManyEnumerable extends Enumerable {
    constructor(arr, colSelector, resultSelector) {
        super();
        this.arr = arr;
        this.colSelector = colSelector;
        this.resultSelector = resultSelector || ((_, x) => x);
        this._index = 0;
    }

    next() {
        var value = this.arr.next();
        if (value.done) {
            return value;
        }
        let arr = this.colSelector(value.value, this._index++);
        return {
            value: this.resultSelector(arr, arr[0]),
            done: false
        };
    }
}

class RangeEnumerable extends Enumerable {
    constructor(start, count) {
        super();
        this.count = count;
        this.index = 0;
        this.start = start;
    }

    next() {
        if (this.index === this.count) {
            return {
                done: true
            };
        }

        var value = this.start;
        this.start++;
        this.index++;
        return {
            value: value,
            done: false
        };
    }
}

class TakeEnumerable extends Enumerable {
    constructor(arr, selector) {
        super();
        this.arr = arr;
        this.selector = selector;
        this.index = 0;
    }

    next() {
        var item = this.arr.next();
        if (item.done) {
            return item;
        }

        if (typeof this.selector === 'number') {
            if (this.index < this.selector) {
                this.index++;
                return item;
            } else {
                return {
                    done: true
                };
            }
        } else {
            if (this.selector(item.value, this.index)) {
                this.index++;
                return item;
            } else {
                return {
                    done: true
                };
            }
        }
    }
}

class SkipEnumerable extends Enumerable {
    constructor(arr, selector) {
        super();
        this.arr = arr;
        this.selector = selector;
        this.index = 0;
        this.flag = false;
    }

    next() {
        var item = this.arr.next();
        if (item.done) {
            return item;
        }

        var i = this.index;
        this.index++;
        if (typeof this.selector === 'number') {
            if (i >= this.selector) {
                return item;
            } else {
                return this.next();
            }
        } else {
            if (!this.flag && !this.selector(item.value, i)) {
                this.flag = true;
            }
            if (this.flag) {
                return item;
            }
            return this.next();
        }
    }
}

class RepeatEnumerable extends Enumerable {
    constructor(item, count) {
        super();
        this.item = item;
        this.count = count;
        this.index = 0;
    }

    next() {
        if (this.index === this.count) {
            return {
                done: true
            };
        }

        var stripped = JSON.stringify(this.item);
        this.index++;
        return {
            value: JSON.parse(stripped),
            done: false
        };
    }
}

// extension methods
Array.prototype.asEnumerable = function() {
    return new Enumerable(this);
};

export default Enumerable;<|endoftext|>


<commit_before>import { setChildren } from './children';

export function list (el, factory, keyResolver) {
  el.lookup = {};

  el.update = function (data) {
    var lookup = el.lookup;
    var newLookup = {};
    var views = [];

    for (var i = 0; i < data.length; i++) {
      var item = data[i];

      if (keyResolver) {
        var id = keyResolver(item);
      } else {
        var id = i;
      }

      var view = newLookup[id] = lookup[id] || (newLookup[id] = factory(item, i));
      view && view.update(item);
      views.push(view);
    }
    setChildren(el, views);
    el.lookup = newLookup;
  }
  return el;
}
<commit_msg>Remove double assignment<commit_after>import { setChildren } from './children';

export function list (el, factory, keyResolver) {
  el.lookup = {};

  el.update = function (data) {
    var lookup = el.lookup;
    var newLookup = {};
    var views = [];

    for (var i = 0; i < data.length; i++) {
      var item = data[i];

      if (keyResolver) {
        var id = keyResolver(item);
      } else {
        var id = i;
      }

      var view = newLookup[id] = lookup[id] || factory(item, i);
      view && view.update(item);
      views.push(view);
    }
    setChildren(el, views);
    el.lookup = newLookup;
  }
  return el;
}
<|endoftext|>


<commit_before>/*
ListJS Alpha 0.1
By Jonny Strömberg (www.poseinteractive.se)

Licence: Dunno yet. Everything should be allowed except for redistributing
the script under an other name. MIT or something.

OBS. The API is not frozen. It WILL change! Wait for beta.

*/

var ListJsHelpers = {
    /* (node, class) Source: http://www.dustindiaz.com/getelementsbyclass */
    //getByClass: function(d,e){if(d.getElementsByClassName){return d.getElementsByClassName(e)}else{return(function getElementsByClass(a,b){if(b==null)b=document;var c=[],els=b.getElementsByTagName("*"),elsLen=els.length,pattern=new RegExp("(^|\\s)"+a+"(\\s|$)"),i,j;for(i=0,j=0;i<elsLen;i++){if(pattern.test(els[i].className)){c[j]=els[i];j++}}return c})(e,d)}}
    /* searchClass,node,tag */
    getByClass: function(a,b,c){var d=new Array();if(b==null)b=document;if(c==null)c='*';var e=b.getElementsByTagName(c);var f=e.length;var g=new RegExp("(^|\\s)"+a+"(\\s|$)");for(i=0,j=0;i<f;i++){if(g.test(e[i].className)){d[j]=e[i];j++}}return d}
    /* (elm, 'event' callback) Source: http://net.tutsplus.com/tutorials/javascript-ajax/javascript-from-null-cross-browser-event-binding/ */
    /*, addEvent: (function(e,f){if(f.addEventListener){return function(a,b,c){if((a&&!a.length)||a===e){a.addEventListener(b,c,false)}else if(a&&a.length){var d=a.length;for(var i=0;i<d;i++){ListJsHelpers.addEvent(a[i],b,c)}}}}else if(f.attachEvent){return function(a,b,c){if((a&&!a.length)||a===e){a.attachEvent('on'+b,function(){return c.call(a,e.event)})}else if(a.length){var d=a.length;for(var i=0;i<d;i++){addEvent(a[i],b,c)}}}}})(this,document)*/
    , addEvent: (function( window, document ) {
    if ( document.addEventListener ) {
        return function( elem, type, cb ) {
            if ( (elem && !(elem instanceof Array)) || elem === window ) {
                elem.addEventListener(type, cb, false );
            }
            else if ( elem && elem[0] !== undefined ) {
                var len = elem.length;
                for ( var i = 0; i < len; i++ ) {
                    ListJsHelpers.addEvent( elem[i], type, cb );
                }
            }
        };
    }
    else if ( document.attachEvent ) {
        return function ( elem, type, cb ) {
            if ( (elem && !elem.length) || elem === window ) {
                elem.attachEvent( 'on' + type, function() { return cb.call(elem, window.event) } );
            }
            else if ( elem.length ) {
                var len = elem.length;
                for ( var i = 0; i < len; i++ ) {
                    ListJsHelpers.addEvent( elem[i], type, cb );
                }
            }
        };
    }
})( this, document )
    /* (elm, attribute) Source: http://stackoverflow.com/questions/3755227/cross-browser-javascript-getattribute-method */
    , getAttribute: function(a,b){var c=(a.getAttribute&&a.getAttribute(b))||null;if(!c){var d=a.attributes;var e=d.length;for(var i=0;i<e;i++)if(b[i].nodeName===b)c=b[i].nodeValue}return c}
};

function List(id, templates, values) {
    var self = this
        ,templater = null;
    this.listContainer = document.getElementById(id);
    this.items = [];
    this.list = null;
    this.templateEngines = {};

    var init = function(values, templates) {
        if (typeof templates.list === 'undefined') {
            templates.list = id;
        }
        templater = new Templater(templates);
        self.list = ListJsHelpers.getByClass('list', self.listContainer)[0];
        ListJsHelpers.addEvent(ListJsHelpers.getByClass('search', self.listContainer)[0], 'keyup', self.search);
        ListJsHelpers.addEvent(ListJsHelpers.getByClass('sort', self.listContainer), 'click', self.sort);
        if (templates.valueNames) {
            getIntialItems(templates.valueNames)
        }
        if (typeof values !== 'undefined') {
            self.add(values);
        }
    };

    var getIntialItems = function(valueNames) {
        var itemElements = ListJsHelpers.getByClass('item', self.list);
        for (var i = 0, il = itemElements.length; i < il; i++) {
            self.items.push(new Item(valueNames, itemElements[i]));
        }
    }

    /* Add object to list
    *
    */
    this.add = function(values, options) {
        var added = [];
        if (typeof values[0] === 'undefined'){
            values = [values];
        }
        for (var i = 0, il = values.length; i < il; i++) {
            var item = null;
            if (values[i] instanceof Item) {
                item = values[i];
                item.reload();
            } else {
                item = new Item(values[i]);
            }
            templater.add(item, options);
            self.items.push(item);
            added.push(item);
        }
        return added;
    };

    /* Removes object from list.
    * Loops through the list and removes objects where
    * property "valuename" === value
    */
    this.remove = function(valueName, value, options) {
        var found = false;
        for (var i = 0, il = self.items.length; i < il; i++) {
            if (self.items[i].getValues()[valueName] === value) {
                templater.remove(self.items[i], options);
                self.items.splice(i,1);
                il--;
                found = true;
            }
        }
        return found;
    };

    /* Gets the objects in the list which
    * property "valueName" === value
    */
    this.get = function(valueName, value) {
        var matchedItems = [];
        for (var i = 0, il = self.items.length; i < il; i++) {
            var item = self.items[i];
            if (item.getValues()[valueName] === value) {
                matchedItems.push(item);
            }
        }
        if (matchedItems.length == 0) {
            return null;
        } else if (matchedItems.length == 1) {
            return matchedItems[0];
        } else {
            return matchedItems;
        }
    };

    /* Sorts the list.
    * @valueOrEvent Either a JavaScript event object or a valueName
    * @sortFunction (optional) Define if natural sorting does not fullfill your needs
    *
    * TODO: Add Desc || Asc
    */
    this.sort = function(valueOrEvent, sortFunction) {
        var length = self.items.length,
            value = null;
        if (valueOrEvent.target === 'undefined') {
            value = valueOrEvent;
        } else {
            value = ListJsHelpers.getAttribute(valueOrEvent.target, 'rel');
        }
        if (sortFunction) {
            sortFunction = sortFunction;
        } else {
            sortFunction = function(a, b) {
                return sorter.alphanum(a.getValues()[value], b.getValues()[value]);
            };
        }
        self.items.sort(sortFunction);
        self.list.innerHTML = "";
        for (var i = 0, il = self.items.length; i < il; i++) {
            self.list.appendChild(self.items[i].elm);
        }
    };

    /*
    * The sort function. From http://my.opera.com/GreyWyvern/blog/show.dml/1671288
    */
    var sorter = {
        alphanum: function(a,b) {
            if (typeof a === 'undefined') {
                a = "";
            }
            if (typeof b === 'undefined') {
                b = "";
            }
            a = a.toString().replace(/&(lt|gt);/g, function (strMatch, p1){
                return (p1 == "lt")? "<" : ">";
            });
            a = a.replace(/<\/?[^>]+(>|$)/g, "");

            b = b.toString().replace(/&(lt|gt);/g, function (strMatch, p1){
                return (p1 == "lt")? "<" : ">";
            });
            b = b.replace(/<\/?[^>]+(>|$)/g, "");
            var aa = this.chunkify(a);
            var bb = this.chunkify(b);

            for (x = 0; aa[x] && bb[x]; x++) {
                if (aa[x] !== bb[x]) {
                    var c = Number(aa[x]), d = Number(bb[x]);
                    if (c == aa[x] && d == bb[x]) {
                        return c - d;
                    } else {
                        return (aa[x] > bb[x]) ? 1 : -1;
                    }
                }
            }
            return aa.length - bb.length;
        },
        chunkify: function(t) {
            var tz = [], x = 0, y = -1, n = 0, i, j;

            while (i = (j = t.charAt(x++)).charCodeAt(0)) {
                var m = (i == 46 || (i >=48 && i <= 57));
                if (m !== n) {
                    tz[++y] = "";
                    n = m;
                }
                tz[y] += j;
            }
            return tz;
        }
    };

    /*
    * Searches the list after values with content "searchStringOrEvent".
    * The columns parameter defines if all values should be included in the search,
    * defaults to undefined which means "all".
    */
    this.search = function(searchStringOrEvent, columns) {
        var searchString = ''
            , foundItems = [];
        if (typeof searchStringOrEvent.target !== 'undefined') {
            searchString = searchStringOrEvent.target.value.toLowerCase();
        } else {
            searchString = searchStringOrEvent.toLowerCase();
        }
        var useAllColumns = false;
        if (typeof columns === 'undefined') {
            useAllColumns = true;
        }
        for (var i = 0, il = self.items.length; i < il; i++) {
            var found = false,
                item = self.items[i];
            if (useAllColumns) {
                columns = item.getValues();
            }
            for(var j in columns) {
                var text = columns[j].toString().toLowerCase();
                if ((searchString !== "") && (text.search(searchString) > -1)) {
                    found = true;
                }
            }
            if (found) {
                foundItems.push(item);
            }
            if (found || (searchString === "")) {
                item.elm.style.display = "block";
            } else {
                item.elm.style.display = "none";
            }
        }
        return foundItems;
    };

    /*
    * Filters the list. If filterFunction() returns False hides the Item.
    * if filterFunction == false are the filter removed
    */
    this.filter = function(filterFunction) {
        var visibleItems = [];
        for (var i = 0, il = self.items.length; i < il; i++) {
            var item = self.items[i];
            if (filterFunction === false || typeof filterFunction === 'undefined') {
                item.show();
                visibleItems.push(item);
            } else {
                if (filterFunction(item.getValues())) {
                    visibleItems.push(item);
                    item.show();
                } else {
                    item.hide();
                }
            }
        }
        return visibleItems;
    };

    /*
    * Get size of the list
    */
    this.size = function() {
        return self.items.length;
    };

    function Item(initValues, element) {
        var item = this,
            values = {};

        var init = function(initValues, element) {
            if (typeof element === 'undefined') {
                templater.create(item);
                item.setValues(initValues);
                templater.add(item);
            } else {
                item.elm = element;
                var values = templater.get(item, initValues);
                item.setValues(values);
            }
        };
        this.setValues = function(newValues) {
            values = newValues;
            templater.set(item, item.getValues());
        };
        this.getValues = function() {
            return values;
        };
        this.show = function() {
            templater.show(item);
        };
        this.hide = function() {
            templater.hide(item);
        };
        init(initValues, element);
    };

    /* Templater with different kinds of template engines.
    * All engines have these methods
    * - reload(item)
    * - remove(item)
    */
    var Templater = function(settings) {
        if (typeof settings.engine === 'undefined') {
            settings.engine = "standard";
        } else {
            settings.engine = settings.engine.toLowerCase();
        }
        return new self.constructor.prototype.templateEngines[settings.engine](settings);
    }

    init(values, templates);
};

List.prototype.templateEngines = {};

List.prototype.templateEngines.standard = function(settings) {
    var listSource = ListJsHelpers.getByClass('list', document.getElementById(settings.list))[0]
        , itemSource = document.getElementById(settings.item);
    /* Get values from element */
    this.get = function(item, valueNames) {
        var values = {};
        for(var i = 0, il = valueNames.length; i < il; i++) {
            values[valueNames[i]] = ListJsHelpers.getByClass(valueNames[i], item.elm)[0].innerHTML;
        }
        return values;
    };

    /* Sets values at element */
    this.set = function(item, values) {
        for(var v in values) {
            var hej = ListJsHelpers.getByClass(v, item.elm)[0];
            if (hej) {
                hej.innerHTML = values[v];
            }
        }
    };

    this.create = function(item) {
        /* If item source does not exists, use the first item in list as
        source for new items */
        if (itemSource === null) {
            itemSource = ListJsHelpers.getByClass('item', listSource)[0];
        }
        var newItem = itemSource.cloneNode(true);
        newItem.id = "";
        newItem.style.display = "block";
        item.elm = newItem;
    };
    this.add = function(item) {
        listSource.appendChild(item.elm);
    }
    this.remove = function(item) {
        listSource.removeChild(item.elm);
    };
    this.show = function(item) {
        item.elm.style.display = "block";
    };
    this.hide = function(item) {
        item.elm.style.display = "none";
    };
};

<commit_msg>Added comments to helpers and some adjustments<commit_after>/*
ListJS Alpha 0.1
By Jonny Strömberg (www.poseinteractive.se)

Licence: Dunno yet. Everything should be allowed except for redistributing
the script under an other name. MIT or something.

OBS. The API is not frozen. It WILL change! Wait for beta.

*/

/*
* These helper functions are not written by List.js author Jonny (they may have been
* adjusted, thought).
*/
var ListJsHelpers = {
    /* (node, class) Source: http://www.dustindiaz.com/getelementsbyclass */
    //getByClass: function(d,e){if(d.getElementsByClassName){return d.getElementsByClassName(e)}else{return(function getElementsByClass(a,b){if(b==null)b=document;var c=[],els=b.getElementsByTagName("*"),elsLen=els.length,pattern=new RegExp("(^|\\s)"+a+"(\\s|$)"),i,j;for(i=0,j=0;i<elsLen;i++){if(pattern.test(els[i].className)){c[j]=els[i];j++}}return c})(e,d)}}
    /* searchClass,node,tag */
    getByClass: function(a,b,c){var d=new Array();if(b==null)b=document;if(c==null)c='*';var e=b.getElementsByTagName(c);var f=e.length;var g=new RegExp("(^|\\s)"+a+"(\\s|$)");for(i=0,j=0;i<f;i++){if(g.test(e[i].className)){d[j]=e[i];j++}}return d}
    /* (elm, 'event' callback) Source: http://net.tutsplus.com/tutorials/javascript-ajax/javascript-from-null-cross-browser-event-binding/ */
    /*, addEvent: (function(e,f){if(f.addEventListener){return function(a,b,c){if((a&&!a.length)||a===e){a.addEventListener(b,c,false)}else if(a&&a.length){var d=a.length;for(var i=0;i<d;i++){ListJsHelpers.addEvent(a[i],b,c)}}}}else if(f.attachEvent){return function(a,b,c){if((a&&!a.length)||a===e){a.attachEvent('on'+b,function(){return c.call(a,e.event)})}else if(a.length){var d=a.length;for(var i=0;i<d;i++){addEvent(a[i],b,c)}}}}})(this,document)*/
    , addEvent: (function( window, document ) {
        if ( document.addEventListener ) {
            return function( elem, type, cb ) {
                if ( (elem && !(elem instanceof Array)) || elem === window ) {
                    elem.addEventListener(type, cb, false );
                }
                else if ( elem && elem[0] !== undefined ) {
                    var len = elem.length;
                    for ( var i = 0; i < len; i++ ) {
                        ListJsHelpers.addEvent( elem[i], type, cb );
                    }
                }
            };
        }
        else if ( document.attachEvent ) {
            return function ( elem, type, cb ) {
                if ( (elem && !elem.length) || elem === window ) {
                    elem.attachEvent( 'on' + type, function() { return cb.call(elem, window.event) } );
                }
                else if ( elem.length ) {
                    var len = elem.length;
                    for ( var i = 0; i < len; i++ ) {
                        ListJsHelpers.addEvent( elem[i], type, cb );
                    }
                }
            };
        }
    })( this, document )
    /* (elm, attribute) Source: http://stackoverflow.com/questions/3755227/cross-browser-javascript-getattribute-method */
    , getAttribute: function(a,b){var c=(a.getAttribute&&a.getAttribute(b))||null;if(!c){var d=a.attributes;var e=d.length;for(var i=0;i<e;i++)if(b[i].nodeName===b)c=b[i].nodeValue}return c}
};

function List(id, templates, values) {
    var self = this
        ,templater = null;
    this.listContainer = document.getElementById(id);
    this.items = [];
    this.list = null;
    this.templateEngines = {};

    var init = function(values, templates) {
        if (typeof templates.list === 'undefined') {
            templates.list = id;
        }
        templater = new Templater(templates);
        self.list = ListJsHelpers.getByClass('list', self.listContainer)[0];
        ListJsHelpers.addEvent(ListJsHelpers.getByClass('search', self.listContainer)[0], 'keyup', self.search);
        ListJsHelpers.addEvent(ListJsHelpers.getByClass('sort', self.listContainer), 'click', self.sort);
        if (templates.valueNames) {
            getIntialItems(templates.valueNames)
        }
        if (typeof values !== 'undefined') {
            self.add(values);
        }
    };

    var getIntialItems = function(valueNames) {
        var itemElements = ListJsHelpers.getByClass('item', self.list);
        for (var i = 0, il = itemElements.length; i < il; i++) {
            self.items.push(new Item(valueNames, itemElements[i]));
        }
    }

    /* Add object to list
    *
    */
    this.add = function(values, options) {
        var added = [];
        if (typeof values[0] === 'undefined'){
            values = [values];
        }
        for (var i = 0, il = values.length; i < il; i++) {
            var item = null;
            if (values[i] instanceof Item) {
                item = values[i];
                item.reload();
            } else {
                item = new Item(values[i]);
            }
            templater.add(item, options);
            self.items.push(item);
            added.push(item);
        }
        return added;
    };

    /* Removes object from list.
    * Loops through the list and removes objects where
    * property "valuename" === value
    */
    this.remove = function(valueName, value, options) {
        var found = false;
        for (var i = 0, il = self.items.length; i < il; i++) {
            if (self.items[i].getValues()[valueName] === value) {
                templater.remove(self.items[i], options);
                self.items.splice(i,1);
                il--;
                found = true;
            }
        }
        return found;
    };

    /* Gets the objects in the list which
    * property "valueName" === value
    */
    this.get = function(valueName, value) {
        var matchedItems = [];
        for (var i = 0, il = self.items.length; i < il; i++) {
            var item = self.items[i];
            if (item.getValues()[valueName] === value) {
                matchedItems.push(item);
            }
        }
        if (matchedItems.length == 0) {
            return null;
        } else if (matchedItems.length == 1) {
            return matchedItems[0];
        } else {
            return matchedItems;
        }
    };

    /* Sorts the list.
    * @valueOrEvent Either a JavaScript event object or a valueName
    * @sortFunction (optional) Define if natural sorting does not fullfill your needs
    *
    * TODO: Add Desc || Asc
    */
    this.sort = function(valueOrEvent, sortFunction) {
        var length = self.items.length,
            value = null;
        if (valueOrEvent.target === 'undefined') {
            value = valueOrEvent;
        } else {
            value = ListJsHelpers.getAttribute(valueOrEvent.target, 'rel');
        }
        if (sortFunction) {
            sortFunction = sortFunction;
        } else {
            sortFunction = function(a, b) {
                return sorter.alphanum(a.getValues()[value], b.getValues()[value]);
            };
        }
        self.items.sort(sortFunction);
        self.list.innerHTML = "";
        for (var i = 0, il = self.items.length; i < il; i++) {
            self.list.appendChild(self.items[i].elm);
        }
    };

    /*
    * The sort function. From http://my.opera.com/GreyWyvern/blog/show.dml/1671288
    */
    var sorter = {
        alphanum: function(a,b) {
            if (typeof a === 'undefined') {
                a = "";
            }
            if (typeof b === 'undefined') {
                b = "";
            }
            a = a.toString().replace(/&(lt|gt);/g, function (strMatch, p1){
                return (p1 == "lt")? "<" : ">";
            });
            a = a.replace(/<\/?[^>]+(>|$)/g, "");

            b = b.toString().replace(/&(lt|gt);/g, function (strMatch, p1){
                return (p1 == "lt")? "<" : ">";
            });
            b = b.replace(/<\/?[^>]+(>|$)/g, "");
            var aa = this.chunkify(a);
            var bb = this.chunkify(b);

            for (x = 0; aa[x] && bb[x]; x++) {
                if (aa[x] !== bb[x]) {
                    var c = Number(aa[x]), d = Number(bb[x]);
                    if (c == aa[x] && d == bb[x]) {
                        return c - d;
                    } else {
                        return (aa[x] > bb[x]) ? 1 : -1;
                    }
                }
            }
            return aa.length - bb.length;
        },
        chunkify: function(t) {
            var tz = [], x = 0, y = -1, n = 0, i, j;

            while (i = (j = t.charAt(x++)).charCodeAt(0)) {
                var m = (i == 46 || (i >=48 && i <= 57));
                if (m !== n) {
                    tz[++y] = "";
                    n = m;
                }
                tz[y] += j;
            }
            return tz;
        }
    };

    /*
    * Searches the list after values with content "searchStringOrEvent".
    * The columns parameter defines if all values should be included in the search,
    * defaults to undefined which means "all".
    */
    this.search = function(searchStringOrEvent, columns) {
        var searchString = ''
            , foundItems = [];
        if (typeof searchStringOrEvent.target !== 'undefined') {
            searchString = searchStringOrEvent.target.value.toLowerCase();
        } else {
            searchString = searchStringOrEvent.toLowerCase();
        }
        var useAllColumns = false;
        if (typeof columns === 'undefined') {
            useAllColumns = true;
        }
        for (var i = 0, il = self.items.length; i < il; i++) {
            var found = false,
                item = self.items[i];
            if (useAllColumns) {
                columns = item.getValues();
            }
            for(var j in columns) {
                var text = columns[j].toString().toLowerCase();
                if ((searchString !== "") && (text.search(searchString) > -1)) {
                    found = true;
                }
            }
            if (found) {
                foundItems.push(item);
            }
            if (found || (searchString === "")) {
                item.elm.style.display = "block";
            } else {
                item.elm.style.display = "none";
            }
        }
        return foundItems;
    };

    /*
    * Filters the list. If filterFunction() returns False hides the Item.
    * if filterFunction == false are the filter removed
    */
    this.filter = function(filterFunction) {
        var visibleItems = [];
        for (var i = 0, il = self.items.length; i < il; i++) {
            var item = self.items[i];
            if (filterFunction === false || typeof filterFunction === 'undefined') {
                item.show();
                visibleItems.push(item);
            } else {
                if (filterFunction(item.getValues())) {
                    visibleItems.push(item);
                    item.show();
                } else {
                    item.hide();
                }
            }
        }
        return visibleItems;
    };

    /*
    * Get size of the list
    */
    this.size = function() {
        return self.items.length;
    };

    function Item(initValues, element) {
        var item = this,
            values = {};

        var init = function(initValues, element) {
            if (typeof element === 'undefined') {
                templater.create(item);
                item.setValues(initValues);
                templater.add(item);
            } else {
                item.elm = element;
                var values = templater.get(item, initValues);
                item.setValues(values);
            }
        };
        this.setValues = function(newValues) {
            values = newValues;
            templater.set(item, item.getValues());
        };
        this.getValues = function() {
            return values;
        };
        this.show = function() {
            templater.show(item);
        };
        this.hide = function() {
            templater.hide(item);
        };
        init(initValues, element);
    };

    /* Templater with different kinds of template engines.
    * All engines have these methods
    * - reload(item)
    * - remove(item)
    */
    var Templater = function(settings) {
        if (typeof settings.engine === 'undefined') {
            settings.engine = "standard";
        } else {
            settings.engine = settings.engine.toLowerCase();
        }
        return new self.constructor.prototype.templateEngines[settings.engine](settings);
    }

    init(values, templates);
};

List.prototype.templateEngines = {};

List.prototype.templateEngines.standard = function(settings) {
    var listSource = ListJsHelpers.getByClass('list', document.getElementById(settings.list))[0]
        , itemSource = document.getElementById(settings.item);
    /* Get values from element */
    this.get = function(item, valueNames) {
        var values = {};
        for(var i = 0, il = valueNames.length; i < il; i++) {
            values[valueNames[i]] = ListJsHelpers.getByClass(valueNames[i], item.elm)[0].innerHTML;
        }
        return values;
    };

    /* Sets values at element */
    this.set = function(item, values) {
        for(var v in values) {
            var hej = ListJsHelpers.getByClass(v, item.elm)[0];
            if (hej) {
                hej.innerHTML = values[v];
            }
        }
    };

    this.create = function(item) {
        /* If item source does not exists, use the first item in list as
        source for new items */
        if (itemSource === null) {
            itemSource = ListJsHelpers.getByClass('item', listSource)[0];
        }
        var newItem = itemSource.cloneNode(true);
        newItem.id = "";
        newItem.style.display = "block";
        item.elm = newItem;
    };
    this.add = function(item) {
        listSource.appendChild(item.elm);
    }
    this.remove = function(item) {
        listSource.removeChild(item.elm);
    };
    this.show = function(item) {
        item.elm.style.display = "block";
    };
    this.hide = function(item) {
        item.elm.style.display = "none";
    };
};

<|endoftext|>


<commit_before>var WebSocketServer = require('ws').Server
var util = require('util');
var events = require('events');

var eventEmitter = new events.EventEmitter();

var shutil = require(global.gBaseDir + '/src/shutil.js');
var shUser = require(global.gBaseDir + '/src/shuser.js');
var shGame = require(global.gBaseDir + '/src/shgame.js');

gPort = 5102;

var live = exports;
var wss = null;

var gUsers = {};

live.notify = function(gameId, data) {
	console.log('notify game: gameId = ' + gameId);
	eventEmitter.emit(channel("game", gameId), data);
}

live.notifyUser = function(uid, data) {
	console.log('notify user: uid = ' + uid);
	eventEmitter.emit(channel("user", uid), data);
}

function channel(name, id)
{
	return "notify." + name + "." + id;
}

live.start = function() {
  wss = new WebSocketServer({port: gPort});
	console.log("websocket listening: " + gPort)

	wss.on('connection', function(ws) {
		console.log("socket: connect")
		var wsUid = 0;
		var wsGames = [];

	  ws.on('message', function(message) {
			var req = {};
			var res = {};

			// fill in req.params
			req.params = JSON.parse(message);

			// fill in req.session
			shutil.fillSession(req, res, function(error, data) {
				if(error != 0) {
					ws.send(JSON.stringify(data));
					return;
				}

				// valid user
				wsUid = req.session.uid;
				gUsers[wsUid] = {status: "online"};
				if(req.params.cmd == "live.user") {
					// hook them into user events
					var userChannel = channel("user", req.session.uid);
					if(eventEmitter.listeners(userChannel).indexOf(socketNotify) == -1) {
						console.log("add user channel: "+userChannel);
						eventEmitter.on(userChannel, socketNotify);
					}
					ws.send(JSON.stringify(shutil.event("event.live.user", {status: "on", uid: wsUid})));
					return;
				}
				if(req.params.cmd == "live.game")
				{
					// hook them into game events
					// SWD validate params: gameId, status
					var gameId = req.params.gameId;
					var gameChannel = channel("game", gameId);

					if(req.params.status == "on") {
						if(eventEmitter.listeners(gameChannel).indexOf(socketNotify) == -1) {
							console.log("add game channel: "+gameChannel);
							global.live.notify(gameId, shutil.event('event.game.user.online', {uid: wsUid}));
							wsGames.push(gameId);
							eventEmitter.on(gameChannel, socketNotify);

							// must send myself notifs for games existing online users
							var game = new shGame();
							game.load(gameId, function (error, data) {
								var players = game.get("players");
								for (uid in players) {
									if(uid != wsUid && typeof(gUsers[uid]) != "undefined") {
										ws.send(JSON.stringify(shutil.event('event.game.user.online', {uid: uid})));
									}
								}
							});
						} else {
							console.log("remove game channel:" + gameChannel);
							eventEmitter.removeListener(gameChannel, socketNotify);
							global.live.notify(gameId, shutil.event('event.game.user.offline', {uid: wsUid}));
						}
					}
					ws.send(JSON.stringify(shutil.event("event.live.game", {status: req.params.status, game : gameId})));
					return;
				}

				shutil.call(req.params.cmd, req, res, function(error, data) {
					console.log("back from call: " + req.params.cmd);
					var cmd = req.params.cmd;
					if(error == 0) {
						if(cmd == 'game.leave') {
							// SWD not sure we want to do this
								var gameChannel = channel("game", req.params.gameId);
								console.log("remove channel: "+gameChannel);
								var idx = wsGames.indexOf(req.params.gameId);
								if(idx != -1) {
									wsGames.splice(idx, 1);
								}
								eventEmitter.removeListener(gameChannel, socketNotify);
							}
						}
						ws.send(JSON.stringify(data));
						return;
				});  // end shutil.call
			});  // end shutil.fillSession
		});  // end ws.on-message

		ws.on('error', function(err) {
			console.log(err);
		})

		ws.on('close', function(ws) {
			console.log("socket: close")

			console.log(gUsers);
			delete gUsers[wsUid];
			console.log(gUsers);

			var userChannel = channel("user", wsUid);
			console.log("socket: close cleanup - " + userChannel)
			eventEmitter.removeAllListeners(userChannel, socketNotify);
			for(var i=0; i<wsGames.length; i++) {
				var gameChannel = channel("game", wsGames[i]);
				console.log("socket: close cleanup - " + gameChannel)
				eventEmitter.removeListener(gameChannel, socketNotify);
				// since game is still in wsGames - user did not "game.leave" - SWD: we could enum the game.players like on set
				global.live.notify(wsGames[i], shutil.event('event.game.user.offline', {uid: wsUid}));
			}
		});

		// helper functions in valid ws scope
		var socketNotify = function(message) {
			console.log("socket: socketNotify")
			if(ws.readyState == 1) {
				// 1 = OPEN - SWD: find this in ws module later
				ws.send(JSON.stringify(message));
			} else {
				console.log("socket: dead socket");
			}
		};

	}); // end wss.on-connection

	wss.on('error', function(err) {
		console.log(err);
	});
}
<commit_msg>fix live.game on/off conditional;<commit_after>var WebSocketServer = require('ws').Server
var util = require('util');
var events = require('events');

var eventEmitter = new events.EventEmitter();

var shutil = require(global.gBaseDir + '/src/shutil.js');
var shUser = require(global.gBaseDir + '/src/shuser.js');
var shGame = require(global.gBaseDir + '/src/shgame.js');

gPort = 5102;

var live = exports;
var wss = null;

var gUsers = {};

live.notify = function(gameId, data) {
	console.log('notify game: gameId = ' + gameId);
	eventEmitter.emit(channel("game", gameId), data);
}

live.notifyUser = function(uid, data) {
	console.log('notify user: uid = ' + uid);
	eventEmitter.emit(channel("user", uid), data);
}

function channel(name, id)
{
	return "notify." + name + "." + id;
}

live.start = function() {
  wss = new WebSocketServer({port: gPort});
	console.log("websocket listening: " + gPort)

	wss.on('connection', function(ws) {
		console.log("socket: connect")
		var wsUid = 0;
		var wsGames = [];

	  ws.on('message', function(message) {
			var req = {};
			var res = {};

			// fill in req.params
			req.params = JSON.parse(message);

			// fill in req.session
			shutil.fillSession(req, res, function(error, data) {
				if(error != 0) {
					ws.send(JSON.stringify(data));
					return;
				}

				// valid user
				wsUid = req.session.uid;
				gUsers[wsUid] = {status: "online"};
				if(req.params.cmd == "live.user") {
					// hook them into user events
					var userChannel = channel("user", req.session.uid);
					if(eventEmitter.listeners(userChannel).indexOf(socketNotify) == -1) {
						console.log("add user channel: "+userChannel);
						eventEmitter.on(userChannel, socketNotify);
					}
					ws.send(JSON.stringify(shutil.event("event.live.user", {status: "on", uid: wsUid})));
					return;
				}
				if(req.params.cmd == "live.game")
				{
					// hook them into game events
					// SWD validate params: gameId, status
					var gameId = req.params.gameId;
					var gameChannel = channel("game", gameId);

					if(req.params.status == "on")
					{
						if(eventEmitter.listeners(gameChannel).indexOf(socketNotify) == -1)
						{
							console.log("add game channel: "+gameChannel);
							global.live.notify(gameId, shutil.event('event.game.user.online', {uid: wsUid}));
							wsGames.push(gameId);
							eventEmitter.on(gameChannel, socketNotify);

							// must send myself notifs for games existing online users
							var game = new shGame();
							game.load(gameId, function (error, data) {
								var players = game.get("players");
								for (uid in players) {
									if(uid != wsUid && typeof(gUsers[uid]) != "undefined") {
										ws.send(JSON.stringify(shutil.event('event.game.user.online', {uid: uid})));
									}
								}
							});
						}
					} else {
						console.log("remove game channel:" + gameChannel);
						eventEmitter.removeListener(gameChannel, socketNotify);
						global.live.notify(gameId, shutil.event('event.game.user.offline', {uid: wsUid}));
					}
					ws.send(JSON.stringify(shutil.event("event.live.game", {status: req.params.status, game : gameId})));
					return;
				}

				shutil.call(req.params.cmd, req, res, function(error, data) {
					console.log("back from call: " + req.params.cmd);
					var cmd = req.params.cmd;
					if(error == 0) {
						if(cmd == 'game.leave') {
							// SWD not sure we want to do this
								var gameChannel = channel("game", req.params.gameId);
								console.log("remove channel: "+gameChannel);
								var idx = wsGames.indexOf(req.params.gameId);
								if(idx != -1) {
									wsGames.splice(idx, 1);
								}
								eventEmitter.removeListener(gameChannel, socketNotify);
							}
						}
						ws.send(JSON.stringify(data));
						return;
				});  // end shutil.call
			});  // end shutil.fillSession
		});  // end ws.on-message

		ws.on('error', function(err) {
			console.log(err);
		})

		ws.on('close', function(ws) {
			console.log("socket: close", wsUid);

			delete gUsers[wsUid];

			var userChannel = channel("user", wsUid);
			console.log("socket: close cleanup - " + userChannel)
			eventEmitter.removeAllListeners(userChannel, socketNotify);
			for(var i=0; i<wsGames.length; i++) {
				var gameChannel = channel("game", wsGames[i]);
				console.log("socket: close cleanup - " + gameChannel)
				eventEmitter.removeListener(gameChannel, socketNotify);
				// since game is still in wsGames - user did not "game.leave" - SWD: we could enum the game.players like on set
				global.live.notify(wsGames[i], shutil.event('event.game.user.offline', {uid: wsUid}));
			}
		});

		// helper functions in valid ws scope
		var socketNotify = function(message) {
			console.log("socket: socketNotify")
			if(ws.readyState == 1) {
				// 1 = OPEN - SWD: find this in ws module later
				ws.send(JSON.stringify(message));
			} else {
				console.log("socket: dead socket");
			}
		};

	}); // end wss.on-connection

	wss.on('error', function(err) {
		console.log(err);
	});
}
<|endoftext|>


<commit_before><commit_msg>修改index.js<commit_after><|endoftext|>


<commit_before>(function(DOM, XHR, location) {
    "use strict";

    var stateData = [], // in-memory storage for states
        currentState = {url: location.href.split("#")[0]},
        previousEls = [],
        switchContent = (state, stateIndex) => {
            if (typeof state !== "object" || typeof state.html !== "object") return;

            currentState.html = {};
            currentState.title = DOM.get("title");
            // always make sure that previous state was completed
            // it can be in-progress on very fast history navigation
            previousEls.forEach((el) => el.remove());

            var currentStateIndex = stateData.indexOf(currentState);

            previousEls = Object.keys(state.html).map((selector) => {
                var el = DOM.find(selector),
                    content = state.html[selector];

                // store reference to node in the state object
                currentState.html[selector] = el;

                if (content != null) {
                    if (typeof content === "string") {
                        // clone el that is already in the hidden state
                        content = el.clone(false).set(content).hide();
                    }
                    // insert new state content
                    el[stateIndex > currentStateIndex ? "before" : "after"](content);
                    // show current content
                    content.show();
                }

                // Hide old content and remove when it's done. Use requestFrame
                // to postpone layout triggered by the remove method call
                el.hide(() => DOM.requestFrame(() => el.remove()));

                return el;
            });
            // store current state in memory
            if (currentStateIndex < 0) {
                stateData.push(currentState);
            }
            // update current state to the latest
            currentState = state;
            // update page title
            DOM.set("title", state.title);
            // update url in address bar
            if (state.url !== location.href) {
                history.pushState(stateIndex, state.title, state.url);
            }
        },
        createXHR = (function() {
            // lock element to prevent double clicks
            var lockedEl;

            return (target, method, url, config) => {
                if (lockedEl === target) return null;

                if (target !== DOM) lockedEl = target;

                var complete = (defaultErrors) => (state) => {
                    // cleanup outer variables
                    if (target !== DOM) lockedEl = null;

                    if (typeof state === "string") {
                        // state is a text content
                        state = {html: state};
                    }

                    // populate local values
                    state.url = state.url || url;
                    state.title = state.title || DOM.get("title");
                    state.errors = state.errors || defaultErrors;

                    return Promise[defaultErrors ? "reject" : "resolve"](state);
                };

                return XHR(method, url, config).then(complete(false), complete(true));
            };
        }()),
        appendParam = (memo, name, value) => {
            if (name in memo) {
                if (Array.isArray(memo[name])) {
                    memo[name].push(value);
                } else {
                    memo[name] = [ memo[name], value ];
                }
            } else {
                memo[name] = value;
            }
        };

    ["get", "post", "put", "delete", "patch"].forEach((method) => {
        DOM.on("ajaxify:" + method, [1, 2, "target"], (url, data, target) => {
            if (typeof url !== "string") return;
            // disable cacheBurst that is not required for IE10+
            var config = {data: data, cacheBurst: false},
                submits = target.matches("form") ? target.findAll("[type=submit]") : [],
                complete = (response) => {
                    submits.forEach((el) => el.set("disabled", false));

                    target.fire("ajaxify:loadend", response);
                };

            if (target.fire("ajaxify:loadstart", config)) {
                submits.forEach((el) => el.set("disabled", true));

                let xhr = createXHR(target, method, url, config);

                if (xhr) xhr.then(complete, complete);
            }
        });
    });

    DOM.on("click", "a", ["currentTarget", "defaultPrevented"], (link, cancel) => {
        if (cancel || link.get("target")) return;

        var url = link.get("href");

        if (!url.indexOf("http")) {
            return !link.fire("ajaxify:get", url);
        }
    });

    DOM.on("submit", ["target", "defaultPrevented"], (form, cancel) => {
        if (cancel || form.get("target")) return;

        var url = form.get("action"),
            method = form.get("method"),
            query = form.serialize();

        if (!method || method === "get") {
            return !form.fire("ajaxify:get", url, query);
        } else {
            return !form.fire("ajaxify:" + method, url, query);
        }
    });

    DOM.on("ajaxify:loadend", [1, "target", "defaultPrevented"], (state, el, cancel) => {
        var eventType = "ajaxify:" + (state.errors ? "error" : "load");

        cancel = cancel || !el.fire(eventType, state);

        if (!cancel) switchContent(state, stateData.length);
    });

    DOM.on("ajaxify:history", [1, "defaultPrevented"], (url, cancel) => {
        if (cancel) return;

        var state = stateData[url], index = url;

        if (state) {
            return switchContent(state, index);
        } else {
            for (index = stateData.length; --index >= 0;) {
                state = stateData[index];

                if (state.url === url) {
                    return switchContent(state, index);
                }
            }
        }

        // if the state hasn't been found - fetch it manually
        DOM.fire("ajaxify:get", url);
    });

    /* istanbul ignore else */
    if (history.pushState) {
        window.addEventListener("popstate", (e) => {
            var stateIndex = e.state;

            if (stateIndex >= 0) {
                DOM.fire("ajaxify:history", stateIndex);
            }
        });
        // update initial state address url
        history.replaceState(0, DOM.get("title"));
        // fix bug with external pages
        window.addEventListener("beforeunload", () => {
            history.replaceState(0, DOM.get("title"));
        });
    } else {
        // when url should be changed don't start request in old browsers
        DOM.on("ajaxify:loadstart", ["target", "defaultPrevented"], (sender, canceled) => {
            if (canceled) return;
            // load a new page in legacy browsers
            if (sender.matches("form")) {
                sender.fire("submit");
            } else if (sender.matches("a")) {
                location.href = sender.get("href");
            }
        });
    }

    DOM.extend("form", {
        serialize(...names) {
            if (!names.length) names = false;

            return this.findAll("[name]").reduce((memo, el) => {
                var name = el.get("name");
                // don't include disabled form fields or without names
                if (name && !el.get("disabled")) {
                    // skip filtered names
                    if (names && names.indexOf(name) < 0) return memo;
                    // skip inner form elements of a disabled fieldset
                    if (el.closest("fieldset").get("disabled")) return memo;

                    switch(el.get("type")) {
                    case "select-one":
                    case "select-multiple":
                        el.children().forEach((option) => {
                            if (option.get("selected")) {
                                appendParam(memo, name, option.get());
                            }
                        });
                        break;

                    case undefined:
                    case "fieldset": // fieldset
                    case "file": // file input
                    case "submit": // submit button
                    case "reset": // reset button
                    case "button": // custom button
                        break;

                    case "radio": // radio button
                    case "checkbox": // checkbox
                        if (!el.get("checked")) break;
                        /* falls through */
                    default:
                        appendParam(memo, name, el.get());
                    }
                }

                return memo;
            }, {});
        }
    });
}(window.DOM, window.XHR, window.location));
<commit_msg>improve state management<commit_after>(function(DOM, XHR, location) {
    "use strict";

    var stateData = [], // in-memory storage for states
        currentState = {url: location.href.split("#")[0]},
        previousEls = [],
        switchContent = (state, stateIndex) => {
            if (typeof state !== "object" || typeof state.html !== "object") return;

            currentState.html = {};
            currentState.title = DOM.get("title");
            // always make sure that previous state was completed
            // it can be in-progress on very fast history navigation
            previousEls.forEach((el) => el.remove());

            var currentStateIndex = stateData.indexOf(currentState);

            if (currentStateIndex < 0) {
                // store the current state in memory
                currentStateIndex = stateData.push(currentState) - 1;
            }

            previousEls = Object.keys(state.html).map((selector) => {
                var el = DOM.find(selector),
                    content = state.html[selector];

                // store reference to node in the state object
                currentState.html[selector] = el;

                if (content != null) {
                    if (typeof content === "string") {
                        // clone el that is already in the hidden state
                        content = el.clone(false).set(content).hide();
                    }
                    // insert new state content
                    el[currentStateIndex > stateIndex ? "before" : "after"](content);
                    // show current content
                    content.show();
                }

                // Hide old content and remove when it's done. Use requestFrame
                // to postpone layout triggered by the remove method call
                el.hide(() => DOM.requestFrame(() => el.remove()));

                return el;
            });
            // update current state to the latest
            currentState = state;
            // update page title
            DOM.set("title", state.title);
            // update url in address bar
            if (state.url !== location.href && stateIndex == null) {
                history.pushState(stateData.length, state.title, state.url);
            }
        },
        createXHR = (function() {
            // lock element to prevent double clicks
            var lockedEl;

            return (target, method, url, config) => {
                if (lockedEl === target) return null;

                if (target !== DOM) lockedEl = target;

                var complete = (defaultErrors) => (state) => {
                    // cleanup outer variables
                    if (target !== DOM) lockedEl = null;

                    if (typeof state === "string") {
                        // state is a text content
                        state = {html: state};
                    }

                    // populate local values
                    state.url = state.url || url;
                    state.title = state.title || DOM.get("title");
                    state.errors = state.errors || defaultErrors;

                    return Promise[defaultErrors ? "reject" : "resolve"](state);
                };

                return XHR(method, url, config).then(complete(false), complete(true));
            };
        }()),
        appendParam = (memo, name, value) => {
            if (name in memo) {
                if (Array.isArray(memo[name])) {
                    memo[name].push(value);
                } else {
                    memo[name] = [ memo[name], value ];
                }
            } else {
                memo[name] = value;
            }
        };

    ["get", "post", "put", "delete", "patch"].forEach((method) => {
        DOM.on("ajaxify:" + method, [1, 2, "target"], (url, data, target) => {
            if (typeof url !== "string") return;
            // disable cacheBurst that is not required for IE10+
            var config = {data: data, cacheBurst: false},
                submits = target.matches("form") ? target.findAll("[type=submit]") : [],
                complete = (response) => {
                    submits.forEach((el) => el.set("disabled", false));

                    target.fire("ajaxify:loadend", response);
                };

            if (target.fire("ajaxify:loadstart", config)) {
                submits.forEach((el) => el.set("disabled", true));

                let xhr = createXHR(target, method, url, config);

                if (xhr) xhr.then(complete, complete);
            }
        });
    });

    DOM.on("click", "a", ["currentTarget", "defaultPrevented"], (link, cancel) => {
        if (cancel || link.get("target")) return;

        var url = link.get("href");

        if (!url.indexOf("http")) {
            return !link.fire("ajaxify:get", url);
        }
    });

    DOM.on("submit", ["target", "defaultPrevented"], (form, cancel) => {
        if (cancel || form.get("target")) return;

        var url = form.get("action"),
            method = form.get("method"),
            query = form.serialize();

        if (!method || method === "get") {
            return !form.fire("ajaxify:get", url, query);
        } else {
            return !form.fire("ajaxify:" + method, url, query);
        }
    });

    DOM.on("ajaxify:loadend", [1, "target", "defaultPrevented"], (state, el, cancel) => {
        var eventType = "ajaxify:" + (state.errors ? "error" : "load");

        cancel = cancel || !el.fire(eventType, state);

        if (!cancel) switchContent(state);
    });

    DOM.on("ajaxify:history", [1, "defaultPrevented"], (url, cancel) => {
        if (cancel) return;

        var stateIndex = +url,
            state = stateData[stateIndex];

        if (!state) {
            // traverse states in reverse order to access the newest first
            for (stateIndex = stateData.length; --stateIndex >= 0;) {
                state = stateData[stateIndex];

                if (state.url === url) break;
            }
        }

        if (stateIndex >= 0) {
            switchContent(state, stateIndex);
        } else {
            // if the state hasn't been found - fetch it manually
            DOM.fire("ajaxify:get", url);
        }
    });

    /* istanbul ignore else */
    if (history.pushState) {
        window.addEventListener("popstate", (e) => {
            var stateIndex = e.state;

            if (stateIndex >= 0) {
                DOM.fire("ajaxify:history", stateIndex);
            }
        });
        // update initial state address url
        history.replaceState(0, DOM.get("title"));
        // fix bug with external pages
        window.addEventListener("beforeunload", () => {
            history.replaceState(0, DOM.get("title"));
        });
    } else {
        // when url should be changed don't start request in old browsers
        DOM.on("ajaxify:loadstart", ["target", "defaultPrevented"], (sender, canceled) => {
            if (canceled) return;
            // load a new page in legacy browsers
            if (sender.matches("form")) {
                sender.fire("submit");
            } else if (sender.matches("a")) {
                location.href = sender.get("href");
            }
        });
    }

    DOM.extend("form", {
        serialize(...names) {
            if (!names.length) names = false;

            return this.findAll("[name]").reduce((memo, el) => {
                var name = el.get("name");
                // don't include disabled form fields or without names
                if (name && !el.get("disabled")) {
                    // skip filtered names
                    if (names && names.indexOf(name) < 0) return memo;
                    // skip inner form elements of a disabled fieldset
                    if (el.closest("fieldset").get("disabled")) return memo;

                    switch(el.get("type")) {
                    case "select-one":
                    case "select-multiple":
                        el.children().forEach((option) => {
                            if (option.get("selected")) {
                                appendParam(memo, name, option.get());
                            }
                        });
                        break;

                    case undefined:
                    case "fieldset": // fieldset
                    case "file": // file input
                    case "submit": // submit button
                    case "reset": // reset button
                    case "button": // custom button
                        break;

                    case "radio": // radio button
                    case "checkbox": // checkbox
                        if (!el.get("checked")) break;
                        /* falls through */
                    default:
                        appendParam(memo, name, el.get());
                    }
                }

                return memo;
            }, {});
        }
    });
}(window.DOM, window.XHR, window.location));
<|endoftext|>


<commit_before>/**
 * Created by khejing on 2015/6/30.
 */

import mqtt from 'mqtt';

let recordingTopic = "recording";
let mqttClientInstance = mqtt.connect("mqtt://localhost:1883", {clientId: recordingTopic});
mqttClientInstance.on('connect', function () {
    console.log("connect mqtt server success");
});
mqttClientInstance.on('error',function(error) {
    console.log("mqtt connect failed: ", error);
});
mqttClientInstance.subscribe(recordingTopic);

mqttClientInstance.on('message', function(messageTopic, data) {
    let object = JSON.parse(data);
    console.log("recv msg: " + data);
});
/* when got exit signal, then exit
mqttClientInstance.end();
mqttClientInstance = null;
console.log("mqtt client has been destroyed");
*/<commit_msg>turn into es5 version<commit_after>/**
 * Created by khejing on 2015/6/30.
 */

var mqtt =require('mqtt');

var recordingTopic = "recording";
var mqttClientInstance = mqtt.connect("mqtt://localhost:1883", {clientId: recordingTopic});
mqttClientInstance.on('connect', function () {
    console.log("connect mqtt server success");
});
mqttClientInstance.on('error',function(error) {
    console.log("mqtt connect failed: ", error);
});
mqttClientInstance.subscribe(recordingTopic);

mqttClientInstance.on('message', function(messageTopic, data) {
    var object = JSON.parse(data);
    console.log("recv msg: " + data);
});
/* when got exit signal, then exit
mqttClientInstance.end();
mqttClientInstance = null;
console.log("mqtt client has been destroyed");
*/<|endoftext|>


<commit_before>// Copyright 2012 Google Inc. All Rights Reserved.

/**
 * @fileoverview The primary SPF entry point.
 *
 * @author nicksay@google.com (Alex Nicksay)
 */

goog.provide('spf.main');

goog.require('spf');
goog.require('spf.history');
goog.require('spf.nav');
goog.require('spf.pubsub');
goog.require('spf.string');

/**
 * Initialize SPF.
 *
 * @param {Object=} opt_config Optional configuration object.  Will be merged
 *     with {@link #defaults} and stored in {@link #config}.
 * @return {boolean} Whether SPF was successfully initialized.  If the HTML5
 *     history modification API is not supported, returns false.
 */
spf.main.init = function(opt_config) {
  var enable = !!(window.history.pushState);
  spf.debug.info('main.init', enable, opt_config);
  var config = opt_config || {};
  for (var key in spf.defaults) {
    var value = (key in config) ? config[key] : spf.defaults[key];
    spf.config[key] = value;
    if (enable && value && spf.string.endsWith(key, '-callback')) {
      spf.debug.debug('subscribing: ', key, value);
      spf.pubsub.subscribe(key, value);
    }
  }
  if (enable) {
    spf.history.init(spf.nav.handleHistory);
    spf.nav.init();
  }
  return enable;
};

/**
 * Dispose SPF.
 */
spf.main.dispose = function() {
  spf.nav.dispose();
  spf.history.dispose();
  spf.pubsub.clear();
  spf.config = {};
};

// Create the external API.
spf.init = spf.main.init;
spf.dispose = spf.main.dispose;
spf.navigate = spf.nav.navigate;
spf.load = spf.nav.load;
spf.process = spf.nav.process;

if (!spf.DEBUG) {
  // When SPF is compiled for a production build, all methods are renamed by
  // the compiler and wrapped in an anonymous function to prevent namespace
  // pollution.  Only the methods exported here will be exposed to the page.
  window['spf'] = {
    'init': spf.init,
    'dispose': spf.dispose,
    'navigate': spf.navigate,
    'load': spf.load,
    'process': spf.process
  }
}
<commit_msg>SPF: Ensure the external API is always exported.<commit_after>// Copyright 2012 Google Inc. All Rights Reserved.

/**
 * @fileoverview The primary SPF entry point.
 *
 * @author nicksay@google.com (Alex Nicksay)
 */

goog.provide('spf.main');

goog.require('spf');
goog.require('spf.history');
goog.require('spf.nav');
goog.require('spf.pubsub');
goog.require('spf.string');

/**
 * Initialize SPF.
 *
 * @param {Object=} opt_config Optional configuration object.  Will be merged
 *     with {@link #defaults} and stored in {@link #config}.
 * @return {boolean} Whether SPF was successfully initialized.  If the HTML5
 *     history modification API is not supported, returns false.
 */
spf.main.init = function(opt_config) {
  var enable = !!(window.history.pushState);
  spf.debug.info('main.init', enable, opt_config);
  var config = opt_config || {};
  for (var key in spf.defaults) {
    var value = (key in config) ? config[key] : spf.defaults[key];
    spf.config[key] = value;
    if (enable && value && spf.string.endsWith(key, '-callback')) {
      spf.debug.debug('subscribing: ', key, value);
      spf.pubsub.subscribe(key, value);
    }
  }
  if (enable) {
    spf.history.init(spf.nav.handleHistory);
    spf.nav.init();
  }
  return enable;
};

/**
 * Dispose SPF.
 */
spf.main.dispose = function() {
  spf.nav.dispose();
  spf.history.dispose();
  spf.pubsub.clear();
  spf.config = {};
};

// Create the external API.
spf.init = spf.main.init;
spf.dispose = spf.main.dispose;
spf.navigate = spf.nav.navigate;
spf.load = spf.nav.load;
spf.process = spf.nav.process;

// When SPF is compiled for a production build, all methods are renamed by
// the compiler and wrapped in an anonymous function to prevent namespace
// pollution.  Only the methods exported here will be exposed to the page.
window['spf'] = {
  'init': spf.init,
  'dispose': spf.dispose,
  'navigate': spf.navigate,
  'load': spf.load,
  'process': spf.process
}
<|endoftext|>


<commit_before>import {
  CREATE_PAYMENT,
  SET_RECEIPT,
  SET_AMOUNT,
  SET_TIP,
  RECEIVE_EXCHANGE_RATE,
  RECEIVE_INTEGRATED_ADDRESS,
  RECEIVE_PAYMENT
} from '../actions/constants/payments'

// TODO This needs to be a more database-like structure
const payments = (state = [], action) => {
  const { type, payload } = action

  const currentPayment = getCurrentPayment(state)
  const archive = state.slice(1)

  switch (type) {
    case CREATE_PAYMENT: {
      return [payload, ...state]
    }
    case RECEIVE_EXCHANGE_RATE:
    case SET_RECEIPT: {
      return [
        Object.assign({}, currentPayment, payload),
        ...archive
      ]
    }
    case SET_AMOUNT: {
      const { rate } = currentPayment
      const requestedAmount = payload.amount
      const convertedAmount = requestedAmount / rate

      return [
        Object.assign({}, currentPayment, {
          requestedAmount,
          convertedAmount,
          tip: 0,
          totalAmount: convertedAmount
        }),
        ...archive
      ]
    }
    case SET_TIP: {
      const { tip, updatedAt } = payload
      return [
        Object.assign({}, currentPayment, {
          tip,
          totalAmount: currentPayment.convertedAmount + tip,
          updatedAt
        }),
        ...archive
      ]
    }
    case RECEIVE_INTEGRATED_ADDRESS: {
      const { integratedAddress, paymentId } = payload

      if (currentPayment.integratedAddress != null && currentPayment.paymentId != null) {
        // TODO could wallet have changed?
        return state
      }
      if (integratedAddress == null || paymentId == null) {
        // one is valid, one is null
        throw new Error(`Invalid state: ${JSON.stringify({integratedAddress, paymentId})}`)
      }

      return [
        Object.assign({}, currentPayment, {
          integratedAddress,
          paymentId
        }),
        ...archive
      ]
    }
    case RECEIVE_PAYMENT: {
      const { received, transactionIds } = payload
      return [
        Object.assign({}, currentPayment, {
          received,
          transactionIds
        }),
        ...archive
      ]
    }
    default: {
      return state
    }
  }
}

export default payments

export const getCurrentPayment = (state) =>
  state.slice(0, 1)[0]
<commit_msg>Extract payment reducer<commit_after>import {
  CREATE_PAYMENT,
  SET_RECEIPT,
  SET_AMOUNT,
  SET_TIP,
  RECEIVE_EXCHANGE_RATE,
  RECEIVE_INTEGRATED_ADDRESS,
  RECEIVE_PAYMENT
} from '../actions/constants/payments'

const payment = (state = {}, action) => {
  const { type, payload } = action
  switch (type) {
    case CREATE_PAYMENT: {
      const { id, createdAt, updatedAt } = payload
      return Object.assign({}, state, {
        id,
        createdAt,
        updatedAt
      })
    }
    case RECEIVE_EXCHANGE_RATE:
      const { rate, exchange, fiatCurrency } = payload
      return Object.assign({}, state, {
        rate,
        exchange,
        fiatCurrency
      })
    case SET_RECEIPT:
      return Object.assign({}, state, { receipt: payload.receipt })
    case SET_AMOUNT: {
      const { rate } = state
      const requestedAmount = payload.amount
      const convertedAmount = requestedAmount / rate
      return Object.assign({}, state, {
        requestedAmount,
        convertedAmount,
        tip: 0,
        totalAmount: convertedAmount
      })
    }
    case SET_TIP: {
      const { tip, updatedAt } = payload
      return Object.assign({}, state, {
        tip,
        totalAmount: state.convertedAmount + tip,
        updatedAt
      })
    }
    case RECEIVE_INTEGRATED_ADDRESS: {
      const { integratedAddress, paymentId } = payload

      if (state.integratedAddress != null && state.paymentId != null) {
        // TODO could wallet have changed?
        return state
      }
      if (integratedAddress == null || paymentId == null) {
        // one is valid, one is null
        throw new Error(`Invalid state: ${JSON.stringify({integratedAddress, paymentId})}`)
      }

      return Object.assign({}, state, {
        integratedAddress,
        paymentId
      })
    }
    case RECEIVE_PAYMENT: {
      const { received, transactionIds } = payload
      return Object.assign({}, state, {
        received,
        transactionIds
      })
    }
    default: {
      return state
    }
  }
}

// TODO This needs to be a more database-like structure
const payments = (state = [], action) => {
  const currentPayment = getCurrentPayment(state)
  const archive = state.slice(1)

  switch (action.type) {
    case CREATE_PAYMENT: {
      return [
        payment(undefined, action),
        ...state
      ]
    }
    case RECEIVE_EXCHANGE_RATE:
    case SET_RECEIPT:
    case SET_AMOUNT:
    case SET_TIP:
    case RECEIVE_INTEGRATED_ADDRESS:
    case RECEIVE_PAYMENT:
      return [
        payment(currentPayment, action),
        ...archive
      ]
    default: {
      return state
    }
  }
}

export default payments

export const getCurrentPayment = (state) =>
  state.slice(0, 1)[0]
<|endoftext|>