<commit_before>/**
 * @fileoverview 옵저버 패턴을 이용하여 객체 간 커스텀 이벤트를 전달할 수 있는 기능을 제공하는 모듈
 * @author FE개발팀
 * @dependency type.js, collection.js object.js
 */

(function(ne) {
    'use strict';
    /* istanbul ignore if */
    if (!ne) {
        ne = window.ne = {};
    }
    if (!ne.util) {
        ne.util = window.ne.util = {};
    }

    /**
     * 이벤트 핸들러에 저장되는 단위
     * @typedef {object} eventItem
     * @property {object.<string, object>} eventObject
     * @property {function()} eventObject.fn 이벤트 핸들러 함수
     * @property {*} [eventObject.ctx] 이벤트 핸들러 실행 시 컨텍스트 지정가능
     */

    /**
     * 커스텀 이벤트 클래스
     * @constructor
     * @exports CustomEvents
     * @class
     */
    function CustomEvents() {

        /**
         * 이벤트 핸들러를 저장하는 객체
         * @type {object.<string, eventItem>}
         * @private
         */
        this._events = {};
    }

    var CustomEventMethod = /** @lends CustomEvents */ {
        /**
         * 인스턴스가 발생하는 이벤트에 핸들러를 등록하는 메서드
         * @param {(object|String)} types - 이벤트 타입 (타입과 함수 쌍으로 된 객체를 전달할 수도 있고 타입만
         * 전달할 수 있다. 후자의 경우 두 번째 인자에 핸들러를 전달해야 한다.)
         * @param {function()=} fn - 이벤트 핸들러 목록
         * @param {*=} context
         * @example
         * // 첫 번째 인자에 이벤트명:핸들러 데이터 객체를 넘긴 경우
         * instance.on({
         *     zoom: function() {},
         *     pan: function() {}
         * }, this);
         *
         * // 여러 이벤트를 한 핸들러로 처리할 수 있도록 함
         * instance.on('zoom pan', function() {});
         */
        on: function(types, fn, context) {
            this._toggle(true, types, fn, context);
        },

        /**
         * 인스턴스에 등록했던 이벤트 핸들러를 해제할 수 있다.
         * @param {(object|string)=} types 등록 해지를 원하는 이벤트 객체 또는 타입명. 아무 인자도 전달하지 않으면 모든 이벤트를 해제한다.
         * @param {Function=} fn
         * @param {*=} context
         * @example
         * // zoom 이벤트만 해제
         * instance.off('zoom', onZoom);
         *
         * // pan 이벤트 해제 (이벤트 바인딩 시에 context를 넘겼으면 그대로 넘겨주어야 한다)
         * instance.off('pan', onPan, this);
         *
         * // 인스턴스 내 모든 이벤트 해제
         * instance.off();
         */
        off: function(types, fn, context) {
            if (!ne.util.isExisty(types)) {
                this._events = null;
                return;
            }

            this._toggle(false, types, fn, context);
        },

        /**
         * on, off 메서드의 중복 코드를 줄이기 위해 만든 on토글 메서드
         * @param {boolean} isOn
         * @param {(Object|String)} types - 이벤트 타입 (타입과 함수 쌍으로 된 객체를 전달할 수도 있고 타입만
         * 전달할 수 있다. 후자의 경우 두 번째 인자에 핸들러를 전달해야 한다.)
         * @param {function()=} fn - 이벤트 핸들러 목록
         * @param {*=} context
         * @private
         */
        _toggle: function(isOn, types, fn, context) {
            var methodName = isOn ? '_on' : '_off',
                method = this[methodName];

            if (ne.util.isObject(types)) {
                ne.util.forEachOwnProperties(types, function(handler, type) {
                    method.call(this, type, handler, fn);
                }, this);
            } else {
                types = types.split(' ');

                ne.util.forEach(types, function(type) {
                    method.call(this, type, fn, context);
                }, this);
            }
        },

        /**
         * 내부적으로 실제로 이벤트를 등록하는 로직을 담는 메서드.
         *
         * 옵션에 따라 이벤트를 배열에 등록하기도 하고 해시에 등록하기도 한다.
         *
         * 두개를 사용하는 기준:
         *
         * 핸들러가 이미 this바인딩이 되어 있고 핸들러를 사용하는 object가 같은 종류가 동시다발적으로 생성/삭제되는 경우에는 context인자를
         * 전달하여 해시의 빠른 접근 속도를 이용하는 것이 좋다.
         *
         * @param {(object.<string, function()>|string)} type - 이벤트 타입 (타입과 함수 쌍으로 된 객체를 전달할 수도 있고 타입만
         * 전달할 수 있다. 후자의 경우 두 번째 인자에 핸들러를 전달해야 한다.)
         * @param {function()} fn - 이벤트 핸들러
         * @param {*=} context
         * @private
         */
        _on: function(type, fn, context) {
            var events = this._events = this._events || {},
                contextId = context && (context !== this) && ne.util.stamp(context);

            if (contextId) {
                /*
                 context가 현재 인스턴스와 다를 때 context의 아이디로 내부의 해시에서 빠르게 해당 핸들러를 컨트롤 하기 위한 로직.
                 이렇게 하면 동시에 많은 이벤트를 발생시키거나 제거할 때 성능면에서 많은 이점을 제공한다.
                 특히 동시에 많은 엘리먼트들이 추가되거나 해제될 때 도움이 될 수 있다.
                 */
                var indexKey = type + '_idx',
                    indexLenKey = type + '_len',
                    typeIndex = events[indexKey] = events[indexKey] || {},
                    id = ne.util.stamp(fn) + '_' + contextId; // 핸들러의 id + context의 id

                if (!typeIndex[id]) {
                    typeIndex[id] = {
                        fn: fn,
                        ctx: context
                    };

                    // 할당된 이벤트의 갯수를 추적해 두고 할당된 핸들러가 없는지 여부를 빠르게 확인하기 위해 사용한다
                    events[indexLenKey] = (events[indexLenKey] || 0) + 1;
                }
            } else {
                // fn이 이미 this 바인딩이 된 상태에서 올 경우 단순하게 처리해준다
                events[type] = events[type] || [];
                events[type].push({fn: fn});
            }
        },

        /**
         * 실제로 구독을 해제하는 메서드
         * @param {(object|string)=} type 등록 해지를 원하는 핸들러명
         * @param {function} fn
         * @param {*} context
         * @private
         */
        _off: function(type, fn, context) {
            var events = this._events,
                indexKey = type + '_idx',
                indexLenKey = type + '_len';

            if (!events) {
                return;
            }

            var contextId = context && (context !== this) && ne.util.stamp(context),
                listeners,
                id;

            if (contextId) {
                id = ne.util.stamp(fn) + '_' + contextId;
                listeners = events[indexKey];

                if (listeners && listeners[id]) {
                    listeners[id] = null;
                    events[indexLenKey] -= 1;
                }

            } else if(!fn) {
                events[type] = null;
            } else {
                listeners = events[type];

                if (listeners) {
                    if(fn){
                        ne.util.forEach(listeners, function(listener, index) {
                            if (ne.util.isExisty(listener) && (listener.fn === fn)) {
                                listeners.splice(index, 1);
                                return true;
                            }
                        });
                    }
                }
            }
        },

        /**
         * 이벤트를 발생시키는 메서드
         *
         * 등록한 리스너들의 실행 결과를 boolean AND 연산하여
         *
         * 반환한다는 점에서 {@link CustomEvents#fire} 와 차이가 있다
         *
         * 보통 컴포넌트 레벨에서 before 이벤트로 사용자에게
         *
         * 이벤트를 취소할 수 있게 해 주는 기능에서 사용한다.
         * @param {string} type
         * @param {*...} data
         * @returns {*}
         * @example
         * // 확대 기능을 지원하는 컴포넌트 내부 코드라 가정
         * if (this.invoke('beforeZoom')) {    // 사용자가 등록한 리스너 결과 체크
         *     // 리스너의 실행결과가 true 일 경우
         *     // doSomething
         * }
         *
         * //
         * // 아래는 사용자의 서비스 코드
         * map.on({
         *     'beforeZoom': function() {
         *         if (that.disabled && this.getState()) {    //서비스 페이지에서 어떤 조건에 의해 이벤트를 취소해야한다
         *             return false;
         *         }
         *         return true;
         *     }
         * });
         */
        invoke: function(type, data) {
            if (!this.hasListener(type)) {
                return true;
            }

            var args = Array.prototype.slice.call(arguments, 1),
                events = this._events;

            if (!events) {
                return true;
            }

            var typeIndex = events[type + '_idx'],
                listeners,
                result = true;

            if (events[type]) {
                listeners = events[type].slice();

                ne.util.forEach(listeners, function(listener) {
                    if (listener.fn.apply(this, args) === false) {
                        result = false;
                    }
                }, this);
            }

            ne.util.forEachOwnProperties(typeIndex, function(eventItem) {
                if (eventItem.fn.apply(eventItem.ctx, args) === false) {
                    result = false;
                }
            });

            return result;
        },

        /**
         * 이벤트를 발생시키는 메서드
         * @param {string} type 이벤트 타입명
         * @param {(object|string)=} data 발생과 함께 전달할 이벤트 데이터
         * @return {*}
         * @example
         * instance.fire('move', { direction: 'left' });
         *
         * // 이벤트 핸들러 처리
         * instance.on('move', function(moveEvent) {
         *     var direction = moveEvent.direction;
         * });
         */
        fire: function(type, data) {
            this.invoke.apply(this, arguments);
            return this;
        },

        /**
         * 이벤트 핸들러 존재 여부 확인
         * @param {string} type 핸들러명
         * @return {boolean}
         */
        hasListener: function(type) {
            var events = this._events,
                existyFunc = ne.util.isExisty;

            return existyFunc(events) && (existyFunc(events[type]) || events[type + '_len']);
        },

        /**
         * 등록된 이벤트 핸들러의 갯수 반환
         * @param {string} type
         * @returns {number}
         */
        getListenerLength: function(type) {
            var events = this._events,
                lenKey = type + '_len',
                length = 0,
                types,
                len;

            if (!ne.util.isExisty(events)) {
                return 0;
            }

            types = events[type];
            len = events[lenKey];

            length += (ne.util.isExisty(types) && ne.util.isArray(types)) ? types.length : 0;
            length += ne.util.isExisty(len) ? len : 0;

            return length;
        },

        /**
         * 단발성 커스텀 이벤트 핸들러 등록 시 사용
         * @param {(object|string)} types 이벤트명:핸들러 객체 또는 이벤트명
         * @param {function()=} fn 핸들러 함수
         * @param {*=} context
         */
        once: function(types, fn, context) {
            var that = this;

            if (ne.util.isObject(types)) {
                ne.util.forEachOwnProperties(types, function(handler, type) {
                    this.once(type, handler, fn);
                }, this);

                return;
            }

            function onceHandler() {
                fn.apply(context, arguments);
                that.off(types, onceHandler, context);
            }

            this.on(types, onceHandler, context);
        }

    };

    CustomEvents.prototype = CustomEventMethod;
    CustomEvents.prototype.constructor = CustomEvents;

    /**
     * 커스텀 이벤트 기능을 믹스인할 때 사용하는 메서드
     * @param {function()} func 생성자 함수
     * @example
     * // 모델 클래스 변경 시 컨트롤러에게 알림을 주고 싶은데
     * // 그 기능을 모델 클래스 자체에게 주고 싶다
     * function Model() {}
     *
     * // 커스텀 이벤트 믹스인
     * ne.util.CustomEvents.mixin(Model);
     *
     * var model = new Model();
     *
     * model.on('changed', function() {}, this);
     */
    CustomEvents.mixin = function(func) {
        ne.util.extend(func.prototype, CustomEventMethod);
    };

    ne.util.CustomEvents = CustomEvents;

})(window.ne);
<commit_msg>customEvent.off() 주석 추가<commit_after>/**
 * @fileoverview 옵저버 패턴을 이용하여 객체 간 커스텀 이벤트를 전달할 수 있는 기능을 제공하는 모듈
 * @author FE개발팀
 * @dependency type.js, collection.js object.js
 */

(function(ne) {
    'use strict';
    /* istanbul ignore if */
    if (!ne) {
        ne = window.ne = {};
    }
    if (!ne.util) {
        ne.util = window.ne.util = {};
    }

    /**
     * 이벤트 핸들러에 저장되는 단위
     * @typedef {object} eventItem
     * @property {object.<string, object>} eventObject
     * @property {function()} eventObject.fn 이벤트 핸들러 함수
     * @property {*} [eventObject.ctx] 이벤트 핸들러 실행 시 컨텍스트 지정가능
     */

    /**
     * 커스텀 이벤트 클래스
     * @constructor
     * @exports CustomEvents
     * @class
     */
    function CustomEvents() {

        /**
         * 이벤트 핸들러를 저장하는 객체
         * @type {object.<string, eventItem>}
         * @private
         */
        this._events = {};
    }

    var CustomEventMethod = /** @lends CustomEvents */ {
        /**
         * 인스턴스가 발생하는 이벤트에 핸들러를 등록하는 메서드
         * @param {(object|String)} types - 이벤트 타입 (타입과 함수 쌍으로 된 객체를 전달할 수도 있고 타입만
         * 전달할 수 있다. 후자의 경우 두 번째 인자에 핸들러를 전달해야 한다.)
         * @param {function()=} fn - 이벤트 핸들러 목록
         * @param {*=} context
         * @example
         * // 첫 번째 인자에 이벤트명:핸들러 데이터 객체를 넘긴 경우
         * instance.on({
         *     zoom: function() {},
         *     pan: function() {}
         * }, this);
         *
         * // 여러 이벤트를 한 핸들러로 처리할 수 있도록 함
         * instance.on('zoom pan', function() {});
         */
        on: function(types, fn, context) {
            this._toggle(true, types, fn, context);
        },

        /**
         * 인스턴스에 등록했던 이벤트 핸들러를 해제할 수 있다.
         * @param {(object|string)=} types 등록 해지를 원하는 이벤트 객체 또는 타입명. 아무 인자도 전달하지 않으면 모든 이벤트를 해제한다.
         * @param {Function=} fn 삭제할 핸들러, 핸들러를 전달하지 않으면 types 해당 이벤트가 모두 삭제된다.
         * @param {*=} context
         * @example
         * // zoom 이벤트만 해제
         * instance.off('zoom', onZoom);
         *
         * // pan 이벤트 해제 (이벤트 바인딩 시에 context를 넘겼으면 그대로 넘겨주어야 한다)
         * instance.off('pan', onPan, this);
         *
         * // 인스턴스 내 모든 이벤트 해제
         * instance.off();
         */
        off: function(types, fn, context) {
            if (!ne.util.isExisty(types)) {
                this._events = null;
                return;
            }

            this._toggle(false, types, fn, context);
        },

        /**
         * on, off 메서드의 중복 코드를 줄이기 위해 만든 on토글 메서드
         * @param {boolean} isOn
         * @param {(Object|String)} types - 이벤트 타입 (타입과 함수 쌍으로 된 객체를 전달할 수도 있고 타입만
         * 전달할 수 있다. 후자의 경우 두 번째 인자에 핸들러를 전달해야 한다.)
         * @param {function()=} fn - 이벤트 핸들러 목록
         * @param {*=} context
         * @private
         */
        _toggle: function(isOn, types, fn, context) {
            var methodName = isOn ? '_on' : '_off',
                method = this[methodName];

            if (ne.util.isObject(types)) {
                ne.util.forEachOwnProperties(types, function(handler, type) {
                    method.call(this, type, handler, fn);
                }, this);
            } else {
                types = types.split(' ');

                ne.util.forEach(types, function(type) {
                    method.call(this, type, fn, context);
                }, this);
            }
        },

        /**
         * 내부적으로 실제로 이벤트를 등록하는 로직을 담는 메서드.
         *
         * 옵션에 따라 이벤트를 배열에 등록하기도 하고 해시에 등록하기도 한다.
         *
         * 두개를 사용하는 기준:
         *
         * 핸들러가 이미 this바인딩이 되어 있고 핸들러를 사용하는 object가 같은 종류가 동시다발적으로 생성/삭제되는 경우에는 context인자를
         * 전달하여 해시의 빠른 접근 속도를 이용하는 것이 좋다.
         *
         * @param {(object.<string, function()>|string)} type - 이벤트 타입 (타입과 함수 쌍으로 된 객체를 전달할 수도 있고 타입만
         * 전달할 수 있다. 후자의 경우 두 번째 인자에 핸들러를 전달해야 한다.)
         * @param {function()} fn - 이벤트 핸들러
         * @param {*=} context
         * @private
         */
        _on: function(type, fn, context) {
            var events = this._events = this._events || {},
                contextId = context && (context !== this) && ne.util.stamp(context);

            if (contextId) {
                /*
                 context가 현재 인스턴스와 다를 때 context의 아이디로 내부의 해시에서 빠르게 해당 핸들러를 컨트롤 하기 위한 로직.
                 이렇게 하면 동시에 많은 이벤트를 발생시키거나 제거할 때 성능면에서 많은 이점을 제공한다.
                 특히 동시에 많은 엘리먼트들이 추가되거나 해제될 때 도움이 될 수 있다.
                 */
                var indexKey = type + '_idx',
                    indexLenKey = type + '_len',
                    typeIndex = events[indexKey] = events[indexKey] || {},
                    id = ne.util.stamp(fn) + '_' + contextId; // 핸들러의 id + context의 id

                if (!typeIndex[id]) {
                    typeIndex[id] = {
                        fn: fn,
                        ctx: context
                    };

                    // 할당된 이벤트의 갯수를 추적해 두고 할당된 핸들러가 없는지 여부를 빠르게 확인하기 위해 사용한다
                    events[indexLenKey] = (events[indexLenKey] || 0) + 1;
                }
            } else {
                // fn이 이미 this 바인딩이 된 상태에서 올 경우 단순하게 처리해준다
                events[type] = events[type] || [];
                events[type].push({fn: fn});
            }
        },

        /**
         * 실제로 구독을 해제하는 메서드
         * @param {(object|string)=} type 등록 해지를 원하는 핸들러명
         * @param {function} [fn] 삭제할 핸들러
         * @param {*} context
         * @private
         */
        _off: function(type, fn, context) {
            var events = this._events,
                indexKey = type + '_idx',
                indexLenKey = type + '_len';

            if (!events) {
                return;
            }

            var contextId = context && (context !== this) && ne.util.stamp(context),
                listeners,
                id;

            if (contextId) {
                id = ne.util.stamp(fn) + '_' + contextId;
                listeners = events[indexKey];

                if (listeners && listeners[id]) {
                    listeners[id] = null;
                    events[indexLenKey] -= 1;
                }

            } else if(!fn) {
                events[type] = null;
            } else {
                listeners = events[type];

                if (listeners) {
                    if(fn){
                        ne.util.forEach(listeners, function(listener, index) {
                            if (ne.util.isExisty(listener) && (listener.fn === fn)) {
                                listeners.splice(index, 1);
                                return true;
                            }
                        });
                    }
                }
            }
        },

        /**
         * 이벤트를 발생시키는 메서드
         *
         * 등록한 리스너들의 실행 결과를 boolean AND 연산하여
         *
         * 반환한다는 점에서 {@link CustomEvents#fire} 와 차이가 있다
         *
         * 보통 컴포넌트 레벨에서 before 이벤트로 사용자에게
         *
         * 이벤트를 취소할 수 있게 해 주는 기능에서 사용한다.
         * @param {string} type
         * @param {*...} data
         * @returns {*}
         * @example
         * // 확대 기능을 지원하는 컴포넌트 내부 코드라 가정
         * if (this.invoke('beforeZoom')) {    // 사용자가 등록한 리스너 결과 체크
         *     // 리스너의 실행결과가 true 일 경우
         *     // doSomething
         * }
         *
         * //
         * // 아래는 사용자의 서비스 코드
         * map.on({
         *     'beforeZoom': function() {
         *         if (that.disabled && this.getState()) {    //서비스 페이지에서 어떤 조건에 의해 이벤트를 취소해야한다
         *             return false;
         *         }
         *         return true;
         *     }
         * });
         */
        invoke: function(type, data) {
            if (!this.hasListener(type)) {
                return true;
            }

            var args = Array.prototype.slice.call(arguments, 1),
                events = this._events;

            if (!events) {
                return true;
            }

            var typeIndex = events[type + '_idx'],
                listeners,
                result = true;

            if (events[type]) {
                listeners = events[type].slice();

                ne.util.forEach(listeners, function(listener) {
                    if (listener.fn.apply(this, args) === false) {
                        result = false;
                    }
                }, this);
            }

            ne.util.forEachOwnProperties(typeIndex, function(eventItem) {
                if (eventItem.fn.apply(eventItem.ctx, args) === false) {
                    result = false;
                }
            });

            return result;
        },

        /**
         * 이벤트를 발생시키는 메서드
         * @param {string} type 이벤트 타입명
         * @param {(object|string)=} data 발생과 함께 전달할 이벤트 데이터
         * @return {*}
         * @example
         * instance.fire('move', { direction: 'left' });
         *
         * // 이벤트 핸들러 처리
         * instance.on('move', function(moveEvent) {
         *     var direction = moveEvent.direction;
         * });
         */
        fire: function(type, data) {
            this.invoke.apply(this, arguments);
            return this;
        },

        /**
         * 이벤트 핸들러 존재 여부 확인
         * @param {string} type 핸들러명
         * @return {boolean}
         */
        hasListener: function(type) {
            var events = this._events,
                existyFunc = ne.util.isExisty;

            return existyFunc(events) && (existyFunc(events[type]) || events[type + '_len']);
        },

        /**
         * 등록된 이벤트 핸들러의 갯수 반환
         * @param {string} type
         * @returns {number}
         */
        getListenerLength: function(type) {
            var events = this._events,
                lenKey = type + '_len',
                length = 0,
                types,
                len;

            if (!ne.util.isExisty(events)) {
                return 0;
            }

            types = events[type];
            len = events[lenKey];

            length += (ne.util.isExisty(types) && ne.util.isArray(types)) ? types.length : 0;
            length += ne.util.isExisty(len) ? len : 0;

            return length;
        },

        /**
         * 단발성 커스텀 이벤트 핸들러 등록 시 사용
         * @param {(object|string)} types 이벤트명:핸들러 객체 또는 이벤트명
         * @param {function()=} fn 핸들러 함수
         * @param {*=} context
         */
        once: function(types, fn, context) {
            var that = this;

            if (ne.util.isObject(types)) {
                ne.util.forEachOwnProperties(types, function(handler, type) {
                    this.once(type, handler, fn);
                }, this);

                return;
            }

            function onceHandler() {
                fn.apply(context, arguments);
                that.off(types, onceHandler, context);
            }

            this.on(types, onceHandler, context);
        }

    };

    CustomEvents.prototype = CustomEventMethod;
    CustomEvents.prototype.constructor = CustomEvents;

    /**
     * 커스텀 이벤트 기능을 믹스인할 때 사용하는 메서드
     * @param {function()} func 생성자 함수
     * @example
     * // 모델 클래스 변경 시 컨트롤러에게 알림을 주고 싶은데
     * // 그 기능을 모델 클래스 자체에게 주고 싶다
     * function Model() {}
     *
     * // 커스텀 이벤트 믹스인
     * ne.util.CustomEvents.mixin(Model);
     *
     * var model = new Model();
     *
     * model.on('changed', function() {}, this);
     */
    CustomEvents.mixin = function(func) {
        ne.util.extend(func.prototype, CustomEventMethod);
    };

    ne.util.CustomEvents = CustomEvents;

})(window.ne);
<|endoftext|>


<commit_before>var canvas, ctx, w, h, world, planeBody, boxBody, boxShape;
var pogo;
var data = [];
var heightfield = new Object();
init();
requestAnimationFrame(animate);

function init() {
	// Init canvas
	canvas = document.getElementById("myCanvas");
	w = canvas.width;
	h = canvas.height;
	ctx = canvas.getContext("2d");
	ctx.lineWidth = 0.05;
	// Init p2.js
	world = new p2.World({
		gravity : [ 0, -7 ]
	});

	world.defaultContactMaterial.friction = 100;
	world.defaultContactMaterial.restitution = 0.1;

	pogo = {
		stick : new Object(),
		frame : new Object(),
		spring : null
	// ,tip: new Object()
	};

	// pogo.tip.shape = new p2.Circle({ radius: 0.3 })
	//
	// pogo.tip.body = new p2.Body({
	// mass: 1,
	// position: [0,2],
	// angularVelocity: -0.5
	// });

	var av = -0.25

	pogo.stick.shape = new p2.Box({
		width : 0.25,
		height : 1
	});

	pogo.stick.body = new p2.Body({
		mass : 0.25,
//		damping: 0.2,
		position : [ 0, 1 ],
		angularVelocity : av,
		velocity : [ 5, 0 ]
	});

	pogo.frame.shape = new p2.Box({
		width : 0.5,
		height : 1.5
	});

	pogo.frame.body = new p2.Body({
		mass : 3,
		position : [ 0, 3 ],
		angularVelocity : av,
		velocity : [ 5, 0 ]
	});

	pogo.frame.body.addShape(pogo.frame.shape);
	pogo.stick.body.addShape(pogo.stick.shape);

	// world.addBody(pogo.tip.body);
	world.addBody(pogo.frame.body);
	world.addBody(pogo.stick.body);

	var stiffness = 350, damping = 0.5, restLength = 1

	var c1 = new p2.PrismaticConstraint(pogo.frame.body, pogo.stick.body, {
		localAnchorA : [ 0, 0.5 ],
		localAnchorB : [ 0, 0.5 ],
		localAxisA : [ 0, 1 ],
		disableRotationalLock : true,
	});
	var c2 = new p2.PrismaticConstraint(pogo.frame.body, pogo.stick.body, {
		localAnchorA : [ 0, 0 ],
		localAnchorB : [ 0, 1 ],
		localAxisA : [ 0, 1 ],
		disableRotationalLock : true,
	});

	c1.setLimits(-1.5, 0.5);
	c2.setLimits(-1.5, 0.5);
	world.addConstraint(c2);
	world.addConstraint(c1);

	// world.addSpring(new p2.LinearSpring(pogo.frame.body, pogo.stick.body, {
	// restLength : restLength,
	// stiffness : stiffness,
	// damping : damping,
	// localAnchorA : [0,0],
	// localAnchorB : [0,0],
	// }));

	pogo.spring = new p2.LinearSpring(pogo.frame.body, pogo.stick.body, {
		restLength : restLength,
		stiffness : stiffness,
		damping : damping,
		localAnchorA : [ 0, 0 ],
		localAnchorB : [ 0, 0 ],
	});

	world.addSpring(pogo.spring);

	// Add a box
	// boxShape = pogo.frame.shape
	// boxBody = pogo.frame.body
	// boxShape = new p2.Box({
	// width: 0.5,
	// height: 1.5
	// });
	// boxBody = new p2.Body({
	// mass: 1,
	// position: [0,3],
	// angularVelocity: -0.5
	// });
	// boxBody.addShape(boxShape);
	// world.addBody(boxBody);
	// Add a plane
	// planeShape = new p2.Plane();
	// planeBody = new p2.Body();
	// planeBody.addShape(planeShape);

	noise.seed(Math.random());
	data.push(0)
	var numDataPoints = 500;
	for (var i = 0; i < numDataPoints; i++) {
		v = i / 10
		var value = noise.simplex2(v, 0);
		var value1 = noise.perlin2(v, 0);
		// data.push(0.5*Math.cos(0.2*i) * Math.sin(0.5*i) + 0.6*Math.sin(0.1*i)
		// * Math.sin(0.05*i));
//		data.push(value * 1.75)
		data.push(0)
	}
	heightfield.shape = new p2.Heightfield({
		heights : data,
		elementWidth : 2
	});
	heightfield.body = new p2.Body({
		position : [ -1, -1 ]
	});
	heightfield.body.addShape(heightfield.shape);
	world.addBody(heightfield.body);

	// world.addBody(planeBody);

	// Setup Collisions
	var FRAME = 1, STICK = 2, GROUND = 4;

	// pogo.tip.shape.collisionGroup = TIP;
	pogo.frame.shape.collisionGroup = FRAME;
	pogo.stick.shape.collisionGroup = STICK;
	heightfield.shape.collisionGroup = GROUND;

	// pogo.tip.shape.collisionMask = ~STICK;
	pogo.frame.shape.collisionMask = ~STICK;
	pogo.stick.shape.collisionMask = ~FRAME;
	heightfield.shape.collisionMask = -1;

	window.addEventListener("keydown", keydown, false);
	window.addEventListener("keyup", keyup, false);
	window.addEventListener("keypress", keypress, false);
}

function keydown(evt) {
	// t = 5;
	// switch(evt.keyCode){
	// case 39: // right
	// torque = -t;
	// break;
	// case 37: // left
	// torque = t;
	// break;
	// }

//	alert(evt.charCode)
	if (evt.keyCode == 65) {
		pogo.spring.restLength = 1.25;
		pogo.spring.applyForce();
	}

	if (evt.keyCode == 83) {
		// alert("Hi")
		pogo.frame.body.angularVelocity = +twistval
		// pogo.stick.body.angularVelocity+=twistval
	}
	if (evt.keyCode == 68) {
		// alert("Hi")
		pogo.frame.body.angularVelocity = -twistval
		// pogo.stick.body.angularVelocity+=twistval
	}
}

function keyup(evt) {
	// t = 5;
	// switch(evt.keyCode){
	// case 39: // right
	// torque = -t;
	// break;
	// case 37: // left
	// torque = t;
	// break;
	// }

	if (evt.keyCode == 65) {
		pogo.spring.restLength = 0.25;
		pogo.spring.applyForce();
	}

	if (evt.keyCode == 83 || evt.keyCode == 68) {
		// alert("Hi")
		pogo.frame.body.angularVelocity = 0
		// pogo.stick.body.angularVelocity+=twistval
	}
//	if (evt.keyCode == 100) {
//		// alert("Hi")
//		pogo.frame.body.angularVelocity = -twistval
//		// pogo.stick.body.angularVelocity+=twistval
//	}

}
var twistval = 5;
function keypress(evt) {
	// alert(evt.keyCode)
	 if (evt.keyCode == 115) {
	 // alert("Hi")
	 pogo.frame.body.angularVelocity=twistval
	 // pogo.stick.body.angularVelocity+=twistval
	 }
	 if (evt.keyCode == 100) {
	 // alert("Hi")
	 pogo.frame.body.angularVelocity=-twistval
	 // pogo.stick.body.angularVelocity+=twistval
	 }
}

function drawpogo() {
	drawbox(pogo.stick)
	drawbox(pogo.frame)
	// drawBox({body: boxBody, shape: boxShape})
}

// function drawcircle(obj) {
// var x = obj.body.interpolatedPosition[0],
// y = obj.body.interpolatedPosition[1];
// ctx.arc(95,50,40,0,2*Math.PI);
// ctx.stroke();
// }

function drawbox(obj) {
	ctx.beginPath();
	var x = obj.body.interpolatedPosition[0], y = obj.body.interpolatedPosition[1];
	ctx.save();
	ctx.translate(x, y); // Translate to the center of the box
	ctx.rotate(obj.body.angle); // Rotate to the box body frame
	ctx.rect(-obj.shape.width / 2, -obj.shape.height / 2, obj.shape.width,
			obj.shape.height);
	ctx.stroke();
	ctx.restore();
}
function drawPlane() {
	var y = planeBody.interpolatedPosition[1];
	ctx.moveTo(-w, y);
	ctx.lineTo(w, y);
	ctx.stroke();
}

var xscroll = 0
function render() {
	// Clear the canvas
	ctx.clearRect(0, 0, w, h);
	// Transform the canvas
	// Note that we need to flip the y axis since Canvas pixel coordinates
	// goes from top to bottom, while physics does the opposite.

	ctx.save();
	xscroll = -pogo.frame.body.position[0]
//	ctx.translate(xscroll, 0)

	ctx.translate(w / 2, h / 2); // Translate to the center
	ctx.scale(50, -50); // Zoom in and flip y axis

	ctx.translate(xscroll, 0)
	// Draw all bodies
	// drawbox(pogo.frame);
	drawpogo();
	// drawBox(pogo.frame)
	// drawPlane();
	var y = heightfield.body.position[1]
	// y = -1
	var x = heightfield.body.position[0]

//	x+=xscroll
	// x = 0

	// ctx.beginPath();
	ctx.moveTo(x, y + data[0]);

	var i = 0;
	for ( var d in data) {
		ctx.lineTo(x + i * heightfield.shape.elementWidth, y + data[i]);
		i += 1;
	}
	// ctx.lineTo(gameWidth/2,gameHeight/2);
	// ctx.lineTo(-gameWidth/2,gameHeight/2);
	// ctx.lineTo(-gameWidth/2,-gameHeight/2);
	ctx.stroke();
	// Restore transform
	ctx.restore();
}
// Animation loop

speed = 60*5
//speed = 1/speed
var lastTime;
var maxSubSteps = 5; // Max physics ticks per render frame
var fixedDeltaTime = 1 / speed; // Physics "tick" delta time
function animate(time) {
	requestAnimationFrame(animate);
	// Get the elapsed time since last frame, in seconds
	var deltaTime = lastTime ? (time - lastTime) / 1000 : 0;
	lastTime = time;
	// Make sure the time delta is not too big (can happen if user switches
	// browser tab)
	deltaTime = Math.min(1 / 10, deltaTime);
	// Move physics bodies forward in time
	world.step(fixedDeltaTime, deltaTime, maxSubSteps);
	// Render scene
	render();
}<commit_msg>Revert "testing flat terrain"<commit_after>var canvas, ctx, w, h, world, planeBody, boxBody, boxShape;
var pogo;
var data = [];
var heightfield = new Object();
init();
requestAnimationFrame(animate);

function init() {
	// Init canvas
	canvas = document.getElementById("myCanvas");
	w = canvas.width;
	h = canvas.height;
	ctx = canvas.getContext("2d");
	ctx.lineWidth = 0.05;
	// Init p2.js
	world = new p2.World({
		gravity : [ 0, -7 ]
	});

	world.defaultContactMaterial.friction = 100;
	world.defaultContactMaterial.restitution = 0.1;

	pogo = {
		stick : new Object(),
		frame : new Object(),
		spring : null
	// ,tip: new Object()
	};

	// pogo.tip.shape = new p2.Circle({ radius: 0.3 })
	//
	// pogo.tip.body = new p2.Body({
	// mass: 1,
	// position: [0,2],
	// angularVelocity: -0.5
	// });

	var av = -0.25

	pogo.stick.shape = new p2.Box({
		width : 0.25,
		height : 1
	});

	pogo.stick.body = new p2.Body({
		mass : 0.25,
//		damping: 0.2,
		position : [ 0, 1 ],
		angularVelocity : av,
		velocity : [ 5, 0 ]
	});

	pogo.frame.shape = new p2.Box({
		width : 0.5,
		height : 1.5
	});

	pogo.frame.body = new p2.Body({
		mass : 3,
		position : [ 0, 3 ],
		angularVelocity : av,
		velocity : [ 5, 0 ]
	});

	pogo.frame.body.addShape(pogo.frame.shape);
	pogo.stick.body.addShape(pogo.stick.shape);

	// world.addBody(pogo.tip.body);
	world.addBody(pogo.frame.body);
	world.addBody(pogo.stick.body);

	var stiffness = 350, damping = 0.5, restLength = 1

	var c1 = new p2.PrismaticConstraint(pogo.frame.body, pogo.stick.body, {
		localAnchorA : [ 0, 0.5 ],
		localAnchorB : [ 0, 0.5 ],
		localAxisA : [ 0, 1 ],
		disableRotationalLock : true,
	});
	var c2 = new p2.PrismaticConstraint(pogo.frame.body, pogo.stick.body, {
		localAnchorA : [ 0, 0 ],
		localAnchorB : [ 0, 1 ],
		localAxisA : [ 0, 1 ],
		disableRotationalLock : true,
	});
	 c1.setLimits(-1.5, 0.5);
	 c2.setLimits(-1.5, 0.5);
	world.addConstraint(c2);
	world.addConstraint(c1);

	// world.addSpring(new p2.LinearSpring(pogo.frame.body, pogo.stick.body, {
	// restLength : restLength,
	// stiffness : stiffness,
	// damping : damping,
	// localAnchorA : [0,0],
	// localAnchorB : [0,0],
	// }));

	pogo.spring = new p2.LinearSpring(pogo.frame.body, pogo.stick.body, {
		restLength : restLength,
		stiffness : stiffness,
		damping : damping,
		localAnchorA : [ 0, 0 ],
		localAnchorB : [ 0, 0 ],
	});

	world.addSpring(pogo.spring);

	// Add a box
	// boxShape = pogo.frame.shape
	// boxBody = pogo.frame.body
	// boxShape = new p2.Box({
	// width: 0.5,
	// height: 1.5
	// });
	// boxBody = new p2.Body({
	// mass: 1,
	// position: [0,3],
	// angularVelocity: -0.5
	// });
	// boxBody.addShape(boxShape);
	// world.addBody(boxBody);
	// Add a plane
	// planeShape = new p2.Plane();
	// planeBody = new p2.Body();
	// planeBody.addShape(planeShape);

	noise.seed(Math.random());
	data.push(0)
	var numDataPoints = 500;
	for (var i = 0; i < numDataPoints; i++) {
		v = i / 10
		var value = noise.simplex2(v, 0);
		var value1 = noise.perlin2(v, 0);
		// data.push(0.5*Math.cos(0.2*i) * Math.sin(0.5*i) + 0.6*Math.sin(0.1*i)
		// * Math.sin(0.05*i));
		data.push(value * 1.75)
	}
	heightfield.shape = new p2.Heightfield({
		heights : data,
		elementWidth : 2
	});
	heightfield.body = new p2.Body({
		position : [ -1, -1 ]
	});
	heightfield.body.addShape(heightfield.shape);
	world.addBody(heightfield.body);

	// world.addBody(planeBody);

	// Setup Collisions
	var FRAME = 1, STICK = 2, GROUND = 4;

	// pogo.tip.shape.collisionGroup = TIP;
	pogo.frame.shape.collisionGroup = FRAME;
	pogo.stick.shape.collisionGroup = STICK;
	heightfield.shape.collisionGroup = GROUND;

	// pogo.tip.shape.collisionMask = ~STICK;
	pogo.frame.shape.collisionMask = ~STICK;
	pogo.stick.shape.collisionMask = ~FRAME;
	heightfield.shape.collisionMask = -1;

	window.addEventListener("keydown", keydown, false);
	window.addEventListener("keyup", keyup, false);
	window.addEventListener("keypress", keypress, false);
}

function keydown(evt) {
	// t = 5;
	// switch(evt.keyCode){
	// case 39: // right
	// torque = -t;
	// break;
	// case 37: // left
	// torque = t;
	// break;
	// }

//	alert(evt.charCode)
	if (evt.keyCode == 65) {
		pogo.spring.restLength = 1.25;
		pogo.spring.applyForce();
	}

	if (evt.keyCode == 83) {
		// alert("Hi")
		pogo.frame.body.angularVelocity = +twistval
		// pogo.stick.body.angularVelocity+=twistval
	}
	if (evt.keyCode == 68) {
		// alert("Hi")
		pogo.frame.body.angularVelocity = -twistval
		// pogo.stick.body.angularVelocity+=twistval
	}
}

function keyup(evt) {
	// t = 5;
	// switch(evt.keyCode){
	// case 39: // right
	// torque = -t;
	// break;
	// case 37: // left
	// torque = t;
	// break;
	// }

	if (evt.keyCode == 65) {
		pogo.spring.restLength = 0.25;
		pogo.spring.applyForce();
	}

	if (evt.keyCode == 83 || evt.keyCode == 68) {
		// alert("Hi")
		pogo.frame.body.angularVelocity = 0
		// pogo.stick.body.angularVelocity+=twistval
	}
//	if (evt.keyCode == 100) {
//		// alert("Hi")
//		pogo.frame.body.angularVelocity = -twistval
//		// pogo.stick.body.angularVelocity+=twistval
//	}

}
var twistval = 5;
function keypress(evt) {
	// alert(evt.keyCode)
	 if (evt.keyCode == 115) {
	 // alert("Hi")
	 pogo.frame.body.angularVelocity=twistval
	 // pogo.stick.body.angularVelocity+=twistval
	 }
	 if (evt.keyCode == 100) {
	 // alert("Hi")
	 pogo.frame.body.angularVelocity=-twistval
	 // pogo.stick.body.angularVelocity+=twistval
	 }
}

function drawpogo() {
	drawbox(pogo.stick)
	drawbox(pogo.frame)
	// drawBox({body: boxBody, shape: boxShape})
}

// function drawcircle(obj) {
// var x = obj.body.interpolatedPosition[0],
// y = obj.body.interpolatedPosition[1];
// ctx.arc(95,50,40,0,2*Math.PI);
// ctx.stroke();
// }

function drawbox(obj) {
	ctx.beginPath();
	var x = obj.body.interpolatedPosition[0], y = obj.body.interpolatedPosition[1];
	ctx.save();
	ctx.translate(x, y); // Translate to the center of the box
	ctx.rotate(obj.body.angle); // Rotate to the box body frame
	ctx.rect(-obj.shape.width / 2, -obj.shape.height / 2, obj.shape.width,
			obj.shape.height);
	ctx.stroke();
	ctx.restore();
}
function drawPlane() {
	var y = planeBody.interpolatedPosition[1];
	ctx.moveTo(-w, y);
	ctx.lineTo(w, y);
	ctx.stroke();
}

var xscroll = 0
function render() {
	// Clear the canvas
	ctx.clearRect(0, 0, w, h);
	// Transform the canvas
	// Note that we need to flip the y axis since Canvas pixel coordinates
	// goes from top to bottom, while physics does the opposite.

	ctx.save();
	xscroll = -pogo.frame.body.position[0]
//	ctx.translate(xscroll, 0)

	ctx.translate(w / 2, h / 2); // Translate to the center
	ctx.scale(50, -50); // Zoom in and flip y axis

	ctx.translate(xscroll, 0)
	// Draw all bodies
	// drawbox(pogo.frame);
	drawpogo();
	// drawBox(pogo.frame)
	// drawPlane();
	var y = heightfield.body.position[1]
	// y = -1
	var x = heightfield.body.position[0]

//	x+=xscroll
	// x = 0

	// ctx.beginPath();
	ctx.moveTo(x, y + data[0]);

	var i = 0;
	for ( var d in data) {
		ctx.lineTo(x + i * heightfield.shape.elementWidth, y + data[i]);
		i += 1;
	}
	// ctx.lineTo(gameWidth/2,gameHeight/2);
	// ctx.lineTo(-gameWidth/2,gameHeight/2);
	// ctx.lineTo(-gameWidth/2,-gameHeight/2);
	ctx.stroke();
	// Restore transform
	ctx.restore();
}
// Animation loop

speed = 60*5
//speed = 1/speed
var lastTime;
var maxSubSteps = 5; // Max physics ticks per render frame
var fixedDeltaTime = 1 / speed; // Physics "tick" delta time
function animate(time) {
	requestAnimationFrame(animate);
	// Get the elapsed time since last frame, in seconds
	var deltaTime = lastTime ? (time - lastTime) / 1000 : 0;
	lastTime = time;
	// Make sure the time delta is not too big (can happen if user switches
	// browser tab)
	deltaTime = Math.min(1 / 10, deltaTime);
	// Move physics bodies forward in time
	world.step(fixedDeltaTime, deltaTime, maxSubSteps);
	// Render scene
	render();
}<|endoftext|>


<commit_before><commit_msg>remove dead code.<commit_after><|endoftext|>


<commit_before>goog.provide('G');

goog.require('goog.dom');
goog.require('goog.array');
goog.require('goog.events');

/**
 * @param {string|Element|Node|Array|goog.array.ArrayLike} input
 * @param {string|Element|Node=} mod
 * @return {goog.array.ArrayLike}
 */
G = function(input, mod) {
    if(goog.isString(mod)) {
        mod = G.elsBySelector(/** @type {string} */(mod))[0];
    }
    if(input.nodeType)
        input = G([input]);
    else if(goog.isString(input)) {
        if(input.charAt(0) == '<') {
            input = [goog.dom.htmlToDocumentFragment(input)];
        } else {
            input = G.elsBySelector(input, mod);
        }
        if(!input) {
            input = []
        }
    }
    else if(!input) {
        input = []
    }
    if(goog.isArrayLike(input)) {
        input = goog.array.clone(input);
        input.__proto__ = function(input){return goog.object.clone([].__proto__)};
        goog.object.extend(input.__proto__, G.prototype);
    }
    /** @type {goog.array.ArrayLike} */
    var ret = input;
    return ret;
};

/**
 * takes a string like 'tagName[ .className]', '.className' or '#elementId'
 * mod is the element to search from
 *
 * @param {string} input
 * @param {Element|Node=} mod
 * @return {Array.<Element|Node>}
 */
G.elsBySelector = function(input, mod) {
    if(input.charAt(0) == '.') {
        return goog.dom.getElementsByClass(input.substring(1), /** @type {Element} */(mod)) || [];
    }
    if(input.charAt(0) == '#') {
        return [goog.dom.getElement(input)];
    }
    return goog.dom.getElementsByTagNameAndClass(input.replace(/\s.*/,''), input.replace(/.*\./,'')||null, /** @type {Element} */(mod));
};

/**
 */
G.prototype = {
    // Array Functions
    /**
     * @param {Function} fn
     * @param {Object} handler
     * @return {goog.array.ArrayLike}
     */
    each: function(fn, handler) {
        goog.array.forEach(this, fn, handler);
        return this;
    },
    /**
     * @param {Function} fn
     * @param {Object} handler
     * @return {goog.array.ArrayLike}
     */
    filter: function(fn, handler) {
        return G(goog.array.filter(this, fn, handler));
    },
    /**
     * @param {Function} fn
     * @param {Object} handler
     * @return {goog.array.ArrayLike}
     */
    map: function(fn, handler) {
        return G(goog.array.map(this, fn, handler));
    },
    /**
     * @param {number} in
     * @return {?Object}
     */
    get: function(ind) {
        if(ind < 0)
            ind = this.length + ind;
        return this[ind || 0];
    },
    /**
     * @return {?Object}
     */
    first: function(){
        return this.get(0);
    },
    /**
     * @return {?Object}
     */
    last: function(){
        return this.get(-1);
    },
    /**
     * @return {number}
     */
    size: function() {
        return this.length;
    },
    /**
     * @param {goog.array.ArrayLike} arr
     * @return {goog.array.ArrayLike}
     */
    add: function(arr) {
        return G(goog.array.concat(this, arr));
    },
    // DOM functions
    /**
     * @param {string} selector
     * @return {goog.array.ArrayLike}
     */
    find: function(selector) {
        var ret = [];
        this.each(function(el) {
            goog.array.forEach(G.elsBySelector(selector, el),
                function(ele) {
                    goog.array.insert(ret, ele);
                }
            );
        });
        return G(ret);
    },
    /**
     * @return {goog.array.ArrayLike}
     */
    next: function() {
        return this.map(function(el) {return el.nextSibling;});
    },
    /**
     * @return {goog.array.ArrayLike}
     */
    prev: function() {
        return this.map(function(el) {return el.previousSibling;});
    },
    /**
     * @param {Element|Node|Function|string=} input
     * @return {goog.array.ArrayLike}
     */
    html: function(input) {
        if(!input)
            return this.get().innerHTML
        if(goog.isFunction(input))
            this.each(input);
        if(input.nodeType)
            this.each(function(el) {goog.dom.append(/** @type {!Node} */(el), input.cloneNode)});
        else
            this.each(function(el) {el.innerHTML = input});
        return this;
    },
    /**
     * @param {Element|Node|Function|string=} input
     * @return {goog.array.ArrayLike}
     */
    text: function(input) {
        if(!input)
            return goog.dom.getTextContent(this.get());
        if(goog.isFunction(input))
            this.each(input);
        else
            this.each(function(el) {goog.dom.setTextContent(el, input)});
        return this;
    },
    // Events
    /**
     * @param {string} eventType
     * @param {Function} fn
     * @param {Object=} handler
     * @param {goog.events=} eventObject
     * @return {goog.array.ArrayLike}
     */
    bind: function(eventType, fn, handler, eventObject) {
        this.each(function(el) {
            if(eventObject)
                eventObject.listen(el, eventType, fn, false, (handler || el));
            else
                goog.events.listen(el, eventType, fn, false, (handler || el));
        });
        return this;
    },
    /**
     * @param {Function} fn
     * @param {Object=} handler
     * @param {goog.events=} eventObject
     * @return {goog.array.ArrayLike}
     */
    click: function(fn, handler, eventObject) {
        return this.bind(goog.events.EventType.CLICK);
    }
}

<commit_msg>more functions<commit_after>goog.provide('G');

goog.require('goog.dom');
goog.require('goog.array');
goog.require('goog.events');

/**
 * @param {string|Element|Node|Array|goog.array.ArrayLike} input
 * @param {string|Element|Node=} mod
 * @return {goog.array.ArrayLike}
 */
G = function(input, mod) {
    if(goog.isString(mod)) {
        mod = G.elsBySelector(/** @type {string} */(mod))[0];
    }
    if(input.nodeType)
        input = G([input]);
    else if(goog.isString(input)) {
        if(input.charAt(0) == '<') {
            input = [goog.dom.htmlToDocumentFragment(input)];
        } else {
            input = G.elsBySelector(input, mod);
        }
        if(!input) {
            input = []
        }
    }
    else if(!input) {
        input = []
    }
    if(goog.isArrayLike(input)) {
        input = goog.array.clone(input);
        input.__proto__ = function(input){return goog.object.clone([].__proto__)};
        goog.object.extend(input.__proto__, G.prototype);
    }
    /** @type {goog.array.ArrayLike} */
    var ret = input;
    return ret;
};

/**
 * takes a string like 'tagName[ .className]', '.className' or '#elementId'
 * mod is the element to search from
 *
 * @param {string} input
 * @param {Element|Node=} mod
 * @return {Array.<Element|Node>}
 */
G.elsBySelector = function(input, mod) {
    if(input.charAt(0) == '.') {
        return goog.dom.getElementsByClass(input.substring(1), /** @type {Element} */(mod)) || [];
    }
    if(input.charAt(0) == '#') {
        return [goog.dom.getElement(input)];
    }
    return goog.dom.getElementsByTagNameAndClass(input.replace(/\s.*/,''), input.replace(/.*\./,'')||null, /** @type {Element} */(mod));
};

/**
 */
G.prototype = {
    // Array Functions
    /**
     * @param {Function} fn
     * @param {Object} handler
     * @return {goog.array.ArrayLike}
     */
    each: function(fn, handler) {
        goog.array.forEach(this, fn, handler);
        return this;
    },
    /**
     * @param {Function} fn
     * @param {Object} handler
     * @return {goog.array.ArrayLike}
     */
    filter: function(fn, handler) {
        return G(goog.array.filter(this, fn, handler));
    },
    /**
     * @param {Function} fn
     * @param {Object} handler
     * @return {goog.array.ArrayLike}
     */
    map: function(fn, handler) {
        return G(goog.array.map(this, fn, handler));
    },
    /**
     * @param {number} in
     * @return {?Object}
     */
    get: function(ind) {
        if(ind < 0)
            ind = this.length + ind;
        return this[ind || 0];
    },
    /**
     * @return {?Object}
     */
    first: function(){
        return this.get(0);
    },
    /**
     * @return {?Object}
     */
    last: function(){
        return this.get(-1);
    },
    /**
     * @return {number}
     */
    size: function() {
        return this.length;
    },
    /**
     * @param {goog.array.ArrayLike} arr
     * @return {goog.array.ArrayLike}
     */
    add: function(arr) {
        return G(goog.array.concat(this, arr));
    },
    // DOM functions
    /**
     * @param {string} selector
     * @return {goog.array.ArrayLike}
     */
    find: function(selector) {
        var ret = [];
        this.each(function(el) {
            goog.array.forEach(G.elsBySelector(selector, el),
                function(ele) {
                    goog.array.insert(ret, ele);
                }
            );
        });
        return G(ret);
    },
    /**
     * @param {boolean} bool
     * @return {goog.array.ArrayLike}
     */
    visible: function(bool) {
        return this.each(function(el) {goog.style.showElement(el, bool)});
    },
    /**
     * @return {goog.array.ArrayLike}
     */
    show: function() {
        return this.visible(true);
    },
    /**
     * @return {goog.array.ArrayLike}
     */
    hide: function(bool) {
        return this.visible(false);
    },
    /**
     * @param {string|Object.<string, string>} key
     * @param {string=} val
     * @return {goog.array.ArrayLike}
     */
    attr: function(key, val) {
         if(goog.isString(key) && goog.isDef(val)) {
             var temp = {};
             goog.object.set(temp, key, val);
             key = temp;
         }
         if(goog.isObject(key)) {
             this.each(function(el) {
                 this.setProperties(key);
             })
             return this;
         }
         return this.map(function(el) {return el.getAttribute(key);});
    },
    /**
     * @oaram {string=} key
     * @param {string=} val
     * @return {goog.array.ArrayLike}
     */
    data: function(key, val) {
        return this.attr('data-'+(key||'id'));
    },
    /**
     * @param {string=} val
     * @return {goog.array.ArrayLike}
     */
    val: function(val) {
        if(goog.isDef(val))
            return this.each(function(el) {el.value = val;});
        return this.map(function(el) {return el.value;});
    },
    /**
     * @return {goog.array.ArrayLike}
     */
    empty: function(){
        this.each(goog.dom.removeChildren);
    },
    /**
     * @return {goog.array.ArrayLike}
     */
    next: function() {
        return this.map(function(el) {return el.nextSibling;});
    },
    /**
     * @return {goog.array.ArrayLike}
     */
    prev: function() {
        return this.map(function(el) {return el.previousSibling;});
    },
    /**
     * @param {string=} class
     * @return {goog.array.ArrayLike}
     */
    hasClass: function(class) {
        return this.filter(function(el) {
            goog.dom.classes.has(el, class);});
    },
    /**
     * @param {Element|Node|Function|string=} input
     * @return {goog.array.ArrayLike}
     */
    html: function(input) {
        if(!input)
            return this.get().innerHTML
        if(goog.isFunction(input))
            this.each(input);
        if(input.nodeType)
            this.each(function(el) {goog.dom.append(/** @type {!Node} */(el), input.cloneNode)});
        else
            this.each(function(el) {el.innerHTML = input});
        return this;
    },
    /**
     * @param {Element|Node|Function|string=} input
     * @return {goog.array.ArrayLike}
     */
    text: function(input) {
        if(!input)
            return goog.dom.getTextContent(this.get());
        if(goog.isFunction(input))
            this.each(input);
        else
            this.each(function(el) {goog.dom.setTextContent(el, input)});
        return this;
    },
    // Events
    /**
     * @param {string} eventType
     * @param {Function} fn
     * @param {Object=} handler
     * @param {goog.events=} eventObject
     * @return {goog.array.ArrayLike}
     */
    bind: function(eventType, fn, handler, eventObject) {
        this.each(function(el) {
            if(eventObject)
                eventObject.listen(el, eventType, fn, false, (handler || el));
            else
                goog.events.listen(el, eventType, fn, false, (handler || el));
        });
        return this;
    },
    /**
     * @param {Function} fn
     * @param {Object=} handler
     * @param {goog.events=} eventObject
     * @return {goog.array.ArrayLike}
     */
    click: function(fn, handler, eventObject) {
        return this.bind(goog.events.EventType.CLICK);
    }
}

<|endoftext|>


<commit_before>/*!
 * Copyright 2013 Robert Katić
 * Released under the MIT license
 * https://github.com/rkatic/p/blob/master/LICENSE
 *
 * High-priority-tasks code-portion based on https://github.com/kriskowal/asap
 */
;(function( factory ){
	// CommonJS
	if ( typeof module !== "undefined" && module && module.exports ) {
		module.exports = factory();

	// RequireJS
	} else if ( typeof define === "function" && define.amd ) {
		define( factory );

	// global
	} else {
		P = factory();
	}
})(function() {
	"use strict";

	var
		isNodeJS = ot(typeof process) &&
			({}).toString.call(process) === "[object process]",

		hasSetImmediate = ot(typeof setImmediate),

		head = { f: null, n: null }, tail = head,
		flushing = false,

		requestFlush =
			isNodeJS && requestFlushForNodeJS ||
			makeRequestCallFromMutationObserver( flush ) ||
			makeRequestCallFromTimer( flush ),

		pendingErrors = [],
		requestErrorThrow = makeRequestCallFromTimer( throwFristError ),

		wrapTask,
		asapSafeTask,

		domain,

		call = ot.call,
		apply = ot.apply;

	function ot( type ) {
		return type === "object" || type === "function";
	}

	function throwFristError() {
		if ( pendingErrors.length ) {
			throw pendingErrors.shift();
		}
	}

	function flush() {
		while ( head.n ) {
			head = head.n;
			var f = head.f;
			head.f = null;
			f.call();
		}
		flushing = false;
	}

	var runLater = function( f ) {
		tail = tail.n = { f: f, n: null };
		if ( !flushing ) {
			flushing = true;
			requestFlush();
		}
	};

	function requestFlushForNodeJS() {
		var currentDomain = process.domain;

		if ( currentDomain ) {
			if ( !domain ) domain = (1,require)("domain");
			domain.active = process.domain = null;
		}

		if ( flushing && hasSetImmediate ) {
			setImmediate( flush );

		} else {
			process.nextTick( flush );
		}

		if ( currentDomain ) {
			domain.active = process.domain = currentDomain;
		}
	}

	function makeRequestCallFromMutationObserver( callback ) {
		var observer =
			ot(typeof MutationObserver) ? new MutationObserver( callback ) :
			ot(typeof WebKitMutationObserver) ? new WebKitMutationObserver( callback ) :
			null;

		if ( !observer ) {
			return null;
		}

		var toggle = 1;
		var node = document.createTextNode("");
		observer.observe( node, {characterData: true} );

		return function() {
			toggle = -toggle;
			node.data = toggle;
		};
	}

	function makeRequestCallFromTimer( callback ) {
		return function() {
			var timeoutHandle = setTimeout( handleTimer, 0 );
			var intervalHandle = setInterval( handleTimer, 50 );

			function handleTimer() {
				clearTimeout( timeoutHandle );
				clearInterval( intervalHandle );
				callback();
			}
		};
	}

	if ( isNodeJS ) {
		wrapTask = function( task ) {
			var d = process.domain;

			return function() {
				if ( d ) {
					if ( d._disabled ) return;
					d.enter();
				}

				try {
					task.call();

				} finally {
					requestFlush();
				}

				if ( d ) {
					d.exit();
				}
			};
		};

		asapSafeTask = function( task ) {
			var d = process.domain;
			runLater(!d ? task : function() {
				if ( !d._disabled ) {
					d.enter();
					task.call();
					d.exit();
				}
			});
		}

	} else {
		wrapTask = function( task ) {
			return function() {
				try {
					task.call();

				} catch ( e ) {
					pendingErrors.push( e );
					requestErrorThrow();
				}
			};
		}

		asapSafeTask = runLater;
	}

	function asap( task ) {
		runLater( wrapTask(task) );
	}

	//__________________________________________________________________________

	function forEach( arr, cb ) {
		for ( var i = 0, l = arr.length; i < l; ++i ) {
			if ( i in arr ) {
				cb( arr[i], i );
			}
		}
	}

	function reportError( error ) {
		asap(function() {
			if ( P.onerror ) {
				P.onerror.call( null, error );

			} else {
				throw error;
			}
		});
	}

	var PENDING = 0;
	var FULFILLED = 1;
	var REJECTED = 2;

	function P( x ) {
		return x instanceof Promise ?
			x :
			Resolve( new Promise(), x );
	}

	function Settle( p, state, value, domain ) {
		if ( p._state ) {
			return p;
		}

		p._state = state;
		p._value = value;

		if ( domain ) {
			p._domain = domain;

		} else if ( isNodeJS && state === REJECTED ) {
			p._domain = process.domain;
		}

		if ( p._pending.length ) {
			forEach( p._pending, runLater );
		}
		p._pending = null;

		return p;
	}

	function OnSettled( p, f ) {
		p._pending.push( f );
	}

	function Propagate( p, p2 ) {
		if ( p._state ) {
			Settle( p2, p._state, p._value, p._domain );

		} else {
			OnSettled(p, function() {
				Settle( p2, p._state, p._value, p._domain );
			});
		}
	}

	function Resolve( p, x ) {
		if ( p._state ) {
			return p;
		}

		if ( x instanceof Promise ) {
			if ( x === p ) {
				Settle( p, REJECTED, new TypeError("You can't resolve a promise with itself") );

			} else {
				Propagate( x, p );
			}

		} else if ( x !== Object(x) ) {
			Settle( p, FULFILLED, x );

		} else {
			asapSafeTask(function() {
				var r = resolverFor( p );

				try {
					var then = x.then;

					if ( typeof then === "function" ) {
						call.call( then, x, r.resolve, r.reject );

					} else {
						Settle( p, FULFILLED, x );
					}

				} catch ( e ) {
					r.reject( e );
				}
			}, true);
		}

		return p;
	}

	function resolverFor( promise ) {
		var done = false;

		return {
			promise: promise,

			resolve: function( y ) {
				if ( !done ) {
					done = true;
					Resolve( promise, y );
				}
			},

			reject: function( reason ) {
				if ( !done ) {
					done = true;
					Settle( promise, REJECTED, reason );
				}
			}
		};
	}

	P.defer = defer;
	function defer() {
		return resolverFor( new Promise() );
	}

	P.reject = reject;
	function reject( reason ) {
		return Settle( new Promise(), REJECTED, reason );
	}

	function Promise() {
		this._state = 0;
		this._value = void 0;
		this._domain = null;
		this._pending = [];
	}

	Promise.prototype.then = function( onFulfilled, onRejected ) {
		var cb = typeof onFulfilled === "function" ? onFulfilled : null;
		var eb = typeof onRejected === "function" ? onRejected : null;

		var p = this;
		var p2 = new Promise();

		if ( cb || eb ) {
			var thenDomain = isNodeJS && process.domain;

			if ( p._state === PENDING ) {
				OnSettled( p, onSettled );

			} else {
				runLater( onSettled );
			}

		} else {
			Propagate( p, p2 );
		}

		function onSettled() {
			var func = p._state === FULFILLED ? cb : eb;
			var x, catched = false;
			var d = p._domain || thenDomain;

			if ( d ) {
				if ( d._disabled ) return;
				d.enter();
			}

			try {
				x = func( p._value );

			} catch ( e ) {
				catched = true;
				Settle( p2, REJECTED, e );
			}

			if ( !catched ) {
				Resolve( p2, x );
			}
		}

		return p2;
	};

	Promise.prototype.done = function( cb, eb ) {
		var p = this;

		if ( cb || eb ) {
			p = p.then( cb, eb );
		}

		p.then( null, reportError );
	};

	Promise.prototype.fail = function( eb ) {
		return this.then( null, eb );
	};

	Promise.prototype.spread = function( cb, eb ) {
		return this.then(cb && function( array ) {
			return all( array, [] ).then(function( values ) {
				return apply.call( cb, void 0, values );
			}, eb);
		}, eb);
	};

	Promise.prototype.timeout = function( ms, msg ) {
		var p = this;
		var p2 = new Promise();

		if ( p._state !== PENDING ) {
			Propagate( p, p2 );

		} else {
			var timeoutId = setTimeout(function() {
				Settle( p2, REJECTED,
					new Error(msg || "Timed out after " + ms + " ms") );
			}, ms);

			OnSettled(p, function() {
				clearTimeout( timeoutId );
				Propagate( p, p2 );
			});
		}

		return p2;
	};

	Promise.prototype.delay = function( ms ) {
		var d = defer();

		this.then(function( value ) {
			setTimeout(function() {
				d.resolve( value );
			}, ms);
		}, d.reject);

		return d.promise;
	};

	Promise.prototype.inspect = function() {
		switch ( this._state ) {
			case PENDING:   return { state: "pending" };
			case FULFILLED: return { state: "fulfilled", value: this._value };
			case REJECTED:  return { state: "rejected", reason: this._value };
			default: throw new TypeError("invalid state");
		}
	};

	function valuesHandler( f ) {
		function onFulfilled( values ) {
			return f( values, [] );
		}

		function handleValues( values ) {
			return P( values ).then( onFulfilled );
		}

		handleValues._ = f;
		return handleValues;
	}

	P.allSettled = valuesHandler( allSettled );
	function allSettled( input, output ) {
		var waiting = 0;
		var promise = new Promise();
		forEach( input, function( x, index ) {
			var p = P( x );
			if ( p._state === PENDING ) {
				++waiting;
				OnSettled(p, function() {
					output[ index ] = p.inspect();
					if ( --waiting === 0 ) {
						Settle( promise, FULFILLED, output );
					}
				});
			} else {
				output[ index ] = p.inspect();
			}
		});
		if ( waiting === 0 ) {
			Settle( promise, FULFILLED, output );
		}
		return promise;
	}

	P.all = valuesHandler( all );
	function all( input, output ) {
		var waiting = 0;
		var d = defer();
		forEach( input, function( x, index ) {
			var p = P( x );
			if ( p._state === FULFILLED ) {
				output[ index ] = p._value;

			} else {
				++waiting;
				p.then(function( value ) {
					output[ index ] = value;
					if ( --waiting === 0 ) {
						d.resolve( output );
					}
				}, d.reject);
			}
		});
		if ( waiting === 0 ) {
			d.resolve( output );
		}
		return d.promise;
	}

	P.promised = promised;
	function promised( f ) {
		function onFulfilled( thisAndArgs ) {
			return apply.apply( f, thisAndArgs );
		}

		return function() {
			var allArgs = all( arguments, [] );
			return all( [this, allArgs], [] ).then( onFulfilled );
		};
	}

	P.onerror = null;

	return P;
});
<commit_msg>exit entered domain in .then<commit_after>/*!
 * Copyright 2013 Robert Katić
 * Released under the MIT license
 * https://github.com/rkatic/p/blob/master/LICENSE
 *
 * High-priority-tasks code-portion based on https://github.com/kriskowal/asap
 */
;(function( factory ){
	// CommonJS
	if ( typeof module !== "undefined" && module && module.exports ) {
		module.exports = factory();

	// RequireJS
	} else if ( typeof define === "function" && define.amd ) {
		define( factory );

	// global
	} else {
		P = factory();
	}
})(function() {
	"use strict";

	var
		isNodeJS = ot(typeof process) &&
			({}).toString.call(process) === "[object process]",

		hasSetImmediate = ot(typeof setImmediate),

		head = { f: null, n: null }, tail = head,
		flushing = false,

		requestFlush =
			isNodeJS && requestFlushForNodeJS ||
			makeRequestCallFromMutationObserver( flush ) ||
			makeRequestCallFromTimer( flush ),

		pendingErrors = [],
		requestErrorThrow = makeRequestCallFromTimer( throwFristError ),

		wrapTask,
		asapSafeTask,

		domain,

		call = ot.call,
		apply = ot.apply;

	function ot( type ) {
		return type === "object" || type === "function";
	}

	function throwFristError() {
		if ( pendingErrors.length ) {
			throw pendingErrors.shift();
		}
	}

	function flush() {
		while ( head.n ) {
			head = head.n;
			var f = head.f;
			head.f = null;
			f.call();
		}
		flushing = false;
	}

	var runLater = function( f ) {
		tail = tail.n = { f: f, n: null };
		if ( !flushing ) {
			flushing = true;
			requestFlush();
		}
	};

	function requestFlushForNodeJS() {
		var currentDomain = process.domain;

		if ( currentDomain ) {
			if ( !domain ) domain = (1,require)("domain");
			domain.active = process.domain = null;
		}

		if ( flushing && hasSetImmediate ) {
			setImmediate( flush );

		} else {
			process.nextTick( flush );
		}

		if ( currentDomain ) {
			domain.active = process.domain = currentDomain;
		}
	}

	function makeRequestCallFromMutationObserver( callback ) {
		var observer =
			ot(typeof MutationObserver) ? new MutationObserver( callback ) :
			ot(typeof WebKitMutationObserver) ? new WebKitMutationObserver( callback ) :
			null;

		if ( !observer ) {
			return null;
		}

		var toggle = 1;
		var node = document.createTextNode("");
		observer.observe( node, {characterData: true} );

		return function() {
			toggle = -toggle;
			node.data = toggle;
		};
	}

	function makeRequestCallFromTimer( callback ) {
		return function() {
			var timeoutHandle = setTimeout( handleTimer, 0 );
			var intervalHandle = setInterval( handleTimer, 50 );

			function handleTimer() {
				clearTimeout( timeoutHandle );
				clearInterval( intervalHandle );
				callback();
			}
		};
	}

	if ( isNodeJS ) {
		wrapTask = function( task ) {
			var d = process.domain;

			return function() {
				if ( d ) {
					if ( d._disabled ) return;
					d.enter();
				}

				try {
					task.call();

				} finally {
					requestFlush();
				}

				if ( d ) {
					d.exit();
				}
			};
		};

		asapSafeTask = function( task ) {
			var d = process.domain;
			runLater(!d ? task : function() {
				if ( !d._disabled ) {
					d.enter();
					task.call();
					d.exit();
				}
			});
		}

	} else {
		wrapTask = function( task ) {
			return function() {
				try {
					task.call();

				} catch ( e ) {
					pendingErrors.push( e );
					requestErrorThrow();
				}
			};
		}

		asapSafeTask = runLater;
	}

	function asap( task ) {
		runLater( wrapTask(task) );
	}

	//__________________________________________________________________________

	function forEach( arr, cb ) {
		for ( var i = 0, l = arr.length; i < l; ++i ) {
			if ( i in arr ) {
				cb( arr[i], i );
			}
		}
	}

	function reportError( error ) {
		asap(function() {
			if ( P.onerror ) {
				P.onerror.call( null, error );

			} else {
				throw error;
			}
		});
	}

	var PENDING = 0;
	var FULFILLED = 1;
	var REJECTED = 2;

	function P( x ) {
		return x instanceof Promise ?
			x :
			Resolve( new Promise(), x );
	}

	function Settle( p, state, value, domain ) {
		if ( p._state ) {
			return p;
		}

		p._state = state;
		p._value = value;

		if ( domain ) {
			p._domain = domain;

		} else if ( isNodeJS && state === REJECTED ) {
			p._domain = process.domain;
		}

		if ( p._pending.length ) {
			forEach( p._pending, runLater );
		}
		p._pending = null;

		return p;
	}

	function OnSettled( p, f ) {
		p._pending.push( f );
	}

	function Propagate( p, p2 ) {
		if ( p._state ) {
			Settle( p2, p._state, p._value, p._domain );

		} else {
			OnSettled(p, function() {
				Settle( p2, p._state, p._value, p._domain );
			});
		}
	}

	function Resolve( p, x ) {
		if ( p._state ) {
			return p;
		}

		if ( x instanceof Promise ) {
			if ( x === p ) {
				Settle( p, REJECTED, new TypeError("You can't resolve a promise with itself") );

			} else {
				Propagate( x, p );
			}

		} else if ( x !== Object(x) ) {
			Settle( p, FULFILLED, x );

		} else {
			asapSafeTask(function() {
				var r = resolverFor( p );

				try {
					var then = x.then;

					if ( typeof then === "function" ) {
						call.call( then, x, r.resolve, r.reject );

					} else {
						Settle( p, FULFILLED, x );
					}

				} catch ( e ) {
					r.reject( e );
				}
			}, true);
		}

		return p;
	}

	function resolverFor( promise ) {
		var done = false;

		return {
			promise: promise,

			resolve: function( y ) {
				if ( !done ) {
					done = true;
					Resolve( promise, y );
				}
			},

			reject: function( reason ) {
				if ( !done ) {
					done = true;
					Settle( promise, REJECTED, reason );
				}
			}
		};
	}

	P.defer = defer;
	function defer() {
		return resolverFor( new Promise() );
	}

	P.reject = reject;
	function reject( reason ) {
		return Settle( new Promise(), REJECTED, reason );
	}

	function Promise() {
		this._state = 0;
		this._value = void 0;
		this._domain = null;
		this._pending = [];
	}

	Promise.prototype.then = function( onFulfilled, onRejected ) {
		var cb = typeof onFulfilled === "function" ? onFulfilled : null;
		var eb = typeof onRejected === "function" ? onRejected : null;

		var p = this;
		var p2 = new Promise();

		if ( cb || eb ) {
			var thenDomain = isNodeJS && process.domain;

			if ( p._state === PENDING ) {
				OnSettled( p, onSettled );

			} else {
				runLater( onSettled );
			}

		} else {
			Propagate( p, p2 );
		}

		function onSettled() {
			var func = p._state === FULFILLED ? cb : eb;
			var x, catched = false;
			var d = p._domain || thenDomain;

			if ( d ) {
				if ( d._disabled ) return;
				d.enter();
			}

			try {
				x = func( p._value );

			} catch ( e ) {
				catched = true;
				Settle( p2, REJECTED, e );
			}

			if ( !catched ) {
				Resolve( p2, x );
			}

			if ( d ) {
				d.exit();
			}
		}

		return p2;
	};

	Promise.prototype.done = function( cb, eb ) {
		var p = this;

		if ( cb || eb ) {
			p = p.then( cb, eb );
		}

		p.then( null, reportError );
	};

	Promise.prototype.fail = function( eb ) {
		return this.then( null, eb );
	};

	Promise.prototype.spread = function( cb, eb ) {
		return this.then(cb && function( array ) {
			return all( array, [] ).then(function( values ) {
				return apply.call( cb, void 0, values );
			}, eb);
		}, eb);
	};

	Promise.prototype.timeout = function( ms, msg ) {
		var p = this;
		var p2 = new Promise();

		if ( p._state !== PENDING ) {
			Propagate( p, p2 );

		} else {
			var timeoutId = setTimeout(function() {
				Settle( p2, REJECTED,
					new Error(msg || "Timed out after " + ms + " ms") );
			}, ms);

			OnSettled(p, function() {
				clearTimeout( timeoutId );
				Propagate( p, p2 );
			});
		}

		return p2;
	};

	Promise.prototype.delay = function( ms ) {
		var d = defer();

		this.then(function( value ) {
			setTimeout(function() {
				d.resolve( value );
			}, ms);
		}, d.reject);

		return d.promise;
	};

	Promise.prototype.inspect = function() {
		switch ( this._state ) {
			case PENDING:   return { state: "pending" };
			case FULFILLED: return { state: "fulfilled", value: this._value };
			case REJECTED:  return { state: "rejected", reason: this._value };
			default: throw new TypeError("invalid state");
		}
	};

	function valuesHandler( f ) {
		function onFulfilled( values ) {
			return f( values, [] );
		}

		function handleValues( values ) {
			return P( values ).then( onFulfilled );
		}

		handleValues._ = f;
		return handleValues;
	}

	P.allSettled = valuesHandler( allSettled );
	function allSettled( input, output ) {
		var waiting = 0;
		var promise = new Promise();
		forEach( input, function( x, index ) {
			var p = P( x );
			if ( p._state === PENDING ) {
				++waiting;
				OnSettled(p, function() {
					output[ index ] = p.inspect();
					if ( --waiting === 0 ) {
						Settle( promise, FULFILLED, output );
					}
				});
			} else {
				output[ index ] = p.inspect();
			}
		});
		if ( waiting === 0 ) {
			Settle( promise, FULFILLED, output );
		}
		return promise;
	}

	P.all = valuesHandler( all );
	function all( input, output ) {
		var waiting = 0;
		var d = defer();
		forEach( input, function( x, index ) {
			var p = P( x );
			if ( p._state === FULFILLED ) {
				output[ index ] = p._value;

			} else {
				++waiting;
				p.then(function( value ) {
					output[ index ] = value;
					if ( --waiting === 0 ) {
						d.resolve( output );
					}
				}, d.reject);
			}
		});
		if ( waiting === 0 ) {
			d.resolve( output );
		}
		return d.promise;
	}

	P.promised = promised;
	function promised( f ) {
		function onFulfilled( thisAndArgs ) {
			return apply.apply( f, thisAndArgs );
		}

		return function() {
			var allArgs = all( arguments, [] );
			return all( [this, allArgs], [] ).then( onFulfilled );
		};
	}

	P.onerror = null;

	return P;
});
<|endoftext|>


<commit_before>function strongEval(str,globalObj) {
	var obj={};
	for (var index in window) {//overwrite all of the items in the global namespace (not actually changing them)
		obj[index]=undefined;
	}
	for (var index in globalObj) {//copy over properties from passed object
		if (globalObj.hasOwnProperty(index)) obj[index]=globalObj[index];
	}
	str=str.replace(/{\s*$/,"").replace(/^[^{]*}/,"");
	console.log(str);
	return (new Function("window","with(window) {"+str+"}"))(obj);
};
function Ai(name) {
	this.createTime=new Date();
	this.name=name;
	//this.reflexes=Ai.reflexes;
	this.memory=[];
	this.definitions=[];
	this.starts={};
};
Ai.prototype.reactTo=function(input) {
	this.memory.push({//create new memory
		input:input,
		memNum:this.memory.length,
		action:"",
	});
	//define things said by the user
	for (var i in input) {
		i=i-0;
		var str=input.substr(i,4);
		if (str.length<4) {
			continue;//skip the last 3
		}
		if (this.memory.length)
		var defHas=false;
		for (var ii in this.definitions) {
			if (this.definitions[ii].str==str) {
				//update context
				this.definitions[ii].context.push(this.memory.length<2?"":this.memory[this.memory.length-2].action);
				this.definitions[ii].before.push(input.substring(i-4<0?0:i-4,i));
				this.definitions[ii].after.push(input.substring(i+4,i+8>input.length?input.length:i+8));
				defHas=true;
			}
		}
		if (defHas===false) {//if not in list
			this.definitions.push({
				str:str,
				context:[this.memory.length<2?"":this.memory[this.memory.length-2].action.substring(0,4)],
				before:[input.substring(i-4<0?0:i-4,i)],
				after:[input.substring(i+4,i+8>input.length?input.length:i+8)],
			});
		}
	}
	//run reflexes
	for (var i in this.reflexes) {
		this.reflexes[i].apply(this,[this.memory[this.memory.length-1]]);//bot changes a little
	}
	//take the average of all the actions(using context)

	//act
	//customParse(this.memory[this.memory.length-1].action,aiFunctions)
};
Ai.prototype.reflexes=[//reflexes are called in numerical order
	function(memory) {//start possable action using context, then use after( along with context) to finish the action
		var newActions="";
		for (var i in this.definitions) {
			for (var ii in this.definitions[i].context) {
				if (this.definitions[i].context[ii]==memory.input.substring(0,4)) {//if this definition has something to do with what should(in the bot's eyes) be done
					console.log("is related at: this.definitions[",i,"].context[",ii,"]")
				}else{
					//console.log("not related at: this.definitions[",i,"].context[",ii,"]")
				}
			}
		}
		this.memory.action+=newActions;
	},
];
<commit_msg>Reset Ai constructor<commit_after>function strongEval(str,globalObj) {
	var obj={};
	for (var index in window) {//overwrite all of the items in the global namespace (not actually changing them)
		obj[index]=undefined;
	}
	for (var index in globalObj) {//copy over properties from passed object
		if (globalObj.hasOwnProperty(index)) obj[index]=globalObj[index];
	}
	str=str.replace(/{\s*$/,"").replace(/^[^{]*}/,"");
	console.log(str);
	return (new Function("window","with(window) {"+str+"}"))(obj);
};
function Ai(name,file/*opt*/) {
};
<|endoftext|>


<commit_before>/*!
 *  browser-detect  version: 0.1
 *  Browser vendor, touch and dpi checking upon loading.
 *  http://github.com/everydaycreative/browser-detect
 *  Authors: @everydaycreative (everydaycreative.ca)
 *  Copyright 2013 Everyday Creative. LGPL licensed.
 */
(function($jquery, win, prefix){
	var copy_feature_tree = function(new_tree, tree){
		for(var x in tree){
			try{
				if(tree[x].constructor === Object){
					new_tree[x] = {};
					copy_feature_tree(new_tree[x], tree[x])
				}else{
					new_tree[x] = tree[x];
				}
			}catch(error){
				new_tree[x] = tree[x]
			}
		}
	};

	var reset_feature_tree = function(tree){
		for(var x in tree){
			try{
				if(tree[x].constructor === Object){
					reset_feature_tree(tree[x]);
				}else{
					tree[x] = false;
				}

			}catch (error){
				tree[x] = false;
			}
		}

	};

	var classname_from_feature = function(feature, state){
		if(feature.constructor === Array){
			if(state){
				return feature[0];
			}else{
				if(feature.length == 2){
					return feature[1];
				}
			}
		}
		return "";
	};

	var evaluate_features = function(states, features){
		var k;
		for(var x in states){
			try{
				if(states[x].constructor === Object){
					evaluate_features(states[x], features[x]);
				}else{
					k = classname_from_feature(features[x], states[x]);
					if(k != "")
						classes.push(k);
				}
			}catch(error){
				k = classname_from_feature(features[x], states[x]);
				if(k != "")
					classes.push(k);
			}
		}
	};

	var classes = classes_init = ['bdjs'];
	var tests = [];
	var feature_detect = {
		"touch": ['touch', 'no-touch'],
		"jq": ['jq', 'no-jq'],
		"devices":{
			"mobile": ['mobile', 'no-mobile'],
			"phone": ['phone'],
			"tablet": ['tablet'],
			"iphone": ['iphone'],
			"ipad": ['ipad'],
			"other": false
		},
		"dpi":{
			"report": false,	//can't report
			"ldpi": ['ldpi'],	//0.75
			"mdpi": ['mdpi'],	//1.0
			"hdpi": ['hdpi'],	//1.5
			"xhdpi":['xhdpi'] 	//2.0
			/*
				it reports
				xhdpi for 2.0 pixel ratios
				hdpi

				if there is no report no-dpi
			*/
		},
		"os":{
			"android": ['android'],
			"blackberry": ['blackberry'],
			"ios": ['ios'],
			"linux": ['linux'],
			"mac": ['mac'],
			"win": ['win'],
			"winmobile": ['winmobile'],
			"winphone": ['winphone'],
			"x11": ['x11']
		},
		"browser":{
			"version": "",
			"safari": ['safari'],
			"safari_mobile": ['safari-mobile'],
			"firefox": ['ff'],
			"chrome": ['chrome'],
			"ie": ['ie'],
			"ie_mobile": ['ie-mobile'],
			"opera": ['opera']
		},

		"bengine":{
			"opera": ['presto'],
			"webkit": ['webkit'],
			"gecko": ['gecko'],
			"trident": ['trident']
		}
	};

	var browser_detect = {
		"test": null,
		"report": null,
		"detect": feature_detect,
		"run": null
	};
	win['bdjs'] = browser_detect;
	var bdetect = feature_detect;
	var features = {};
	copy_feature_tree(features, feature_detect);
	reset_feature_tree(feature_detect);

	var test = function(){
		for(var k = 0; k < tests.length;k++){
			tests[k]();
		}
	};

	var report = function(pprefix){
		var dom = document.documentElement;
		var cint = classes_init;
		var prefix_label = (pprefix == true)?"bdjs-":"";
		//reset the registered classes
		//for a fresh report.
		classes = cint.slice(0);
		//evaluate features-tree based on states-tree
		evaluate_features(feature_detect, features);
		//process existing classnames, remove no-js if it
		//exists
		var csn = dom.className.split(" ");
		var leftovers = "";
		var c,v=[];

		//prefix if necessary
		if(pprefix){
			for(var x = 0; x < classes.length; x++){
				c = classes[x];
		 		c = (cint.indexOf(c) == -1)?prefix_label+c:c;
		 		v.push(c);
			}
			classes = v;
		}

		//now combine the rest, and deduplicate
		for(var x = 0; x < classes.length; x++){
			c = classes[x];
			if(csn.indexOf(c) == -1){
				csn.push(c);
			}
		}

		var end_result = [];
		for(var x = 0; x < csn.length; x++){
			c = csn[x];
			if(c != "no-js" && c != ""){
				end_result.push(c);
			}
		}
		leftovers = end_result.join(" ");
		dom.className = leftovers;
	};

	var run = function(_prefix){
		test();
		report(_prefix);
	};

	browser_detect['report'] = report;
	browser_detect['run'] = run;
	browser_detect['test'] = test;

	var detect_touch = function(){
		//http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
		if('ontouchstart' in window || 'onmsgesturechange' in window){
			bdetect['touch'] = true;
		}
	};
	tests.push(detect_touch);

	var detect_dpi = function(){
		var odpi = bdetect['dpi'];
		if('devicePixelRatio' in window){
			//console.log("devicePixelRatio registered");
			odpi['report'] = true;
			var dpi = parseFloat(window.devicePixelRatio);
			if(dpi < 1.0){
				odpi['ldpi'] = true;
			}else if(dpi >= 1.0 && dpi < 1.5){
				odpi['mdpi'] = true;
			}else if(dpi >= 1.5 && dpi < 2.0){
				odpi['hdpi'] = true;
			}else if(dpi >= 2.0){
				odpi['xhdpi'] = true;
			}
		}
	};
	tests.push(detect_dpi);

	var detect_jquery = function(){
		//check for jQuery
		if($jquery){
			bdetect['jq'] = true;
		}
	};
	tests.push(detect_jquery);

	var regex_collection = function(){
		this.collection = [];
		this.add = function(p){
			this.collection.push(p);
		}
		this.test = function(s){
			var res = false, c = this.collection;
			for(var k = 0; k < c.length; k++){
				res = res || c[k].test(s);
			}
			return res;
		};
	};

	var recombine = function(rexp, collection){
		if(!(collection == null || collection === undefined)){
			collection.add(rexp);
		}
		return rexp;
	};
	var detect_device_type = function(){
		var ua = navigator.userAgent;
		var devices = bdetect['devices'];
		var os = bdetect['os'];

		var mobile_regexp = new regex_collection();
		var test_tablet = recombine(/(?:Tablet)/i, mobile_regexp);
		var test_phone = recombine(/(?:Phone)/i, mobile_regexp);
		var test_mobile_str = recombine(/(?:Mobile)/i, mobile_regexp);

		//run the check for android
		var test_android = recombine(/(?:Android)/i, mobile_regexp);
		var test_linux = /(?:Linux)/i;
		var test_iphone = recombine(/(?:iPhone)/i, mobile_regexp);
		var test_ipad = recombine(/(?:iPad)/i, mobile_regexp);
		var test_ipod = recombine(/(?:iPod)/i, mobile_regexp);

		//var test_IEMobile = recombine(/(?:IEMobile)/i, mobile_regexp);
		//var test_arm = /(?:ARM)/i;
		var test_windows = /(?:Windows NT)/i;
		var test_windowsphone = recombine(/(?:Windows Phone)/i, mobile_regexp);
		var test_windowsmobile = recombine(/(?:Windows Mobile)/i, mobile_regexp);

		var test_blackberry = recombine(/(?:BlackBerry)/i, mobile_regexp);
		var test_bbplaybook = recombine(/(?:Playbook)|(?:RIM Tablet)/i, mobile_regexp);

		var test_macos = /(?:Mac Os X)/i;
		var test_macintosh = /(?:Macintosh)/i;

		var test_x11 = /(?:x11)/i;

		//test mobile
		//var mobile_regexp = /(?:iPhone)|(?:iPad)|(?:Android)|(?:Windows Phone)|(?:Playbook)|(?:Windows Mobile)|(?:BlackBerry)|(?:Mobile)|(?:tablet)|(?:phone)/i;
		var mobile = mobile_regexp.test(ua);
		if(mobile){
			devices['mobile'] = true;
		}

		//detect device and os
		//and also only report OS here,
		//not the device family because the device family
		//might change else where
		if(test_android.test(ua)){
			os['android'] = true;
			classes.push('android');
			//check if the user-agent string contains 'mobile'
			if(test_mobile_str.test(ua)){
				//then it is a phone
				devices['phone'] = true;
			}else{
				//if no mobile string is present, it's a tablet
				devices['tablet'] = true;
			}
		}else if(test_linux.test(ua) && !test_android.test(ua))
		{
			os['linux'] = true;
		}else if(test_iphone.test(ua) || test_ipod.test(ua))
		{
			//iphone or ipod
			os['ios'] = true;
			devices['phone'] = true;
			devices['iphone'] = true;

		}else if(test_ipad.test(ua)){
			//ipad
			os['ios'] = true;
			devices['tablet'] = true;
			devices['ipad'] = true;
		}else if(test_blackberry.test(ua)){
			//black berry
			os['blackberry'] = true;
			devices['phone'] = true;
		}else if(test_bbplaybook.test(ua)){
			//playbook
			os['blackberry'] = true;
			devices['tablet'] = true;
		}else if(test_windows.test(ua) && !test_windowsmobile.test(ua) && !test_windowsphone.test(ua)){
			os['win'] = true;
			if(mobile){
				if(test_phone.test(ua)){
					devices['phone'] = true;
				}else if(test_tablet.test(ua)){
					devices['tablet'] = true;
				}
			}
		}else if(test_windowsmobile.test(ua)){
			os['winmobile'] = true;
		}else if(test_windowsphone.test(ua)){
			os['winphone'] = true;
			if(test_tablet.test(ua)){
				devices['tablet'] = true;
			}else{
				devices['phone'] = true;
			}
		}else if(!test_iphone.test(ua) && !test_ipad.test(ua) && test_macos.test(ua) && test_macintosh.test(ua)){
			os['mac'] = true;
		}else if(test_x11.test(ua) && !test_blackberry.test(ua)){
			os['x11'] = true;
		}

		//test if the UA is reporting tablet or phone,
		//and overright the conditions above
		//if and only we didn't already registered it the
		//opposite
		if(test_tablet.test(ua) && !devices['phone']){
			devices['tablet'] = true;
		}else if(test_phone.test(ua) && !devices['tablet']){
			devices['phone'] = true;
		}
	}
	tests.push(detect_device_type);

	var detect_browser = function(){

	}
	tests.push(detect_browser);
	run(prefix);

})(this.jQuery, window, false); //(typeof jQuery=="undefined"?undefined:jQuery)<commit_msg>[MAJOR] Tested bdjs.js for<commit_after>/*!
 *  Browser-Detect  version: 0.5
 *  Reports browser vendor, touch and dpi checking upon loading.
 *  http://github.com/everydaycreative/browser-detect
 *  Authors: @everydaycreative (everydaycreative.ca)
 *  Copyright 2013 Everyday Creative, unless stated otherwise.
 *  LGPL licensed.
 */
(function($jquery, win, prefix){
	var copy_feature_tree = function(new_tree, tree){
		for(var x in tree){
			try{
				if(tree[x].constructor === Object){
					new_tree[x] = {};
					copy_feature_tree(new_tree[x], tree[x])
				}else{
					new_tree[x] = tree[x];
				}
			}catch(error){
				new_tree[x] = tree[x]
			}
		}
	};

	var reset_feature_tree = function(tree){
		for(var x in tree){
			try{
				if(tree[x].constructor === Object){
					reset_feature_tree(tree[x]);
				}else{
					tree[x] = false;
				}

			}catch (error){
				tree[x] = false;
			}
		}

	};

	var classname_from_feature = function(feature, state){
		if(feature.constructor === Array){
			if(state){
				return feature[0];
			}else{
				if(feature.length == 2){
					return feature[1];
				}
			}
		}
		return "";
	};

	var evaluate_features = function(states, features){
		var k;
		for(var x in states){
			try{
				if(states[x].constructor === Object){
					evaluate_features(states[x], features[x]);
				}else{
					k = classname_from_feature(features[x], states[x]);
					if(k != "")
						classes.push(k);
				}
			}catch(error){
				k = classname_from_feature(features[x], states[x]);
				if(k != "")
					classes.push(k);
			}
		}
	};

	var classes = classes_init = ['bdjs'];
	var tests = [];
	var feature_detect = {
		"touch": ['touch', 'no-touch'],
		"jq": ['jq', 'no-jq'],
		"devices":{
			"mobile": ['mobile', 'no-mobile'],
			"phone": ['phone'],
			"tablet": ['tablet'],
			"iphone": ['iphone'],
			"ipad": ['ipad'],
			"other": false
		},
		"dpi":{
			"report": false,	//can't report
			"ldpi": ['ldpi'],	//0.75
			"mdpi": ['mdpi'],	//1.0
			"hdpi": ['hdpi'],	//1.5
			"xhdpi":['xhdpi'] 	//2.0
			/*
				it reports
				xhdpi for 2.0 pixel ratios
				hdpi

				if there is no report no-dpi
			*/
		},
		/*
			implementing the definition
			provided by android guide lines
			are not useful. The small, medium, large
			and xlarge classes for screens correspond
			basically to the land of mobile devices
			with physical screen sizes of less than 10
			inches.
		"screen":{
			"report": false,
			"small": ['small-screen'],
			"medium": ['medium-screen'],
			"large": ['large-screen'],
			"xlarge": ['xlarge-screen']
		}, */
		"os":{
			"android": ['android'],
			"blackberry": ['blackberry'],
			"ios": ['ios'],
			"linux": ['linux'],
			"mac": ['mac'],
			"win": ['win'],
			"winmobile": ['winmobile'],
			"winphone": ['winphone'],
			"x11": ['x11']
		},
		"browser":{
			"version": "",
			"safari": ['safari'],
			"safari_mobile": ['safari-mobile'],
			"firefox": ['ff'],
			"chrome": ['chrome'],
			"ie": ['ie'],
			"ie_mobile": ['ie-mobile'],
			"opera": ['opera']
		},

		"bengine":{
			"opera": ['presto'],
			"webkit": ['webkit'],
			"gecko": ['gecko'],
			"trident": ['trident']
		}
	};

	var browser_detect = {
		"test": null,
		"report": null,
		"detect": feature_detect,
		"run": null
	};
	win['bdjs'] = browser_detect;
	var bdetect = feature_detect;
	var features = {};
	copy_feature_tree(features, feature_detect);
	reset_feature_tree(feature_detect);

	var test = function(){
		for(var k = 0; k < tests.length;k++){
			tests[k]();
		}
	};

	var report = function(pprefix){
		var dom = document.documentElement;
		var cint = classes_init;
		var prefix_label = (pprefix == true)?"bdjs-":"";
		//reset the registered classes
		//for a fresh report.
		classes = cint.slice(0);
		//evaluate features-tree based on states-tree
		evaluate_features(feature_detect, features);
		//process existing classnames, remove no-js if it
		//exists
		var csn = dom.className.split(" ");
		var leftovers = "";
		var c,v=[];

		//prefix if necessary
		if(pprefix){
			for(var x = 0; x < classes.length; x++){
				c = classes[x];
		 		c = (cint.indexOf(c) == -1)?prefix_label+c:c;
		 		v.push(c);
			}
			classes = v;
		}

		//now combine the rest, and deduplicate
		for(var x = 0; x < classes.length; x++){
			c = classes[x];
			if(csn.indexOf(c) == -1){
				csn.push(c);
			}
		}

		var end_result = [];
		for(var x = 0; x < csn.length; x++){
			c = csn[x];
			if(c != "no-js" && c != ""){
				end_result.push(c);
			}
		}
		leftovers = end_result.join(" ");
		dom.className = leftovers;
	};

	var run = function(_prefix){
		test();
		report(_prefix);
	};

	browser_detect['report'] = report;
	browser_detect['run'] = run;
	browser_detect['test'] = test;

	var detect_touch = function(){
		//@Author: http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
		if('ontouchstart' in window || 'onmsgesturechange' in window){
			bdetect['touch'] = true;
		}
	};
	tests.push(detect_touch);

	var detect_dpi = function(){
		var odpi = bdetect['dpi'];
		if('devicePixelRatio' in window){
			//console.log("devicePixelRatio registered");
			odpi['report'] = true;
			var dpi = parseFloat(window.devicePixelRatio);
			if(dpi < 1.0){
				odpi['ldpi'] = true;
			}else if(dpi >= 1.0 && dpi < 1.5){
				odpi['mdpi'] = true;
			}else if(dpi >= 1.5 && dpi < 2.0){
				odpi['hdpi'] = true;
			}else if(dpi >= 2.0){
				odpi['xhdpi'] = true;
			}
		}
	};
	tests.push(detect_dpi);

	var detect_jquery = function(){
		//check for jQuery
		if($jquery){
			bdetect['jq'] = true;
		}
	};
	tests.push(detect_jquery);

	var regex_collection = function(){
		this.collection = [];
		this.add = function(p){
			this.collection.push(p);
		}
		this.test = function(s){
			var res = false, c = this.collection;
			for(var k = 0; k < c.length; k++){
				res = res || c[k].test(s);
			}
			return res;
		};
	};

	var recombine = function(rexp, collection){
		if(!(collection == null || collection === undefined)){
			collection.add(rexp);
		}
		return rexp;
	};
	var detect_device_type = function(){
		var ua = navigator.userAgent;
		var devices = bdetect['devices'];
		var os = bdetect['os'];

		var mobile_regexp = new regex_collection();
		var test_tablet = recombine(/(?:Tablet)/i, mobile_regexp);
		var test_phone = recombine(/(?:Phone)/i, mobile_regexp);
		var test_mobile_str = recombine(/(?:Mobile)/i, mobile_regexp);

		//run the check for android
		var test_android = recombine(/(?:Android)/i, mobile_regexp);
		var test_linux = /(?:Linux)/i;
		var test_iphone = recombine(/(?:iPhone)/i, mobile_regexp);
		var test_ipad = recombine(/(?:iPad)/i, mobile_regexp);
		var test_ipod = recombine(/(?:iPod)/i, mobile_regexp);

		//var test_IEMobile = recombine(/(?:IEMobile)/i, mobile_regexp);
		//var test_arm = /(?:ARM)/i;
		var test_windows = /(?:Windows NT)/i;
		var test_windowsphone = recombine(/(?:Windows Phone)/i, mobile_regexp);
		var test_windowsmobile = recombine(/(?:Windows Mobile)/i, mobile_regexp);

		var test_blackberry = recombine(/(?:BlackBerry)/i, mobile_regexp);
		var test_bbplaybook = recombine(/(?:Playbook)|(?:RIM Tablet)/i, mobile_regexp);

		var test_macos = /(?:Mac Os X)/i;
		var test_macintosh = /(?:Macintosh)/i;

		var test_x11 = /(?:x11)/i;

		//test mobile
		//var mobile_regexp = /(?:iPhone)|(?:iPad)|(?:Android)|(?:Windows Phone)|(?:Playbook)|(?:Windows Mobile)|(?:BlackBerry)|(?:Mobile)|(?:tablet)|(?:phone)/i;
		var mobile = mobile_regexp.test(ua);
		if(mobile){
			devices['mobile'] = true;
		}

		//detect device and os
		//and also only report OS here,
		//not the device family because the device family
		//might change else where
		if(test_android.test(ua)){
			os['android'] = true;
			classes.push('android');
			//check if the user-agent string contains 'mobile'
			if(test_mobile_str.test(ua)){
				//then it is a phone
				devices['phone'] = true;
			}else{
				//if no mobile string is present, it's a tablet
				devices['tablet'] = true;
			}
		}else if(test_linux.test(ua) && !test_android.test(ua))
		{
			os['linux'] = true;
		}else if(test_iphone.test(ua) || test_ipod.test(ua))
		{
			//iphone or ipod
			os['ios'] = true;
			devices['phone'] = true;
			devices['iphone'] = true;

		}else if(test_ipad.test(ua)){
			//ipad
			os['ios'] = true;
			devices['tablet'] = true;
			devices['ipad'] = true;
		}else if(test_blackberry.test(ua)){
			//black berry
			os['blackberry'] = true;
			devices['phone'] = true;
		}else if(test_bbplaybook.test(ua)){
			//playbook
			os['blackberry'] = true;
			devices['tablet'] = true;
		}else if(test_windows.test(ua) && !test_windowsmobile.test(ua) && !test_windowsphone.test(ua)){
			os['win'] = true;
			if(mobile){
				if(test_phone.test(ua)){
					devices['phone'] = true;
				}else if(test_tablet.test(ua)){
					devices['tablet'] = true;
				}
			}
		}else if(test_windowsmobile.test(ua)){
			os['winmobile'] = true;
		}else if(test_windowsphone.test(ua)){
			os['winphone'] = true;
			if(test_tablet.test(ua)){
				devices['tablet'] = true;
			}else{
				devices['phone'] = true;
			}
		}else if(!test_iphone.test(ua) && !test_ipad.test(ua) && test_macos.test(ua) && test_macintosh.test(ua)){
			os['mac'] = true;
		}else if(test_x11.test(ua) && !test_blackberry.test(ua)){
			os['x11'] = true;
		}

		//test if the UA is reporting tablet or phone,
		//and overright the conditions above
		//if and only we didn't already registered it the
		//opposite
		if(test_tablet.test(ua) && !devices['phone']){
			devices['tablet'] = true;
		}else if(test_phone.test(ua) && !devices['tablet']){
			devices['phone'] = true;
		}
	}
	tests.push(detect_device_type);

	/**
	var detect_screen = function(){
		var xlarge = [960,0];
	};**/

	var detect_browser = function(){

	}
	tests.push(detect_browser);
	run(prefix);

})(this.jQuery, window, false); //(typeof jQuery=="undefined"?undefined:jQuery)<|endoftext|>