<commit_before>//实例初始化
window.pageManager = new PageManager();
window.historyManager = new HistoryManager();
window.navManager = new NavManager();
//应用管理======================================================
var isTouch = 'ontouchstart' in window;
var events = (isTouch) ? {start: 'touchstart', move: 'touchmove', end: 'touchend'} : {start: 'mousedown', move: 'mousemove', end: 'mouseup'};
console.log(isTouch)
var AppManager = function(){

};
AppManager.prototype = {
    init: function(){
        var _this = this;
        pageManager.init();
        navManager.init();
        var $document = $(document);
        $document.on('touchmove', function(e){
            e.preventDefault();
        })
        //回退事件
        $document.on('history.back', function(e, obj){
            navManager.render(obj.header, obj.footer);
            pageManager.navTo(obj.page.name, {_back: true})
        });
        //添加历史事件
        $document.on('history.add', function(e, obj){
            //console.log('add event')
        });
        //动态配置页面功能
        $document.on('page', function(){//e, pageName, fromPage, param
            var args = arguments;
            var _method = _this.makeFun(args[1]);
            if(!_this[_method]){
                console.log('额，方法还没定义哦:this.' + _method);
                return;
            }
            _this[_method].apply(_this, Array.prototype.slice.call(arguments));
        })
        //默认使用登录页
        pageManager.navTo('login', {_effect: -1});

        //动态创建页面例子
        var p = pageManager.createPage('', 'Hello UED');
        //动态创建动态方法
        /*_this[_this.makeFun(p.name)] = function(e, pageName, fromPage, param){
            alert(arguments[1]);
            console.log(_this)
        }*/
        //pageManager.navTo(p.name, {id: 100});
        //pageManager.stretch();
        //end
    },
    makeFun: function(pageName){
        var upName = pageName.replace(/\b\w+\b/g, function(word){
            //console.log(word)
            return word.substring(0,1).toUpperCase() + word.substring(1);
        });
        return 'init' + upName + 'Page';
    },
    initPageFn: function(page, fn){
        this[this.makeFun(page.name)] = fn;
    },
    //滚动一个布局
    scrollPanel: function(id, option){
        var defaultOption = {
            scrollbarClass: 'myScrollbar',
            onBeforeScrollStart: function(e){
                var target = e.target;
                while (target.nodeType != 1) target = target.parentNode;
                if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA')
                  e.preventDefault();
            }
        };
        var opt = $.extend(defaultOption, option);
        var $panel = $('#' + id);
        if($panel.data('scroller')){
            return;
        }
        var myScroll = new iScroll(id, opt);
        $panel.data('scroller', myScroll);
    },
    initLoginPage: function(e, pageName, fromPage, param){
        //console.log(e, pageName, fromPage, param, 'lll')
        navManager.headerWrap.hide();
        navManager.footerWrap.hide();
        pageManager.stretch();
        $('#J_btn_login').off().on('click', function(){
            /*historyManager.add({
                page: {
                    name: 'login'
                }
            });*/
            pageManager.navTo('home');
        });

        //滚动控制
        this.scrollPanel('J_page_login');
    },
    //Home
    initHomePage: function(){
        navManager.headerWrap.show();
        var header = {
            title: {
                text: 'Home Page'
            },
            left: {},
            right: {
                actions: [
                    {
                        type: 'normal',
                        text: '登出',
                        fn: function(){
                            this.on('touchstart click', function(e){
                                e.preventDefault();
                                pageManager.navTo('login');
                            })
                        }
                    }
                ]
            }
        };
        historyManager.add({
            header: header,
            page: {
                name: 'home'
            }
        });
        navManager.render(header);
        navManager.footerWrap.show();
        navManager.footerWrap.find('li').removeClass('active').eq(0).addClass('active');
        $('#J_home_list li').off().on('click', function(e){
            e.preventDefault();
            var $this = $(this);
            var name = $this.attr('data-nav');
            pageManager.navTo(name);
        });
        //滚动控制
        this.scrollPanel('J_scroll_home');
    },
    //列表页
    initListPage: function(e, pageName, fromPage, param){
        //console.log(fromPage)
        navManager.headerWrap.show();
        var header = {
            title: {
                text: '服务器列表'
            },
            right: {
                actions: [
                    {
                        type: 'refresh',
                        text: '刷新',
                        fn: function(){
                            this.on('touchstart click', function(e){
                                e.preventDefault();
                                alert('不要刷新我');
                            })
                        }
                    }
                ]
            }
        };
        navManager.render(header);

        navManager.footerWrap.show();
        navManager.footerWrap.find('li').removeClass('active').eq(1).addClass('active');
        $('.list-view li').off().on('touchstart click', function(e){
            e.preventDefault();
            historyManager.add({
                header: header,
                page: {
                    name: 'list'
                }
            });
            pageManager.navTo('detail');
        });
        //滚动控制
        this.scrollPanel('J_scroll_list');

    },
    initDetailPage: function(e, pageName, fromPage, param){
        //console.log(fromPage)
        navManager.headerWrap.show();
        navManager.footerWrap.show();
        var header = {
            title: {
                text: '服务器详情'
            },
            right: {}
        };
        navManager.render(header);
        navManager.footerWrap.find('li').removeClass('active').eq(2).addClass('active');
        $('.list-group li').off().on('click', function(e){
            e.preventDefault();
            historyManager.add({
                header: header,
                page: {
                    name: 'detail'
                }
            });
            pageManager.navTo('about');
        });
        //滚动控制
        this.scrollPanel('J_scroll_chart');
        this.scrollPanel('J_scroll_detail');
        var pageDetail =new PageDetail(this, navManager, pageManager, historyManager);
        pageDetail.init();
        $('.tab-nav li').on('click', function(e){
            e.preventDefault();
            var $this = $(this);
            var tabContent = $this.parent().parent().next().find('.tab-content');
            var index = $('.tab-nav li').index($this);
            $('.tab-nav li').removeClass('active').eq(index).addClass('active');
            tabContent.find('.tab-pane').removeClass('active').eq(index).addClass('active').data('scroller').refresh();

        })

    },
    initAboutPage: function(e, pageName, fromPage, param){
        var _this = this;
        var header = {
            title: {
                text: '关于我们'
            },
            right: {}
        };
        navManager.render(header);
        navManager.footerWrap.find('li').removeClass('active').eq(3).addClass('active');
        $('#J_create').off().on('click', function(){
            historyManager.add({
                header: header,
                page: {
                    name: 'about'
                }
            });
            var newHeader = {
                title: {
                    text: 'UI规范'
                },
                right: {
                    actions: [
                        {
                            text: "设置",
                            cls: '',
                            fn: function(){
                                $(this).on('click', function(e){
                                    e.preventDefault();
                                    alert($(this).html())
                                })
                            }
                        },
                        {
                            text: "设置1",
                            cls: '',
                            fn: function(){
                                $(this).on('click', function(e){
                                    e.preventDefault();
                                    alert($(this).html())
                                })
                            }
                        },
                        {
                            text: "设置2",
                            cls: '',
                            fn: function(){
                                $(this).on('click', function(e){
                                    e.preventDefault();
                                    alert($(this).html())
                                })
                            }
                        }
                    ]
                }
            };
            var $this= $(this);
            if($this.attr('data-nav')){
                pageManager.navTo($this.attr('data-nav'));
                navManager.render(newHeader);
                navManager.footerWrap.hide();
                pageManager.stretchFooter();
                return false;
            }
            var html = new TemplateManager().load('tmpl');
            var p = pageManager.createPage('', html);
            $this.attr('data-nav', p.name);

            //动态创建动态方法如: initXxxPage
            _this.initPageFn(p, function(e, pageName, fromPage, param){
                //alert(arguments[1]);
                //console.log(arguments)
                navManager.render(newHeader);
                navManager.footerWrap.hide();
                p.$.attr('id', p.name);
                p.$.css({
                    //backgroundColor: '#fff',
                    color: '#fff'

                }).find('.scroller').css({
                    paddingBottom: '50px'
                });
                pageManager.stretchFooter(p);
                _this.scrollPanel(p.name);
                //
                $('.list-group li').off().on('click', function(e){
                    e.preventDefault();
                    historyManager.add({
                        header: newHeader,
                        page: {
                            name: pageName
                        }
                    });
                    pageManager.navTo('about');
                });
                //fun
                $('.J_alert').off().on('click', function(e){
                    e.preventDefault();
                    alert('你点击了确定')
                });
                $('.J_back').off().on('click', function(e){
                    e.preventDefault();
                    historyManager.back();
                })

                //Loading初始化
                var opts = {
                    lines: 12, // The number of lines to draw
                    length: 4, // The length of each line
                    width: 2, // The line thickness
                    radius: 5, // The radius of the inner circle
                    color: '#000', // #rgb or #rrggbb
                    speed: 1, // Rounds per second
                    trail: 60, // Afterglow percentage
                    shadow: false, // Whether to render a shadow
                    hwaccel: false, // Whether to use hardware acceleration
                    className: 'spinner', // The CSS class to assign to the spinner
                    zIndex: 2e9, // The z-index (defaults to 2000000000)
                    top: 'auto', // Top position relative to parent in px
                    left: 'auto' // Left position relative to parent in px
                };
                $('.icon-loading').each(function(){
                    if($(this).find('.spinner').length > 0){
                        return;
                    }
                    new Spinner(opts).spin(this);
                })
            });
            pageManager.navTo(p.name);

            //pageManager.stretch();
        })
    }
};

var appManager = new AppManager();
appManager.init();<commit_msg>这里不能用touchstart<commit_after>//实例初始化
window.pageManager = new PageManager();
window.historyManager = new HistoryManager();
window.navManager = new NavManager();
//应用管理======================================================
var isTouch = 'ontouchstart' in window;
var events = (isTouch) ? {start: 'touchstart', move: 'touchmove', end: 'touchend'} : {start: 'mousedown', move: 'mousemove', end: 'mouseup'};
console.log(isTouch)
var AppManager = function(){

};
AppManager.prototype = {
    init: function(){
        var _this = this;
        pageManager.init();
        navManager.init();
        var $document = $(document);
        $document.on('touchmove', function(e){
            e.preventDefault();
        })
        //回退事件
        $document.on('history.back', function(e, obj){
            navManager.render(obj.header, obj.footer);
            pageManager.navTo(obj.page.name, {_back: true})
        });
        //添加历史事件
        $document.on('history.add', function(e, obj){
            //console.log('add event')
        });
        //动态配置页面功能
        $document.on('page', function(){//e, pageName, fromPage, param
            var args = arguments;
            var _method = _this.makeFun(args[1]);
            if(!_this[_method]){
                console.log('额，方法还没定义哦:this.' + _method);
                return;
            }
            _this[_method].apply(_this, Array.prototype.slice.call(arguments));
        })
        //默认使用登录页
        pageManager.navTo('login', {_effect: -1});

        //动态创建页面例子
        var p = pageManager.createPage('', 'Hello UED');
        //动态创建动态方法
        /*_this[_this.makeFun(p.name)] = function(e, pageName, fromPage, param){
            alert(arguments[1]);
            console.log(_this)
        }*/
        //pageManager.navTo(p.name, {id: 100});
        //pageManager.stretch();
        //end
    },
    makeFun: function(pageName){
        var upName = pageName.replace(/\b\w+\b/g, function(word){
            //console.log(word)
            return word.substring(0,1).toUpperCase() + word.substring(1);
        });
        return 'init' + upName + 'Page';
    },
    initPageFn: function(page, fn){
        this[this.makeFun(page.name)] = fn;
    },
    //滚动一个布局
    scrollPanel: function(id, option){
        var defaultOption = {
            scrollbarClass: 'myScrollbar',
            onBeforeScrollStart: function(e){
                var target = e.target;
                while (target.nodeType != 1) target = target.parentNode;
                if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA')
                  e.preventDefault();
            }
        };
        var opt = $.extend(defaultOption, option);
        var $panel = $('#' + id);
        if($panel.data('scroller')){
            return;
        }
        var myScroll = new iScroll(id, opt);
        $panel.data('scroller', myScroll);
    },
    initLoginPage: function(e, pageName, fromPage, param){
        //console.log(e, pageName, fromPage, param, 'lll')
        navManager.headerWrap.hide();
        navManager.footerWrap.hide();
        pageManager.stretch();
        $('#J_btn_login').off().on('click', function(){
            /*historyManager.add({
                page: {
                    name: 'login'
                }
            });*/
            pageManager.navTo('home');
        });

        //滚动控制
        this.scrollPanel('J_page_login');
    },
    //Home
    initHomePage: function(){
        navManager.headerWrap.show();
        var header = {
            title: {
                text: 'Home Page'
            },
            left: {},
            right: {
                actions: [
                    {
                        type: 'normal',
                        text: '登出',
                        fn: function(){
                            this.on('touchstart click', function(e){
                                e.preventDefault();
                                pageManager.navTo('login');
                            })
                        }
                    }
                ]
            }
        };
        historyManager.add({
            header: header,
            page: {
                name: 'home'
            }
        });
        navManager.render(header);
        navManager.footerWrap.show();
        navManager.footerWrap.find('li').removeClass('active').eq(0).addClass('active');
        $('#J_home_list li').off().on('click', function(e){
            e.preventDefault();
            var $this = $(this);
            var name = $this.attr('data-nav');
            pageManager.navTo(name);
        });
        //滚动控制
        this.scrollPanel('J_scroll_home');
    },
    //列表页
    initListPage: function(e, pageName, fromPage, param){
        //console.log(fromPage)
        navManager.headerWrap.show();
        var header = {
            title: {
                text: '服务器列表'
            },
            right: {
                actions: [
                    {
                        type: 'refresh',
                        text: '刷新',
                        fn: function(){
                            this.on('touchstart click', function(e){
                                e.preventDefault();
                                alert('不要刷新我');
                            })
                        }
                    }
                ]
            }
        };
        navManager.render(header);

        navManager.footerWrap.show();
        navManager.footerWrap.find('li').removeClass('active').eq(1).addClass('active');
        $('.list-view li').off().on('click', function(e){
            e.preventDefault();
            historyManager.add({
                header: header,
                page: {
                    name: 'list'
                }
            });
            pageManager.navTo('detail');
        });
        //滚动控制
        this.scrollPanel('J_scroll_list');

    },
    initDetailPage: function(e, pageName, fromPage, param){
        //console.log(fromPage)
        navManager.headerWrap.show();
        navManager.footerWrap.show();
        var header = {
            title: {
                text: '服务器详情'
            },
            right: {}
        };
        navManager.render(header);
        navManager.footerWrap.find('li').removeClass('active').eq(2).addClass('active');
        $('.list-group li').off().on('click', function(e){
            e.preventDefault();
            historyManager.add({
                header: header,
                page: {
                    name: 'detail'
                }
            });
            pageManager.navTo('about');
        });
        //滚动控制
        this.scrollPanel('J_scroll_chart');
        this.scrollPanel('J_scroll_detail');
        var pageDetail =new PageDetail(this, navManager, pageManager, historyManager);
        pageDetail.init();
        $('.tab-nav li').on('click', function(e){
            e.preventDefault();
            var $this = $(this);
            var tabContent = $this.parent().parent().next().find('.tab-content');
            var index = $('.tab-nav li').index($this);
            $('.tab-nav li').removeClass('active').eq(index).addClass('active');
            tabContent.find('.tab-pane').removeClass('active').eq(index).addClass('active').data('scroller').refresh();

        })

    },
    initAboutPage: function(e, pageName, fromPage, param){
        var _this = this;
        var header = {
            title: {
                text: '关于我们'
            },
            right: {}
        };
        navManager.render(header);
        navManager.footerWrap.find('li').removeClass('active').eq(3).addClass('active');
        $('#J_create').off().on('click', function(){
            historyManager.add({
                header: header,
                page: {
                    name: 'about'
                }
            });
            var newHeader = {
                title: {
                    text: 'UI规范'
                },
                right: {
                    actions: [
                        {
                            text: "设置",
                            cls: '',
                            fn: function(){
                                $(this).on('click', function(e){
                                    e.preventDefault();
                                    alert($(this).html())
                                })
                            }
                        },
                        {
                            text: "设置1",
                            cls: '',
                            fn: function(){
                                $(this).on('click', function(e){
                                    e.preventDefault();
                                    alert($(this).html())
                                })
                            }
                        },
                        {
                            text: "设置2",
                            cls: '',
                            fn: function(){
                                $(this).on('click', function(e){
                                    e.preventDefault();
                                    alert($(this).html())
                                })
                            }
                        }
                    ]
                }
            };
            var $this= $(this);
            if($this.attr('data-nav')){
                pageManager.navTo($this.attr('data-nav'));
                navManager.render(newHeader);
                navManager.footerWrap.hide();
                pageManager.stretchFooter();
                return false;
            }
            var html = new TemplateManager().load('tmpl');
            var p = pageManager.createPage('', html);
            $this.attr('data-nav', p.name);

            //动态创建动态方法如: initXxxPage
            _this.initPageFn(p, function(e, pageName, fromPage, param){
                //alert(arguments[1]);
                //console.log(arguments)
                navManager.render(newHeader);
                navManager.footerWrap.hide();
                p.$.attr('id', p.name);
                p.$.css({
                    //backgroundColor: '#fff',
                    color: '#fff'

                }).find('.scroller').css({
                    paddingBottom: '50px'
                });
                pageManager.stretchFooter(p);
                _this.scrollPanel(p.name);
                //
                $('.list-group li').off().on('click', function(e){
                    e.preventDefault();
                    historyManager.add({
                        header: newHeader,
                        page: {
                            name: pageName
                        }
                    });
                    pageManager.navTo('about');
                });
                //fun
                $('.J_alert').off().on('click', function(e){
                    e.preventDefault();
                    alert('你点击了确定')
                });
                $('.J_back').off().on('click', function(e){
                    e.preventDefault();
                    historyManager.back();
                })

                //Loading初始化
                var opts = {
                    lines: 12, // The number of lines to draw
                    length: 4, // The length of each line
                    width: 2, // The line thickness
                    radius: 5, // The radius of the inner circle
                    color: '#000', // #rgb or #rrggbb
                    speed: 1, // Rounds per second
                    trail: 60, // Afterglow percentage
                    shadow: false, // Whether to render a shadow
                    hwaccel: false, // Whether to use hardware acceleration
                    className: 'spinner', // The CSS class to assign to the spinner
                    zIndex: 2e9, // The z-index (defaults to 2000000000)
                    top: 'auto', // Top position relative to parent in px
                    left: 'auto' // Left position relative to parent in px
                };
                $('.icon-loading').each(function(){
                    if($(this).find('.spinner').length > 0){
                        return;
                    }
                    new Spinner(opts).spin(this);
                })
            });
            pageManager.navTo(p.name);

            //pageManager.stretch();
        })
    }
};

var appManager = new AppManager();
appManager.init();<|endoftext|>


<commit_before>/* global math */
/* eslint-disable no-unused-vars */
var utils = {
  strWrap: (str) => {
    if (str.includes('\n') || (str.includes('"') && str.includes("'"))) return '`';
    return !str.includes("'") ? "'" : '"';
  },

  escapeRegExp: (str, q) => str.replace(new RegExp(`[\\\\${q}\\n]`, 'g'), '\\$&').replace(/-/g, '\\x2d'),

  _unescape: (str) => {
    for (let i = 32; i < 128; i++) {
      str = str.replace(new RegExp('\\\\x' + i.toString(16), 'ig'), String.fromCharCode(i));
    }
    str = str.replace(/\\x09/g, '\t');
    return str;
  },

  strMerge: (str) => str.replace(/'\s*\+\s*'|"\s*\+\s*"/g, ''),

  methodChain: (str) => str.replace(/(?<=[a-z_\d$\])])\[("|')((?!\d)[a-z_\d$]*)("|')\]/gi, '.$2 '),

  calcHex: (str) => {
    str = str.replace(/(?<!['"])(?<!\w)0x[a-f\d]+((?<!\w)0x[a-f\d]+|[+\-*/])*(?<!\w)0x[a-f\d]+(?!['"])/gi, (m) =>
      math.format(eval(m), 14),
    );
    str = str.replace(/(?<!['"])(?<!\w)0x[a-f\d]+(?!['"])/gi, (m) => eval(m));
    return str;
  },

  calcNumber: (str) => {
    str = str.replace(/\(([\d.]+)\)/g, '$1');
    str = str.replace(/(?<!['"\w])\d[+\-*/\d.]*\d(?!['"\w])/g, (m) => {
      if (!/[+\-*/]/.test(m)) return m;
      return math.format(math.evaluate(m), 14);
    });
    return str;
  },

  _boolean: (str) =>
    str.replace(/((?<=([=:(,|&[]|return|=>))|^)!{1,2}(\[\]|0|1)((?=[;,)}|&\]])|$)/g, (m) => eval(m).toString()),

  propArr: (str) => str.replace(/\[\((['"])((?!\d)[a-z_\d$]*)['"]\)\]/gi, '[$1$2$1]'),
};
<commit_msg>Fix methodChain<commit_after>/* global math */
/* eslint-disable no-unused-vars */
var utils = {
  strWrap: (str) => {
    if (str.includes('\n') || (str.includes('"') && str.includes("'"))) return '`';
    return !str.includes("'") ? "'" : '"';
  },

  escapeRegExp: (str, q) => str.replace(new RegExp(`[\\\\${q}\\n]`, 'g'), '\\$&').replace(/-/g, '\\x2d'),

  _unescape: (str) => {
    for (let i = 32; i < 128; i++) {
      str = str.replace(new RegExp('\\\\x' + i.toString(16), 'ig'), String.fromCharCode(i));
    }
    str = str.replace(/\\x09/g, '\t');
    return str;
  },

  strMerge: (str) => str.replace(/'\s*\+\s*'|"\s*\+\s*"/g, ''),

  methodChain: (str) => {
    str = str.replace(
      /(?<=(\b(return|throw|in|of|new|delete|default|function|async|await|get|set)|\{|\}|\*))\s*\[('|")((?![\d\W])[\w$]*)\3\]\s*\(/gi,
      ' $4( ',
    );
    str = str.replace(/(?<=((?![\d\W])[\w$]*)|\])\[('|")((?![\d\W])[\w$]*)\2\]/gi, ' .$3 ');
    return str;
  },

  calcHex: (str) => {
    str = str.replace(/(?<!['"])(?<!\w)0x[a-f\d]+((?<!\w)0x[a-f\d]+|[+\-*/])*(?<!\w)0x[a-f\d]+(?!['"])/gi, (m) =>
      math.format(eval(m), 14),
    );
    str = str.replace(/(?<!['"])(?<!\w)0x[a-f\d]+(?!['"])/gi, (m) => eval(m));
    return str;
  },

  calcNumber: (str) => {
    str = str.replace(/\(([\d.]+)\)/g, '$1');
    str = str.replace(/(?<!['"\w])\d[+\-*/\d.]*\d(?!['"\w])/g, (m) => {
      if (!/[+\-*/]/.test(m)) return m;
      return math.format(math.evaluate(m), 14);
    });
    return str;
  },

  _boolean: (str) =>
    str.replace(/((?<=([=:(,|&[]|return|=>))|^)!{1,2}(\[\]|0|1)((?=[;,)}|&\]])|$)/g, (m) => eval(m).toString()),

  propArr: (str) => str.replace(/\[\((['"])((?![\d\W])[\w$]*)['"]\)\]/gi, '[$1$2$1]'),
};
<|endoftext|>


<commit_before>function AbstractThreeLoaderObjPlugin() {
	ThreeObject3DPlugin.apply(this, arguments)

	this.desc = 'THREE.js OBJ loader'

	this.dirty = true

	this.childrenByMaterialName = {}
	this.state = { url: '' }
	this.materials = {}
}

AbstractThreeLoaderObjPlugin.prototype = Object.create(ThreeObject3DPlugin.prototype)

AbstractThreeLoaderObjPlugin.prototype.create_ui = function() {
	var inp = makeButton('Change', 'No scene selected.', 'url')
	var that = this

	inp.click(function() {
		var oldValue = that.state.url
		var newValue

		FileSelectControl
			.createSceneSelector(that.state.url)
			.onChange(function(v) {
				newValue = that.state.url = v
				that.state_changed(null)
				that.state_changed(inp)
				that.updated = true
			})
			.on('closed', function() {
				if (newValue === oldValue)
					return
				console.log('closed', newValue, that.state.url)
				that.undoableSetState('url', newValue, oldValue)
			})
	})

	return inp
}

AbstractThreeLoaderObjPlugin.prototype.update_input = function(slot, data) {
	if (slot.uid) {
		this.materials[slot.name] = data

		if (!this.childrenByMaterialName[slot.name])
			return;

		for (var i=0; i < this.childrenByMaterialName[slot.name].length; i++)
			this.childrenByMaterialName[slot.name][i].material = data

		return;
	}

	return ThreeObject3DPlugin.prototype.update_input.apply(this, arguments)
}

AbstractThreeLoaderObjPlugin.prototype.adjustMaterialSlots = function() {
	var that = this
	var materialSlots = this.node.getDynamicInputSlots()
	var materialNames = Object.keys(this.childrenByMaterialName)

	// filter out any old material slots that don't belong to this obj
	materialSlots.slice().map(function(mSlot) {
		if (materialNames.indexOf(mSlot.name) === -1) {
			that.node.remove_slot(E2.slot_type.input, mSlot.uid)
		}
	})

	// then check that all the material slots of this obj are there
	materialNames.map(function(matName) {
		var found = materialSlots.some(function(mSlot) {
			return (mSlot.name === matName)
		})

		if (found)
			return;

		var slotUid = that.node.uid + matName

		that.node.add_slot(E2.slot_type.input, {
			dt: E2.dt.MATERIAL,
			uid: slotUid,
			name: matName
		})
	})
}

AbstractThreeLoaderObjPlugin.prototype.onObjLoaded = function(obj) {
	var that = this

	this.object3d = obj

	msg('Finished loading '+ this.state.url)

	this.childrenByMaterialName = {}

	obj.traverse(function(child) {
		if (child instanceof THREE.Mesh && child.material.name) {
			var matName = 'mat-'+child.material.name
				.toLowerCase()
				.replace(/[^a-z0-9]+/g,' ')
				.replace(/ +/g, '-')

			if (!that.childrenByMaterialName[matName])
				that.childrenByMaterialName[matName] = []

			that.childrenByMaterialName[matName].push(child)
		}
	})

	this.adjustMaterialSlots()

	this.updated = true
}

AbstractThreeLoaderObjPlugin.prototype.update_state = function() {
	if (!this.dirty)
		return

	if (!this.state.url)
		return;

	if (this.object3d)
		this.object3d = null

	THREE.Loader.Handlers.add(/\.dds$/i, new THREE.DDSLoader())

	console.log('AbstractThreeLoaderObjPlugin loading', this.state.url)

	this.loadObj()

	this.dirty = false
}

AbstractThreeLoaderObjPlugin.prototype.loadObj = function() {
	var that = this
	var loader = new THREE.OBJLoader()
	loader.load(this.state.url, this.onObjLoaded.bind(this), function() {
		console.log('Loading progress', that.state.url, arguments)
	}, function(err) {
		msg('ERROR: '+err.toString())
	})
}

AbstractThreeLoaderObjPlugin.prototype.state_changed = function(ui) {
	console.log('state_changed', this.state)
	if (!this.state.url)
		return

	if (ui)
		ui.attr('title', this.state.url)
	else
		this.dirty = true
}

<commit_msg>fix material loading for geometries - updating dynamic material inputs didn't work the first time a graph was loaded<commit_after>function AbstractThreeLoaderObjPlugin() {
	ThreeObject3DPlugin.apply(this, arguments)

	this.desc = 'THREE.js OBJ loader'

	this.dirty = true

	this.childrenByMaterialName = {}
	this.state = { url: '' }
	this.materials = {}
}

AbstractThreeLoaderObjPlugin.prototype = Object.create(ThreeObject3DPlugin.prototype)

AbstractThreeLoaderObjPlugin.prototype.create_ui = function() {
	var inp = makeButton('Change', 'No scene selected.', 'url')
	var that = this

	inp.click(function() {
		var oldValue = that.state.url
		var newValue

		FileSelectControl
			.createSceneSelector(that.state.url)
			.onChange(function(v) {
				newValue = that.state.url = v
				that.state_changed(null)
				that.state_changed(inp)
				that.updated = true
			})
			.on('closed', function() {
				if (newValue === oldValue)
					return
				console.log('closed', newValue, that.state.url)
				that.undoableSetState('url', newValue, oldValue)
			})
	})

	return inp
}

AbstractThreeLoaderObjPlugin.prototype.update_input = function(slot, data) {
	if (slot.uid) {
		this.materials[slot.name] = data

		if (!this.childrenByMaterialName[slot.name]) {
			if (!this.object3d) {
				// object hasn't finished loading yet - add to pending list
				this.pendingMaterialInput = this.pendingMaterialInput || {}

				this.pendingMaterialInput[slot.name] = data
			}

			return;
		}

		for (var i=0; i < this.childrenByMaterialName[slot.name].length; i++)
			this.childrenByMaterialName[slot.name][i].material = data

		return;
	}

	return ThreeObject3DPlugin.prototype.update_input.apply(this, arguments)
}

AbstractThreeLoaderObjPlugin.prototype.adjustMaterialSlots = function() {
	var that = this
	var materialSlots = this.node.getDynamicInputSlots()
	var materialNames = Object.keys(this.childrenByMaterialName)

	// filter out any old material slots that don't belong to this obj
	materialSlots.slice().map(function(mSlot) {
		if (materialNames.indexOf(mSlot.name) === -1) {
			that.node.remove_slot(E2.slot_type.input, mSlot.uid)
		}
	})

	// then check that all the material slots of this obj are there
	materialNames.map(function(matName) {
		var found = materialSlots.some(function(mSlot) {
			return (mSlot.name === matName)
		})

		if (found)
			return;

		var slotUid = that.node.uid + matName

		that.node.add_slot(E2.slot_type.input, {
			dt: E2.dt.MATERIAL,
			uid: slotUid,
			name: matName
		})
	})
}

AbstractThreeLoaderObjPlugin.prototype.onObjLoaded = function(obj) {
	var that = this

	this.object3d = obj

	msg('Finished loading '+ this.state.url)

	this.childrenByMaterialName = {}

	obj.traverse(function(child) {
		if (child instanceof THREE.Mesh && child.material.name) {
			var matName = 'mat-'+child.material.name
				.toLowerCase()
				.replace(/[^a-z0-9]+/g,' ')
				.replace(/ +/g, '-')

			if (!that.childrenByMaterialName[matName])
				that.childrenByMaterialName[matName] = []

			that.childrenByMaterialName[matName].push(child)
		}
	})

	if (this.pendingMaterialInput) {
		for (input in Object.keys(this.pendingMaterialInput)) {
			for (var i=0; i < this.childrenByMaterialName[input].length; i++)
				this.childrenByMaterialName[input][i].material = this.pendingMaterialInput[input]
		}

		this.pendingMaterialInput = undefined
	}

	this.adjustMaterialSlots()

	this.updated = true
}

AbstractThreeLoaderObjPlugin.prototype.update_state = function() {
	if (!this.dirty)
		return

	if (!this.state.url)
		return;

	if (this.object3d)
		this.object3d = null

	THREE.Loader.Handlers.add(/\.dds$/i, new THREE.DDSLoader())

	console.log('AbstractThreeLoaderObjPlugin loading', this.state.url)

	this.loadObj()

	this.dirty = false
}

AbstractThreeLoaderObjPlugin.prototype.loadObj = function() {
	var that = this
	var loader = new THREE.OBJLoader()
	loader.load(this.state.url, this.onObjLoaded.bind(this), function() {
		console.log('Loading progress', that.state.url, arguments)
	}, function(err) {
		msg('ERROR: '+err.toString())
	})
}

AbstractThreeLoaderObjPlugin.prototype.state_changed = function(ui) {
	console.log('state_changed', this.state)
	if (!this.state.url)
		return

	if (ui)
		ui.attr('title', this.state.url)
	else
		this.dirty = true
}

<|endoftext|>


<commit_before>/*global describe:true, it:true, before:true, after:true */

var
	chai = require('chai'),
	assert = chai.assert,
	expect = chai.expect,
	should = chai.should()
	;

var
	child = require('child_process'),
	fs = require('fs'),
	path = require('path'),
	powerline = require('../powerline')
	;

function execute(args, callback)
{
	child.exec('./powerline.js' + args, function(err, stdout, stderr)
	{
		if (err)
			throw(err);

		callback(stderr, stdout);
	});
}

describe('powerline.js', function()
{
	describe('#constructor options', function()
	{
		it('is constructed with reasonable defaults', function()
		{
			var p = new powerline.Powerline();
			p.should.have.property('options');
			p.options.should.be.an('object');
			p.options.shell.should.equal('zsh');
			p.options.error.should.equal(false);
			p.options.mode.should.equal('patched');
			p.options.depth.should.equal(5);
			p.options.showRepo.should.equal(true);
			p.options.showPath.should.equal(true);
			p.options.cwdOnly.should.equal(false);
		});

		it('throws when passed an unknown shell', function()
		{
			var badOptions = function()
			{
				var p = new Powerline({ shell: 'tcsh'});
			};

			badOptions.should.throw();
		});

		it('obeys the mode option', function()
		{
			var p = new powerline.Powerline({mode: 'compatible'});
			p.options.mode.should.equal('compatible');
			p.separator.should.equal('\u25b6');
			p.separator_thin.should.equal('\u276f');

		});

		it('obeys the repo-only option', function()
		{

		});

		it('obeys the no-repo option', function()
		{

		});
	});

	describe('#draw', function()
	{
		it('returns a string with color escape sequences', function()
		{

		});

	});

	describe('#working directory', function()
	{
		var deepPath = '/this/is/a/deep/path/certainly/ohyes';

		it('obeys the depth option', function()
		{
			var p = new powerline.Powerline({depth: 6});
			p.cwd = path.normalize(process.cwd() + deepPath);
			p.addCWDSegment();
			p.segments.length.should.equal(7);
			p.segments[0].content.should.equal(' ~ ');
			p.segments[2].content.should.equal(' … ');
			p.segments[6].content.should.equal(' ohyes ');
		});

		it('handles very shallow depths gracefully', function()
		{
			var p = new powerline.Powerline({depth: 1});
			p.cwd = path.normalize(process.cwd() + deepPath);
			p.addCWDSegment();
			p.segments.length.should.equal(1);
			p.segments[0].content.should.equal(' ohyes ');
		});

		it('obeys the cwd-only option', function()
		{
			var p = new powerline.Powerline({cwdOnly: true});
			p.addCWDSegment();
			p.segments.length.should.equal(1);
			p.segments[0].content.should.equal(' powerline-js ');
		});

	});

	describe('#git', function()
	{
		it('displays the git repo branch', function(done)
		{
			var p = new powerline.Powerline();
			p.addGitSegment(function()
			{
				done();
			});
		});

		it('shows upstream changes', function()
		{

		});

		it('shows unpushed changes', function()
		{

		});
	});

	describe('#svn', function()
	{

	});

	describe('#hg', function()
	{
		// feature unimplemented
	});
});
<commit_msg>Starting to write the git segment tests, which is a strange process.<commit_after>/*global describe:true, it:true, before:true, after:true */

var
	chai = require('chai'),
	assert = chai.assert,
	expect = chai.expect,
	should = chai.should()
	;

var
	child = require('child_process'),
	fs = require('fs'),
	path = require('path'),
	powerline = require('../powerline')
	;

function execute(args, callback)
{
	child.exec('./powerline.js' + args, function(err, stdout, stderr)
	{
		if (err)
			throw(err);

		callback(stderr, stdout);
	});
}

describe('powerline.js', function()
{
	describe('#constructor options', function()
	{
		it('is constructed with reasonable defaults', function()
		{
			var p = new powerline.Powerline();
			p.should.have.property('options');
			p.options.should.be.an('object');
			p.options.shell.should.equal('zsh');
			p.options.error.should.equal(false);
			p.options.mode.should.equal('patched');
			p.options.depth.should.equal(5);
			p.options.showRepo.should.equal(true);
			p.options.showPath.should.equal(true);
			p.options.cwdOnly.should.equal(false);
		});

		it('throws when passed an unknown shell', function()
		{
			var badOptions = function()
			{
				var p = new Powerline({ shell: 'tcsh'});
			};

			badOptions.should.throw();
		});

		it('obeys the mode option', function()
		{
			var p = new powerline.Powerline({mode: 'compatible'});
			p.options.mode.should.equal('compatible');
			p.separator.should.equal('\u25b6');
			p.separator_thin.should.equal('\u276f');

		});

		it('obeys the repo-only option', function()
		{

		});

		it('obeys the no-repo option', function()
		{

		});
	});

	describe('#draw', function()
	{
		it('returns a string with color escape sequences', function()
		{

		});

	});

	describe('#working directory', function()
	{
		var deepPath = '/this/is/a/deep/path/certainly/ohyes';

		it('obeys the depth option', function()
		{
			var p = new powerline.Powerline({depth: 6});
			p.cwd = path.normalize(process.cwd() + deepPath);
			p.addCWDSegment();
			p.segments.length.should.equal(7);
			p.segments[0].content.should.equal(' ~ ');
			p.segments[2].content.should.equal(' … ');
			p.segments[6].content.should.equal(' ohyes ');
		});

		it('handles very shallow depths gracefully', function()
		{
			var p = new powerline.Powerline({depth: 1});
			p.cwd = path.normalize(process.cwd() + deepPath);
			p.addCWDSegment();
			p.segments.length.should.equal(1);
			p.segments[0].content.should.equal(' ohyes ');
		});

		it('obeys the cwd-only option', function()
		{
			var p = new powerline.Powerline({cwdOnly: true});
			p.addCWDSegment();
			p.segments.length.should.equal(1);
			p.segments[0].content.should.equal(' powerline-js ');
		});

	});

	describe('#git', function()
	{
		it('displays the git repo branch', function(done)
		{
			var p = new powerline.Powerline();
			p.addGitSegment(function()
			{
				p.segments.length.should.equal(1);
				p.segments[0].content.indexOf(' master').should.equal(0);
				console.log(p.segments);
				done();
			});
		});

		it('displays the presence of untracked files', function(done)
		{
			child.exec('touch foobar.txt', function(err, stdout, stderr)
			{
				var p = new powerline.Powerline();
				p.addGitSegment(function()
				{
					p.segments.length.should.equal(1);
					p.segments[0].content.indexOf('+').should.be.above(7);
					fs.unlinkSync('foobar.txt');
					done();
				});
			});
		});

		it('displays in a different color if files are modified', function(done)
		{
			var p = new powerline.Powerline();
			p.addGitSegment(function()
			{
				console.log(p.segments);
				p.segments.length.should.equal(1);
				done();
			});
		});

		it('shows upstream changes', function()
		{

		});

		it('shows unpushed changes', function()
		{

		});
	});

	describe('#svn', function()
	{

	});

	describe('#hg', function()
	{
		// feature unimplemented
	});
});
<|endoftext|>


<commit_before>'use strict';

const cellStates = {
	x: 'x',
	o: 'o',
	_: '_'
};

const Box = React.createClass({
	getInitialState: function () {
		return {
			size: 3,
			player: cellStates.x,
			matrix: [
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._]
			]
		};
	},
	changePlayer: function () {
		const player = (this.state.player === cellStates.x)? cellStates.o : cellStates.x;
		this.setState({player: player});
	},
	changeStateOfCell: function (x, y) {
		const playerSimbol = (this.state.player == cellStates.x) ? cellStates.x : cellStates.o;
		var matrix = this.state.matrix;
		matrix[x][y] = playerSimbol;
		this.setState({matrix: matrix});
	},
	render: function () {
		const matrix = this.state.matrix;
		return (

						{matrix.map(function(row, index){
							return <Row cells={row} changeStateOfCell={this.changeStateOfCell} rowindex={index} />;
						}, this)}

				<Player changePlayer={this.changePlayer} player={this.state.player} />

			);
	}
});

const Row = React.createClass({
	rowCellOnClick: function (x, y) {
		this.props.changeStateOfCell(x, y);
	},
	render: function () {
		const cells = this.props.cells;
		const rowIndex = this.props.rowindex;
		return (

				{cells.map(function(cell, index){
					return <Cell item={cell} rowCellOnClick={this.rowCellOnClick} rowindex={rowIndex} cellindex={index} />;
				}, this)}

);
	}
});

const Cell = React.createClass({
	handlerOnClick: function () {
		const x = this.props.rowindex;
		const y = this.props.cellindex;
		this.props.rowCellOnClick(x, y);
	},
	render: function () {
		const item = this.props.item;
		return (| {item} |	);
	}
});

const Player = React.createClass({
	changePlayer: function () {
		this.props.changePlayer();
	},
	render: function () {
		const player = this.props.player;
		return (

Player: {player}

			<button onClick={this.changePlayer}>Change Player</ button>

);
	}
});

ReactDOM.render(
	<Box />,
  	document.getElementById('xo')
);
<commit_msg>add RESTART button<commit_after>'use strict';

const cellStates = {
	x: 'x',
	o: 'o',
	_: '_'
};

const Box = React.createClass({
	getInitialState: function () {
		return {
			size: 3,
			player: cellStates.x,
			matrix: [
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._]
			]
		};
	},
	changePlayer: function () {
		const player = (this.state.player === cellStates.x)? cellStates.o : cellStates.x;
		this.setState({player: player});
	},
	changeStateOfCell: function (x, y) {
		const playerSimbol = (this.state.player == cellStates.x) ? cellStates.x : cellStates.o;
		var matrix = this.state.matrix;
		matrix[x][y] = playerSimbol;
		this.setState({matrix: matrix});
	},
	restartAll: function () {
		const matrix = [
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._]
			];
		const player = cellStates.x;
		this.setState({matrix: matrix, player: player});
	},
	render: function () {
		const matrix = this.state.matrix;
		return (

						{matrix.map(function(row, index){
							return <Row cells={row} changeStateOfCell={this.changeStateOfCell} rowindex={index} />;
						}, this)}

				<Player changePlayer={this.changePlayer} player={this.state.player} />
				<Restart restart={this.restartAll} />

			);
	}
});

const Row = React.createClass({
	rowCellOnClick: function (x, y) {
		this.props.changeStateOfCell(x, y);
	},
	render: function () {
		const cells = this.props.cells;
		const rowIndex = this.props.rowindex;
		return (

				{cells.map(function(cell, index){
					return <Cell item={cell} rowCellOnClick={this.rowCellOnClick} rowindex={rowIndex} cellindex={index} />;
				}, this)}

);
	}
});

const Cell = React.createClass({
	handlerOnClick: function () {
		const x = this.props.rowindex;
		const y = this.props.cellindex;
		this.props.rowCellOnClick(x, y);
	},
	render: function () {
		const item = this.props.item;
		return (| {item} |	);
	}
});

const Player = React.createClass({
	render: function () {
		const player = this.props.player;
		return (

Player: {player}

			<button onClick={this.props.changePlayer}>Change Player</ button>

);
	}
});

const Restart = React.createClass({
	render: function () {
		return (<button onClick={this.props.restart}>Restart</button>);
	}
});

ReactDOM.render(
	<Box />,
  	document.getElementById('xo')
);
<|endoftext|>


<commit_before>/* jshint esversion: 6, node: true */

// TODO: Improve promisify methods (e.g. adding code-generation, or improving
// server processing: the `onMessage` method shouldn't be overridden).

'use strict';

/** Various utilities. */

const Promise = require('bluebird');
const avro = require('avsc');

// Type references used when assembling schemas.
const TYPE_REFS = {
  date: {type: 'long', logicalType: 'timestamp-ms'}
};

/** Custom logical type to represent dates as `Date` objects. */
class DateType extends avro.types.LogicalType {

  _fromValue(val) { return new Date(val); }

  _toValue(any) {
    if (any !== null && (any instanceof Date || !isNaN(any))) {
      return +any;
    }
  }

  _resolve(type) {
    if (avro.Type.isType(type, 'long', 'string')) {
      return this._fromValue;
    }
  }
}

/**
 * Transform (in-place) all named client RPC methods to also support promises.
 *
 * If no callback is passed when emitting a message, the handler will return a
 * promise. This promise will be bound to the call's context (similar to the
 * callback-based API). Additionally, the new methods will unwrap any remote
 * errors to enable the standard `.catch(MyError, fn)` bluebird idiom; see the
 * attached example for details.
 */
function promisifyClient(client) {
  // Note that we can't just promisify the method the standard way since we
  // would have no way to access the call's context (only present in the
  // request's callback). Hence the promise twiddling below.
  const originalFn = client.emitMessage;
  client.emitMessage = function (name, req, opts, cb) {
    if (cb || (!cb && typeof opts == 'function')) {
      return originalFn.call(this, name, req, opts, cb);
    }
    let reject, resolve, resolveCtx;
    const promise = new Promise(function (resolve_, reject_) {
      resolve = resolve_;
      reject = reject_;
    });
    const ctxPromise = new Promise(function (resolveCtx_) {
      resolveCtx = resolveCtx_; // Never rejected.
    });
    originalFn.call(this, name, req, opts, function (err, res) {
      resolveCtx(this);
      if (err !== undefined && err !== null) {
        // This will yield a false positive if someone defined `null` as valid
        // error type in the service's protocol. (Why would anyone do that?)
        if (typeof err.unwrapped == 'function') {
          const cause = err.unwrapped();
          if (cause.constructor.type.typeName === 'error') {
            err = cause;
          }
        }
        reject(err);
      } else {
        resolve(res);
      }
    });
    return promise.bind(ctxPromise);
  };
  return client;
}

/**
 * Transform (in-place) a server to also accept promise-based handlers.
 *
 * As a convenience, we also allow handlers to throw remote errors without
 * having to wrap them first (even when the message's errors type is a wrapped
 * union); see the attached example for details.
 */
function promisifyServer(server) {
  const fn = server.onMessage;
  server.onMessage = function (name, handler) {
    const msg = this.service.message(name);
    if (!msg || msg.oneWay || handler.length > 1) {
      return fn.call(this, name, handler);
    }
    const wrappedHandler = Promise.method(handler);
    return fn.call(this, name, function (req, cb) {
      wrappedHandler.call(this, req)
        .then(function (res) { cb(undefined, res); })
        .catch(function (err) {
          if (
            err && typeof err.wrapped == 'function' &&
            msg.errorType.typeName === 'union:wrapped'
          ) {
            // Allow handlers to throw custom error instances directly even
            // when the message's error type is a wrapped union.
            cb(err.wrapped());
          } else {
            cb(err);
          }
        });
    });
  };
  return server;
}

/**
 * Allow a server or client to support promise-based middleware handlers.
 *
 * If the handler returns a promise, the promise-based version will be used.
 */
function promisifyMiddleware(clientOrServer) {
  const fn = clientOrServer.use;
  clientOrServer.use = function (...handlers) {
    for (const handler_ of handlers) {
      // We might be dealing with dynamic middleware.
      const handler = handler_.length < 3 ? handler_(this) : handler_;
      fn.call(this, promisifyHandler(handler));
    }
    return this;
  };
  return clientOrServer;

  function promisifyHandler(handler) {
    const wrappedHandler = Promise.method(handler);
    return function (wreq, wres, next) {
      // Avoid creating the promise when possible.
      // TODO: Remove this optimization since we also use the argument to
      // determine dynamic/static middleware. So we should be doubly consistent
      // and not work when it isn't called.
      let promise, resolve, reject, prev;
      wrappedHandler.call(this, wreq, wres, function () {
        if (!promise) {
          promise = new Promise(function (resolve_, reject_) {
            resolve = resolve_;
            reject = reject_;
          });
          next(null, function (err, prev_) {
            prev = prev_;
            if (err) {
              reject(err);
            } else {
              resolve();
            }
          });
        }
        return promise;
      }).then(function () { done(); }, done);

      function done(err) {
        if (!promise) {
          // TODO: Can we handle this better? It would be better to throw an
          // error here (happens if the function returns before next is
          // called).
          next(err);
        } else if (prev) {
          prev(err);
        } else {
          // Handler returned, but not ready yet.
          promise.then(done, done);
        }
      }
    };
  }
}

/** Public API to promisify a single client or server. */
function promisify(clientOrServer) {
  if (clientOrServer instanceof avro.Service.Client) {
    promisifyClient(clientOrServer);
  } else if (clientOrServer instanceof avro.Service.Server) {
    promisifyServer(clientOrServer);
  } else {
    throw new TypeError(`unable to promisify ${clientOrServer}`);
  }
  return promisifyMiddleware(clientOrServer);
}

/** Permanently and globally promisify clients and servers. */
function promisifyAll() {
  promisifyClient(avro.Service.Client.prototype);
  promisifyServer(avro.Service.Server.prototype);
  promisifyMiddleware(avro.Service.Client.prototype);
  promisifyMiddleware(avro.Service.Server.prototype);
}

module.exports = {
  LOGICAL_TYPES: {
    'timestamp-ms': DateType
  },
  TYPE_REFS,
  promisify,
  promisifyAll
};
<commit_msg>Update middleware promisification.<commit_after>/* jshint esversion: 6, node: true */

// TODO: Improve promisify methods (e.g. adding code-generation, or improving
// server processing: the `onMessage` method shouldn't be overridden).

'use strict';

/** Various utilities. */

const Promise = require('bluebird');
const avro = require('avsc');

// Type references used when assembling schemas.
const TYPE_REFS = {
  date: {type: 'long', logicalType: 'timestamp-ms'}
};

/** Custom logical type to represent dates as `Date` objects. */
class DateType extends avro.types.LogicalType {

  _fromValue(val) { return new Date(val); }

  _toValue(any) {
    if (any !== null && (any instanceof Date || !isNaN(any))) {
      return +any;
    }
  }

  _resolve(type) {
    if (avro.Type.isType(type, 'long', 'string')) {
      return this._fromValue;
    }
  }
}

/**
 * Transform (in-place) all named client RPC methods to also support promises.
 *
 * If no callback is passed when emitting a message, the handler will return a
 * promise. This promise will be bound to the call's context (similar to the
 * callback-based API). Additionally, the new methods will unwrap any remote
 * errors to enable the standard `.catch(MyError, fn)` bluebird idiom; see the
 * attached example for details.
 */
function promisifyClient(client) {
  // Note that we can't just promisify the method the standard way since we
  // would have no way to access the call's context (only present in the
  // request's callback). Hence the promise twiddling below.
  const originalFn = client.emitMessage;
  client.emitMessage = function (name, req, opts, cb) {
    if (cb || (!cb && typeof opts == 'function')) {
      return originalFn.call(this, name, req, opts, cb);
    }
    let reject, resolve, resolveCtx;
    const promise = new Promise(function (resolve_, reject_) {
      resolve = resolve_;
      reject = reject_;
    });
    const ctxPromise = new Promise(function (resolveCtx_) {
      resolveCtx = resolveCtx_; // Never rejected.
    });
    originalFn.call(this, name, req, opts, function (err, res) {
      resolveCtx(this);
      if (err !== undefined && err !== null) {
        // This will yield a false positive if someone defined `null` as valid
        // error type in the service's protocol. (Why would anyone do that?)
        if (typeof err.unwrapped == 'function') {
          const cause = err.unwrapped();
          if (cause.constructor.type.typeName === 'error') {
            err = cause;
          }
        }
        reject(err);
      } else {
        resolve(res);
      }
    });
    return promise.bind(ctxPromise);
  };
  return client;
}

/**
 * Transform (in-place) a server to also accept promise-based handlers.
 *
 * As a convenience, we also allow handlers to throw remote errors without
 * having to wrap them first (even when the message's errors type is a wrapped
 * union); see the attached example for details.
 */
function promisifyServer(server) {
  const fn = server.onMessage;
  server.onMessage = function (name, handler) {
    const msg = this.service.message(name);
    if (!msg || msg.oneWay || handler.length > 1) {
      return fn.call(this, name, handler);
    }
    const wrappedHandler = Promise.method(handler);
    return fn.call(this, name, function (req, cb) {
      wrappedHandler.call(this, req)
        .then(function (res) { cb(undefined, res); })
        .catch(function (err) {
          if (
            err && typeof err.wrapped == 'function' &&
            msg.errorType.typeName === 'union:wrapped'
          ) {
            // Allow handlers to throw custom error instances directly even
            // when the message's error type is a wrapped union.
            cb(err.wrapped());
          } else {
            cb(err);
          }
        });
    });
  };
  return server;
}

/**
 * Allow a server or client to support promise-based middleware handlers.
 *
 * If the handler returns a promise, the promise-based version will be used.
 */
function promisifyMiddleware(clientOrServer) {
  const fn = clientOrServer.use;
  clientOrServer.use = function (...handlers) {
    for (const handler_ of handlers) {
      // We might be dealing with dynamic middleware.
      const handler = handler_.length < 3 ? handler_(this) : handler_;
      fn.call(this, promisifyHandler(handler));
    }
    return this;
  };
  return clientOrServer;

  function promisifyHandler(handler) {
    return function (wreq, wres, next) {
      let reject, resolve, prev;
      const promise = new Promise(function (resolve_, reject_) {
        resolve = resolve_;
        reject = reject_;
      });
      let ret;
      try {
        ret = handler.call(this, wreq, wres, function (err, cb) {
          if (cb) {
            // Always use the callback API if one is provided here.
            next(err, cb);
            return;
          }
          next(null, function (err, prev_) {
            prev = prev_;
            if (err) {
              reject(err);
            } else {
              resolve();
            }
          });
          return promise;
        });
      } catch (err) {
        // If an error is thrown synchronously in the handler, we'll be
        // accommodating and assume that this is a promise's rejection.
        next(err);
        return;
      }
      if (ret && typeof ret.then === 'function') {
        // Cheap way of testing whether `ret` is a promise. If so, we use the
        // promise-based API: we wait until the returned promise is complete to
        // trigger any backtracking. We also don't use `.return` in case the
        // returned promise isn't a bluebird based one.
        ret.then(function () { done(); }, done);
      } else {
        promise.then(prev, prev);
      }

      function done(err) {
        if (prev) {
          prev(err);
        } else {
          // This will happen if the returned promise is complete before the
          // one returned by `next()` is. There is no clear ideal behavior
          // here, we take the approach to allow it. If both calls fail, the
          // returned promise's will take precedence.
          promise.then(prev, function (cause) { prev(err || cause); });
        }
      }
    };
  }
}

/** Public API to promisify a single client or server. */
function promisify(clientOrServer) {
  if (clientOrServer instanceof avro.Service.Client) {
    promisifyClient(clientOrServer);
  } else if (clientOrServer instanceof avro.Service.Server) {
    promisifyServer(clientOrServer);
  } else {
    throw new TypeError(`unable to promisify ${clientOrServer}`);
  }
  return promisifyMiddleware(clientOrServer);
}

/** Permanently and globally promisify clients and servers. */
function promisifyAll() {
  promisifyClient(avro.Service.Client.prototype);
  promisifyServer(avro.Service.Server.prototype);
  promisifyMiddleware(avro.Service.Client.prototype);
  promisifyMiddleware(avro.Service.Server.prototype);
}

module.exports = {
  LOGICAL_TYPES: {
    'timestamp-ms': DateType
  },
  TYPE_REFS,
  promisify,
  promisifyAll
};
<|endoftext|>


<commit_before><commit_msg>Removed dead code<commit_after><|endoftext|>


<commit_before>window.VarnishDashboard = Ember.Application.create({});

VarnishDashboard.IndexRoute = Ember.Route.extend({
  setupController: function(controller) {
    controller.set('content', ['red', 'yellow', 'blue']);
  }
});

VarnishDashboard.ApplicationController = Ember.Controller.extend({
	first: "Tom",
	last: "Brand"
});

VarnishDashboard.Store = DS.Store.extend({
	revision: 11,
	url: "http://localhost:6085",
	adapter: "DS.RESTAdapter"
});

VarnishDashboard.Stat = DS.Model.extend({

});

DS.RESTAdapter.reopen({
	url: "http://localhost:6085",
	// buildURL: function(record, suffix) {
	//     var s = this._super(record, suffix);
	//     return s + "?callback=?";
	//   }
	ajax: function (url, type, hash) {
	     hash.url = url;
         hash.type = type;
         hash.dataType = 'json';
         hash.contentType = 'application/json; charset=utf-8';
         hash.context = this;

         if (hash.data && type !== 'GET') {
             hash.data = JSON.stringify(hash.data);
         }

         jQuery.ajax(hash).done = function(msg) {
			alert("Recibido");
		};
     },
});<commit_msg>first commit<commit_after>window.VarnishDashboard = Ember.Application.create({});

VarnishDashboard.IndexRoute = Ember.Route.extend({
  setupController: function(controller) {
    controller.set('content', ['red', 'yellow', 'blue']);
  }
});

VarnishDashboard.ApplicationController = Ember.Controller.extend({
	first: "Tom",
	last: "Brand"
});

VarnishDashboard.Store = DS.Store.extend({
	revision: 11,
	url: "http://localhost:6085",
	adapter: "DS.RESTAdapter"
});

VarnishDashboard.Stat = DS.Model.extend({
	timestamp: DS.attr('date'),

});
<|endoftext|>


<commit_before>var _               = require('underscore');
var path = require('path');
var fs = require('fs');
var exists = fs.existsSync || path.existsSync;
var app = require('./app');

   // like the python dictionary pop.
   // return obj[prop] and remove property from obj.
   // if obj[prop] doesn't exist, return default value.
exports.pyPop = function (obj, prop, defaultValue){
   obj = (obj === undefined) ? {} : obj;

   if(obj.hasOwnProperty(prop)){
       var rv =  obj[prop];
       delete obj[prop];
       return rv;
   } else {
       return defaultValue;
   }
}

// useful type determination: Function, Object, string, number, Array, null, undefined, RegExp.
exports.trueTypeof = function(value) {
    if (value === null) {
        return "null";
    }
    var t = typeof value;
    switch(t) {
        case "function":
        case "object":
            if (value.constructor) {
                if (value.constructor.name) {
                    return value.constructor.name;
                } else {
                    // Internet Explorer
                    // Anonymous functions are stringified as follows: 'function () {}'
                    // => the regex below does not match
                    var match = value.constructor.toString().match(/^function (.+)\(.*$/);
                    if (match) {
                        return match[1];
                    }
                }
            }
            // fallback, for nameless constructors etc.
            return Object.prototype.toString.call(value).match(/^\[object (.+)\]$/)[1];
        default:
            return t;
    }
};

exports.getTimeLeft = function(target_date) {
  var difference = target_date - new Date();

  // basic math variables
  var _second = 1000,
    _minute = _second * 60,
    _hour = _minute * 60,
    _day = _hour * 24;

  // calculate dates
  var days = Math.floor(difference / _day),
    hours = Math.floor((difference % _day) / _hour),
    minutes = Math.floor((difference % _hour) / _minute),
    seconds = Math.floor((difference % _minute) / _second);

  // fix dates so that it will show two digits
  days = (String(days).length >= 2) ? days : '0' + days;
  hours = (String(hours).length >= 2) ? hours : '0' + hours;
  minutes = (String(minutes).length >= 2) ? minutes : '0' + minutes;
  seconds = (String(seconds).length >= 2) ? seconds : '0' + seconds;

  return [days, hours, minutes, seconds];
}

// route middleware to make sure a user is logged in
var isLoggedIn = function(doRecentCheck, req, res, next) {
  var config = app.app.loadConfig('auth');

  var failed = !req.isAuthenticated();
  if (!failed && doRecentCheck) {
    //note: loginTime maybe a string since sessions are serialized as json
    failed = !req.session.loginTime ||
      !(new Date() - new Date(req.session.loginTime) <= config.recentLoginTimeoutSeconds*1000);
    if (failed) { //force reauthentication (e.g. for facebook login for example)
      req.session.reauthenticate  = true;
    }
  }

  // if user is not authenticated, redirect to login
  if (failed) {
    // remember original requested url
    req.session.returnTo = req.url;
    res.redirect('/login');
    return;
  }

  // user may appear logged in but with an unconfirmed account
  // redirect to the email verification page
  if (config.requireEmailVerification
      && isDefined(req, 'user.local.verified') && !req.user.local.verified) {
    console.log("local user has not been verified!");
    res.redirect('/verification');
    return;
  }

  return next();
}

// route middleware to make sure a user is logged in
exports.isLoggedIn = _.partial(isLoggedIn, false);

// route middleware to make sure a user is logged in
exports.isRecentlyLoggedIn = _.partial(isLoggedIn, true);

// vars set here are available on all pages with sharedPageVars in the route
exports.sharedPageVars = function(req, res, next) {
  console.log("deprecated; dont use this, it no longer does anything");
  next();
}

exports.renderer = function(view) {
  return function(req, res) { res.render(view);};
};

exports.eatErr = function(cb) {
  var startstack = new Error().stack;
  return function() {
    try {
      return cb.apply(cb, arguments);
    } catch (err){
      console.log("caught error:", err.stack);
      console.log("callback set at:", startstack);
    }
  }
};

exports.searchPath = function(paths, local) {
  for (var i=0; i< paths.length; i++) {
    var test = path.join(paths[i], local);
    if (exists(test)) {
      return test;
    }
  }
  throw new Error(local + " not found on " + JSON.stringify(paths));
}

// check for nested object properties
var isDefined = exports.isDefined = function(obj, propertyPath) {
  var target = obj;
  var props = propertyPath.split('.');
  for (var i = 0; i < props.length; i++) {
    var propName = props[i];
    if (typeof(target) == 'object' && propName in target) {
      target = target[propName];
    } else {
      return false;
    }
  };
  return true;
}
<commit_msg>fix order of initialization issue<commit_after>var _               = require('underscore');
var path = require('path');
var fs = require('fs');
var exists = fs.existsSync || path.existsSync;

   // like the python dictionary pop.
   // return obj[prop] and remove property from obj.
   // if obj[prop] doesn't exist, return default value.
exports.pyPop = function (obj, prop, defaultValue){
   obj = (obj === undefined) ? {} : obj;

   if(obj.hasOwnProperty(prop)){
       var rv =  obj[prop];
       delete obj[prop];
       return rv;
   } else {
       return defaultValue;
   }
}

// useful type determination: Function, Object, string, number, Array, null, undefined, RegExp.
exports.trueTypeof = function(value) {
    if (value === null) {
        return "null";
    }
    var t = typeof value;
    switch(t) {
        case "function":
        case "object":
            if (value.constructor) {
                if (value.constructor.name) {
                    return value.constructor.name;
                } else {
                    // Internet Explorer
                    // Anonymous functions are stringified as follows: 'function () {}'
                    // => the regex below does not match
                    var match = value.constructor.toString().match(/^function (.+)\(.*$/);
                    if (match) {
                        return match[1];
                    }
                }
            }
            // fallback, for nameless constructors etc.
            return Object.prototype.toString.call(value).match(/^\[object (.+)\]$/)[1];
        default:
            return t;
    }
};

exports.getTimeLeft = function(target_date) {
  var difference = target_date - new Date();

  // basic math variables
  var _second = 1000,
    _minute = _second * 60,
    _hour = _minute * 60,
    _day = _hour * 24;

  // calculate dates
  var days = Math.floor(difference / _day),
    hours = Math.floor((difference % _day) / _hour),
    minutes = Math.floor((difference % _hour) / _minute),
    seconds = Math.floor((difference % _minute) / _second);

  // fix dates so that it will show two digits
  days = (String(days).length >= 2) ? days : '0' + days;
  hours = (String(hours).length >= 2) ? hours : '0' + hours;
  minutes = (String(minutes).length >= 2) ? minutes : '0' + minutes;
  seconds = (String(seconds).length >= 2) ? seconds : '0' + seconds;

  return [days, hours, minutes, seconds];
}

// route middleware to make sure a user is logged in
var isLoggedIn = function(doRecentCheck, req, res, next) {
  var config = require('./app').app.loadConfig('auth');

  var failed = !req.isAuthenticated();
  if (!failed && doRecentCheck) {
    //note: loginTime maybe a string since sessions are serialized as json
    failed = !req.session.loginTime ||
      !(new Date() - new Date(req.session.loginTime) <= config.recentLoginTimeoutSeconds*1000);
    if (failed) { //force reauthentication (e.g. for facebook login for example)
      req.session.reauthenticate  = true;
    }
  }

  // if user is not authenticated, redirect to login
  if (failed) {
    // remember original requested url
    req.session.returnTo = req.url;
    res.redirect('/login');
    return;
  }

  // user may appear logged in but with an unconfirmed account
  // redirect to the email verification page
  if (config.requireEmailVerification
      && isDefined(req, 'user.local.verified') && !req.user.local.verified) {
    console.log("local user has not been verified!");
    res.redirect('/verification');
    return;
  }

  return next();
}

// route middleware to make sure a user is logged in
exports.isLoggedIn = _.partial(isLoggedIn, false);

// route middleware to make sure a user is logged in
exports.isRecentlyLoggedIn = _.partial(isLoggedIn, true);

// vars set here are available on all pages with sharedPageVars in the route
exports.sharedPageVars = function(req, res, next) {
  console.log("deprecated; dont use this, it no longer does anything");
  next();
}

exports.renderer = function(view) {
  return function(req, res) { res.render(view);};
};

exports.eatErr = function(cb) {
  var startstack = new Error().stack;
  return function() {
    try {
      return cb.apply(cb, arguments);
    } catch (err){
      console.log("caught error:", err.stack);
      console.log("callback set at:", startstack);
    }
  }
};

exports.searchPath = function(paths, local) {
  for (var i=0; i< paths.length; i++) {
    var test = path.join(paths[i], local);
    if (exists(test)) {
      return test;
    }
  }
  throw new Error(local + " not found on " + JSON.stringify(paths));
}

// check for nested object properties
var isDefined = exports.isDefined = function(obj, propertyPath) {
  var target = obj;
  var props = propertyPath.split('.');
  for (var i = 0; i < props.length; i++) {
    var propName = props[i];
    if (typeof(target) == 'object' && propName in target) {
      target = target[propName];
    } else {
      return false;
    }
  };
  return true;
}
<|endoftext|>


<commit_before>define
([
    'jquery', 'jquery/superdesk', 'dust/core', 'utils/sha512', 'jquery/tmpl', 'jquery/rest', 'bootstrap',
    'tmpl!auth',
],
function($, superdesk, dust, jsSHA)
{
    var AuthLogin = function(username, password, logintoken){
		var shaObj = new jsSHA(logintoken, "ASCII"),shaPassword = new jsSHA(password, "ASCII"),
			authLogin = new $.rest('Authentication');
			authLogin.resetData().insert({
			UserName: username,
			LoginToken: logintoken,
			HashedLoginToken: shaObj.getHMAC(username+shaPassword.getHash("SHA-512", "HEX"), "ASCII", "SHA-512", "HEX")
		}).done(function(user){
			localStorage.setItem('superdesk.login.session', user.Session);
			localStorage.setItem('superdesk.login.id', user.Id);
			localStorage.setItem('superdesk.login.name', user.UserName);
			localStorage.setItem('superdesk.login.email', user.EMail);
			$(authLogin).trigger('success');
		});
		return $(authLogin);
	},
	AuthToken = function(username, password) {
		var authToken = new $.rest('Authentication');
		authToken.resetData().select({ userName: username }).done(
			function(data){
				authLogin = AuthLogin(username, password, data.Token);
				authLogin.on('failed', function(){
					$(authToken).trigger('failed', 'authToken');
				}).on('success', function(){
					$(authToken).trigger('success');
				});
			}
		);
		return $(authToken);
	},
	AuthApp =
    {
        success: $.noop,
		showed: false,
        require: function()
        {
			if(AuthApp.showed) return;
            var self = this; // rest
			AuthApp.showed = true;
            $.tmpl('auth', null, function(e, o)
            {
                var dialog = $(o).eq(0).dialog
                ({
                    draggable: false,
                    resizable: false,
                    modal: true,
                    width: "40.1709%",
                    buttons:
                    [
                         { text: "Login", click: function(){ $(form).trigger('submit'); }, class: "btn btn-primary"},
                         { text: "Close", click: function(){ $(this).dialog('close'); }, class: "btn"}
                    ]
                }),
                    form = dialog.find('form');
                form.off('submit.superdesk')
                .on('submit.superdesk', function(event)
                {
                    var username = $(this).find('#username'), password=$(this).find('#password');
					AuthToken(username.val(), password.val()).on('failed',function(evt, type){
						username.val('');
						password.val('')
					}).on('success', function(){
                        AuthApp.success && AuthApp.success.apply();
						$(dialog).dialog('close');
						AuthApp.showed = false;
					});
                    event.preventDefault();

                });
            });
        }
    };
    return AuthApp;
});<commit_msg>NR-226: Create login functionality in Ally framework<commit_after>define
([
    'jquery', 'jquery/superdesk', 'dust/core', 'utils/sha512', 'jquery/tmpl', 'jquery/rest', 'bootstrap',
    'tmpl!auth',
],
function($, superdesk, dust, jsSHA)
{
    var AuthLogin = function(username, password, logintoken){
		var shaObj = new jsSHA(logintoken, "ASCII"),shaPassword = new jsSHA(password, "ASCII"),
			authLogin = new $.rest('Authentication');
			authLogin.resetData().insert({
			UserName: username,
			LoginToken: logintoken,
			HashedLoginToken: shaObj.getHMAC(username+shaPassword.getHash("SHA-512", "HEX"), "ASCII", "SHA-512", "HEX")
		}).done(function(user){
			localStorage.setItem('superdesk.login.session', user.Session);
			localStorage.setItem('superdesk.login.id', user.Id);
			localStorage.setItem('superdesk.login.name', user.UserName);
			localStorage.setItem('superdesk.login.email', user.EMail);
			$.restAuth.prototype.requestOptions.headers.Authorization = localStorage.getItem('superdesk.login.session');
			superdesk.login = {Id: localStorage.getItem('superdesk.login.id'), Name: localStorage.getItem('superdesk.login.name'), EMail: localStorage.getItem('superdesk.login.email')}
			$(authLogin).trigger('success');
		});
		return $(authLogin);
	},
	AuthToken = function(username, password) {
		var authToken = new $.rest('Authentication');
		authToken.resetData().select({ userName: username }).done(
			function(data){
				authLogin = AuthLogin(username, password, data.Token);
				authLogin.on('failed', function(){
					$(authToken).trigger('failed', 'authToken');
				}).on('success', function(){
					$(authToken).trigger('success');
				});
			}
		);
		return $(authToken);
	},
	AuthApp =
    {
        success: $.noop,
		showed: false,
        require: function()
        {
			if(AuthApp.showed) return;
            var self = this; // rest
			AuthApp.showed = true;
            $.tmpl('auth', null, function(e, o)
            {
                var dialog = $(o).eq(0).dialog
                ({
                    draggable: false,
                    resizable: false,
                    modal: true,
                    width: "40.1709%",
                    buttons:
                    [
                         { text: "Login", click: function(){ $(form).trigger('submit'); }, class: "btn btn-primary"},
                         { text: "Close", click: function(){ $(this).dialog('close'); }, class: "btn"}
                    ]
                }),
                    form = dialog.find('form');
                form.off('submit.superdesk')
                .on('submit.superdesk', function(event)
                {
                    var username = $(this).find('#username'), password=$(this).find('#password');
					AuthToken(username.val(), password.val()).on('failed',function(evt, type){
						//username.val('');
						password.val('')
					}).on('success', function(){
                        AuthApp.success && AuthApp.success.apply();
						$(dialog).dialog('close');
						AuthApp.showed = false;
					});
                    event.preventDefault();

                });
            });
        }
    };
    return AuthApp;
});<|endoftext|>


<commit_before>#!/usr/bin/env node

const Ajv = require('ajv');
const fs = require("fs");
const path = require("path");
const shell = require("shelljs");
const Main = require("../output/Main");
const Samples = require("../output/Samples");

function pathToString(path) {
    return path.join(".");
}

// https://stackoverflow.com/questions/1068834/object-comparison-in-javascript
function deepEquals(x, y, path) {
    var i;
    var p;

    // remember that NaN === NaN returns false
    // and isNaN(undefined) returns true
    if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
        return true;
    }

    // Compare primitives and functions.
    // Check if both arguments link to the same object.
    // Especially useful on the step where we compare prototypes
    if (x === y) {
        return true;
    }

    if ((x instanceof String && y instanceof String) || (x instanceof Number && y instanceof Number)) {
        if (x.toString() !== y.toString()) {
            console.error(`Number or string not equal at path ${pathToString(path)}.`);
            return false;
        }
        return true;
    }

    // At last checking prototypes as good as we can
    if (!(x instanceof Object && y instanceof Object)) {
        console.error(`One is not an object at path ${pathToString(path)}.`)
        return false;
    }

    if (x.constructor !== y.constructor) {
        console.error(`Not the same constructor at path ${pathToString(path)}.`);
        return false;
    }

    if (x.prototype !== y.prototype) {
        console.error(`Not the same prototype at path ${pathToString(path)}.`);
        return false;
    }

    if (Array.isArray(x)) {
        if (x.length !== y.length){
            console.error(`Arrays don't have the same length at path ${pathToString(path)}.`);
            return false;
        }
        for (i = 0; i < x.length; i++) {
            path.push(i)
            if (!deepEquals(x[i], y[i], path))
                return false;
            path.pop();
        }
        return true;
    }

    for (p in y) {
        // We allow properties in y that aren't present in x
        // so long as they're null.
        if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
            if (y[p] !== null) {
                console.error(`Non-null property ${p} is not expected at path ${pathToString(path)}.`);
                return false;
            }
            continue;
        }
        if (typeof y[p] !== typeof x[p]) {
            console.error(`Properties ${p} don't have the same types at path ${pathToString(path)}.`);
            return false;
        }
    }

    for (p in x) {
        if (x.hasOwnProperty(p) && !y.hasOwnProperty(p)) {
            console.error(`Expected property ${p} not found at path ${pathToString(path)}.`);
            return false;
        }
        if (typeof x[p] !== typeof y[p]) {
            console.error(`Properties ${p} don't have the same types at path ${pathToString(path)}.`);
            return false;
        }

        switch (typeof(x[p])) {
        case 'object':
            path.push(p);
            if (!deepEquals(x[p], y[p], path)) {
                return false;
            }
            path.pop(p);
            break;

        default:
            if (x[p] !== y[p]) {
                console.error(`Non-object properties ${p} are not equal at path ${pathToString(path)}.`)
                return false;
            }
            break;
        }
    }

    return true;
}

function exec(s, opts, cb) {
    let result = shell.exec(s, opts, cb);
    if (result.code !== 0) {
        console.error(`Error: Command failed: ${s}`);
        shell.exit(result.code);
    }
    return result;
}

function execAndCompare(cmd, p, knownFails) {
    let outputString = exec(cmd, {silent:true}).stdout;
    if (knownFails.indexOf(path.basename(p)) < 0) {
        let outputJSON = JSON.parse(outputString);
        let inputJSON = JSON.parse(fs.readFileSync(p));
        if (!deepEquals(inputJSON, outputJSON, [])) {
            console.error("Error: Output is not equivalent to input.");
            process.exit(1);
        }
    } else {
        console.log("Known to fail - not checking output.");
    }
}

function absolutize(p) {
    if (path.isAbsolute(p))
        return p;
    return path.join(process.cwd(), p);
}

function execQuicktype(source, output, sourceLanguage) {
    exec(`node ../../cli/quicktype.js --srcLang "${sourceLanguage}" -o "${output}" "${source}"`);
}

function runTests(description, samples, dir, prepareCmd, filename, testFn) {
    shell.cd(dir);
    if (prepareCmd)
        shell.exec(prepareCmd, { silent: true });

    samples.forEach((sample) => {
        let stats = fs.statSync(sample);
        if (stats.size > 32 * 1024 * 1024) {
            console.log(`* Skipping ${sample} because it's too large`);
            return;
        }
        console.error(`* Building ${description} for ${sample}`);
        execQuicktype(sample, filename, "json");
        testFn(sample);
    });

    shell.cd("../..");
}

function testCSharp(samples, knownFails) {
    runTests("C# code", samples, "test/csharp", "dotnet restore", "QuickType.cs",
        function (p) {
            execAndCompare(`dotnet run "${p}"`, p, knownFails);
        }
    );
}

function testGolang(samples, knownFails) {
    runTests("Go code", samples, "test/golang", null, "quicktype.go",
        function (p) {
            execAndCompare(`go run main.go quicktype.go < "${p}"`, p, knownFails);
        }
    );
}

function testJsonSchema(samples, knownFails, knownGoFails) {
    runTests("JSON Schema", samples, "test/golang", null, "schema.json",
        function (p) {
            let input = JSON.parse(fs.readFileSync(p));
            let schema = JSON.parse(fs.readFileSync("schema.json"));
            let ajv = new Ajv();
            let valid = ajv.validate(schema, input);
            if (!valid) {
                console.log("Error: Generated schema does not validate input JSON.");
                process.exit(1);
            }
            execQuicktype("schema.json", "quicktype.go", "json-schema");
            execAndCompare(`go run main.go quicktype.go < "${p}"`, p, knownGoFails);
        }
    );
}

function testAll(samples, goFails, csFails, jsonSchemaFails) {
    testJsonSchema(samples, jsonSchemaFails, goFails);
    testGolang(samples, goFails);
    testCSharp(samples, csFails);
}

function testAllInDir(dir, goFails, csFails, jsonSchemaFails) {
    let samples =
        fs.readdirSync(dir)
            .filter((name) => name.endsWith(".json") && !name.startsWith("."))
            .map((name) => absolutize(path.join(dir, name)));
    testAll(samples, goFails, csFails, jsonSchemaFails);
}

function main(sources) {
    if (sources.length == 0) {
        let samples = Samples.samples.map((name) => path.join("..", "..", "app", "public", "sample", "json", name));
        testAll(samples, [], [], []);
        testAllInDir(path.join("test", "inputs", "json"), ["identifiers.json"], [], []);
    } else {
        sources.forEach((source) => {
            if (fs.lstatSync(source).isDirectory()) {
                testAllInDir(source, [], [], []);
            } else {
                testAll([absolutize(source)], [], [], []);
            }
        });
    }
}

// skip 2 `node` args
main(process.argv.slice(2));
<commit_msg>Compare numbers approximately because Newtonsoft's bad at math<commit_after>#!/usr/bin/env node

const Ajv = require('ajv');
const fs = require("fs");
const path = require("path");
const shell = require("shelljs");
const Main = require("../output/Main");
const Samples = require("../output/Samples");

function pathToString(path) {
    return path.join(".");
}

// https://stackoverflow.com/questions/1068834/object-comparison-in-javascript
function deepEquals(x, y, path) {
    var i;
    var p;

    // remember that NaN === NaN returns false
    // and isNaN(undefined) returns true
    if (typeof x === 'number' && typeof y === 'number') {
        if (isNaN(x) && isNaN(y))
            return true;
        // because sometimes Newtonsoft.JSON is not exact
        return Math.fround(x) === Math.fround(y);
    }

    // Compare primitives and functions.
    // Check if both arguments link to the same object.
    // Especially useful on the step where we compare prototypes
    if (x === y) {
        return true;
    }

    if ((x instanceof String && y instanceof String) || (x instanceof Number && y instanceof Number)) {
        if (x.toString() !== y.toString()) {
            console.error(`Number or string not equal at path ${pathToString(path)}.`);
            return false;
        }
        return true;
    }

    // At last checking prototypes as good as we can
    if (!(x instanceof Object && y instanceof Object)) {
        console.error(`One is not an object at path ${pathToString(path)}.`)
        return false;
    }

    if (x.constructor !== y.constructor) {
        console.error(`Not the same constructor at path ${pathToString(path)}.`);
        return false;
    }

    if (x.prototype !== y.prototype) {
        console.error(`Not the same prototype at path ${pathToString(path)}.`);
        return false;
    }

    if (Array.isArray(x)) {
        if (x.length !== y.length){
            console.error(`Arrays don't have the same length at path ${pathToString(path)}.`);
            return false;
        }
        for (i = 0; i < x.length; i++) {
            path.push(i)
            if (!deepEquals(x[i], y[i], path))
                return false;
            path.pop();
        }
        return true;
    }

    for (p in y) {
        // We allow properties in y that aren't present in x
        // so long as they're null.
        if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) {
            if (y[p] !== null) {
                console.error(`Non-null property ${p} is not expected at path ${pathToString(path)}.`);
                return false;
            }
            continue;
        }
        if (typeof y[p] !== typeof x[p]) {
            console.error(`Properties ${p} don't have the same types at path ${pathToString(path)}.`);
            return false;
        }
    }

    for (p in x) {
        if (x.hasOwnProperty(p) && !y.hasOwnProperty(p)) {
            console.error(`Expected property ${p} not found at path ${pathToString(path)}.`);
            return false;
        }
        if (typeof x[p] !== typeof y[p]) {
            console.error(`Properties ${p} don't have the same types at path ${pathToString(path)}.`);
            return false;
        }

        switch (typeof(x[p])) {
        case 'object':
            path.push(p);
            if (!deepEquals(x[p], y[p], path)) {
                return false;
            }
            path.pop(p);
            break;

        default:
            if (x[p] !== y[p]) {
                console.error(`Non-object properties ${p} are not equal at path ${pathToString(path)}.`)
                return false;
            }
            break;
        }
    }

    return true;
}

function exec(s, opts, cb) {
    let result = shell.exec(s, opts, cb);
    if (result.code !== 0) {
        console.error(`Error: Command failed: ${s}`);
        shell.exit(result.code);
    }
    return result;
}

function execAndCompare(cmd, p, knownFails) {
    let outputString = exec(cmd, {silent:true}).stdout;
    if (knownFails.indexOf(path.basename(p)) < 0) {
        let outputJSON = JSON.parse(outputString);
        let inputJSON = JSON.parse(fs.readFileSync(p));
        if (!deepEquals(inputJSON, outputJSON, [])) {
            console.error("Error: Output is not equivalent to input.");
            process.exit(1);
        }
    } else {
        console.log("Known to fail - not checking output.");
    }
}

function absolutize(p) {
    if (path.isAbsolute(p))
        return p;
    return path.join(process.cwd(), p);
}

function execQuicktype(source, output, sourceLanguage) {
    exec(`node ../../cli/quicktype.js --srcLang "${sourceLanguage}" -o "${output}" "${source}"`);
}

function runTests(description, samples, dir, prepareCmd, filename, testFn) {
    shell.cd(dir);
    if (prepareCmd)
        shell.exec(prepareCmd, { silent: true });

    samples.forEach((sample) => {
        let stats = fs.statSync(sample);
        if (stats.size > 32 * 1024 * 1024) {
            console.log(`* Skipping ${sample} because it's too large`);
            return;
        }
        console.error(`* Building ${description} for ${sample}`);
        execQuicktype(sample, filename, "json");
        testFn(sample);
    });

    shell.cd("../..");
}

function testCSharp(samples, knownFails) {
    runTests("C# code", samples, "test/csharp", "dotnet restore", "QuickType.cs",
        function (p) {
            execAndCompare(`dotnet run "${p}"`, p, knownFails);
        }
    );
}

function testGolang(samples, knownFails) {
    runTests("Go code", samples, "test/golang", null, "quicktype.go",
        function (p) {
            execAndCompare(`go run main.go quicktype.go < "${p}"`, p, knownFails);
        }
    );
}

function testJsonSchema(samples, knownFails, knownGoFails) {
    runTests("JSON Schema", samples, "test/golang", null, "schema.json",
        function (p) {
            let input = JSON.parse(fs.readFileSync(p));
            let schema = JSON.parse(fs.readFileSync("schema.json"));
            let ajv = new Ajv();
            let valid = ajv.validate(schema, input);
            if (!valid) {
                console.log("Error: Generated schema does not validate input JSON.");
                process.exit(1);
            }
            execQuicktype("schema.json", "quicktype.go", "json-schema");
            execAndCompare(`go run main.go quicktype.go < "${p}"`, p, knownGoFails);
        }
    );
}

function testAll(samples, goFails, csFails, jsonSchemaFails) {
    testJsonSchema(samples, jsonSchemaFails, goFails);
    testGolang(samples, goFails);
    testCSharp(samples, csFails);
}

function testAllInDir(dir, goFails, csFails, jsonSchemaFails) {
    let samples =
        fs.readdirSync(dir)
            .filter((name) => name.endsWith(".json") && !name.startsWith("."))
            .map((name) => absolutize(path.join(dir, name)));
    testAll(samples, goFails, csFails, jsonSchemaFails);
}

function main(sources) {
    if (sources.length == 0) {
        let samples = Samples.samples.map((name) => path.join("..", "..", "app", "public", "sample", "json", name));
        testAll(samples, [], [], []);
        testAllInDir(path.join("test", "inputs", "json"), ["identifiers.json"], [], []);
    } else {
        sources.forEach((source) => {
            if (fs.lstatSync(source).isDirectory()) {
                testAllInDir(source, [], [], []);
            } else {
                testAll([absolutize(source)], [], [], []);
            }
        });
    }
}

// skip 2 `node` args
main(process.argv.slice(2));
<|endoftext|>