2016-08-08 04:27:53 -07:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2016-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/*eslint no-bitwise: "off"*/
|
|
|
|
/*eslint no-console-disallow: "off"*/
|
|
|
|
|
|
|
|
// TODO: future features
|
|
|
|
// put in a module.exports
|
|
|
|
// filtering / search
|
|
|
|
// pivot around frames in the middle of a stack by callers / callees
|
|
|
|
// graphing?
|
|
|
|
|
2016-10-25 07:06:11 -07:00
|
|
|
function StringInterner(){// eslint-disable-line no-unused-vars
|
2016-08-08 04:27:53 -07:00
|
|
|
var strings=[];
|
|
|
|
var ids={};
|
2016-08-16 06:55:48 -07:00
|
|
|
return{
|
2016-08-08 04:27:53 -07:00
|
|
|
intern:function internString(s){
|
|
|
|
var find=ids[s];
|
|
|
|
if(find===undefined){
|
|
|
|
var id=strings.length;
|
|
|
|
ids[s]=id;
|
|
|
|
strings.push(s);
|
2016-08-16 06:55:48 -07:00
|
|
|
return id;
|
|
|
|
}else{
|
|
|
|
return find;
|
|
|
|
}
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
get:function getString(id){
|
2016-08-16 06:55:48 -07:00
|
|
|
return strings[id];
|
|
|
|
}};
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-10-25 07:06:11 -07:00
|
|
|
function StackRegistry(){// eslint-disable-line no-unused-vars
|
2016-08-16 06:55:48 -07:00
|
|
|
return{
|
2016-08-08 04:27:53 -07:00
|
|
|
root:{id:0},
|
|
|
|
nodeCount:1,
|
2016-10-25 07:06:11 -07:00
|
|
|
maxDepth:-1,
|
|
|
|
stackIdMap:null,
|
2016-10-06 07:52:35 -07:00
|
|
|
insert:function insertNode(parent,frameId){
|
2016-10-25 07:06:11 -07:00
|
|
|
if(this.stackIdMap!==null){
|
|
|
|
throw'stacks already flattened';
|
|
|
|
}
|
2016-10-06 07:52:35 -07:00
|
|
|
var node=parent[frameId];
|
2016-08-08 04:27:53 -07:00
|
|
|
if(node===undefined){
|
|
|
|
node={id:this.nodeCount};
|
|
|
|
this.nodeCount++;
|
2016-10-06 07:52:35 -07:00
|
|
|
parent[frameId]=node;
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
|
|
|
return node;
|
|
|
|
},
|
2016-10-25 07:06:11 -07:00
|
|
|
get:function getStackArray(id){
|
|
|
|
return this.stackIdMap[id];
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
flatten:function flattenStacks(){
|
2016-10-25 07:06:11 -07:00
|
|
|
if(this.stackIdMap!==null){
|
|
|
|
return;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
var stackFrameCount=0;
|
|
|
|
function countStacks(tree,depth){
|
|
|
|
var leaf=true;
|
|
|
|
for(var frameId in tree){
|
|
|
|
if(frameId!=='id'){
|
2016-08-16 06:55:48 -07:00
|
|
|
leaf=countStacks(tree[frameId],depth+1);
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
if(leaf){
|
2016-08-16 06:55:48 -07:00
|
|
|
stackFrameCount+=depth;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
countStacks(this.root,0);
|
|
|
|
console.log('size needed to store stacks: '+(stackFrameCount*4).toString()+'B');
|
|
|
|
var stackIdMap=new Array(this.nodeCount);
|
|
|
|
var stackArray=new Int32Array(stackFrameCount);
|
|
|
|
var maxStackDepth=0;
|
|
|
|
stackFrameCount=0;
|
|
|
|
function flattenStacksImpl(tree,stack){
|
|
|
|
var childStack=void 0;
|
|
|
|
maxStackDepth=Math.max(maxStackDepth,stack.length);
|
|
|
|
for(var frameId in tree){
|
|
|
|
if(frameId!=='id'){
|
|
|
|
stack.push(Number(frameId));
|
|
|
|
childStack=flattenStacksImpl(tree[frameId],stack);
|
2016-08-16 06:55:48 -07:00
|
|
|
stack.pop();
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
var id=tree.id;
|
|
|
|
if(id<0||id>=stackIdMap.length||stackIdMap[id]!==undefined){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'invalid stack id!';
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
if(childStack!==undefined){
|
|
|
|
// each child must have our stack as a prefix, so just use that
|
2016-08-16 06:55:48 -07:00
|
|
|
stackIdMap[id]=childStack.subarray(0,stack.length);
|
|
|
|
}else{
|
2016-08-08 04:27:53 -07:00
|
|
|
var newStack=stackArray.subarray(stackFrameCount,stackFrameCount+stack.length);
|
|
|
|
stackFrameCount+=stack.length;
|
|
|
|
for(var i=0;i<stack.length;i++){
|
2016-08-16 06:55:48 -07:00
|
|
|
newStack[i]=stack[i];
|
|
|
|
}
|
|
|
|
stackIdMap[id]=newStack;
|
|
|
|
}
|
|
|
|
return stackIdMap[id];
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
flattenStacksImpl(this.root,[]);
|
2016-10-25 07:06:11 -07:00
|
|
|
this.root=null;
|
|
|
|
this.stackIdMap=stackIdMap;
|
|
|
|
this.maxDepth=maxStackDepth;
|
|
|
|
}};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function AggrowData(columns){// eslint-disable-line no-unused-vars
|
|
|
|
var columnCount=columns.length;
|
|
|
|
var columnConverter=columns.map(function(c){
|
|
|
|
switch(c.type){
|
|
|
|
case'int':// stores raw value
|
|
|
|
return function(i){return i;};
|
|
|
|
case'string':// stores interned id of string
|
|
|
|
return function(s){return c.strings.intern(s);};
|
|
|
|
case'stack':// stores id of stack node
|
|
|
|
return function(s){return s.id;};
|
|
|
|
default:
|
|
|
|
throw'unknown AggrowData column type';}
|
|
|
|
|
|
|
|
});
|
|
|
|
return{
|
|
|
|
data:new Int32Array(0),
|
|
|
|
columns:columns,
|
|
|
|
rowCount:0,
|
|
|
|
rowInserter:function rowInserter(numRows){
|
|
|
|
console.log(
|
|
|
|
'increasing row data from '+(this.data.length*4).toLocaleString()+' B to '+
|
|
|
|
(this.data.length*4+numRows*columnCount*4).toLocaleString()+' B');
|
|
|
|
|
|
|
|
var newData=new Int32Array(this.data.length+numRows*columnCount);
|
|
|
|
newData.set(this.data);
|
|
|
|
var currOffset=this.data.length;
|
|
|
|
var endOffset=newData.length;
|
|
|
|
this.data=newData;
|
|
|
|
this.rowCount=newData.length/columnCount;
|
|
|
|
return{
|
|
|
|
insertRow:function insertRow(){
|
|
|
|
if(currOffset>=endOffset){
|
|
|
|
throw'tried to insert data off end of added range';
|
|
|
|
}
|
|
|
|
if(arguments.length!==columnCount){
|
|
|
|
throw'expected data for '+columnCount.toString()+' columns, got'+
|
|
|
|
arguments.length.toString()+' columns';
|
|
|
|
}
|
|
|
|
for(var i=0;i<arguments.length;i++){
|
|
|
|
newData[currOffset+i]=columnConverter[i](arguments[i]);
|
|
|
|
}
|
|
|
|
currOffset+=columnCount;
|
|
|
|
},
|
|
|
|
done:function done(){
|
|
|
|
if(currOffset!==endOffset){
|
|
|
|
throw'unfilled rows';
|
|
|
|
}
|
|
|
|
}};
|
|
|
|
|
|
|
|
}};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function Aggrow(aggrowData){
|
|
|
|
var columns=aggrowData.columns;
|
|
|
|
var columnCount=columns.length;
|
|
|
|
var data=aggrowData.data;
|
|
|
|
function columnIndex(columnName,columnType){
|
|
|
|
var index=columns.findIndex(function(c){return c.name===columnName&&c.type===columnType;});
|
|
|
|
if(index<0){
|
|
|
|
throw'did not find data column '+columnName+' with type '+columnType;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
for(var i=0;i<columns.length;i++){
|
|
|
|
if(columns[i].type==='stack'){
|
|
|
|
columns[i].stacks.flatten();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return{
|
|
|
|
expander:new AggrowExpander(aggrowData.rowCount),
|
|
|
|
addSumAggregator:function addSumAggregator(aggregatorName,columnName){
|
|
|
|
var index=columnIndex(columnName,'int');
|
|
|
|
return this.expander.addAggregator(
|
|
|
|
aggregatorName,
|
|
|
|
function aggregateSize(indices){
|
|
|
|
var size=0;
|
|
|
|
for(var _i=0;_i<indices.length;_i++){
|
|
|
|
var row=indices[_i];
|
|
|
|
size+=data[row*columnCount+index];
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
},
|
|
|
|
function(value){return value.toLocaleString();},
|
|
|
|
function(a,b){return b-a;});
|
|
|
|
|
|
|
|
},
|
|
|
|
addCountAggregator:function addCountAggregator(aggregatorName){
|
|
|
|
return this.expander.addAggregator(
|
|
|
|
aggregatorName,
|
|
|
|
function aggregateCount(indices){
|
|
|
|
return indices.length;
|
|
|
|
},
|
|
|
|
function(value){return value.toLocaleString();},
|
|
|
|
function(a,b){return b-a;});
|
|
|
|
|
|
|
|
},
|
|
|
|
addStringExpander:function addStringExpander(expanderName,columnName){
|
|
|
|
var index=columnIndex(columnName,'string');
|
|
|
|
var strings=columns[index].strings;
|
|
|
|
return this.expander.addFieldExpander(
|
|
|
|
expanderName,
|
|
|
|
function(row){return strings.get(data[row*columnCount+index]);},
|
|
|
|
function(rowA,rowB){return data[rowA*columnCount+index]-data[rowB*columnCount+index];});
|
|
|
|
|
|
|
|
},
|
|
|
|
addNumberExpander:function addNumberExpander(expanderName,columnName){
|
|
|
|
var index=columnIndex(columnName,'int');
|
|
|
|
return this.expander.addFieldExpander(
|
|
|
|
expanderName,
|
|
|
|
function(row){return data[row*columnCount+index].toLocaleString();},
|
|
|
|
function(rowA,rowB){return data[rowA*columnCount+index]-data[rowB*columnCount+index];});
|
|
|
|
|
|
|
|
},
|
|
|
|
addPointerExpander:function addPointerExpander(expanderName,columnName){
|
|
|
|
var index=columnIndex(columnName,'int');
|
|
|
|
return this.expander.addFieldExpander(
|
|
|
|
expanderName,
|
|
|
|
function(row){return'0x'+(data[row*columnCount+index]>>>0).toString();},
|
|
|
|
function(rowA,rowB){return data[rowA*columnCount+index]-data[rowB*columnCount+index];});
|
|
|
|
|
|
|
|
},
|
|
|
|
addStackExpander:function addStackExpander(expanderName,columnName,formatter){
|
|
|
|
// TODO: options for caller/callee, pivoting
|
|
|
|
var index=columnIndex(columnName,'stack');
|
|
|
|
var stacks=columns[index].stacks;
|
|
|
|
return this.expander.addCalleeStackExpander(
|
|
|
|
expanderName,
|
|
|
|
stacks.maxDepth,
|
|
|
|
function(row){return stacks.get(data[row*columnCount+index]);},
|
|
|
|
formatter);
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-08-16 06:55:48 -07:00
|
|
|
}};
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-10-25 07:06:11 -07:00
|
|
|
function AggrowExpander(numRows){// eslint-disable-line no-unused-vars
|
2016-08-08 04:27:53 -07:00
|
|
|
// expander ID definitions
|
|
|
|
var FIELD_EXPANDER_ID_MIN=0x0000;
|
|
|
|
var FIELD_EXPANDER_ID_MAX=0x7fff;
|
|
|
|
var STACK_EXPANDER_ID_MIN=0x8000;
|
|
|
|
var STACK_EXPANDER_ID_MAX=0xffff;
|
|
|
|
|
|
|
|
// used for row.expander which reference state.activeExpanders (with frame index masked in)
|
|
|
|
var INVALID_ACTIVE_EXPANDER=-1;
|
|
|
|
var ACTIVE_EXPANDER_MASK=0xffff;
|
|
|
|
var ACTIVE_EXPANDER_FRAME_SHIFT=16;
|
|
|
|
|
|
|
|
// aggregator ID definitions
|
|
|
|
var AGGREGATOR_ID_MAX=0xffff;
|
|
|
|
|
|
|
|
// active aggragators can have sort order changed in the reference
|
|
|
|
var ACTIVE_AGGREGATOR_MASK=0xffff;
|
|
|
|
var ACTIVE_AGGREGATOR_ASC_BIT=0x10000;
|
|
|
|
|
|
|
|
// tree node state definitions
|
2016-08-16 06:55:48 -07:00
|
|
|
var NODE_EXPANDED_BIT=0x0001;// this row is expanded
|
|
|
|
var NODE_REAGGREGATE_BIT=0x0002;// children need aggregates
|
|
|
|
var NODE_REORDER_BIT=0x0004;// children need to be sorted
|
|
|
|
var NODE_REPOSITION_BIT=0x0008;// children need position
|
2016-08-08 04:27:53 -07:00
|
|
|
var NODE_INDENT_SHIFT=16;
|
|
|
|
|
2016-10-06 07:52:35 -07:00
|
|
|
function calleeFrameIdGetter(stack,depth){
|
2016-08-16 06:55:48 -07:00
|
|
|
return stack[depth];
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-10-06 07:52:35 -07:00
|
|
|
function callerFrameIdGetter(stack,depth){
|
2016-08-16 06:55:48 -07:00
|
|
|
return stack[stack.length-depth-1];
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-10-06 07:52:35 -07:00
|
|
|
function createStackComparers(stackGetter,frameIdGetter,maxStackDepth){
|
|
|
|
var comparers=new Array(maxStackDepth);var _loop=function _loop(
|
2016-08-08 04:27:53 -07:00
|
|
|
depth){
|
2016-08-16 06:55:48 -07:00
|
|
|
var captureDepth=depth;// NB: to capture depth per loop iteration
|
2016-08-08 04:27:53 -07:00
|
|
|
comparers[depth]=function calleeStackComparer(rowA,rowB){
|
|
|
|
var a=stackGetter(rowA);
|
|
|
|
var b=stackGetter(rowB);
|
|
|
|
// NB: we put the stacks that are too short at the top,
|
|
|
|
// so they can be grouped into the '<exclusive>' bucket
|
|
|
|
if(a.length<=captureDepth&&b.length<=captureDepth){
|
2016-08-16 06:55:48 -07:00
|
|
|
return 0;
|
|
|
|
}else if(a.length<=captureDepth){
|
|
|
|
return-1;
|
|
|
|
}else if(b.length<=captureDepth){
|
|
|
|
return 1;
|
|
|
|
}
|
2016-10-06 07:52:35 -07:00
|
|
|
return frameIdGetter(a,captureDepth)-frameIdGetter(b,captureDepth);
|
|
|
|
};};for(var depth=0;depth<maxStackDepth;depth++){_loop(depth);
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
|
|
|
return comparers;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function createTreeNode(parent,label,indices,expander){
|
|
|
|
var indent=parent===null?0:(parent.state>>>NODE_INDENT_SHIFT)+1;
|
|
|
|
var state=NODE_REPOSITION_BIT|
|
|
|
|
NODE_REAGGREGATE_BIT|
|
|
|
|
NODE_REORDER_BIT|
|
|
|
|
indent<<NODE_INDENT_SHIFT;
|
2016-08-16 06:55:48 -07:00
|
|
|
return{
|
|
|
|
parent:parent,// null if root
|
|
|
|
children:null,// array of children nodes
|
|
|
|
label:label,// string to show in UI
|
|
|
|
indices:indices,// row indices under this node
|
|
|
|
aggregates:null,// result of aggregate on indices
|
|
|
|
expander:expander,// index into state.activeExpanders
|
|
|
|
top:0,// y position of top row (in rows)
|
|
|
|
height:1,// number of rows including children
|
|
|
|
state:state};
|
|
|
|
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function noSortOrder(a,b){
|
2016-08-16 06:55:48 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
var indices=new Int32Array(numRows);
|
|
|
|
for(var i=0;i<numRows;i++){
|
2016-08-16 06:55:48 -07:00
|
|
|
indices[i]=i;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
var state={
|
2016-08-16 06:55:48 -07:00
|
|
|
fieldExpanders:[],// tree expanders that expand on simple values
|
|
|
|
stackExpanders:[],// tree expanders that expand stacks
|
|
|
|
activeExpanders:[],// index into field or stack expanders, hierarchy of tree
|
|
|
|
aggregators:[],// all available aggregators, might not be used
|
|
|
|
activeAggregators:[],// index into aggregators, to actually compute
|
|
|
|
sorter:noSortOrder,// compare function that uses sortOrder to sort row.children
|
2016-08-08 04:27:53 -07:00
|
|
|
root:createTreeNode(null,'<root>',indices,INVALID_ACTIVE_EXPANDER)};
|
|
|
|
|
|
|
|
|
|
|
|
function evaluateAggregate(row){
|
|
|
|
var activeAggregators=state.activeAggregators;
|
|
|
|
var aggregates=new Array(activeAggregators.length);
|
|
|
|
for(var j=0;j<activeAggregators.length;j++){
|
|
|
|
var aggregator=state.aggregators[activeAggregators[j]];
|
2016-08-16 06:55:48 -07:00
|
|
|
aggregates[j]=aggregator.aggregator(row.indices);
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
row.aggregates=aggregates;
|
2016-08-16 06:55:48 -07:00
|
|
|
row.state|=NODE_REAGGREGATE_BIT;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function evaluateAggregates(row){
|
|
|
|
if((row.state&NODE_EXPANDED_BIT)!==0){
|
|
|
|
var children=row.children;
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i2=0;_i2<children.length;_i2++){
|
|
|
|
evaluateAggregate(children[_i2]);
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
|
|
|
row.state|=NODE_REORDER_BIT;
|
|
|
|
}
|
|
|
|
row.state^=NODE_REAGGREGATE_BIT;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function evaluateOrder(row){
|
|
|
|
if((row.state&NODE_EXPANDED_BIT)!==0){
|
|
|
|
var children=row.children;
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i3=0;_i3<children.length;_i3++){
|
|
|
|
var child=children[_i3];
|
2016-08-16 06:55:48 -07:00
|
|
|
child.state|=NODE_REORDER_BIT;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
children.sort(state.sorter);
|
2016-08-16 06:55:48 -07:00
|
|
|
row.state|=NODE_REPOSITION_BIT;
|
|
|
|
}
|
|
|
|
row.state^=NODE_REORDER_BIT;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function evaluatePosition(row){
|
|
|
|
if((row.state&NODE_EXPANDED_BIT)!==0){
|
|
|
|
var children=row.children;
|
|
|
|
var childTop=row.top+1;
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i4=0;_i4<children.length;_i4++){
|
|
|
|
var child=children[_i4];
|
2016-08-08 04:27:53 -07:00
|
|
|
if(child.top!==childTop){
|
|
|
|
child.top=childTop;
|
2016-08-16 06:55:48 -07:00
|
|
|
child.state|=NODE_REPOSITION_BIT;
|
|
|
|
}
|
|
|
|
childTop+=child.height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
row.state^=NODE_REPOSITION_BIT;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function getRowsImpl(row,top,height,result){
|
|
|
|
if((row.state&NODE_REAGGREGATE_BIT)!==0){
|
2016-08-16 06:55:48 -07:00
|
|
|
evaluateAggregates(row);
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
if((row.state&NODE_REORDER_BIT)!==0){
|
2016-08-16 06:55:48 -07:00
|
|
|
evaluateOrder(row);
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
if((row.state&NODE_REPOSITION_BIT)!==0){
|
2016-08-16 06:55:48 -07:00
|
|
|
evaluatePosition(row);
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
if(row.top>=top&&row.top<top+height){
|
|
|
|
if(result[row.top-top]!=null){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'getRows put more than one row at position '+row.top+' into result';
|
|
|
|
}
|
|
|
|
result[row.top-top]=row;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
if((row.state&NODE_EXPANDED_BIT)!==0){
|
|
|
|
var children=row.children;
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i5=0;_i5<children.length;_i5++){
|
|
|
|
var child=children[_i5];
|
2016-08-08 04:27:53 -07:00
|
|
|
if(child.top<top+height&&top<child.top+child.height){
|
2016-08-16 06:55:48 -07:00
|
|
|
getRowsImpl(child,top,height,result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function updateHeight(row,heightChange){
|
|
|
|
while(row!==null){
|
|
|
|
row.height+=heightChange;
|
|
|
|
row.state|=NODE_REPOSITION_BIT;
|
2016-08-16 06:55:48 -07:00
|
|
|
row=row.parent;
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function addChildrenWithFieldExpander(row,expander,nextActiveIndex){
|
|
|
|
var rowIndices=row.indices;
|
|
|
|
var comparer=expander.comparer;
|
|
|
|
rowIndices.sort(comparer);
|
|
|
|
var begin=0;
|
|
|
|
var end=1;
|
|
|
|
row.children=[];
|
|
|
|
while(end<rowIndices.length){
|
|
|
|
if(comparer(rowIndices[begin],rowIndices[end])!==0){
|
|
|
|
row.children.push(createTreeNode(
|
|
|
|
row,
|
|
|
|
expander.name+': '+expander.formatter(rowIndices[begin]),
|
|
|
|
rowIndices.subarray(begin,end),
|
|
|
|
nextActiveIndex));
|
2016-08-16 06:55:48 -07:00
|
|
|
begin=end;
|
|
|
|
}
|
|
|
|
end++;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
row.children.push(createTreeNode(
|
|
|
|
row,
|
|
|
|
expander.name+': '+expander.formatter(rowIndices[begin]),
|
|
|
|
rowIndices.subarray(begin,end),
|
2016-08-16 06:55:48 -07:00
|
|
|
nextActiveIndex));
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function addChildrenWithStackExpander(row,expander,activeIndex,depth,nextActiveIndex){
|
|
|
|
var rowIndices=row.indices;
|
|
|
|
var stackGetter=expander.stackGetter;
|
2016-10-06 07:52:35 -07:00
|
|
|
var frameIdGetter=expander.frameIdGetter;
|
2016-08-08 04:27:53 -07:00
|
|
|
var frameGetter=expander.frameGetter;
|
|
|
|
var comparer=expander.comparers[depth];
|
|
|
|
var expandNextFrame=activeIndex|depth+1<<ACTIVE_EXPANDER_FRAME_SHIFT;
|
|
|
|
rowIndices.sort(comparer);
|
|
|
|
var columnName='';
|
|
|
|
if(depth===0){
|
2016-08-16 06:55:48 -07:00
|
|
|
columnName=expander.name+': ';
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
// put all the too-short stacks under <exclusive>
|
|
|
|
var begin=0;
|
|
|
|
var beginStack=null;
|
|
|
|
row.children=[];
|
|
|
|
while(begin<rowIndices.length){
|
|
|
|
beginStack=stackGetter(rowIndices[begin]);
|
|
|
|
if(beginStack.length>depth){
|
2016-08-16 06:55:48 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
begin++;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
if(begin>0){
|
|
|
|
row.children.push(createTreeNode(
|
|
|
|
row,
|
|
|
|
columnName+'<exclusive>',
|
|
|
|
rowIndices.subarray(0,begin),
|
2016-08-16 06:55:48 -07:00
|
|
|
nextActiveIndex));
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
// aggregate the rest under frames
|
|
|
|
if(begin<rowIndices.length){
|
|
|
|
var end=begin+1;
|
|
|
|
while(end<rowIndices.length){
|
|
|
|
var endStack=stackGetter(rowIndices[end]);
|
2016-10-06 07:52:35 -07:00
|
|
|
if(frameIdGetter(beginStack,depth)!==frameIdGetter(endStack,depth)){
|
2016-08-08 04:27:53 -07:00
|
|
|
row.children.push(createTreeNode(
|
|
|
|
row,
|
2016-10-06 07:52:35 -07:00
|
|
|
columnName+frameGetter(frameIdGetter(beginStack,depth)),
|
2016-08-08 04:27:53 -07:00
|
|
|
rowIndices.subarray(begin,end),
|
|
|
|
expandNextFrame));
|
|
|
|
begin=end;
|
2016-08-16 06:55:48 -07:00
|
|
|
beginStack=endStack;
|
|
|
|
}
|
|
|
|
end++;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
row.children.push(createTreeNode(
|
|
|
|
row,
|
2016-10-06 07:52:35 -07:00
|
|
|
columnName+frameGetter(frameIdGetter(beginStack,depth)),
|
2016-08-08 04:27:53 -07:00
|
|
|
rowIndices.subarray(begin,end),
|
2016-08-16 06:55:48 -07:00
|
|
|
expandNextFrame));
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function contractRow(row){
|
|
|
|
if((row.state&NODE_EXPANDED_BIT)===0){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'can not contract row, already contracted';
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
row.state^=NODE_EXPANDED_BIT;
|
|
|
|
var heightChange=1-row.height;
|
2016-08-16 06:55:48 -07:00
|
|
|
updateHeight(row,heightChange);
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
|
|
|
function pruneExpanders(row,oldExpander,newExpander){
|
|
|
|
row.state|=NODE_REPOSITION_BIT;
|
|
|
|
if(row.expander===oldExpander){
|
|
|
|
row.state|=NODE_REAGGREGATE_BIT|NODE_REORDER_BIT|NODE_REPOSITION_BIT;
|
|
|
|
if((row.state&NODE_EXPANDED_BIT)!==0){
|
2016-08-16 06:55:48 -07:00
|
|
|
contractRow(row);
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
row.children=null;
|
2016-08-16 06:55:48 -07:00
|
|
|
row.expander=newExpander;
|
|
|
|
}else{
|
2016-08-08 04:27:53 -07:00
|
|
|
row.state|=NODE_REPOSITION_BIT;
|
|
|
|
var children=row.children;
|
|
|
|
if(children!=null){
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i6=0;_i6<children.length;_i6++){
|
|
|
|
var child=children[_i6];
|
2016-08-16 06:55:48 -07:00
|
|
|
pruneExpanders(child,oldExpander,newExpander);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-08-16 06:55:48 -07:00
|
|
|
return{
|
2016-08-08 04:27:53 -07:00
|
|
|
addFieldExpander:function addFieldExpander(name,formatter,comparer){
|
|
|
|
if(FIELD_EXPANDER_ID_MIN+state.fieldExpanders.length>=FIELD_EXPANDER_ID_MAX){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'too many field expanders!';
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
state.fieldExpanders.push({
|
2016-08-16 06:55:48 -07:00
|
|
|
name:name,// name for column
|
|
|
|
formatter:formatter,// row index -> display string
|
|
|
|
comparer:comparer});
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-08-16 06:55:48 -07:00
|
|
|
return FIELD_EXPANDER_ID_MIN+state.fieldExpanders.length-1;
|
|
|
|
},
|
2016-10-06 07:52:35 -07:00
|
|
|
addCalleeStackExpander:function addCalleeStackExpander(name,maxStackDepth,stackGetter,frameGetter){
|
2016-08-08 04:27:53 -07:00
|
|
|
if(STACK_EXPANDER_ID_MIN+state.fieldExpanders.length>=STACK_EXPANDER_ID_MAX){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'too many stack expanders!';
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
state.stackExpanders.push({
|
2016-08-16 06:55:48 -07:00
|
|
|
name:name,// name for column
|
|
|
|
stackGetter:stackGetter,// row index -> stack array
|
2016-10-06 07:52:35 -07:00
|
|
|
comparers:createStackComparers(stackGetter,calleeFrameIdGetter,maxStackDepth),// depth -> comparer
|
|
|
|
frameIdGetter:calleeFrameIdGetter,// (stack, depth) -> string id
|
|
|
|
frameGetter:frameGetter});
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-08-16 06:55:48 -07:00
|
|
|
return STACK_EXPANDER_ID_MIN+state.stackExpanders.length-1;
|
|
|
|
},
|
2016-10-06 07:52:35 -07:00
|
|
|
addCallerStackExpander:function addCallerStackExpander(name,maxStackDepth,stackGetter,frameGetter){
|
2016-08-08 04:27:53 -07:00
|
|
|
if(STACK_EXPANDER_ID_MIN+state.fieldExpanders.length>=STACK_EXPANDER_ID_MAX){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'too many stack expanders!';
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
state.stackExpanders.push({
|
|
|
|
name:name,
|
|
|
|
stackGetter:stackGetter,
|
2016-10-06 07:52:35 -07:00
|
|
|
comparers:createStackComparers(stackGetter,callerFrameIdGetter,maxStackDepth),
|
|
|
|
frameIdGetter:callerFrameIdGetter,
|
|
|
|
frameGetter:frameGetter});
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-08-16 06:55:48 -07:00
|
|
|
return STACK_EXPANDER_ID_MIN+state.stackExpanders.length-1;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getExpanders:function getExpanders(){
|
|
|
|
var expanders=[];
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i7=0;_i7<state.fieldExpanders.length;_i7++){
|
|
|
|
expanders.push(FIELD_EXPANDER_ID_MIN+_i7);
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i8=0;_i8<state.stackExpanders.length;_i8++){
|
|
|
|
expanders.push(STACK_EXPANDER_ID_MIN+_i8);
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
|
|
|
return expanders;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getExpanderName:function getExpanderName(id){
|
|
|
|
if(id>=FIELD_EXPANDER_ID_MIN&&id<=FIELD_EXPANDER_ID_MAX){
|
2016-08-16 06:55:48 -07:00
|
|
|
return state.fieldExpanders[id-FIELD_EXPANDER_ID_MIN].name;
|
|
|
|
}else if(id>=STACK_EXPANDER_ID_MIN&&id<=STACK_EXPANDER_ID_MAX){
|
|
|
|
return state.stackExpanders[id-STACK_EXPANDER_ID_MIN].name;
|
|
|
|
}
|
|
|
|
throw'Unknown expander ID '+id.toString();
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
setActiveExpanders:function setActiveExpanders(ids){
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i9=0;_i9<ids.length;_i9++){
|
|
|
|
var id=ids[_i9];
|
2016-08-08 04:27:53 -07:00
|
|
|
if(id>=FIELD_EXPANDER_ID_MIN&&id<=FIELD_EXPANDER_ID_MAX){
|
|
|
|
if(id-FIELD_EXPANDER_ID_MIN>=state.fieldExpanders.length){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'field expander for id '+id.toString()+' does not exist!';
|
|
|
|
}
|
|
|
|
}else if(id>=STACK_EXPANDER_ID_MIN&&id<=STACK_EXPANDER_ID_MAX){
|
2016-08-08 04:27:53 -07:00
|
|
|
if(id-STACK_EXPANDER_ID_MIN>=state.stackExpanders.length){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'stack expander for id '+id.toString()+' does not exist!';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i10=0;_i10<ids.length;_i10++){
|
|
|
|
if(state.activeExpanders.length<=_i10){
|
|
|
|
pruneExpanders(state.root,INVALID_ACTIVE_EXPANDER,_i10);
|
2016-08-16 06:55:48 -07:00
|
|
|
break;
|
2016-10-25 07:06:11 -07:00
|
|
|
}else if(ids[_i10]!==state.activeExpanders[_i10]){
|
|
|
|
pruneExpanders(state.root,_i10,_i10);
|
2016-08-16 06:55:48 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
// TODO: if ids is prefix of activeExpanders, we need to make an expander invalid
|
2016-08-16 06:55:48 -07:00
|
|
|
state.activeExpanders=ids.slice();
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getActiveExpanders:function getActiveExpanders(){
|
2016-08-16 06:55:48 -07:00
|
|
|
return state.activeExpanders.slice();
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
addAggregator:function addAggregator(name,aggregator,formatter,sorter){
|
|
|
|
if(state.aggregators.length>=AGGREGATOR_ID_MAX){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'too many aggregators!';
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
state.aggregators.push({
|
2016-08-16 06:55:48 -07:00
|
|
|
name:name,// name for column
|
|
|
|
aggregator:aggregator,// index array -> aggregate value
|
|
|
|
formatter:formatter,// aggregate value -> display string
|
|
|
|
sorter:sorter});
|
2016-08-08 04:27:53 -07:00
|
|
|
|
2016-08-16 06:55:48 -07:00
|
|
|
return state.aggregators.length-1;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getAggregators:function getAggregators(){
|
|
|
|
var aggregators=[];
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i11=0;_i11<state.aggregators.length;_i11++){
|
|
|
|
aggregators.push(_i11);
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
|
|
|
return aggregators;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getAggregatorName:function getAggregatorName(id){
|
2016-08-16 06:55:48 -07:00
|
|
|
return state.aggregators[id&ACTIVE_AGGREGATOR_MASK].name;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
setActiveAggregators:function setActiveAggregators(ids){
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i12=0;_i12<ids.length;_i12++){
|
|
|
|
var id=ids[_i12]&ACTIVE_AGGREGATOR_MASK;
|
2016-08-08 04:27:53 -07:00
|
|
|
if(id<0||id>state.aggregators.length){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'aggregator id '+id.toString()+' not valid';
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
state.activeAggregators=ids.slice();
|
|
|
|
// NB: evaluate root here because dirty bit is for children
|
|
|
|
// so someone has to start with root, and it might as well be right away
|
|
|
|
evaluateAggregate(state.root);
|
|
|
|
var sorter=noSortOrder;var _loop2=function _loop2(
|
2016-10-25 07:06:11 -07:00
|
|
|
_i13){
|
|
|
|
var ascending=(ids[_i13]&ACTIVE_AGGREGATOR_ASC_BIT)!==0;
|
|
|
|
var id=ids[_i13]&ACTIVE_AGGREGATOR_MASK;
|
2016-08-08 04:27:53 -07:00
|
|
|
var comparer=state.aggregators[id].sorter;
|
|
|
|
var captureSorter=sorter;
|
2016-10-25 07:06:11 -07:00
|
|
|
var captureIndex=_i13;
|
2016-08-08 04:27:53 -07:00
|
|
|
sorter=function sorter(a,b){
|
|
|
|
var c=comparer(a.aggregates[captureIndex],b.aggregates[captureIndex]);
|
|
|
|
if(c===0){
|
2016-08-16 06:55:48 -07:00
|
|
|
return captureSorter(a,b);
|
|
|
|
}
|
|
|
|
return ascending?-c:c;
|
2016-10-25 07:06:11 -07:00
|
|
|
};};for(var _i13=ids.length-1;_i13>=0;_i13--){_loop2(_i13);
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
state.sorter=sorter;
|
2016-08-16 06:55:48 -07:00
|
|
|
state.root.state|=NODE_REORDER_BIT;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getActiveAggregators:function getActiveAggregators(){
|
2016-08-16 06:55:48 -07:00
|
|
|
return state.activeAggregators.slice();
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getRows:function getRows(top,height){
|
|
|
|
var result=new Array(height);
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i14=0;_i14<height;_i14++){
|
|
|
|
result[_i14]=null;
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
getRowsImpl(state.root,top,height,result);
|
2016-08-16 06:55:48 -07:00
|
|
|
return result;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getRowLabel:function getRowLabel(row){
|
2016-08-16 06:55:48 -07:00
|
|
|
return row.label;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getRowIndent:function getRowIndent(row){
|
2016-08-16 06:55:48 -07:00
|
|
|
return row.state>>>NODE_INDENT_SHIFT;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getRowAggregate:function getRowAggregate(row,index){
|
|
|
|
var aggregator=state.aggregators[state.activeAggregators[index]];
|
2016-08-16 06:55:48 -07:00
|
|
|
return aggregator.formatter(row.aggregates[index]);
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
getHeight:function getHeight(){
|
2016-08-16 06:55:48 -07:00
|
|
|
return state.root.height;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
canExpand:function canExpand(row){
|
2016-08-16 06:55:48 -07:00
|
|
|
return(row.state&NODE_EXPANDED_BIT)===0&&row.expander!==INVALID_ACTIVE_EXPANDER;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
canContract:function canContract(row){
|
2016-08-16 06:55:48 -07:00
|
|
|
return(row.state&NODE_EXPANDED_BIT)!==0;
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
expand:function expand(row){
|
|
|
|
if((row.state&NODE_EXPANDED_BIT)!==0){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'can not expand row, already expanded';
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
if(row.height!==1){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'unexpanded row has height '+row.height.toString()+' != 1';
|
|
|
|
}
|
|
|
|
if(row.children===null){// first expand, generate children
|
2016-08-08 04:27:53 -07:00
|
|
|
var activeIndex=row.expander&ACTIVE_EXPANDER_MASK;
|
2016-08-16 06:55:48 -07:00
|
|
|
var nextActiveIndex=activeIndex+1;// NB: if next is stack, frame is 0
|
2016-08-08 04:27:53 -07:00
|
|
|
if(nextActiveIndex>=state.activeExpanders.length){
|
2016-08-16 06:55:48 -07:00
|
|
|
nextActiveIndex=INVALID_ACTIVE_EXPANDER;
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
if(activeIndex>=state.activeExpanders.length){
|
2016-08-16 06:55:48 -07:00
|
|
|
throw'invalid active expander index '+activeIndex.toString();
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
var exId=state.activeExpanders[activeIndex];
|
|
|
|
if(exId>=FIELD_EXPANDER_ID_MIN&&
|
|
|
|
exId<FIELD_EXPANDER_ID_MIN+state.fieldExpanders.length){
|
|
|
|
var expander=state.fieldExpanders[exId-FIELD_EXPANDER_ID_MIN];
|
2016-08-16 06:55:48 -07:00
|
|
|
addChildrenWithFieldExpander(row,expander,nextActiveIndex);
|
|
|
|
}else if(exId>=STACK_EXPANDER_ID_MIN&&
|
2016-08-08 04:27:53 -07:00
|
|
|
exId<STACK_EXPANDER_ID_MIN+state.stackExpanders.length){
|
|
|
|
var depth=row.expander>>>ACTIVE_EXPANDER_FRAME_SHIFT;
|
|
|
|
var _expander=state.stackExpanders[exId-STACK_EXPANDER_ID_MIN];
|
2016-08-16 06:55:48 -07:00
|
|
|
addChildrenWithStackExpander(row,_expander,activeIndex,depth,nextActiveIndex);
|
|
|
|
}else{
|
|
|
|
throw'state.activeIndex '+activeIndex.toString()+
|
|
|
|
' has invalid expander'+exId.toString();
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
row.state|=NODE_EXPANDED_BIT|
|
|
|
|
NODE_REAGGREGATE_BIT|NODE_REORDER_BIT|NODE_REPOSITION_BIT;
|
|
|
|
var heightChange=0;
|
2016-10-25 07:06:11 -07:00
|
|
|
for(var _i15=0;_i15<row.children.length;_i15++){
|
|
|
|
heightChange+=row.children[_i15].height;
|
2016-08-16 06:55:48 -07:00
|
|
|
}
|
2016-08-08 04:27:53 -07:00
|
|
|
updateHeight(row,heightChange);
|
|
|
|
// if children only contains one node, then expand it as well
|
|
|
|
if(row.children.length===1&&this.canExpand(row.children[0])){
|
2016-08-16 06:55:48 -07:00
|
|
|
this.expand(row.children[0]);
|
|
|
|
}
|
|
|
|
},
|
2016-08-08 04:27:53 -07:00
|
|
|
contract:function contract(row){
|
2016-08-16 06:55:48 -07:00
|
|
|
contractRow(row);
|
|
|
|
}};
|
|
|
|
|
2016-10-06 07:52:35 -07:00
|
|
|
}
|
2016-10-25 07:06:11 -07:00
|
|
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9hZ2dyb3cuanMiXSwibmFtZXMiOlsiU3RyaW5nSW50ZXJuZXIiLCJzdHJpbmdzIiwiaWRzIiwiaW50ZXJuIiwiaW50ZXJuU3RyaW5nIiwicyIsImZpbmQiLCJ1bmRlZmluZWQiLCJpZCIsImxlbmd0aCIsInB1c2giLCJnZXQiLCJnZXRTdHJpbmciLCJTdGFja1JlZ2lzdHJ5Iiwicm9vdCIsIm5vZGVDb3VudCIsIm1heERlcHRoIiwic3RhY2tJZE1hcCIsImluc2VydCIsImluc2VydE5vZGUiLCJwYXJlbnQiLCJmcmFtZUlkIiwibm9kZSIsImdldFN0YWNrQXJyYXkiLCJmbGF0dGVuIiwiZmxhdHRlblN0YWNrcyIsInN0YWNrRnJhbWVDb3VudCIsImNvdW50U3RhY2tzIiwidHJlZSIsImRlcHRoIiwibGVhZiIsImNvbnNvbGUiLCJsb2ciLCJ0b1N0cmluZyIsIkFycmF5Iiwic3RhY2tBcnJheSIsIkludDMyQXJyYXkiLCJtYXhTdGFja0RlcHRoIiwiZmxhdHRlblN0YWNrc0ltcGwiLCJzdGFjayIsImNoaWxkU3RhY2siLCJNYXRoIiwibWF4IiwiTnVtYmVyIiwicG9wIiwic3ViYXJyYXkiLCJuZXdTdGFjayIsImkiLCJBZ2dyb3dEYXRhIiwiY29sdW1ucyIsImNvbHVtbkNvdW50IiwiY29sdW1uQ29udmVydGVyIiwibWFwIiwiYyIsInR5cGUiLCJkYXRhIiwicm93Q291bnQiLCJyb3dJbnNlcnRlciIsIm51bVJvd3MiLCJ0b0xvY2FsZVN0cmluZyIsIm5ld0RhdGEiLCJzZXQiLCJjdXJyT2Zmc2V0IiwiZW5kT2Zmc2V0IiwiaW5zZXJ0Um93IiwiYXJndW1lbnRzIiwiZG9uZSIsIkFnZ3JvdyIsImFnZ3Jvd0RhdGEiLCJjb2x1bW5JbmRleCIsImNvbHVtbk5hbWUiLCJjb2x1bW5UeXBlIiwiaW5kZXgiLCJmaW5kSW5kZXgiLCJuYW1lIiwic3RhY2tzIiwiZXhwYW5kZXIiLCJBZ2dyb3dFeHBhbmRlciIsImFkZFN1bUFnZ3JlZ2F0b3IiLCJhZ2dyZWdhdG9yTmFtZSIsImFkZEFnZ3JlZ2F0b3IiLCJhZ2dyZWdhdGVTaXplIiwiaW5kaWNlcyIsInNpemUiLCJyb3ciLCJ2YWx1ZSIsImEiLCJiIiwiYWRkQ291bnRBZ2dyZWdhdG9yIiwiYWdncmVnYXRlQ291bnQiLCJhZGRTdHJpbmdFeHBhbmRlciIsImV4cGFuZGVyTmFtZSIsImFkZEZpZWxkRXhwYW5kZXIiLCJyb3dBIiwicm93QiIsImFkZE51bWJlckV4cGFuZGVyIiwiYWRkUG9pbnRlckV4cGFuZGVyIiwiYWRkU3RhY2tFeHBhbmRlciIsImZvcm1hdHRlciIsImFkZENhbGxlZVN0YWNrRXhwYW5kZXIiLCJGSUVMRF9FWFBBTkRFUl9JRF9NSU4iLCJGSUVMRF9FWFBBTkRFUl9JRF9NQVgiLCJTVEFDS19FWFBBTkRFUl9JRF9NSU4iLCJTVEFDS19FWFBBTkRFUl9JRF9NQVgiLCJJTlZBTElEX0FDVElWRV9FWFBBTkRFUiIsIkFDVElWRV9FWFBBTkRFUl9NQVNLIiwiQUNUSVZFX0VYUEFOREVSX0ZSQU1FX1NISUZUIiwiQUdHUkVHQVRPUl9JRF9NQVgiLCJBQ1RJVkVfQUdHUkVHQVRPUl9NQVNLIiwiQUNUSVZFX0FHR1JFR0FUT1JfQVNDX0JJVCIsIk5PREVfRVhQQU5ERURfQklUIiwiTk9ERV9SRUFHR1JFR0FURV9CSVQiLCJOT0RFX1JFT1JERVJfQklUIiwiTk9ERV9SRVBPU0lUSU9OX0JJVCIsIk5PREVfSU5ERU5UX1NISUZUIiwiY2FsbGVlRnJhbWVJZEdldHRlciIsImNhbGxlckZyYW1lSWRHZXR0ZXIiLCJjcmVhdGVTdGFja0NvbXBhcmVycyIsInN0YWNrR2V0dGVyIiwiZnJhbWVJZEdldHRlciIsImNvbXBhcmVycyIsImNhcHR1cmVEZXB0aCIsImNhbGxlZVN0YWNrQ29tcGFyZXIiLCJjcmVhdGVUcmVlTm9kZSIsImxhYmVsIiwiaW5kZW50Iiwic3RhdGUiLCJjaGlsZHJlbiIsImFnZ3JlZ2F0ZXMiLCJ0b3AiLCJoZWlnaHQiLCJub1NvcnRPcmRlciIsImZpZWxkRXhwYW5kZXJzIiwic3RhY2tFeHBhbmRlcnMiLCJhY3RpdmVFeHBhbmRlcnMiLCJhZ2dyZWdhdG9ycyIsImFjdGl2ZUFnZ3JlZ2F0b3JzIiwic29ydGVyIiwiZXZhbHVhdGVBZ2dyZWdhdGUiLCJqIiwiYWdncmVnYXRvciIsImV2YWx1YXRlQWdncmVnYXRlcyIsImV2YWx1YXRlT3JkZXIiLCJjaGlsZCIsInNvcnQiLCJldmFsdWF0ZVBvc2l0aW9uIiwiY2hpbGRUb3AiLCJnZXRSb3dzSW1wbCIsInJlc3VsdCIsInVwZGF0ZUhlaWdodCIsImhlaWdodENoYW5nZSIsImFkZENoaWxkcmVuV2l0aEZpZWxkRXhwYW5kZXIiLCJuZXh0QWN0aXZlSW5kZXgiLCJyb3dJbmRpY2VzIiwiY29tcGFyZXIiLCJiZWdpbiIsImVuZCIsImFkZENoaWxkcmVuV2l0aFN0YWNrRXhwYW5kZXIiLCJhY3RpdmVJbmRleCIsImZyYW1lR2V0dGVyIiwiZXhwYW5kTmV4dEZyYW1lIiwiYmVnaW5TdGFjayIsImVuZFN0YWNrIiwiY29udHJhY3RSb3ciLCJwcnVuZUV4cGFuZGVycyIsIm9sZEV4cGFuZGVyIiwibmV3RXhwYW5kZXIiLCJhZGRDYWxsZXJTdGFja0V4cGFuZGVyIiwiZ2V0RXhwYW5kZXJzIiwiZXhwYW5kZXJzIiwiZ2V0RXhwYW5kZXJOYW1lIiwic2V0QWN0aXZlRXhwYW5kZXJzIiwic2xpY2UiLCJnZXRBY3RpdmVFeHBhbmRlcnMiLCJnZXRBZ2dyZWdhdG9ycyIsImdldEFnZ3JlZ2F0b3JOYW1lIiwic2V0QWN0aXZlQWdncmVnYXRvcnMiLCJhc2NlbmRpbmciLCJjYXB0dXJlU29ydGVyIiwiY2FwdHVyZUluZGV4IiwiZ2V0QWN0aXZlQWdncmVnYXRvcnMiLCJnZXRSb3dzIiwiZ2V0Um93TGFiZWwiLCJnZXRSb3dJbmRlbnQiLCJnZXRSb3dBZ2dyZWdhdGUiLCJnZXRIZWlnaHQiLCJjYW5FeHBhbmQiLCJjYW5Db250cmFjdCIsImV4cGFuZCIsImV4SWQiLCJjb250cmFjdCJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0FBUUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsUUFBU0EsZUFBVCxFQUEwQixDQUFFO0FBQzFCLEdBQU1DLFNBQVUsRUFBaEI7QUFDQSxHQUFNQyxLQUFNLEVBQVo7QUFDQSxNQUFPO0FBQ0xDLE9BQVEsUUFBU0MsYUFBVCxDQUFzQkMsQ0FBdEIsQ0FBeUI7QUFDL0IsR0FBTUMsTUFBT0osSUFBSUcsQ0FBSixDQUFiO0FBQ0EsR0FBSUMsT0FBU0MsU0FBYixDQUF3QjtBQUN0QixHQUFNQyxJQUFLUCxRQUFRUSxNQUFuQjtBQUNBUCxJQUFJRyxDQUFKLEVBQVNHLEVBQVQ7QUFDQVAsUUFBUVMsSUFBUixDQUFhTCxDQUFiO0FBQ0EsTUFBT0csR0FBUDtBQUNELENBTEQsSUFLTztBQUNML
|
2016-10-06 07:52:35 -07:00
|
|
|
// @generated
|