<commit_before>Ext.define('WsapiReference', {
        extend:'Rally.app.App',
        componentCls:'app',

        baseServerUrl:'',

        launch:function () {
            this.baseServerUrl =  new Rally.sdk.env.Environment().getServer().getUrl();
            this.add({
                xtype:'panel',
                cls: 'mainPanel',
                width:'100%',
                height:'100%',
//                title:'WSAPI Object Models',
                layout:'border',
                items:[
                    {
                        region:'north',
                        itemId:'northRegion',
                        height:45,
                        margins:'5 5 10 5',
                        layout:'hbox',
                        items:this._getNorthItems()
                    },
                    {
                        title:'Query',
                        region:'west',
                        itemId:'westRegion',
                        margins:'0 5 5 5',
                        width:260,
                        collapsible:true,
                        layout:'fit'
                    },
                    {
                        title:'Attributes',
                        region:'center',
                        itemId:'centerRegion',
                        layout:'fit',
                        margins:'0 5 5 5'
                    },
                    {
                        title:'Misc',
                        region:'east',
                        itemId:'eastRegion',
                        split: true,
                        width:250,
                        collapsible:true,
                        collapsed: true,
                        layout:'fit',
                        margins:'0 5 5 0'
                    }
                ]
            });
        },

        _getNorthItems:function () {
            return [
//                {
//                    xtype:'rallycombobox',
//                    fieldLabel:'Wsapi Version:',
//                    store: Ext.create('Ext.data.Store', {
//                        fields: ['version'],
//                        data : [
//                            {version:'Coming Soon!'}
//                        ]
//                    }),
//                    queryMode: 'local',
//                    displayField: 'version',
//                    valueField: 'version',
//                    value: 'Coming Soon!',
//                    style:{
//                        margin:'10px'
//                    }
//                },
                {
                    xtype:'rallycombobox',
                    fieldLabel:'Workspace:',
                    itemId:'workspaceCombobox',
                    storeConfig:{
                        model:'Workspace',
                        autoLoad:true,
                        fetch:['Name', 'ObjectID'],
                        sorters:[
                            { property:'Name' }
                        ],
                        context:{
                            project:null,
                            workspace:'null' // must be a string
                        },
                        limit:Infinity
                    },
                    style:{
                        margin:'10px'
                    },
                    listeners:{
                        afterrender:this._onWorkspaceComboboxChange,
                        change:this._onWorkspaceComboboxChange,
                        scope:this
                    }
                }
            ];
        },

        _onWorkspaceComboboxChange:function (combobox, newvalue, oldvalue) {
            if (this.objectCombobox) {
                this.objectModel = null;
                this.objectCombobox.destroy();
            }

            this._refresh();

            // TODO filter out any objects we don't want to display
            this.objectCombobox = this.down('#northRegion').add({
                xtype:'rallycombobox',
                fieldLabel:'Object Model:',
                itemId:'objectCombobox',
                value: ' ',
                storeConfig:{
                    autoLoad:true,
                    model:'TypeDefinition',
                    fetch:['DisplayName', 'ElementName', 'Name'],
                    sorters:[
                        {
                            property:'Name',
                            direction:'ASC'
                        }
                    ],
                    context:{
                        project:null,
                        workspace:Rally.util.Ref.getRelativeUri(combobox.getValue())
                    },
                    listeners: {
                        load:{
                            fn:function(store, data){
                                store.insert(new store.model, 0);
                            }
                        }
                    }
                },
                valueField:'Name',
                listeners:{
                    change:{
                        fn:this._onObjectComboboxChanged,
                        workspace:combobox,
                        scope:this
                    }
                },
                style:{
                    margin:'10px'
                }
            });
        },

        _onObjectComboboxChanged:function (combobox, newValue, oldValue, eOpts) {
            this.objectModel = newValue.length > 1 ? newValue : null;
            Ext.create('Rally.data.WsapiDataStore', {
                model:'TypeDefinition',
                autoLoad:true,
                filters:{
                    property:'Name',
                    operator:'=',
                    value:newValue
                },
                sorters:[
                    {
                        property:'Name',
                        direction:'ASC'
                    }
                ],
                listeners:{
                    load:this._onObjectModelDataLoaded,
                    scope:this
                },
                context:{
                    project:null,
                    workspace: Rally.util.Ref.getRelativeUri(this.down('#workspaceCombobox').getValue())
                }
            });
        },

        _refresh:function () {
            if (!this.objectModel && this.down('#westRegion').title !== 'Query') {
                this.down('#westRegion').setTitle('Query');
                this.down('#centerRegion').setTitle('Attributes');
                this.down('#eastRegion').setTitle('Misc');
            }

            this.down('#westRegion').removeAll();
            this.down('#centerRegion').removeAll();
            this.down('#eastRegion').removeAll();
        },

        _onObjectModelDataLoaded:function (store, data) {
            this._refresh();
            if(data.length > 0){
                this._setWestItems();
                this._setCenterItems(data);
                this._setEastItems(data);
            }
        },

        _setWestItems:function () {
            this.down('#westRegion').setTitle(this.objectModel + ' Query');
            this.down('#westRegion').add(
                    {
                        xtype:'container',
                        items:[
                            {
                                xtype:'container',
                                defaults:{
                                    labelWidth:100
                                },
                                items:[
                                    {
                                        xtype:'rallytextfield',
                                        fieldLabel:'Query String',
                                        itemId:'queryString'
                                    },
                                    {
                                        xtype:'rallytextfield',
                                        fieldLabel:'Order',
                                        itemId:'order' //TODO add Desc/Asc option
                                    },
                                    {
                                        xtype:'numberfield',
                                        fieldLabel:'Start Index',
                                        value:1,
                                        itemId:'startIndex',
                                        width:150,
                                        allowBlank:false
                                    },
                                    {
                                        xtype:'numberfield',
                                        fieldLabel:'Page Size',
                                        value:20,
                                        itemId:'pageSize',
                                        width:150,
                                        allowBlank:false
                                    },
                                    {
                                        xtype:'checkbox',
                                        fieldLabel:'Full objects',
                                        itemId:'fullObjects',
                                        value:true
                                    },
                                    {
                                        xtype:'checkbox',
                                        fieldLabel:'XSL stylesheet',
                                        itemId:'xslStylesheet',
                                        value:true
                                    },
                                    {
                                        xtype:'checkbox',
                                        fieldLabel:'JSON output',
                                        itemId:'jsonOutput',
                                        value:true
                                    },
                                    {
                                        xtype:'checkbox',
                                        fieldLabel:'Open new tab',
                                        itemId:'newTab',
                                        value:true
                                    }
                                ]
                            },
                            {
                                xtype:'container',
                                layout:{
                                    type:'hbox',
                                    pack:'center'
                                },
                                style:{
                                    paddingTop:'20px'
                                },
                                items:[
                                    {
                                        xtype:'rallybutton',
                                        text:'Query',
                                        handler:Ext.bind(this._onQueryButtonClick, this),
                                        style:{
                                            display:'inline-block',
                                            marginRight:'10px'
                                        }
                                    },
                                    {
                                        xtype:'container',
                                        html:'Reset',
                                        style:{
                                            display:'inline-block',
                                            paddingTop:'5px'
                                        },
                                        listeners:{
                                            afterrender:{
                                                fn:function (container, eOpts) {
                                                    Ext.fly(container.el.dom.firstChild).on('click', function(){
                                                        var westRegion = this.down('#westRegion');
                                                        westRegion.down('#queryString').setValue('');
                                                        westRegion.down('#order').setValue('');
                                                        westRegion.down('#pageSize').setValue(20);
                                                        westRegion.down('#startIndex').setValue(1);
                                                        westRegion.down('#fullObjects').reset();
                                                        westRegion.down('#xslStylesheet').reset();
                                                        westRegion.down('#jsonOutput').reset();
                                                        westRegion.down('#newTab').reset();
                                                        westRegion.down('#queryString').focus();
                                                    }, this);
                                                }
                                            },
                                            scope: this
                                        }
                                    }
                                ]
                            }

                        ],
                        style:{
                            padding:'10px'
                        }

                    });
        },

        _setCenterItems:function (data) {
            this.down('#centerRegion').setTitle(this.objectModel + ' Attributes');
            this.down('#centerRegion').add(
                    {
                        xtype:'treepanel',
                        itemId:'treepanel',
                        flex:1,
                        store:Ext.create('Ext.data.TreeStore', {
                            fields:['Name', 'Field', 'Value', 'Filter'],
                            root:{
                                expanded:true,
                                text:this.objectModel + ' Object Model',
                                children:this._buildRows(data)
                            }
                        }),
                        rootVisible:false,
                        listeners: {
                            afterrender: {
                                fn: function(tree, eOpts){
                                    tree.filterableRootNode = tree.getRootNode();
                                }
                            },
                            cellclick: {
                                fn: function (o, idx, column, e) {
                                    if (column == 2) {
                                        var objectModel = idx.getElementsByTagName('a')[0].id;
                                        if(objectModel){
                                            this.objectCombobox.setValue(objectModel);
                                        }
                                    }
                                },
                                scope: this
                            }
                        },
                        columns:[
                            {
                                xtype:'treecolumn',
                                header:"Attribute",
                                dataIndex:'Name',
                                width:200
                            },
                            {
                                xtype:'gridcolumn',
                                header:'Field',
                                dataIndex:'Field',
                                menuDisabled:true,
                                sortable:false,
                                width:200
                            },
                            {
                                xtype:'gridcolumn',
                                header:'Value',
                                dataIndex:'Value',
                                menuDisabled:true,
                                sortable:false,
                                flex:1
                            }
                        ],
                        dockedItems:[
                            {
                                xtype:'toolbar',
                                dock:'top',
                                layout:{
                                    pack:'end'
                                },
                                items:[
                                    {
                                        xtype:'checkbox',
                                        boxLabel:'Expand All',
                                        itemId:'expandCheckbox',
                                        style:{
                                            fontSize:'9px',
                                            lineHeight:'12px',
                                            marginRight:'5px'
                                        },
                                        handler:function (checkbox, checked) {
                                            if (checked) {
                                                this.up('#treepanel').expandAll();
                                            } else {
                                                this.up('#treepanel').collapseAll();
                                            }
                                        }
                                    },
                                    '-',
                                    {
                                        xtype:'rallytextfield',
                                        fieldLabel:'Filter',
                                        enableKeyEvents:true,
                                        listeners:{
                                            focus:{
                                                fn:function (view, record, item, index, even) {
                                                    this.setValue("");
                                                }
                                            },
                                            keyup:{
                                                fn:function (view, record, item, index, even) {
                                                    //TODO fix where filtering rows messes up attributes expand/collapse
                                                    this.up('#treepanel').setRootNode(this.up('#treepanel').filterableRootNode);

                                                    var removeArray = [];
                                                    var filterText = this.getValue();

                                                    this.up('#treepanel').getRootNode().cascadeBy(function () {
                                                        if (!(this.data.Filter.toLowerCase().indexOf(filterText.toLowerCase()) > -1)) {
                                                            removeArray.push(this);
                                                        }
                                                    });

                                                    Ext.Array.each(removeArray, function (row) {
                                                        row.remove();
                                                    });
                                                }
                                            }
                                        }
                                    }
                                ]
                            }
                        ]
                    }
            );
        },

        _setEastItems: function(data){
            this.down('#eastRegion').setTitle(this.objectModel + ' Misc');
            this.down('#eastRegion').add(
                    {
                        xtype: 'rallygrid',
                        store: Ext.create('Rally.data.custom.Store', {
                                data: this._getEastRegionData(data),
                                pageSize: 5
                            }),
                        showPagingToolbar: false,
                        columnCfgs: [
                            {
                                dataIndex: 'Field', width: 75
                            },
                            {
                               dataIndex: 'Value', flex: 1
                            }
                         ]
                    }
            );
        },

        _getEastRegionData: function(data){
            var miscData = [];

            if(data[0].data.Parent && data[0].data.Parent._refObjectName){
                miscData.push({Field: 'Parent', Value: data[0].data.Parent._refObjectName});
            }

            if(data[0].data.Note){
                miscData.push({Field: 'Note', Value: data[0].data.Note});
            }

//            miscData.push({Field: 'Usage', Value: 'Coming soon!'});
//            miscData.push({Field: 'Rest URLs', Value: 'Coming soon!'});
            return miscData;
        },

        _buildRows:function (data) {
            var ignoreAttributes = [
                'Creation Date',
                'Object ID',
                'Subscription',
                'Workspace',
                'Changesets',
                'Description',
                'Discussion',
                'Formatted ID',
                'Last Update Date',
                'Name',
                'Notes',
                'Owner',
                'Project',
                'Revision History',
                'Tags'
            ];
            var rows = [];
            Ext.each(data[0].data.Attributes, function (attr) {
                if (!Ext.Array.contains(ignoreAttributes, attr.Name)) {
                    Ext.each(attr, function (field) {
                        var children = [];

                        if (field.AllowedValues.length) {
                            var values = [];
                            Ext.each(field.AllowedValues, function (value) {
                                values.push(value.StringValue);
                            });
                            children.push({Field:'Allowed Values', Value:values.join(", "), leaf:true, Filter:field.Name});
                        }

                        children.push({Field:'Data Type', Value:field.AttributeType, leaf:true, Filter:field.Name});

                        if (field.MaxLength !== -1) {
                            children.push({Field:'Max Length', Value:field.MaxLength, leaf:true, Filter:field.Name});
                        }

                        children.push({Field:'Note', Value:field.Note, leaf:true});

                        if (field.AllowedValueType) {
                            children.push({Field:'Ownership Relationship', Value:field.Owned, leaf:true, Filter:field.Name});
                        }

                        if (field.AllowedQueryOperators.length) {
                            var operators = [];
                            Ext.each(field.AllowedQueryOperators, function (value) {
                                operators.push(value.OperatorName);
                            });
                            children.push({Field:'Query Expression Operators', Value:operators.join(", "), leaf:true, Filter:field.Name});
                        }

                        children.push({Field:'Read Only', Value:field.ReadOnly, leaf:true});

                        if (field.AllowedValueType) {
                            var row = {Field:'Relationship', Value:field.AllowedValueType._refObjectName, leaf:true, Filter:field.Name};

                            //TODO make sure it is a selectable object model from the comobobox
                            if (field.AttributeType === 'OBJECT' || field.AttributeType === 'COLLECTION') {
                                var elementName = field.AllowedValueType._refObjectName.replace(/\s/g, "");
                                row.Value = '' +
                                        field.AllowedValueType._refObjectName + '';
                            }

                            children.push(row);
                        }

                        children.push(
                                {Field:'Required', Value:field.Required, leaf:true, Filter:field.Name},
                                {Field:'Sortable', Value:field.Filterable, leaf:true, Filter:field.Name}
                        );

                        rows.push({Name:field.Name, children:children, Filter:field.Name});
                    }, this);
                }
            }, this);

            this.rows = rows;
            return rows;
        },

        _onQueryButtonClick:function (button) {
            var params = {};
            var extension = '.js?';
            params.pretty = true;
            params.start = this.down('#startIndex').getValue();
            params.pagesize = this.down('#pageSize').getValue();

            if (this.down('#queryString').getValue() !== '') {
                params.query = this.down('#queryString').getValue();
            }

            if (this.down('#order').getValue() !== '') {
                params.order = this.down('#order').getValue();
            }

            if (this.down('#fullObjects').getSubmitValue()) {
                params.fetch = true;
            }

            if (this.down('#xslStylesheet').getSubmitValue()) {
                extension = '?';
            }

            var url = this.baseServerUrl+'/slm/webservice/1.33/' + this.objectModel.toLowerCase().replace(/\s/g, "") + extension + Ext.Object.toQueryString(params);

            if (this.down('#xslStylesheet').getSubmitValue()) {
                url = url + '&stylesheet='+this.baseServerUrl+'/slm/doc/webservice/browser.xsl'
            }

            var workspace = this.down('#workspaceCombobox').getValue();
            url = url + '&workspace=' + workspace.slice(0, workspace.length - 3);

            if (this.down('#jsonOutput').getSubmitValue()) {
                url = this.baseServerUrl+'/slm/doc/webservice/jsonDisplay.jsp?uri=' + url;
            }

            console.log(url);
            if (this.down('#newTab').getSubmitValue()) { // results open in new tab
                window.open(url, '_newtab');
            } else { // results open in dialog
                Ext.create('Ext.window.Window', {
                    title:this.objectModel + ' Query Results',
                    height:400,
                    width:600,
                    layout:'fit',
                    html:'',
                    style:{
                        background:'#fff'
                    }
                }).show();
            }
        }
    });<commit_msg>unhiding the parent objects from the changeset<commit_after>Ext.define('WsapiReference', {
        extend:'Rally.app.App',
        componentCls:'app',

        baseServerUrl:'',

        launch:function () {
            this.baseServerUrl =  new Rally.sdk.env.Environment().getServer().getUrl();
            this.add({
                xtype:'panel',
                cls: 'mainPanel',
                width:'100%',
                height:'100%',
//                title:'WSAPI Object Models',
                layout:'border',
                items:[
                    {
                        region:'north',
                        itemId:'northRegion',
                        height:45,
                        margins:'5 5 10 5',
                        layout:'hbox',
                        items:this._getNorthItems()
                    },
                    {
                        title:'Query',
                        region:'west',
                        itemId:'westRegion',
                        margins:'0 5 5 5',
                        width:260,
                        collapsible:true,
                        layout:'fit'
                    },
                    {
                        title:'Attributes',
                        region:'center',
                        itemId:'centerRegion',
                        layout:'fit',
                        margins:'0 5 5 5'
                    },
                    {
                        title:'Misc',
                        region:'east',
                        itemId:'eastRegion',
                        split: true,
                        width:250,
                        collapsible:true,
                        collapsed: true,
                        layout:'fit',
                        margins:'0 5 5 0'
                    }
                ]
            });
        },

        _getNorthItems:function () {
            return [
//                {
//                    xtype:'rallycombobox',
//                    fieldLabel:'Wsapi Version:',
//                    store: Ext.create('Ext.data.Store', {
//                        fields: ['version'],
//                        data : [
//                            {version:'Coming Soon!'}
//                        ]
//                    }),
//                    queryMode: 'local',
//                    displayField: 'version',
//                    valueField: 'version',
//                    value: 'Coming Soon!',
//                    style:{
//                        margin:'10px'
//                    }
//                },
                {
                    xtype:'rallycombobox',
                    fieldLabel:'Workspace:',
                    itemId:'workspaceCombobox',
                    storeConfig:{
                        model:'Workspace',
                        autoLoad:true,
                        fetch:['Name', 'ObjectID'],
                        sorters:[
                            { property:'Name' }
                        ],
                        context:{
                            project:null,
                            workspace:'null' // must be a string
                        },
                        limit:Infinity
                    },
                    style:{
                        margin:'10px'
                    },
                    listeners:{
                        afterrender:this._onWorkspaceComboboxChange,
                        change:this._onWorkspaceComboboxChange,
                        scope:this
                    }
                }
            ];
        },

        _onWorkspaceComboboxChange:function (combobox, newvalue, oldvalue) {
            if (this.objectCombobox) {
                this.objectModel = null;
                this.objectCombobox.destroy();
            }

            this._refresh();

            // TODO filter out any objects we don't want to display
            this.objectCombobox = this.down('#northRegion').add({
                xtype:'rallycombobox',
                fieldLabel:'Object Model:',
                itemId:'objectCombobox',
                value: ' ',
                storeConfig:{
                    autoLoad:true,
                    model:'TypeDefinition',
                    fetch:['DisplayName', 'ElementName', 'Name'],
                    sorters:[
                        {
                            property:'Name',
                            direction:'ASC'
                        }
                    ],
                    context:{
                        project:null,
                        workspace:Rally.util.Ref.getRelativeUri(combobox.getValue())
                    },
                    listeners: {
                        load:{
                            fn:function(store, data){
                                store.insert(new store.model, 0);
                            }
                        }
                    }
                },
                valueField:'Name',
                listeners:{
                    change:{
                        fn:this._onObjectComboboxChanged,
                        workspace:combobox,
                        scope:this
                    }
                },
                style:{
                    margin:'10px'
                }
            });
        },

        _onObjectComboboxChanged:function (combobox, newValue, oldValue, eOpts) {
            this.objectModel = newValue.length > 1 ? newValue : null;
            Ext.create('Rally.data.WsapiDataStore', {
                model:'TypeDefinition',
                autoLoad:true,
                filters:{
                    property:'Name',
                    operator:'=',
                    value:newValue
                },
                sorters:[
                    {
                        property:'Name',
                        direction:'ASC'
                    }
                ],
                listeners:{
                    load:this._onObjectModelDataLoaded,
                    scope:this
                },
                context:{
                    project:null,
                    workspace: Rally.util.Ref.getRelativeUri(this.down('#workspaceCombobox').getValue())
                }
            });
        },

        _refresh:function () {
            if (!this.objectModel && this.down('#westRegion').title !== 'Query') {
                this.down('#westRegion').setTitle('Query');
                this.down('#centerRegion').setTitle('Attributes');
                this.down('#eastRegion').setTitle('Misc');
            }

            this.down('#westRegion').removeAll();
            this.down('#centerRegion').removeAll();
            this.down('#eastRegion').removeAll();
        },

        _onObjectModelDataLoaded:function (store, data) {
            this._refresh();
            if(data.length > 0){
                this._setWestItems();
                this._setCenterItems(data);
                this._setEastItems(data);
            }
        },

        _setWestItems:function () {
            this.down('#westRegion').setTitle(this.objectModel + ' Query');
            this.down('#westRegion').add(
                    {
                        xtype:'container',
                        items:[
                            {
                                xtype:'container',
                                defaults:{
                                    labelWidth:100
                                },
                                items:[
                                    {
                                        xtype:'rallytextfield',
                                        fieldLabel:'Query String',
                                        itemId:'queryString'
                                    },
                                    {
                                        xtype:'rallytextfield',
                                        fieldLabel:'Order',
                                        itemId:'order' //TODO add Desc/Asc option
                                    },
                                    {
                                        xtype:'numberfield',
                                        fieldLabel:'Start Index',
                                        value:1,
                                        itemId:'startIndex',
                                        width:150,
                                        allowBlank:false
                                    },
                                    {
                                        xtype:'numberfield',
                                        fieldLabel:'Page Size',
                                        value:20,
                                        itemId:'pageSize',
                                        width:150,
                                        allowBlank:false
                                    },
                                    {
                                        xtype:'checkbox',
                                        fieldLabel:'Full objects',
                                        itemId:'fullObjects',
                                        value:true
                                    },
                                    {
                                        xtype:'checkbox',
                                        fieldLabel:'XSL stylesheet',
                                        itemId:'xslStylesheet',
                                        value:true
                                    },
                                    {
                                        xtype:'checkbox',
                                        fieldLabel:'JSON output',
                                        itemId:'jsonOutput',
                                        value:true
                                    },
                                    {
                                        xtype:'checkbox',
                                        fieldLabel:'Open new tab',
                                        itemId:'newTab',
                                        value:true
                                    }
                                ]
                            },
                            {
                                xtype:'container',
                                layout:{
                                    type:'hbox',
                                    pack:'center'
                                },
                                style:{
                                    paddingTop:'20px'
                                },
                                items:[
                                    {
                                        xtype:'rallybutton',
                                        text:'Query',
                                        handler:Ext.bind(this._onQueryButtonClick, this),
                                        style:{
                                            display:'inline-block',
                                            marginRight:'10px'
                                        }
                                    },
                                    {
                                        xtype:'container',
                                        html:'Reset',
                                        style:{
                                            display:'inline-block',
                                            paddingTop:'5px'
                                        },
                                        listeners:{
                                            afterrender:{
                                                fn:function (container, eOpts) {
                                                    Ext.fly(container.el.dom.firstChild).on('click', function(){
                                                        var westRegion = this.down('#westRegion');
                                                        westRegion.down('#queryString').setValue('');
                                                        westRegion.down('#order').setValue('');
                                                        westRegion.down('#pageSize').setValue(20);
                                                        westRegion.down('#startIndex').setValue(1);
                                                        westRegion.down('#fullObjects').reset();
                                                        westRegion.down('#xslStylesheet').reset();
                                                        westRegion.down('#jsonOutput').reset();
                                                        westRegion.down('#newTab').reset();
                                                        westRegion.down('#queryString').focus();
                                                    }, this);
                                                }
                                            },
                                            scope: this
                                        }
                                    }
                                ]
                            }

                        ],
                        style:{
                            padding:'10px'
                        }

                    });
        },

        _setCenterItems:function (data) {
            this.down('#centerRegion').setTitle(this.objectModel + ' Attributes');
            this.down('#centerRegion').add(
                    {
                        xtype:'treepanel',
                        itemId:'treepanel',
                        flex:1,
                        store:Ext.create('Ext.data.TreeStore', {
                            fields:['Name', 'Field', 'Value', 'Filter'],
                            root:{
                                expanded:true,
                                text:this.objectModel + ' Object Model',
                                children:this._buildRows(data)
                            }
                        }),
                        rootVisible:false,
                        listeners: {
                            afterrender: {
                                fn: function(tree, eOpts){
                                    tree.filterableRootNode = tree.getRootNode();
                                }
                            },
                            cellclick: {
                                fn: function (o, idx, column, e) {
                                    if (column == 2) {
                                        var objectModel = idx.getElementsByTagName('a')[0].id;
                                        if(objectModel){
                                            this.objectCombobox.setValue(objectModel);
                                        }
                                    }
                                },
                                scope: this
                            }
                        },
                        columns:[
                            {
                                xtype:'treecolumn',
                                header:"Attribute",
                                dataIndex:'Name',
                                width:200
                            },
                            {
                                xtype:'gridcolumn',
                                header:'Field',
                                dataIndex:'Field',
                                menuDisabled:true,
                                sortable:false,
                                width:200
                            },
                            {
                                xtype:'gridcolumn',
                                header:'Value',
                                dataIndex:'Value',
                                menuDisabled:true,
                                sortable:false,
                                flex:1
                            }
                        ],
                        dockedItems:[
                            {
                                xtype:'toolbar',
                                dock:'top',
                                layout:{
                                    pack:'end'
                                },
                                items:[
                                    {
                                        xtype:'checkbox',
                                        boxLabel:'Expand All',
                                        itemId:'expandCheckbox',
                                        style:{
                                            fontSize:'9px',
                                            lineHeight:'12px',
                                            marginRight:'5px'
                                        },
                                        handler:function (checkbox, checked) {
                                            if (checked) {
                                                this.up('#treepanel').expandAll();
                                            } else {
                                                this.up('#treepanel').collapseAll();
                                            }
                                        }
                                    },
                                    '-',
                                    {
                                        xtype:'rallytextfield',
                                        fieldLabel:'Filter',
                                        enableKeyEvents:true,
                                        listeners:{
                                            focus:{
                                                fn:function (view, record, item, index, even) {
                                                    this.setValue("");
                                                }
                                            },
                                            keyup:{
                                                fn:function (view, record, item, index, even) {
                                                    //TODO fix where filtering rows messes up attributes expand/collapse
                                                    this.up('#treepanel').setRootNode(this.up('#treepanel').filterableRootNode);

                                                    var removeArray = [];
                                                    var filterText = this.getValue();

                                                    this.up('#treepanel').getRootNode().cascadeBy(function () {
                                                        if (!(this.data.Filter.toLowerCase().indexOf(filterText.toLowerCase()) > -1)) {
                                                            removeArray.push(this);
                                                        }
                                                    });

                                                    Ext.Array.each(removeArray, function (row) {
                                                        row.remove();
                                                    });
                                                }
                                            }
                                        }
                                    }
                                ]
                            }
                        ]
                    }
            );
        },

        _setEastItems: function(data){
            this.down('#eastRegion').setTitle(this.objectModel + ' Misc');
            this.down('#eastRegion').add(
                    {
                        xtype: 'rallygrid',
                        store: Ext.create('Rally.data.custom.Store', {
                                data: this._getEastRegionData(data),
                                pageSize: 5
                            }),
                        showPagingToolbar: false,
                        columnCfgs: [
                            {
                                dataIndex: 'Field', width: 75
                            },
                            {
                               dataIndex: 'Value', flex: 1
                            }
                         ]
                    }
            );
        },

        _getEastRegionData: function(data){
            var miscData = [];

            if(data[0].data.Parent && data[0].data.Parent._refObjectName){
                miscData.push({Field: 'Parent', Value: data[0].data.Parent._refObjectName});
            }

            if(data[0].data.Note){
                miscData.push({Field: 'Note', Value: data[0].data.Note});
            }

//            miscData.push({Field: 'Usage', Value: 'Coming soon!'});
//            miscData.push({Field: 'Rest URLs', Value: 'Coming soon!'});
            return miscData;
        },

        _buildRows:function (data) {

            var rows = [];
            Ext.each(data[0].data.Attributes, function (attr) {
                Ext.each(attr, function (field) {
                        var children = [];

                        if (field.AllowedValues.length) {
                            var values = [];
                            Ext.each(field.AllowedValues, function (value) {
                                values.push(value.StringValue);
                            });
                            children.push({Field:'Allowed Values', Value:values.join(", "), leaf:true, Filter:field.Name});
                        }

                        children.push({Field:'Data Type', Value:field.AttributeType, leaf:true, Filter:field.Name});

                        if (field.MaxLength !== -1) {
                            children.push({Field:'Max Length', Value:field.MaxLength, leaf:true, Filter:field.Name});
                        }

                        children.push({Field:'Note', Value:field.Note, leaf:true});

                        if (field.AllowedValueType) {
                            children.push({Field:'Ownership Relationship', Value:field.Owned, leaf:true, Filter:field.Name});
                        }

                        if (field.AllowedQueryOperators.length) {
                            var operators = [];
                            Ext.each(field.AllowedQueryOperators, function (value) {
                                operators.push(value.OperatorName);
                            });
                            children.push({Field:'Query Expression Operators', Value:operators.join(", "), leaf:true, Filter:field.Name});
                        }

                        children.push({Field:'Read Only', Value:field.ReadOnly, leaf:true});

                        if (field.AllowedValueType) {
                            var row = {Field:'Relationship', Value:field.AllowedValueType._refObjectName, leaf:true, Filter:field.Name};

                            //TODO make sure it is a selectable object model from the comobobox
                            if (field.AttributeType === 'OBJECT' || field.AttributeType === 'COLLECTION') {
                                var elementName = field.AllowedValueType._refObjectName.replace(/\s/g, "");
                                row.Value = '' +
                                        field.AllowedValueType._refObjectName + '';
                            }

                            children.push(row);
                        }

                        children.push(
                                {Field:'Required', Value:field.Required, leaf:true, Filter:field.Name},
                                {Field:'Sortable', Value:field.Filterable, leaf:true, Filter:field.Name}
                        );

                        rows.push({Name:field.Name, children:children, Filter:field.Name});
                    }, this);
            }, this);

            this.rows = rows;
            return rows;
        },

        _onQueryButtonClick:function (button) {
            var params = {};
            var extension = '.js?';
            params.pretty = true;
            params.start = this.down('#startIndex').getValue();
            params.pagesize = this.down('#pageSize').getValue();

            if (this.down('#queryString').getValue() !== '') {
                params.query = this.down('#queryString').getValue();
            }

            if (this.down('#order').getValue() !== '') {
                params.order = this.down('#order').getValue();
            }

            if (this.down('#fullObjects').getSubmitValue()) {
                params.fetch = true;
            }

            if (this.down('#xslStylesheet').getSubmitValue()) {
                extension = '?';
            }

            var url = this.baseServerUrl+'/slm/webservice/1.33/' + this.objectModel.toLowerCase().replace(/\s/g, "") + extension + Ext.Object.toQueryString(params);

            if (this.down('#xslStylesheet').getSubmitValue()) {
                url = url + '&stylesheet='+this.baseServerUrl+'/slm/doc/webservice/browser.xsl'
            }

            var workspace = this.down('#workspaceCombobox').getValue();
            url = url + '&workspace=' + workspace.slice(0, workspace.length - 3);

            if (this.down('#jsonOutput').getSubmitValue()) {
                url = this.baseServerUrl+'/slm/doc/webservice/jsonDisplay.jsp?uri=' + url;
            }

            console.log(url);
            if (this.down('#newTab').getSubmitValue()) { // results open in new tab
                window.open(url, '_newtab');
            } else { // results open in dialog
                Ext.create('Ext.window.Window', {
                    title:this.objectModel + ' Query Results',
                    height:400,
                    width:600,
                    layout:'fit',
                    html:'',
                    style:{
                        background:'#fff'
                    }
                }).show();
            }
        }
    });<|endoftext|>


<commit_before>function Ecs() {
	let entities = [];
	let components = {};
	let systems = [];
	let idCounter = 1;

	function Entity() {};

	Entity.prototype = {
		add(name, ...args) {
			const preSystems = this.getMatchingSystems();
			this[name] = new components[name](...args);
			this.handleEnrollToSystems(preSystems);
			return this;
		},
		addMultiple(...calls) {
			const preSystems = this.getMatchingSystems();
			calls.forEach(call => {
				const [name, ...args] = call;
				this[name] = new components[name](...args);
			});
			this.handleEnrollToSystems(preSystems);
			return this;
		},
		handleEnrollToSystems(preSystems = []) {
			const postSystems = this.getMatchingSystems();
			const enteredSystems = postSystems.filter(system => !preSystems.includes(system));
			const leftSystems = preSystems.filter(system => !postSystems.includes(system));
			this.updateSystemsEntityLists(leftSystems, enteredSystems);
		},
		updateSystemsEntityLists(left, entered) {
			entered.forEach(system => {
				system.entities.push(this);
				system.enter(this);
			});
			left.forEach(system => {
				system.entities = system.entities.filter(entity => entity !== this);
				system.leave(this);
			});
		},
		remove(name) {
			const preSystems = this.getMatchingSystems();
			delete this[name];
			this.handleEnrollToSystems(preSystems);
			return this;
		},
		has(...keys) {
			return keys.every(key => !!this[key]);
		},
		destroy() {
			const preSystems = this.getMatchingSystems();
			this._garbage = true;
			this.updateSystemsEntityLists(preSystems, []);
			entities = entities.filter(entity => entity !== this);
		},
		matches(system) {
			return system.has.every(key => this.has(key)) && system.not.every(key => !this.has(key));
		},
		getMatchingSystems() {
			return systems.filter(system => this.matches(system));
		},
	};

	function System({
		mounted = noop,
		pre = noop,
		post = noop,
		forEach = noop,
		enter = noop,
		leave = noop,
		has = [],
		not = [],
		order = 0,
		group = '',
	}) {
		this.mounted = mounted;
		this.pre = pre;
		this.post = post;
		this.enter = enter;
		this.leave = leave;
		this.forEach = forEach;
		this.has = has;
		this.not = not;
		this.order = order;
		this.entities = [];
		this.group = group;
	};

	System.prototype = {
		run(globalArg) {
			this.pre(globalArg);
			this.entities.forEach(entity => this.forEach(entity, globalArg));
			this.post(globalArg);
		},
	};

	function createEntity() {
		const entity = new Entity();
		Object.defineProperty(entity, 'id', {
			enumerable: false,
			value: idCounter++
		});
		entities.push(entity);
		entity.handleEnrollToSystems();
		return entity;
	};

	function registerComponent(name, component = defaultComponent) {
		components[name] = component;
		return name;
	};

	function registerSystem(conf) {
		const system = new System(conf);
		systems.push(system);
		systems = systems.sort((a, b) => a.order - b.order);
		system.mounted();
		return system;
	};

	function run(globalArg) {
		systems.forEach(system => system.run(globalArg));
	};

	function runGroup(name, globalArg) {
		systems.forEach(system => system.group === name && system.run(globalArg));
	};

	function defaultComponent(v) {
		this.value = v;
	};

	function noop() {};

	return { createEntity, registerComponent, registerSystem, run, runGroup, entities };
}

try {
	module.exports = Ecs;
} catch(err) {}
<commit_msg>Add entity method removeMultiple<commit_after>function Ecs() {
	let entities = [];
	let components = {};
	let systems = [];
	let idCounter = 1;

	function Entity() {};

	Entity.prototype = {
		add(name, ...args) {
			const preSystems = this.getMatchingSystems();
			this[name] = new components[name](...args);
			this.handleEnrollToSystems(preSystems);
			return this;
		},
		addMultiple(...calls) {
			const preSystems = this.getMatchingSystems();
			calls.forEach(call => {
				const [name, ...args] = call;
				this[name] = new components[name](...args);
			});
			this.handleEnrollToSystems(preSystems);
			return this;
		},
		removeMultiple(...names) {
			const preSystems = this.getMatchingSystems();
			names.forEach(name => {
				delete this[name];
			});
			this.handleEnrollToSystems(preSystems);
			return this;
		},
		handleEnrollToSystems(preSystems = []) {
			const postSystems = this.getMatchingSystems();
			const enteredSystems = postSystems.filter(system => !preSystems.includes(system));
			const leftSystems = preSystems.filter(system => !postSystems.includes(system));
			this.updateSystemsEntityLists(leftSystems, enteredSystems);
		},
		updateSystemsEntityLists(left, entered) {
			entered.forEach(system => {
				system.entities.push(this);
				system.enter(this);
			});
			left.forEach(system => {
				system.entities = system.entities.filter(entity => entity !== this);
				system.leave(this);
			});
		},
		remove(name) {
			const preSystems = this.getMatchingSystems();
			delete this[name];
			this.handleEnrollToSystems(preSystems);
			return this;
		},
		has(...keys) {
			return keys.every(key => !!this[key]);
		},
		destroy() {
			const preSystems = this.getMatchingSystems();
			this._garbage = true;
			this.updateSystemsEntityLists(preSystems, []);
			entities = entities.filter(entity => entity !== this);
		},
		matches(system) {
			return system.has.every(key => this.has(key)) && system.not.every(key => !this.has(key));
		},
		getMatchingSystems() {
			return systems.filter(system => this.matches(system));
		},
	};

	function System({
		mounted = noop,
		pre = noop,
		post = noop,
		forEach = noop,
		enter = noop,
		leave = noop,
		has = [],
		not = [],
		order = 0,
		group = '',
	}) {
		this.mounted = mounted;
		this.pre = pre;
		this.post = post;
		this.enter = enter;
		this.leave = leave;
		this.forEach = forEach;
		this.has = has;
		this.not = not;
		this.order = order;
		this.entities = [];
		this.group = group;
	};

	System.prototype = {
		run(globalArg) {
			this.pre(globalArg);
			this.entities.forEach(entity => this.forEach(entity, globalArg));
			this.post(globalArg);
		},
	};

	function createEntity() {
		const entity = new Entity();
		Object.defineProperty(entity, 'id', {
			enumerable: false,
			value: idCounter++
		});
		entities.push(entity);
		entity.handleEnrollToSystems();
		return entity;
	};

	function registerComponent(name, component = defaultComponent) {
		components[name] = component;
		return name;
	};

	function registerSystem(conf) {
		const system = new System(conf);
		systems.push(system);
		systems = systems.sort((a, b) => a.order - b.order);
		system.mounted();
		return system;
	};

	function run(globalArg) {
		systems.forEach(system => system.run(globalArg));
	};

	function runGroup(name, globalArg) {
		systems.forEach(system => system.group === name && system.run(globalArg));
	};

	function defaultComponent(v) {
		this.value = v;
	};

	function noop() {};

	return { createEntity, registerComponent, registerSystem, run, runGroup, entities };
}

try {
	module.exports = Ecs;
} catch(err) {}
<|endoftext|>


<commit_before>var iconv = require('iconv-lite');
var zlib = require('zlib');
var MAX_REQ_SIZE = 128 * 1024;
var MAX_RES_SIZE = 256 * 1024;
var TIMEOUT = 10000;
var MAX_LENGTH = 256;
var count = 0;
var ids = [];
var data = {};
var proxy, binded, timeout, util;

function disable() {
	proxy.removeListener('request', handleRequest);
	binded = false;
}

function passThrough(chunk, encoding, callback) {
	callback(null, chunk);
}

function decode(body) {
	if (body) {
		var _body = body + '';
		if (_body.indexOf('�') != -1) {
			_body = iconv.decode(body, 'gbk');
		}
		body = _body;
	}

	return body;
}

function get() {
	!binded && proxy.on('request', handleRequest);
	binded = true;
	clearTimeout(timeout);
	timeout = setTimeout(disable, TIMEOUT);

	return [];
}

function handleRequest(req) {
	var startTime = Date.now() - (req.dnsTime || 0);
	var id = startTime + '-' + ++count;
	var reqData = {
			dnsTime: req.dnsTime,
			method: req.method || 'GET',
			httpVersion: req.httpVersion || '1.1',
            host: req.host || '127.0.0.1',
            headers: req.headers
		};
	var resData = {};

	data[id] = {
			url: req.url,
			startTime: startTime,
			req: reqData,
			res: resData
	};

	ids.push(id);
	if (ids.length > MAX_LENGTH) {
		delete data[ids.shift()];
	}

	req.on('response', handleResponse);
	req.on('error', function(err) {
		reqData.state = 'error';
		reqData.body = err && err.stack;
		reqData.requestTime = Date.now() - startTime;
		req.removeListener('response', handleResponse);
		req._transform = passThrough;
	});

	var reqBody;

	if (util.hasRequestBody(req)) {
		reqBody = false;
	}
	req._transform = function(chunk, encoding, callback) {

		if (chunk && (reqBody || reqBody === false)) {
			reqBody = reqBody ? Buffer.concat([reqBody, chunk]) : chunk;
		}

		if (reqBody && reqBody.length > MAX_REQ_SIZE) {
			reqBody = null;
		}

		if (!chunk) {
			reqData.requestTime = Date.now() - startTime;
			reqData.state = 'close';
			reqData.body = decode(reqBody);
		}

		callback(null, chunk);
	};

	function handleResponse(res) {
		resData.responseTime = Date.now() - startTime;
		res.on('error', function(err) {
			resData.state = 'error';
			resData.body = err && err.stack;
			resData.totalTime = Date.now() - startTime;
			res._transform = passThrough;
		});

		var resBody;
		var contentType = util.getContentType(res.headers);
		if (contentType && contentType != 'IMG' && util.hasBody(res)) {
			resBody = false;
		}

		res._transform = function(chunk, encoding, callback) {
			if (chunk && (resBody || resBody === false)) {
				resBody = resBody ? Buffer.concat([resBody, chunk]) : chunk;
			}

			if (resBody && resBody.length > MAX_RES_SIZE) {
				resBody = null;
			}

			if (!chunk) {
				resData.totalTime = Date.now() - startTime;
				resData.state = 'close';
				if (resBody) {
					var contentEncoding = res.headers['content-encoding'];
					var unzip;
					switch (util.toLowerCase(contentEncoding)) {
					    case 'gzip':
					    	unzip = zlib.gunzip.bind(zlib);
					      break;
					    case 'deflate':
					    	unzip = zlib.inflate.bind(zlib);
					      break;
					}

					if (unzip) {
						unzip(resBody, function(err, body) {
							if (err) {
								callback(err, chunk);
								return;
							}
							resData.body = decode(body);
							callback(null, chunk);
						});
						return;
					}
				}
				resData.body = decode(resBody);
			}

			callback(null, chunk);
		};
	}

}

module.exports = function init(_proxy) {
	proxy = _proxy;
	util = proxy.util;
	proxy.on('request', handleRequest);
	module.exports = get;
};<commit_msg>content-encoding<commit_after>var iconv = require('iconv-lite');
var zlib = require('zlib');
var MAX_REQ_SIZE = 128 * 1024;
var MAX_RES_SIZE = 256 * 1024;
var TIMEOUT = 10000;
var MAX_LENGTH = 256;
var count = 0;
var ids = [];
var data = {};
var proxy, binded, timeout, util;

function disable() {
	proxy.removeListener('request', handleRequest);
	binded = false;
}

function passThrough(chunk, encoding, callback) {
	callback(null, chunk);
}

function decode(body) {
	if (body) {
		var _body = body + '';
		if (_body.indexOf('�') != -1) {
			_body = iconv.decode(body, 'gbk');
		}
		body = _body;
	}

	return body;
}

function get() {
	!binded && proxy.on('request', handleRequest);
	binded = true;
	clearTimeout(timeout);
	timeout = setTimeout(disable, TIMEOUT);

	return [];
}

function handleRequest(req) {
	var startTime = Date.now() - (req.dnsTime || 0);
	var id = startTime + '-' + ++count;
	var reqData = {
			dnsTime: req.dnsTime,
			method: req.method || 'GET',
			httpVersion: req.httpVersion || '1.1',
            host: req.host || '127.0.0.1',
            headers: req.headers
		};
	var resData = {};

	data[id] = {
			url: req.url,
			startTime: startTime,
			req: reqData,
			res: resData
	};

	ids.push(id);
	if (ids.length > MAX_LENGTH) {
		delete data[ids.shift()];
	}

	req.on('response', handleResponse);
	req.on('error', function(err) {
		reqData.state = 'error';
		reqData.body = err && err.stack;
		reqData.requestTime = Date.now() - startTime;
		req.removeListener('response', handleResponse);
		req._transform = passThrough;
	});

	var reqBody;

	if (util.hasRequestBody(req)) {
		reqBody = false;
	}
	req._transform = function(chunk, encoding, callback) {

		if (chunk && (reqBody || reqBody === false)) {
			reqBody = reqBody ? Buffer.concat([reqBody, chunk]) : chunk;
		}

		if (reqBody && reqBody.length > MAX_REQ_SIZE) {
			reqBody = null;
		}

		if (!chunk) {
			reqData.requestTime = Date.now() - startTime;
			reqData.state = 'close';
			reqData.body = decode(reqBody);
		}

		callback(null, chunk);
	};

	function handleResponse(res) {
		resData.responseTime = Date.now() - startTime;
		res.on('error', function(err) {
			resData.state = 'error';
			resData.body = err && err.stack;
			resData.totalTime = Date.now() - startTime;
			res._transform = passThrough;
		});

		var resBody;
		var contentType = util.getContentType(res.headers);
		if (contentType && contentType != 'IMG' && util.hasBody(res)) {
			resBody = false;
		}

		res._transform = function(chunk, encoding, callback) {
			if (chunk && (resBody || resBody === false)) {
				resBody = resBody ? Buffer.concat([resBody, chunk]) : chunk;
			}

			if (resBody && resBody.length > MAX_RES_SIZE) {
				resBody = null;
			}

			if (!chunk) {
				resData.totalTime = Date.now() - startTime;
				resData.state = 'close';
				if (resBody) {
					var unzip;
					switch (util.toLowerCase(res.headers['content-encoding'])) {
					    case 'gzip':
					    	unzip = zlib.gunzip.bind(zlib);
					      break;
					    case 'deflate':
					    	unzip = zlib.inflate.bind(zlib);
					      break;
					}

					if (unzip) {
						unzip(resBody, function(err, body) {
							if (err) {
								callback(err, chunk);
								return;
							}
							resData.body = decode(body);
							callback(null, chunk);
						});
						return;
					}
				}
				resData.body = decode(resBody);
			}

			callback(null, chunk);
		};
	}

}

module.exports = function init(_proxy) {
	proxy = _proxy;
	util = proxy.util;
	proxy.on('request', handleRequest);
	module.exports = get;
};<|endoftext|>


<commit_before>var iconv = require('iconv-lite');
var zlib = require('zlib');
var MAX_REQ_SIZE = 128 * 1024;
var MAX_RES_SIZE = 256 * 1024;
var TIMEOUT = 10000;
var MAX_LENGTH = 512;
var count = 0;
var ids = [];
var data = {};
var proxy, binded, timeout, interval, util;

function disable() {
	proxy.removeListener('request', handleRequest);
	ids = [];
	data = {};
	interval && clearInterval(interval);
	interval = null;
	binded = false;
}

/**
 * 如果超过最大缓存数，清理如下请求数据：
 * 1. 已经请求结束且结束时间超过10秒
 * 2. 请求#1前面的未结束且未被ui读取过的请求
 */
function clearCache() {
	if (ids.length <= MAX_LENGTH) {
		return;
	}

	var index = -1; //已经完成，且缓存超过10s的最后一个请求
	var now = Date.now();
	for (var i = ids.length - 1; i >= 0; i--) {
		var curData = data[ids[i]];
		if (curData.endTime && now - curData.endTime > TIMEOUT) {
			index = i;
			break;
		}
	}

	if (index < 0) {
		return;
	}

	var _ids = [];
	++index;
	for (var i = 0; i < index; i++) {
		var id = ids[i];
		var curData = data[id];
		if (curData.read && !curData.endTime) {
			_ids.push(id);
		} else {
			delete data[id];
		}
	}
	ids = _ids.concat(ids.slice(index));
}

function passThrough(chunk, encoding, callback) {
	callback(null, chunk);
}

function decode(body) {
	if (body) {
		var _body = body + '';
		if (_body.indexOf('�') != -1) {
			_body = iconv.decode(body, 'gbk');
		}
		body = _body;
	}

	return body;
}

function get() {
	!binded && proxy.on('request', handleRequest);
	binded = true;
	clearTimeout(timeout);
	timeout = setTimeout(disable, TIMEOUT);
	if (!interval) {
		interval = setInterval(clearCache, 3000);
	}
	return [];
}

function handleRequest(req) {
	var dnsTime = req.dnsTime || 0;
	req.dnsTime = Date.now();
	var startTime = startTime - dnsTime;
	var id = startTime + '-' + ++count;
	var reqData = {
			dnsTime: req.dnsTime,
			method: req.method || 'GET',
			httpVersion: req.httpVersion || '1.1',
            host: req.host || '127.0.0.1',
            headers: req.headers
		};
	var resData = {};

	var curData = data[id] = {
			url: req.url,
			startTime: startTime,
			req: reqData,
			res: resData
	};

	ids.push(id);
	req.on('response', handleResponse);
	req.on('error', function(err) {
		reqData.body = err && err.stack;
		curData.endTime = reqData.requestTime = Date.now();
		req.removeListener('response', handleResponse);
		req._transform = passThrough;
		curData.reqError = true;
	});

	var reqBody;

	if (util.hasRequestBody(req)) {
		reqBody = false;
	}
	req._transform = function(chunk, encoding, callback) {

		if (chunk && (reqBody || reqBody === false)) {
			reqBody = reqBody ? Buffer.concat([reqBody, chunk]) : chunk;
		}

		if (reqBody && reqBody.length > MAX_REQ_SIZE) {
			reqBody = null;
		}

		if (!chunk) {
			reqData.requestTime = Date.now();
			curData.reqEnd = true;
			reqData.body = decode(reqBody);
		}

		callback(null, chunk);
	};

	function handleResponse(res) {
		resData.responseTime = Date.now();
		res.on('error', function(err) {
			resData.body = err && err.stack;
			curData.endTime = Date.now();
			res._transform = passThrough;
			curData.resError = true;
		});

		var resBody;
		var contentType = util.getContentType(res.headers);
		if (contentType && contentType != 'IMG' && util.hasBody(res)) {
			resBody = false;
		}

		res._transform = function(chunk, encoding, callback) {
			if (chunk && (resBody || resBody === false)) {
				resBody = resBody ? Buffer.concat([resBody, chunk]) : chunk;
			}

			if (resBody && resBody.length > MAX_RES_SIZE) {
				resBody = null;
			}

			if (!chunk) {
				curData.endTime = Date.now();
				resData.state = 'close';
				curData.resEnd = true;
				if (resBody) {
					var unzip;
					switch (util.toLowerCase(res.headers['content-encoding'])) {
					    case 'gzip':
					    	unzip = zlib.gunzip.bind(zlib);
					      break;
					    case 'deflate':
					    	unzip = zlib.inflate.bind(zlib);
					      break;
					}

					if (unzip) {
						unzip(resBody, function(err, body) {
							if (err) {
								callback(err, chunk);
								return;
							}
							resData.body = decode(body);
							callback(null, chunk);
						});
						return;
					}
				}
				resData.body = decode(resBody);
			}

			callback(null, chunk);
		};
	}

}

module.exports = function init(_proxy) {
	proxy = _proxy;
	util = proxy.util;
	proxy.on('request', handleRequest);
	binned = true;
	interval = setInterval(clearCache, 3000);
	module.exports = get;
};<commit_msg>clear cache<commit_after>var iconv = require('iconv-lite');
var zlib = require('zlib');
var MAX_REQ_SIZE = 128 * 1024;
var MAX_RES_SIZE = 256 * 1024;
var TIMEOUT = 10000;
var MAX_LENGTH = 512;
var MIN_LENGTH = 256;
var count = 0;
var ids = [];
var data = {};
var proxy, binded, timeout, interval, util;

function disable() {
	proxy.removeListener('request', handleRequest);
	ids = [];
	data = {};
	interval && clearInterval(interval);
	interval = null;
	binded = false;
}

/**
 * 如果超过最大缓存数，清理如下请求数据：
 * 1. 已经请求结束且结束时间超过10秒
 * 2. 请求#1前面的未结束且未被ui读取过的请求
 */
function clearCache() {
	var len = ids.length;
	if (len <= MAX_LENGTH) {
		return;
	}

	var index = -1; //已经完成，且缓存超过10s的最后一个请求
	var now = Date.now();
	for (var i = len - 1; i >= 0; i--) {
		var curData = data[ids[i]];
		if (curData.endTime && now - curData.endTime > TIMEOUT) {
			index = i;
			break;
		}
	}

	if (index < 0) {
		return;
	}

	var _ids = [];
	++index;
	for (var i = 0; i < index; i++) {
		var id = ids[i];
		var curData = data[id];
		if (curData.read && !curData.endTime) {
			_ids.push(id);
		} else {
			delete data[id];
			if (--len <= MIN_LENGTH) {
				break;
			}
		}
	}
	ids = _ids.concat(ids.slice(index));
}

function passThrough(chunk, encoding, callback) {
	callback(null, chunk);
}

function decode(body) {
	if (body) {
		var _body = body + '';
		if (_body.indexOf('�') != -1) {
			_body = iconv.decode(body, 'gbk');
		}
		body = _body;
	}

	return body;
}

function get() {
	!binded && proxy.on('request', handleRequest);
	binded = true;
	clearTimeout(timeout);
	timeout = setTimeout(disable, TIMEOUT);
	if (!interval) {
		interval = setInterval(clearCache, 3000);
	}
	return [];
}

function handleRequest(req) {
	var dnsTime = req.dnsTime || 0;
	req.dnsTime = Date.now();
	var startTime = startTime - dnsTime;
	var id = startTime + '-' + ++count;
	var reqData = {
			dnsTime: req.dnsTime,
			method: req.method || 'GET',
			httpVersion: req.httpVersion || '1.1',
            host: req.host || '127.0.0.1',
            headers: req.headers
		};
	var resData = {};

	var curData = data[id] = {
			url: req.url,
			startTime: startTime,
			req: reqData,
			res: resData
	};

	ids.push(id);
	req.on('response', handleResponse);
	req.on('error', function(err) {
		reqData.body = err && err.stack;
		curData.endTime = reqData.requestTime = Date.now();
		req.removeListener('response', handleResponse);
		req._transform = passThrough;
		curData.reqError = true;
	});

	var reqBody;

	if (util.hasRequestBody(req)) {
		reqBody = false;
	}
	req._transform = function(chunk, encoding, callback) {

		if (chunk && (reqBody || reqBody === false)) {
			reqBody = reqBody ? Buffer.concat([reqBody, chunk]) : chunk;
		}

		if (reqBody && reqBody.length > MAX_REQ_SIZE) {
			reqBody = null;
		}

		if (!chunk) {
			reqData.requestTime = Date.now();
			curData.reqEnd = true;
			reqData.body = decode(reqBody);
		}

		callback(null, chunk);
	};

	function handleResponse(res) {
		resData.responseTime = Date.now();
		res.on('error', function(err) {
			resData.body = err && err.stack;
			curData.endTime = Date.now();
			res._transform = passThrough;
			curData.resError = true;
		});

		var resBody;
		var contentType = util.getContentType(res.headers);
		if (contentType && contentType != 'IMG' && util.hasBody(res)) {
			resBody = false;
		}

		res._transform = function(chunk, encoding, callback) {
			if (chunk && (resBody || resBody === false)) {
				resBody = resBody ? Buffer.concat([resBody, chunk]) : chunk;
			}

			if (resBody && resBody.length > MAX_RES_SIZE) {
				resBody = null;
			}

			if (!chunk) {
				curData.endTime = Date.now();
				resData.state = 'close';
				curData.resEnd = true;
				if (resBody) {
					var unzip;
					switch (util.toLowerCase(res.headers['content-encoding'])) {
					    case 'gzip':
					    	unzip = zlib.gunzip.bind(zlib);
					      break;
					    case 'deflate':
					    	unzip = zlib.inflate.bind(zlib);
					      break;
					}

					if (unzip) {
						unzip(resBody, function(err, body) {
							if (err) {
								callback(err, chunk);
								return;
							}
							resData.body = decode(body);
							callback(null, chunk);
						});
						return;
					}
				}
				resData.body = decode(resBody);
			}

			callback(null, chunk);
		};
	}

}

module.exports = function init(_proxy) {
	proxy = _proxy;
	util = proxy.util;
	proxy.on('request', handleRequest);
	binned = true;
	interval = setInterval(clearCache, 3000);
	module.exports = get;
};<|endoftext|>


<commit_before>"use strict";

/*global module, require*/

var d3 = require("d3"),
    _ = require("lodash"),
    colour = require("./colour.js"),
    callbackFactory = require("./helpers.js").callbackHandler;

var shapeHeaders = function(shapeData) {
    var headers = ["id"];

    shapeData.forEach(function(shape) {
	headers = headers.concat(Object.keys(shape.properties));
    });

    headers = _.uniq(headers);

    return headers;
};

module.exports = function() {
    var colours = d3.scale.category10(),
	colourI = 0;

    var nextColour = function() {
	return colours((colourI++ % 10));
    };

    return function(shapeData) {
	var headers = shapeHeaders(shapeData),
	    sortProperties = [],
	    reverseSort = [],
	    sortPropertyChanged = callbackFactory(),
	    baseColourChanged = callbackFactory(),
	    baseColour = nextColour(),
	    sortColour = "black",
	    /* Cache the colour funciton for this layer. Expires when the sort property changes. */
	    colourFun;

	var m = {
	    baseColour: function(val) {
		colourFun = undefined;

		if (val === undefined) {
		    return baseColour;
		} else {
		    baseColour = val;
		    baseColourChanged(val);
		    return m;
		}
	    },

	    colour: function() {
		if (!colourFun) {
		    if (sortProperties.length === 0) {
			colourFun = baseColour;
		    } else {
			var column = sortProperties[0],
			    data = m.data([column]),
			    scale = colour.scale(data, baseColour);

			colourFun = scale;
		    }
		}

		return colourFun;
	    },

	    shapeColour: function() {
		var colour = m.colour();

		if (sortProperties.length === 0) {
		    return colour;
		} else {
		    var col = sortProperties[0];
		    return function(d, i) {
			return colour(d.properties[col]);
		    };
		}
	    },

	    sortProperty : function(property, additional) {
		colourFun = undefined;

		if (property) {

		    var i = sortProperties.indexOf(property);
		    if (i >= 0) {
			if (additional) {
			    /* reverse this property */
			    reverseSort[i] = !reverseSort[i];
			} else {
			    /* sort on just this property, reversed  */
			    sortProperties = [property];
			    reverseSort = [!reverseSort[i]];
			}
		    } else {
			if (additional) {
			    /* add new property with normal sort  */
			    sortProperties.push(property);
			    reverseSort.push(false);
			} else {
			    /* sort on just this property */
			    sortProperties = [property];
			    reverseSort = [false];
			}
		    }
		} else if (sortProperties.length > 0) {
		    // Clear sort properties;
		    sortProperties = [];
		    reverseSort = [];
		}
		sortPropertyChanged();
	    },

	    getSortProperties : function() {
		return {
		    "properties" : sortProperties,
		    "reverse" : reverseSort
		};
	    },

	    firstSortPropertyI: function() {
		if (sortProperties.length === 0) {
		    throw new Error("No sort properties");
		}

		return headers.indexOf(sortProperties[0]);
	    },

	    data: function(columns) {
		if (columns === undefined) {
		    return m.data(headers);
		} else {
		    return shapeData.map(function(s) {
			return columns.map(function(c) {
			    if (c === "id") {
				return s[c];
			    } else {
				return s.properties[c];
			    }
			});
		    });
		}
	    },

	    headers: function() {
		return headers;
	    },

	    sortPropertyChanged: sortPropertyChanged.add,

	    baseColourChanged: baseColourChanged.add
	};
	return m;
    };
};<commit_msg>Fixed colouring map when sorting by id.<commit_after>"use strict";

/*global module, require*/

var d3 = require("d3"),
    _ = require("lodash"),
    colour = require("./colour.js"),
    callbackFactory = require("./helpers.js").callbackHandler;

var shapeHeaders = function(shapeData) {
    var headers = ["id"];

    shapeData.forEach(function(shape) {
	headers = headers.concat(Object.keys(shape.properties));
    });

    headers = _.uniq(headers);

    return headers;
};

module.exports = function() {
    var colours = d3.scale.category10(),
	colourI = 0;

    var nextColour = function() {
	return colours((colourI++ % 10));
    };

    return function(shapeData) {
	var headers = shapeHeaders(shapeData),
	    sortProperties = [],
	    reverseSort = [],
	    sortPropertyChanged = callbackFactory(),
	    baseColourChanged = callbackFactory(),
	    baseColour = nextColour(),
	    sortColour = "black",
	    /* Cache the colour funciton for this layer. Expires when the sort property changes. */
	    colourFun;

	var m = {
	    baseColour: function(val) {
		colourFun = undefined;

		if (val === undefined) {
		    return baseColour;
		} else {
		    baseColour = val;
		    baseColourChanged(val);
		    return m;
		}
	    },

	    colour: function() {
		if (!colourFun) {
		    if (sortProperties.length === 0) {
			colourFun = baseColour;
		    } else {
			var column = sortProperties[0],
			    data = m.data([column]),
			    scale = colour.scale(data, baseColour);

			colourFun = scale;
		    }
		}

		return colourFun;
	    },

	    shapeColour: function() {
		var colour = m.colour();

		if (sortProperties.length === 0) {
		    return colour;
		} else {
		    var col = sortProperties[0];
		    if (col === "id") {
			return function(d, i) {
			    return colour(d.id);
			};
		    }

		    return function(d, i) {
			return colour(d.properties[col]);
		    };
		}
	    },

	    sortProperty : function(property, additional) {
		colourFun = undefined;

		if (property) {

		    var i = sortProperties.indexOf(property);
		    if (i >= 0) {
			if (additional) {
			    /* reverse this property */
			    reverseSort[i] = !reverseSort[i];
			} else {
			    /* sort on just this property, reversed  */
			    sortProperties = [property];
			    reverseSort = [!reverseSort[i]];
			}
		    } else {
			if (additional) {
			    /* add new property with normal sort  */
			    sortProperties.push(property);
			    reverseSort.push(false);
			} else {
			    /* sort on just this property */
			    sortProperties = [property];
			    reverseSort = [false];
			}
		    }
		} else if (sortProperties.length > 0) {
		    // Clear sort properties;
		    sortProperties = [];
		    reverseSort = [];
		}
		sortPropertyChanged();
	    },

	    getSortProperties : function() {
		return {
		    "properties" : sortProperties,
		    "reverse" : reverseSort
		};
	    },

	    firstSortPropertyI: function() {
		if (sortProperties.length === 0) {
		    throw new Error("No sort properties");
		}

		return headers.indexOf(sortProperties[0]);
	    },

	    data: function(columns) {
		if (columns === undefined) {
		    return m.data(headers);
		} else {
		    return shapeData.map(function(s) {
			return columns.map(function(c) {
			    if (c === "id") {
				return s[c];
			    } else {
				return s.properties[c];
			    }
			});
		    });
		}
	    },

	    headers: function() {
		return headers;
	    },

	    sortPropertyChanged: sortPropertyChanged.add,

	    baseColourChanged: baseColourChanged.add
	};
	return m;
    };
};<|endoftext|>