<commit_before>var d3 = require('d3');

// dont change! implementation depends on it being sorted to the start of any list of numbers
var misval = -Number.MAX_VALUE;

/**
 * Filter implementation specific:
 *
 *   crossfilter objects to be passed directly to a dc chart:
 *   dx_dimension dx.filter()       for chart.dimension()
 *   dx_group     dx.filter.group() contains the group operations; for chart.group()
 *                   mostly group.all()
 */

var filter1dCategorial = function (domain) {
    return function (d) {
        if(d == misval) return false;

        var i;
        for (i=0; i<domain.length; i++) {
            if(domain[i] == d) {
                return true;
            }
        }
        return false;
    };
};

// return true if domain[0] <= d < domain[1]
var filter1dContinuous = function (domain) {
    var min = domain[0];
    var max = domain[1];
    if(min > max) {
        min = domain[1];
        max = domain[0];
    }

    return function (d) {
        return (d >= min && d < max && d != misval);
    };
};

var wrapSumCountOrAverage = function(facet) {

    var valueAccessor;
    if(facet.reduceSum) {
        return function (d) {
            return d.value.sum;
        };
    }
    else if(facet.reduceCount) {
        return function (d) {
            return d.value.count;
        };
    }
    else if(facet.reduceAverage) {
        return function (d) {
            if(d.value.count > 0) {
                return d.value.sum / d.value.count;
            }
            else {
                return 0.0;
            }
        };
    }
    else {
        console.log("Reduction not implemented for this facet", facet);
    }
    return null;
};

var wrapAbsoluteOrRelative = function(group, facet) {
    if(facet.reducePercentage) {
        return {
            all: function () {
                var records = group.all();

                // Create a copy, as we shouldnt modify the crossfilters groups
                var scaled_records = [];

                var fullsum = 0, fullcount = 0;
                records.forEach(function(f) {
                    fullsum += f.value.sum;
                    fullcount += f.value.count;
                    scaled_records.push(f);
                });

                records.forEach(function(f) {
                    f.value.count = f.value.count / fullcount;
                    f.value.sum = f.value.count / fullsum;
                });
                return scaled_records;
            },
        };
    }
    else if (facet.reduceAbsolute) {
        return group;
    }
    else {
        console.log("Reduction not implemented for facet", facet);
        return null;
    }
};

// Usecase: general purpose
//   A continuous or categorial
//   B continuous or false
// Data format:
//    [ {key: facetA.group(d), value: {sum: facetB.value(d)., count: sum 1}, ....]
var dxGlue1d = function (facetA,facetB) {
    var valueFn;
    if(facetB) {
        valueFn = facetB.value;
    }
    else  {
        valueFn = function (d) {
            return 1;
        };
    }

    var dimension = window.app.crossfilter.dimension(facetA.value);
    var group = dimension.group(facetA.group);

    group.reduce(
        function (p,v) { // add
            var value = valueFn(v);
            if(value != misval) {
                p.sum += value;
                p.count++;
            }
            return p;
        },
        function (p,v) { // subtract
            var value = valueFn(v);
            if(value != misval) {
                p.sum -= value;
                p.count--;
            }
            return p;
        },
        function () { // initialize
            return {count: 0, sum: 0};
        }
    );

    var valueAccessor;
    var wrapped_group;
    if(facetB) {
        valueAccessor = wrapSumCountOrAverage(facetB);
        wrapped_group = wrapAbsoluteOrRelative(group, facetB);
    }
    else {
        valueAccessor = function (d) {return d.value.count;};
        wrapped_group = wrapAbsoluteOrRelative(group, facetA);
    }

    return {
        dimension: dimension,
        group: wrapped_group,
        valueAccessor: valueAccessor,
    };
};

// Usecase: boxplot
// FIXME: currently, boxplots are slow
//  A continuous or categorial
//  B continuous (categorial untested, will probably crash dcjs)
// Dataformat:
//  [ {key: facetA.group(d), value: [ d0, d1, d2, d3, ... ] }, ... ]
var dxGlueAwithBs = function (facetA,facetB) {
    var dimension = window.app.crossfilter.dimension(facetA.value);
    var group = dimension.group(facetA.group);

    var valueFn = facetB.value;

    group.reduce(
        function(p,v) {
            var value = valueFn(v);
            if(value!=misval) {
                p.push(valueFn(v));
            }
            return p;
        },
        function(p,v) {
            var value = valueFn(v);
            if(value!=misval) {
                p.splice(p.indexOf(valueFn(v)), 1);
            }
            return p;
        },
        function() {
            return [];
        }
    );

    return {
        dimension: dimension,
        group: group,
    };
};

// Usecase: scatterplot correlationplot
//  A continuous or categorial         (x-axis)
//  B continuous or categorial         (y-axis)
//  C continuous [default f(d)=1]      (z-axis)
// Dataformat:
//   [ {key: [ facetA.group(d), facetB.group(d) ], value: {count: sum 1, sum: .., xsum:., ysum:., xysum:., yysum:. } }, ... ]
var dxGlue2d = function (facetA, facetB, facetC) {

    var valueA = facetA.value;
    var valueB = facetB.value;

    var valueC;
    if(facetC) {
        valueC = facetC.value;
    }
    else {
        valueC = function () {return 1;};
    }

    var dimension = window.app.crossfilter.dimension(function(d) {return [valueA(d), valueB(d)];});

    // Setup grouping
    var xgroupFn = facetA.group;
    var ygroupFn = facetB.group;
    var group = dimension.group(function(d) {
        var x = d[0];
        var y = d[1];
        if(x != misval) {
            x = xgroupFn(x);
        }
        if(y != misval) {
            y = ygroupFn(y);
        }
        return [x,y];
    });

    group.reduce(
        function (p,v) { // add
            var x = valueA(v);
            var y = valueB(v);
            var z = valueC(v);
            if(x != misval && y != misval && z != misval) {
                p.count++;
                p.sum += z;
                p.xsum += x;
                p.ysum += y;
                p.xysum += x * y;
                p.xxsum += x * x;
                p.yysum += y * y;
            }
            return p;
        },
        function (p,v) { // subtract
            var x = valueA(v);
            var y = valueB(v);
            var z = valueC(v);
            if(x != misval && y != misval && z != misval) {
                p.count--;
                p.sum -= z;
                p.xsum -= x;
                p.ysum -= y;
                p.xysum -= x * y;
                p.xxsum -= x * x;
                p.yysum -= y * y;
            }
            return p;
        },
        function () { // initialize
            return {
                count: 0,
                sum: 0,
                xsum: 0,
                ysum: 0,
                xysum: 0,
                xxsum: 0,
                yysum: 0,
            };
        });

    var valueAccessor;
    if(facetC) {
        valueAccessor = wrapSumCountOrAverage(facetC);
    }
    else {
        valueAccessor = wrapSumCountOrAverage(facetB);
    }

    var wrapped_group = wrapAbsoluteOrRelative(group, facetB);

    return {
        dimension: dimension,
        group: wrapped_group,
        valueAccessor: valueAccessor
    };
};

// Usecase: stacked barchart
//   A: continuous or categorial          (x-axis)
//   B: categorial: [ C1, C2, C3, ...]    (y-axis)
//   C: continuous [default: f(d)=1]      (z-axis)
// Dataformat:
//  [ {key: facetA.group(d), value: {C1: sum(facetC.value(d)), C2: sum(facetC.value(d)), ..., _total: sum 1}, ...]
var dxGlueAbyCatB = function (facetA, facetB, facetC) {
    var dimension = window.app.crossfilter.dimension(facetA.value);
    var group = dimension.group(facetA.group);

    var valueB = facetB.value;
    var domain = facetB.x.domain();

    var valueC;
    if(facetC) {
        valueC = facetC.value;
    }
    else {
        valueC = function () {return 1;};
    }

    group.reduce(
        function (p,v) { // add
            var y = valueB(v);
            var z = valueC(v);
            if(y != misval && z != misval) {
                p[y] += z;
                p._total += z;
            }
            return p;
        },
        function (p,v) { // subtract
            var y = valueB(v);
            var z = valueC(v);
            if(y != misval && z != misval) {
                p[y] -= z;
                p._total -= z;
            }
            return p;
        },
        function () { // initialize
            var result = {_total: 0};
            domain.forEach(function (f) {
                result[f] = 0;
            });
            return result;
        });

    return {
        dimension: dimension,
        group: group
    };
};

// Usecase: find all values on an ordinal (categorial) axis
// returns Array [ {key: .., value: ...}, ... ]
// NOTE: numbers are parsed: so not {key:'5', 20} but {key:5, value: 20}
var dxGetCategories = function (facet) {

    var rawValue = function (d) {
        var val = facet.basevalue(d);

        // User should only see user-defined missing data values
        if (val == misval) {
            return facet.misval[0];
        }
        return val;
    };

    var dimension = window.app.crossfilter.dimension(rawValue);
    var group = dimension.group().reduceCount();

    function compare(a,b) {
        if (a.key < b.key) return -1;
        if (a.key > b.key) return 1;
        return 0;
    }

    var data = group.top(Infinity).sort(compare);
    dimension.dispose();

    return data;
};

// Usecase: transformPercentiles
// Calculate 100 percentiles (ie. 1,2,3,4 etc.)
// approximate the nth percentile by taking the data at index:
//     i ~= floor(0.01 * n * len(data))
var dxGetPercentiles = function (facet) {
    var basevalueFn = facet.basevalue;
    var dimension = window.app.crossfilter.dimension(basevalueFn);
    var data = dimension.bottom(Infinity);

    var percentiles = [];
    var p, i, value;

    // drop missing values, which should be sorted at the start of the array
    i=0;
    while(basevalueFn(data[i]) == misval) i++;
    data.splice(0,i);

    for(p=0; p<101; p++) {
        i = Math.trunc(p * 0.01 * (data.length-1));
        value = basevalueFn(data[i]);
        percentiles.push({x: value, p:p});
    }

    dimension.dispose();

    return percentiles;
};

// Usecase: transformExceedances
// Calculate value where exceedance probability is one in 10,20,30,40,50,
// and the same for -exceedance -50, -60, -70, -80, -90, -99, -99.9, -99.99, ... percent
// Approximate from data: 1 in 10 is larger than value at index trunc(0.1 * len(data))
var dxGetExceedances = function (facet) {
    var basevalueFn = facet.basevalue;
    var dimension = window.app.crossfilter.dimension(basevalueFn);
    var data = dimension.bottom(Infinity);
    var exceedance;
    var i, value, oom, mult, n;

    // drop missing values, which should be sorted at the start of the array
    i=0;
    while(basevalueFn(data[i]) == misval) i++;
    data.splice(0,i);

    // percentilel
    // P(p)=x := p% of the data is smaller than x, (100-p)% is larger than x
    //
    // exceedance:
    // '1 in n' value, or what is the value x such that the probabiltiy drawing a value y with y > x is 1 / n

    exceedance = [{x: basevalueFn(data[ data.length / 2]), e: 2}];

    // order of magnitude
    oom = 1;
    mult = 3;
    while( mult * oom < data.length ) {

        n = oom * mult;

        // exceedance
        i = data.length - Math.trunc(data.length/n);
        value = basevalueFn(data[i]);

        // only add it if it is different form the previous value
        if( value > exceedance[ exceedance.length - 1].x ) {
            exceedance.push({x: value, e:n});
        }

        // subceedance (?)
        i = data.length - i;
        value = basevalueFn(data[i]);

        // only add it if it is different form the previous value
        if( value < exceedance[0].x ) {
            exceedance.unshift({x: value, e: -n});
        }

        mult++;
        if(mult==10) {
            oom = oom * 10;
            mult=1;
        }
    }
    dimension.dispose();
    return exceedance;
};

// FIXME: creating and disposing dimension is slow.. maybe keep it around somewhere..
//        this is used in the heatmap, should be refactored when adding/joining data is implemented
var dxDataGet = function () {
    var dimension = window.app.crossfilter.dimension(function (d) {return 1;});
    var data = dimension.top(Infinity);
    dimension.dispose();
    return data;
};

module.exports = {
    dxGlue1d: dxGlue1d,
    dxGlue2d: dxGlue2d,
    dxDataGet: dxDataGet,
    dxGetCategories: dxGetCategories,
    dxGetPercentiles: dxGetPercentiles,
    dxGetExceedances: dxGetExceedances,
    dxGlueAbyCatB: dxGlueAbyCatB,
    dxGlueAwithBs: dxGlueAwithBs,
    misval: misval,
    filter1dCategorial: filter1dCategorial,
    filter1dContinuous: filter1dContinuous,
};
<commit_msg>Doc update<commit_after>var d3 = require('d3');

// dont change! implementation depends on it being sorted to the start of any list of numbers
var misval = -Number.MAX_VALUE;

/**
 * Filter implementation specific:
 *
 *   dimension:      crossfilter dimension, supporting filter() method
 *   group:          crossfilter group with a method all() that returns [ {key: {}, value: {}}, ... ]
 *   valueAccessor:  valueAccessor, function that produces group value: valueAccessor(value)
 */

var filter1dCategorial = function (domain) {
    return function (d) {
        if(d == misval) return false;

        var i;
        for (i=0; i<domain.length; i++) {
            if(domain[i] == d) {
                return true;
            }
        }
        return false;
    };
};

// return true if domain[0] <= d < domain[1]
var filter1dContinuous = function (domain) {
    var min = domain[0];
    var max = domain[1];
    if(min > max) {
        min = domain[1];
        max = domain[0];
    }

    return function (d) {
        return (d >= min && d < max && d != misval);
    };
};

var wrapSumCountOrAverage = function(facet) {

    var valueAccessor;
    if(facet.reduceSum) {
        return function (d) {
            return d.value.sum;
        };
    }
    else if(facet.reduceCount) {
        return function (d) {
            return d.value.count;
        };
    }
    else if(facet.reduceAverage) {
        return function (d) {
            if(d.value.count > 0) {
                return d.value.sum / d.value.count;
            }
            else {
                return 0.0;
            }
        };
    }
    else {
        console.log("Reduction not implemented for this facet", facet);
    }
    return null;
};

var wrapAbsoluteOrRelative = function(group, facet) {
    if(facet.reducePercentage) {
        return {
            all: function () {
                var records = group.all();

                // Create a copy, as we shouldnt modify the crossfilters groups
                var scaled_records = [];

                var fullsum = 0, fullcount = 0;
                records.forEach(function(f) {
                    fullsum += f.value.sum;
                    fullcount += f.value.count;
                    scaled_records.push(f);
                });

                records.forEach(function(f) {
                    f.value.count = f.value.count / fullcount;
                    f.value.sum = f.value.count / fullsum;
                });
                return scaled_records;
            },
        };
    }
    else if (facet.reduceAbsolute) {
        return group;
    }
    else {
        console.log("Reduction not implemented for facet", facet);
        return null;
    }
};

// Usecase: general purpose
//   A continuous or categorial
//   B continuous or false
// Data format:
//    [ {key: facetA.group(d), value: {sum: facetB.value(d)., count: sum 1}, ....]
var dxGlue1d = function (facetA,facetB) {
    var valueFn;
    if(facetB) {
        valueFn = facetB.value;
    }
    else  {
        valueFn = function (d) {
            return 1;
        };
    }

    var dimension = window.app.crossfilter.dimension(facetA.value);
    var group = dimension.group(facetA.group);

    group.reduce(
        function (p,v) { // add
            var value = valueFn(v);
            if(value != misval) {
                p.sum += value;
                p.count++;
            }
            return p;
        },
        function (p,v) { // subtract
            var value = valueFn(v);
            if(value != misval) {
                p.sum -= value;
                p.count--;
            }
            return p;
        },
        function () { // initialize
            return {count: 0, sum: 0};
        }
    );

    var valueAccessor;
    var wrapped_group;
    if(facetB) {
        valueAccessor = wrapSumCountOrAverage(facetB);
        wrapped_group = wrapAbsoluteOrRelative(group, facetB);
    }
    else {
        valueAccessor = function (d) {return d.value.count;};
        wrapped_group = wrapAbsoluteOrRelative(group, facetA);
    }

    return {
        dimension: dimension,
        group: wrapped_group,
        valueAccessor: valueAccessor,
    };
};

// Usecase: boxplot
// FIXME: currently, boxplots are slow
//  A continuous or categorial
//  B continuous (categorial untested, will probably crash dcjs)
// Dataformat:
//  [ {key: facetA.group(d), value: [ d0, d1, d2, d3, ... ] }, ... ]
var dxGlueAwithBs = function (facetA,facetB) {
    var dimension = window.app.crossfilter.dimension(facetA.value);
    var group = dimension.group(facetA.group);

    var valueFn = facetB.value;

    group.reduce(
        function(p,v) {
            var value = valueFn(v);
            if(value!=misval) {
                p.push(valueFn(v));
            }
            return p;
        },
        function(p,v) {
            var value = valueFn(v);
            if(value!=misval) {
                p.splice(p.indexOf(valueFn(v)), 1);
            }
            return p;
        },
        function() {
            return [];
        }
    );

    return {
        dimension: dimension,
        group: group,
    };
};

// Usecase: scatterplot correlationplot
//  A continuous or categorial         (x-axis)
//  B continuous or categorial         (y-axis)
//  C continuous [default f(d)=1]      (z-axis)
// Dataformat:
//   [ {key: [ facetA.group(d), facetB.group(d) ], value: {count: sum 1, sum: .., xsum:., ysum:., xysum:., yysum:. } }, ... ]
var dxGlue2d = function (facetA, facetB, facetC) {

    var valueA = facetA.value;
    var valueB = facetB.value;

    var valueC;
    if(facetC) {
        valueC = facetC.value;
    }
    else {
        valueC = function () {return 1;};
    }

    var dimension = window.app.crossfilter.dimension(function(d) {return [valueA(d), valueB(d)];});

    // Setup grouping
    var xgroupFn = facetA.group;
    var ygroupFn = facetB.group;
    var group = dimension.group(function(d) {
        var x = d[0];
        var y = d[1];
        if(x != misval) {
            x = xgroupFn(x);
        }
        if(y != misval) {
            y = ygroupFn(y);
        }
        return [x,y];
    });

    group.reduce(
        function (p,v) { // add
            var x = valueA(v);
            var y = valueB(v);
            var z = valueC(v);
            if(x != misval && y != misval && z != misval) {
                p.count++;
                p.sum += z;
                p.xsum += x;
                p.ysum += y;
                p.xysum += x * y;
                p.xxsum += x * x;
                p.yysum += y * y;
            }
            return p;
        },
        function (p,v) { // subtract
            var x = valueA(v);
            var y = valueB(v);
            var z = valueC(v);
            if(x != misval && y != misval && z != misval) {
                p.count--;
                p.sum -= z;
                p.xsum -= x;
                p.ysum -= y;
                p.xysum -= x * y;
                p.xxsum -= x * x;
                p.yysum -= y * y;
            }
            return p;
        },
        function () { // initialize
            return {
                count: 0,
                sum: 0,
                xsum: 0,
                ysum: 0,
                xysum: 0,
                xxsum: 0,
                yysum: 0,
            };
        });

    var valueAccessor;
    if(facetC) {
        valueAccessor = wrapSumCountOrAverage(facetC);
    }
    else {
        valueAccessor = wrapSumCountOrAverage(facetB);
    }

    var wrapped_group = wrapAbsoluteOrRelative(group, facetB);

    return {
        dimension: dimension,
        group: wrapped_group,
        valueAccessor: valueAccessor
    };
};

// Usecase: stacked barchart
//   A: continuous or categorial          (x-axis)
//   B: categorial: [ C1, C2, C3, ...]    (y-axis)
//   C: continuous [default: f(d)=1]      (z-axis)
// Dataformat:
//  [ {key: facetA.group(d), value: {C1: sum(facetC.value(d)), C2: sum(facetC.value(d)), ..., _total: sum 1}, ...]
var dxGlueAbyCatB = function (facetA, facetB, facetC) {
    var dimension = window.app.crossfilter.dimension(facetA.value);
    var group = dimension.group(facetA.group);

    var valueB = facetB.value;
    var domain = facetB.x.domain();

    var valueC;
    if(facetC) {
        valueC = facetC.value;
    }
    else {
        valueC = function () {return 1;};
    }

    group.reduce(
        function (p,v) { // add
            var y = valueB(v);
            var z = valueC(v);
            if(y != misval && z != misval) {
                p[y] += z;
                p._total += z;
            }
            return p;
        },
        function (p,v) { // subtract
            var y = valueB(v);
            var z = valueC(v);
            if(y != misval && z != misval) {
                p[y] -= z;
                p._total -= z;
            }
            return p;
        },
        function () { // initialize
            var result = {_total: 0};
            domain.forEach(function (f) {
                result[f] = 0;
            });
            return result;
        });

    return {
        dimension: dimension,
        group: group
    };
};

// Usecase: find all values on an ordinal (categorial) axis
// returns Array [ {key: .., value: ...}, ... ]
// NOTE: numbers are parsed: so not {key:'5', 20} but {key:5, value: 20}
var dxGetCategories = function (facet) {

    var rawValue = function (d) {
        var val = facet.basevalue(d);

        // User should only see user-defined missing data values
        if (val == misval) {
            return facet.misval[0];
        }
        return val;
    };

    var dimension = window.app.crossfilter.dimension(rawValue);
    var group = dimension.group().reduceCount();

    function compare(a,b) {
        if (a.key < b.key) return -1;
        if (a.key > b.key) return 1;
        return 0;
    }

    var data = group.top(Infinity).sort(compare);
    dimension.dispose();

    return data;
};

// Usecase: transformPercentiles
// Calculate 100 percentiles (ie. 1,2,3,4 etc.)
// approximate the nth percentile by taking the data at index:
//     i ~= floor(0.01 * n * len(data))
var dxGetPercentiles = function (facet) {
    var basevalueFn = facet.basevalue;
    var dimension = window.app.crossfilter.dimension(basevalueFn);
    var data = dimension.bottom(Infinity);

    var percentiles = [];
    var p, i, value;

    // drop missing values, which should be sorted at the start of the array
    i=0;
    while(basevalueFn(data[i]) == misval) i++;
    data.splice(0,i);

    for(p=0; p<101; p++) {
        i = Math.trunc(p * 0.01 * (data.length-1));
        value = basevalueFn(data[i]);
        percentiles.push({x: value, p:p});
    }

    dimension.dispose();

    return percentiles;
};

// Usecase: transformExceedances
// Calculate value where exceedance probability is one in 10,20,30,40,50,
// and the same for -exceedance -50, -60, -70, -80, -90, -99, -99.9, -99.99, ... percent
// Approximate from data: 1 in 10 is larger than value at index trunc(0.1 * len(data))
var dxGetExceedances = function (facet) {
    var basevalueFn = facet.basevalue;
    var dimension = window.app.crossfilter.dimension(basevalueFn);
    var data = dimension.bottom(Infinity);
    var exceedance;
    var i, value, oom, mult, n;

    // drop missing values, which should be sorted at the start of the array
    i=0;
    while(basevalueFn(data[i]) == misval) i++;
    data.splice(0,i);

    // percentilel
    // P(p)=x := p% of the data is smaller than x, (100-p)% is larger than x
    //
    // exceedance:
    // '1 in n' value, or what is the value x such that the probabiltiy drawing a value y with y > x is 1 / n

    exceedance = [{x: basevalueFn(data[ data.length / 2]), e: 2}];

    // order of magnitude
    oom = 1;
    mult = 3;
    while( mult * oom < data.length ) {

        n = oom * mult;

        // exceedance
        i = data.length - Math.trunc(data.length/n);
        value = basevalueFn(data[i]);

        // only add it if it is different form the previous value
        if( value > exceedance[ exceedance.length - 1].x ) {
            exceedance.push({x: value, e:n});
        }

        // subceedance (?)
        i = data.length - i;
        value = basevalueFn(data[i]);

        // only add it if it is different form the previous value
        if( value < exceedance[0].x ) {
            exceedance.unshift({x: value, e: -n});
        }

        mult++;
        if(mult==10) {
            oom = oom * 10;
            mult=1;
        }
    }
    dimension.dispose();
    return exceedance;
};

// FIXME: creating and disposing dimension is slow.. maybe keep it around somewhere..
//        this is used in the heatmap, should be refactored when adding/joining data is implemented
var dxDataGet = function () {
    var dimension = window.app.crossfilter.dimension(function (d) {return 1;});
    var data = dimension.top(Infinity);
    dimension.dispose();
    return data;
};

module.exports = {
    dxGlue1d: dxGlue1d,
    dxGlue2d: dxGlue2d,
    dxDataGet: dxDataGet,
    dxGetCategories: dxGetCategories,
    dxGetPercentiles: dxGetPercentiles,
    dxGetExceedances: dxGetExceedances,
    dxGlueAbyCatB: dxGlueAbyCatB,
    dxGlueAwithBs: dxGlueAwithBs,
    misval: misval,
    filter1dCategorial: filter1dCategorial,
    filter1dContinuous: filter1dContinuous,
};
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import {connect} from 'react-redux'
import R from 'ramda'
import * as Constants from '../../../../constants/ComponentTypes'
import {fetchUsers} from '../../../../actions/User'
import {fetchOrganizations} from '../../../../actions/Organization'
import {fetchAudiences} from '../../../../actions/Audience'
import {fetchComcheck, fetchComcheckStatuses} from '../../../../actions/Comcheck'
import {List} from '../../../../components/List'
import {AvatarListItem, AvatarHeaderItem} from '../../../../components/list/ListItem';
import {Avatar} from '../../../../components/Avatar'
import {Icon} from '../../../../components/Icon'
import {dateFormat} from '../../../../utils/Time'

const styles = {
  'container': {}
  ,
  'header': {
    'avatar': {
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700',
      padding: '12px 0 0 15px'
    },
    'user_firstname': {
      float: 'left',
      width: '22%',
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700'
    },
    'user_email': {
      float: 'left',
      width: '35%',
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700'
    },
    'user_organization': {
      float: 'left',
      width: '22%',
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700'
    },
    'status_last_update': {
      float: 'left',
      width: '15%',
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700'
    },
    'status_state': {
      float: 'right',
      width: '8%',
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700'
    }
  },
  'title': {
    float: 'left',
    fontSize: '20px',
    fontWeight: 600
  },
  'subtitle': {
    float: 'left',
    fontSize: '14px',
  },
  'empty': {
    marginTop: 40,
    fontSize: '18px',
    fontWeight: 500,
    textAlign: 'center'
  },
  'number': {
    float: 'right',
    color: '#9E9E9E',
    fontSize: '12px',
  },
  'name': {
    float: 'left',
    width: '22%',
    padding: '5px 0 0 0'
  },
  'mail': {
    float: 'left',
    width: '35%',
    padding: '5px 0 0 0'
  },
  'org': {
    float: 'left',
    width: '22%',
    padding: '5px 0 0 0'
  },
  'update': {
    float: 'left',
    width: '15%',
    padding: '5px 0 0 0'
  },
  'state': {
    float: 'right',
    textAlign: 'center',
    padding: 0
  }
}

class Comcheck extends Component {
  constructor(props) {
    super(props);
    this.state = {sortBy: 'user_firstname', orderAsc: true}
  }

  componentDidMount() {
    this.props.fetchComcheck(this.props.exerciseId, this.props.comcheckId)
    this.props.fetchComcheckStatuses(this.props.exerciseId, this.props.comcheckId)
    this.props.fetchAudiences(this.props.exerciseId)
    this.props.fetchUsers()
    this.props.fetchOrganizations()
    this.repeatTimeout()
  }

  componentWillUnmount() {
    //noinspection Eslint
    clearTimeout(this.repeat)
  }

  repeatTimeout() {
    //noinspection Eslint
    const context = this
    //noinspection Eslint
    this.repeat = setTimeout(function () {
      context.circularFetch()
      context.repeatTimeout(context);
    }, 5000)
  }

  circularFetch() {
    this.props.fetchComcheckStatuses(this.props.exerciseId, this.props.comcheckId, true)
  }

  reverseBy(field) {
    this.setState({sortBy: field, orderAsc: !this.state.orderAsc})
  }

  SortHeader(field, label) {
    var icon = this.state.orderAsc ? Constants.ICON_NAME_NAVIGATION_ARROW_DROP_DOWN
      : Constants.ICON_NAME_NAVIGATION_ARROW_DROP_UP
    const IconDisplay = this.state.sortBy === field ? <Icon type={Constants.ICON_TYPE_SORT} name={icon}/> : ""
    return

      {label} {IconDisplay}

  }

  //TODO replace with sortWith after Ramdajs new release
  ascend(a, b) {
    return a  b ? 1 : 0;
  }

  descend(a, b) {
    return a > b ? -1 : a < b ? 1 : 0;
  }

  render() {
    return

Comcheck to audience {this.props.audience.audience_name}

From {dateFormat(this.props.comcheck.comcheck_start_date)} to {dateFormat(this.props.comcheck.comcheck_end_date)}

        <List>
          <AvatarHeaderItem leftAvatar={#} primaryText={

              {this.SortHeader('user_firstname', 'name')}
              {this.SortHeader('user_email', 'Email address')}
              {this.SortHeader('user_organization', 'Organization')}
              {this.SortHeader('status_last_update', 'Last update')}
              {this.SortHeader('status_state', 'State')}

}
          />

          {this.props.comcheck_statuses.map(status => {
            let status_id = R.propOr(Math.random(), 'status_id', status)
            let status_state = R.propOr(0, 'status_state', status)
            let status_last_update = R.propOr('', 'status_last_update', status)
            let user_id = R.pathOr(Math.random(), ['status_user', 'user_id'], status)
            let user = R.propOr({}, user_id, this.props.users)
            let user_firstname = R.propOr('-', 'user_firstname', user)
            let user_lastname = R.propOr('-', 'user_lastname', user)
            let user_email = R.propOr('-', 'user_email', user)
            let user_gravatar = R.propOr('', 'user_gravatar', user)
            let user_organization = R.propOr({}, user.user_organization, this.props.organizations)
            let organizationName = R.propOr('-', 'organization_name', user_organization)

            //Return the dom
            return <AvatarListItem
              key={status_id}
              leftAvatar={<Avatar type={Constants.AVATAR_TYPE_MAINLIST} src={user_gravatar}/>}
              primaryText={

{user_firstname} {user_lastname}

{user_email}

{organizationName}

{dateFormat(status_last_update)}

<Icon name={Constants.ICON_NAME_ACTION_CHECK_CIRCLE} color={status_state ? "#4CAF50": "#F44336"}/>

              }
            />
          })}
        </List>

  }
}

Comcheck.propTypes = {
  exerciseId: PropTypes.string,
  comcheckId: PropTypes.string,
  audience: PropTypes.object,
  comcheck: PropTypes.object,
  comcheck_statuses: PropTypes.array,
  users: PropTypes.object,
  organizations: PropTypes.object,
  fetchComcheck: PropTypes.func,
  fetchComcheckStatuses: PropTypes.func,
  fetchUsers: PropTypes.func,
  fetchAudiences: PropTypes.func,
  fetchOrganizations: PropTypes.func,
}

const filterComcheckStatuses = (statuses, comcheckId) => {
  let statusesFilter = R.pipe(
    R.values,
    R.filter(n => n.status_comcheck.comcheck_id === comcheckId)
  )
  return statusesFilter(statuses)
}

const select = (state, ownProps) => {
  let exerciseId = ownProps.params.exerciseId
  let comcheckId = ownProps.params.comcheckId
  let comcheck = R.propOr({}, comcheckId, state.referential.entities.comchecks)
  let audience = comcheck.comcheck_audience ? R.propOr({}, comcheck.comcheck_audience.audience_id, state.referential.entities.audiences) : {}
  let comcheck_statuses = filterComcheckStatuses(state.referential.entities.comchecks_statuses, comcheckId)

  return {
    exerciseId,
    comcheckId,
    audience,
    comcheck,
    comcheck_statuses,
    users: state.referential.entities.users,
    organizations: state.referential.entities.organizations,
  }
}

export default connect(select, {
  fetchComcheck,
  fetchComcheckStatuses,
  fetchUsers,
  fetchAudiences,
  fetchOrganizations
})(Comcheck);
<commit_msg>Handle sorting by implementing intermediate model<commit_after>import React, {Component, PropTypes} from 'react'
import {connect} from 'react-redux'
import R from 'ramda'
import * as Constants from '../../../../constants/ComponentTypes'
import {fetchUsers} from '../../../../actions/User'
import {fetchOrganizations} from '../../../../actions/Organization'
import {fetchAudiences} from '../../../../actions/Audience'
import {fetchComcheck, fetchComcheckStatuses} from '../../../../actions/Comcheck'
import {List} from '../../../../components/List'
import {AvatarListItem, AvatarHeaderItem} from '../../../../components/list/ListItem';
import {Avatar} from '../../../../components/Avatar'
import {Icon} from '../../../../components/Icon'
import {dateFormat} from '../../../../utils/Time'

const styles = {
  'container': {}
  ,
  'header': {
    'avatar': {
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700',
      padding: '12px 0 0 15px'
    },
    'user_firstname': {
      float: 'left',
      width: '22%',
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700'
    },
    'user_email': {
      float: 'left',
      width: '35%',
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700'
    },
    'user_organization': {
      float: 'left',
      width: '22%',
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700'
    },
    'status_last_update': {
      float: 'left',
      width: '15%',
      fontSize: '12px',
      textTransform: 'uppercase',
      fontWeight: '700'
    },
    'status_state': {
      float: 'right',
      width: '6%',
      fontSize: '12px',
      textTransform: 'uppercase',
      textAlign: 'right',
      fontWeight: '700'
    }
  },
  'title': {
    float: 'left',
    fontSize: '20px',
    fontWeight: 600
  },
  'subtitle': {
    float: 'left',
    fontSize: '14px',
  },
  'empty': {
    marginTop: 40,
    fontSize: '18px',
    fontWeight: 500,
    textAlign: 'center'
  },
  'number': {
    float: 'right',
    color: '#9E9E9E',
    fontSize: '12px',
  },
  'name': {
    float: 'left',
    width: '22%',
    padding: '5px 0 0 0'
  },
  'mail': {
    float: 'left',
    width: '35%',
    padding: '5px 0 0 0'
  },
  'org': {
    float: 'left',
    width: '22%',
    padding: '5px 0 0 0'
  },
  'update': {
    float: 'left',
    width: '15%',
    padding: '5px 0 0 0'
  },
  'state': {
    float: 'right',
    textAlign: 'center',
    padding: 0
  }
}

class Comcheck extends Component {
  constructor(props) {
    super(props);
    this.state = {sortBy: 'user_firstname', orderAsc: true}
  }

  componentDidMount() {
    this.props.fetchComcheck(this.props.exerciseId, this.props.comcheckId)
    this.props.fetchComcheckStatuses(this.props.exerciseId, this.props.comcheckId)
    this.props.fetchAudiences(this.props.exerciseId)
    this.props.fetchUsers()
    this.props.fetchOrganizations()
    //this.repeatTimeout()
  }

  componentWillUnmount() {
    //noinspection Eslint
    clearTimeout(this.repeat)
  }

  repeatTimeout() {
    //noinspection Eslint
    const context = this
    //noinspection Eslint
    this.repeat = setTimeout(function () {
      context.circularFetch()
      context.repeatTimeout(context);
    }, 5000)
  }

  circularFetch() {
    this.props.fetchComcheckStatuses(this.props.exerciseId, this.props.comcheckId, true)
  }

  reverseBy(field) {
    this.setState({sortBy: field, orderAsc: !this.state.orderAsc})
  }

  SortHeader(field, label) {
    var icon = this.state.orderAsc ? Constants.ICON_NAME_NAVIGATION_ARROW_DROP_DOWN
      : Constants.ICON_NAME_NAVIGATION_ARROW_DROP_UP
    const IconDisplay = this.state.sortBy === field ? <Icon type={Constants.ICON_TYPE_SORT} name={icon}/> : ""
    return

      {label} {IconDisplay}

  }

  //TODO MOVE THAT TO UTILS
  ascend(a, b) { // replace with sortWith after Ramdajs new release
    return a  b ? 1 : 0;
  }

  descend(a, b) { // replace with sortWith after Ramdajs new release
    return a > b ? -1 : a < b ? 1 : 0;
  }

  modelSorting(criteria, ascending, a, b) {
      //TODO Add real type support for instant, boolean, ...
      var fieldA = R.compose(R.toLower, R.toString, R.propOr('', criteria))(a)
      var fieldB = R.compose(R.toLower, R.toString, R.propOr('', criteria))(b)
      return ascending ? this.ascend(fieldA, fieldB) : this.descend(fieldA, fieldB)
  }
  //TODO MOVE THAT TO UTILS

  buildUserModel(status) {
      let user_id = R.pathOr(Math.random(), ['status_user', 'user_id'], status)
      let user = R.propOr({}, user_id, this.props.users)
      let user_organization = R.propOr({}, user.user_organization, this.props.organizations)
      return {
          user_firstname:     R.propOr('-', 'user_firstname', user),
          user_lastname:      R.propOr('-', 'user_lastname', user),
          user_email:         R.propOr('-', 'user_email', user),
          user_gravatar:      R.propOr('', 'user_gravatar', user),
          user_organization:  R.propOr('-', 'organization_name', user_organization),
          status_id:          R.propOr(Math.random(), 'status_id', status),
          status_state:       R.propOr(false, 'status_state', status),
          status_last_update: R.propOr('', 'status_last_update', status)
      }
  }

  render() {

    const data = R.pipe(
        R.map(status => this.buildUserModel(status)),
        R.sort((a, b) => this.modelSorting(this.state.sortBy, this.state.orderAsc, a, b))
    )(this.props.comcheck_statuses);

    return

Comcheck to audience {this.props.audience.audience_name}

From {dateFormat(this.props.comcheck.comcheck_start_date)} to {dateFormat(this.props.comcheck.comcheck_end_date)}

        <List>
          <AvatarHeaderItem leftAvatar={#} primaryText={

              {this.SortHeader('user_firstname', 'name')}
              {this.SortHeader('user_email', 'Email address')}
              {this.SortHeader('user_organization', 'Organization')}
              {this.SortHeader('status_last_update', 'Last update')}
              {this.SortHeader('status_state', 'State')}

}
          />

          {data.map(item => {
            //Return the dom
            return <AvatarListItem
              key={item.status_id}
              leftAvatar={<Avatar type={Constants.AVATAR_TYPE_MAINLIST} src={item.user_gravatar}/>}
              primaryText={

{item.user_firstname} {item.user_lastname}

{item.user_email}

{item.user_organization}

{dateFormat(item.status_last_update)}

<Icon name={Constants.ICON_NAME_ACTION_CHECK_CIRCLE} color={item.status_state ? "#4CAF50": "#F44336"}/>

              }
            />
          })}
        </List>

  }
}

Comcheck.propTypes = {
  exerciseId: PropTypes.string,
  comcheckId: PropTypes.string,
  audience: PropTypes.object,
  comcheck: PropTypes.object,
  comcheck_statuses: PropTypes.array,
  users: PropTypes.object,
  organizations: PropTypes.object,
  fetchComcheck: PropTypes.func,
  fetchComcheckStatuses: PropTypes.func,
  fetchUsers: PropTypes.func,
  fetchAudiences: PropTypes.func,
  fetchOrganizations: PropTypes.func,
}

const filterComcheckStatuses = (statuses, comcheckId) => {
  let statusesFilter = R.pipe(
    R.values,
    R.filter(n => n.status_comcheck.comcheck_id === comcheckId)
  )
  return statusesFilter(statuses)
}

const select = (state, ownProps) => {
  let exerciseId = ownProps.params.exerciseId
  let comcheckId = ownProps.params.comcheckId
  let comcheck = R.propOr({}, comcheckId, state.referential.entities.comchecks)
  let audience = comcheck.comcheck_audience ? R.propOr({}, comcheck.comcheck_audience.audience_id, state.referential.entities.audiences) : {}
  let comcheck_statuses = filterComcheckStatuses(state.referential.entities.comchecks_statuses, comcheckId)

  return {
    exerciseId,
    comcheckId,
    audience,
    comcheck,
    comcheck_statuses,
    users: state.referential.entities.users,
    organizations: state.referential.entities.organizations,
  }
}

export default connect(select, {
  fetchComcheck,
  fetchComcheckStatuses,
  fetchUsers,
  fetchAudiences,
  fetchOrganizations
})(Comcheck);
<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter
var inherits     = require('util').inherits
var ranges       = require('string-range')
var fixRange     = require('level-fix-range')

inherits(SubDB, EventEmitter)

function SubDB (db, prefix, sep) {
  if(!(this instanceof SubDB)) return new SubDB(db, prefix, sep)
  if(!db)     throw new Error('must provide db')
  if(!prefix) throw new Error('must provide prefix')

  this._parent = db
  this._sep = sep || '\xff'
  this._prefix = prefix
  this._root = root(this)
  db.sublevels[prefix] = this
  this.sublevels = {}
  this.methods = {}
  var self = this
  this.hooks = {
    pre: function () {
      return self.pre.apply(self, arguments)
    },
    post: function () {
      return self.post.apply(self, arguments)
    }
  }
}

var SDB = SubDB.prototype

SDB._key = function (key) {
  return this._sep
    + this._prefix
    + this._sep
    + key
}

SDB.sublevel = function (prefix, sep) {
  if(this.sublevels[prefix])
    return this.sublevels[prefix]
  return new SubDB(this, prefix, sep || this._sep)
}

SDB.put = function (key, value, opts, cb) {
  this._root.put(this.prefix(key), value, opts, cb)
//  this._parent.put(this._key(key), value, opts, cb)
}

SDB.get = function (key, opts, cb) {
  this._root.get(this.prefix(key), opts, cb)
//  this._parent.get(this._key(key), opts, cb)
}

SDB.del = function (key, opts, cb) {
  this._root.del(this.prefix(key), opts, cb)
//  this._parent.del(this._key(key), opts, cb)
}

SDB.batch = function (changes, opts, cb) {
  var self = this
  changes.forEach(function (ch) {
    //OH YEAH, WE NEED TO VALIDATE THAT UPDATING THIS KEY/PREFIX IS ALLOWED
    if('string' === typeof ch.prefix) {
      ch.key = ch.prefix + ch.key
    } else
    ch.key = (ch.prefix || self).prefix(ch.key)
    if(ch.prefix) ch.prefix = null
  })
  this._root.batch(changes, opts, cb)
}

SDB.prefix = function (key) {
  return this._parent.prefix() + this._sep + this._prefix + this._sep + (key || '')
}

SDB.keyStream =
SDB.createKeyStream = function (opts) {
  opts = opts || {}
  opts.keys = true
  return this.createReadStream(opts)
}

SDB.valueStream =
SDB.createValueStream = function (opts) {
  opts = opts || {}
  opts.values = true
  return this.createReadStream(opts)
}

SDB.readStream =
SDB.createReadStream = function (opts) {
  opts = opts || {}
  var r = root(this)
  var p = this.prefix()
  //opts.start = p + (opts.start || '')
  //opts.end = p + (opts.end || this._sep)

  var _opts = ranges.prefix(opts, p)
  _opts.reverse = opts.reverse
  _opts.limit = opts.limit
  _opts.fillCache = opts.fillCache
  return r.createReadStream(_opts)
    .on('data', function (d) {
      //mutate the prefix!
      //this doesn't work for createKeyStream admittedly.
      if(d.key)
        d.key = d.key.substring(p.length)
    })
}

SDB.writeStream =
SDB.createWriteStream = function () {
  var r = root(this)
  var p = this.prefix()
  var ws = r.createWriteStream.apply(r, arguments)
  var write = ws.write
  ws.write = function (data) {
    data.key = p + data.key
    return write.call(ws, data)
  }
  return ws
}

SDB.approximateSize = function () {
  var r = root(db)
  return r.approximateSize.apply(r, arguments)
}

function root(db) {
  if(!db._parent) return db
  return root(db._parent)
}

SDB.pre = function (range, hook) {
  if(!hook) hook = range, range = null
  range = ranges.prefix(range, this.prefix(), this._sep)
  var r = root(this._parent)
  var p = this.prefix()
  return r.hooks.pre(fixRange(range), function (ch, add) {
    hook({
      key: ch.key.substring(p.length),
      value: ch.value,
      type: ch.type
    }, function (ch, _p) {
      //maybe remove the second add arg now
      //that op can have prefix?
      add(ch, ch.prefix ? _p : (_p || p))
    })
  })
}

SDB.post = function (range, hook) {
  if(!hook) hook = range, range = null
  var r = root(this._parent)
  var p = this.prefix()
  range = ranges.prefix(range, p, this._sep)
  return r.hooks.post(fixRange(range), function (data) {
    hook({key: data.key.substring(p.length), value: data.value, type: data.type})
  })
}

var exports = module.exports = SubDB

<commit_msg>support key and value streams<commit_after>var EventEmitter = require('events').EventEmitter
var inherits     = require('util').inherits
var ranges       = require('string-range')
var fixRange     = require('level-fix-range')

inherits(SubDB, EventEmitter)

function SubDB (db, prefix, sep) {
  if(!(this instanceof SubDB)) return new SubDB(db, prefix, sep)
  if(!db)     throw new Error('must provide db')
  if(!prefix) throw new Error('must provide prefix')

  this._parent = db
  this._sep = sep || '\xff'
  this._prefix = prefix
  this._root = root(this)
  db.sublevels[prefix] = this
  this.sublevels = {}
  this.methods = {}
  var self = this
  this.hooks = {
    pre: function () {
      return self.pre.apply(self, arguments)
    },
    post: function () {
      return self.post.apply(self, arguments)
    }
  }
}

var SDB = SubDB.prototype

SDB._key = function (key) {
  return this._sep
    + this._prefix
    + this._sep
    + key
}

SDB.sublevel = function (prefix, sep) {
  if(this.sublevels[prefix])
    return this.sublevels[prefix]
  return new SubDB(this, prefix, sep || this._sep)
}

SDB.put = function (key, value, opts, cb) {
  this._root.put(this.prefix(key), value, opts, cb)
//  this._parent.put(this._key(key), value, opts, cb)
}

SDB.get = function (key, opts, cb) {
  this._root.get(this.prefix(key), opts, cb)
//  this._parent.get(this._key(key), opts, cb)
}

SDB.del = function (key, opts, cb) {
  this._root.del(this.prefix(key), opts, cb)
//  this._parent.del(this._key(key), opts, cb)
}

SDB.batch = function (changes, opts, cb) {
  var self = this
  changes.forEach(function (ch) {
    //OH YEAH, WE NEED TO VALIDATE THAT UPDATING THIS KEY/PREFIX IS ALLOWED
    if('string' === typeof ch.prefix) {
      ch.key = ch.prefix + ch.key
    } else
    ch.key = (ch.prefix || self).prefix(ch.key)
    if(ch.prefix) ch.prefix = null
  })
  this._root.batch(changes, opts, cb)
}

SDB.prefix = function (key) {
  return this._parent.prefix() + this._sep + this._prefix + this._sep + (key || '')
}

SDB.keyStream =
SDB.createKeyStream = function (opts) {
  opts = opts || {}
  opts.keys = true
  opts.values = false
  return this.createReadStream(opts)
}

SDB.valueStream =
SDB.createValueStream = function (opts) {
  opts = opts || {}
  opts.keys = false
  opts.values = true
  return this.createReadStream(opts)
}

SDB.readStream =
SDB.createReadStream = function (opts) {
  opts = opts || {}
  var _opts = {}
  Object.keys(opts).forEach(function (k) {
    _opts[k] = opts[k]
  })
  var r = root(this)
  var p = this.prefix()
  //opts.start = p + (opts.start || '')
  //opts.end = p + (opts.end || this._sep)
  var prefixedRange = ranges.prefix(opts, p)
  Object.keys(prefixedRange).forEach(function (k) {
    _opts[k] = prefixedRange[k]
  })
  if (typeof _opts.keys === 'undefined') _opts.keys = true
  if (typeof _opts.values === 'undefined') _opts.values = true

  var s = r.createReadStream(_opts)
  var _makeData = s._makeData
  s._makeData = function (key, value) {
    var d = _makeData(key, value)
    // mutate the prefix!
    if (_opts.keys && _opts.values) d.key = d.key.substring(p.length)
    else if (_opts.keys) d = d.substring(p.length)
    return d
  }

  return s
}

SDB.writeStream =
SDB.createWriteStream = function () {
  var r = root(this)
  var p = this.prefix()
  var ws = r.createWriteStream.apply(r, arguments)
  var write = ws.write
  ws.write = function (data) {
    data.key = p + data.key
    return write.call(ws, data)
  }
  return ws
}

SDB.approximateSize = function () {
  var r = root(db)
  return r.approximateSize.apply(r, arguments)
}

function root(db) {
  if(!db._parent) return db
  return root(db._parent)
}

SDB.pre = function (range, hook) {
  if(!hook) hook = range, range = null
  range = ranges.prefix(range, this.prefix(), this._sep)
  var r = root(this._parent)
  var p = this.prefix()
  return r.hooks.pre(fixRange(range), function (ch, add) {
    hook({
      key: ch.key.substring(p.length),
      value: ch.value,
      type: ch.type
    }, function (ch, _p) {
      //maybe remove the second add arg now
      //that op can have prefix?
      add(ch, ch.prefix ? _p : (_p || p))
    })
  })
}

SDB.post = function (range, hook) {
  if(!hook) hook = range, range = null
  var r = root(this._parent)
  var p = this.prefix()
  range = ranges.prefix(range, p, this._sep)
  return r.hooks.post(fixRange(range), function (data) {
    hook({key: data.key.substring(p.length), value: data.value, type: data.type})
  })
}

var exports = module.exports = SubDB

<|endoftext|>


<commit_before>/*!
	Preview script for Campaign Monitor template tags.

	See https://github.com/alexdunae/cm-preview/ for usage and the latest source.

	Copyright (c) 2010 Alex Dunae. See LICENSE for details.

	By Alex Dunae at Dialect (http://dialect.ca/).  Enjoy!
*/

var body = document.getElementsByTagName('body');

// show default text
// single quoted
repl = body[0].innerHTML.replace(/\<\$(title|description)[\s]+([\w]+='[\w]+'[\s]*)?default='(.[^']+)'[\s]*([\w]+='[\w]+'[\s]*)?\$\>/ig, "$3");
// double quoted
repl = repl.replace(/\<\$(title|description)[\s]+([\w]+="[\w]+"[\s]*)?default="(.[^"]+)"[\s]*([\w]+="[\w]+"[\s]*)?\$\>/ig, "$3");

// and show personalization fallbacks
repl = repl.replace(/\[(\w+),fallback=(.[^\]]+)\]/ig, "$2");

repl = repl.replace(/\<\$repeatertitle\$\>/ig, 'Table of contents');

// dates
var dt = new Date(),
    days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
    months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

repl = repl.replace(/\<\$currentday\$\>/ig, dt.getDate());
repl = repl.replace(/\<\$currentdayname\$\>/ig, days[dt.getDay()]);
repl = repl.replace(/\<\$currentmonth\$\>/ig, dt.getMonth() + 1);
repl = repl.replace(/\<\$currentmonthname\$\>/ig, months[dt.getMonth()]);
repl = repl.replace(/\<\$currentyear\$\>/ig, dt.getFullYear());

body[0].innerHTML = repl;

// show default images
var imgs = document.getElementsByTagName('img');
for(var i = 0; i < imgs.length; i++) {
	var src = imgs[i].getAttribute('src', 2);
	if (/\$imagesrc/i.test(src)) {
		// unescape attributes for Firefox
		if (src.indexOf('%') !== false) {
			src = unescape(src);
		}

		src = src.match(/default=['"](.[^'"]+)/i);
		if (src && src[1]) {
			imgs[i].setAttribute('src', src[1]);
		}
	}
}

// show multiple repeaters
var repeaters = document.getElementsByTagName('repeater'),
    clones = [];

for(var i = 0; i < repeaters.length; i++) {
	clones[i] = repeaters[i].cloneNode(true);
}

for(var i = 0; i < clones.length; i++) {
	repeaters[i].insertBefore(clones[i], null);
}

// show multiple tableofcontents entries
var tocs = document.getElementsByTagName('tableofcontents'),
    clones = [];

for(var i = 0; i < tocs.length; i++) {
	clones[i] = tocs[i].cloneNode(true);
}

for(var i = 0; i < clones.length; i++) {
	tocs[i].insertBefore(clones[i], null);
}
<commit_msg>Email personalization tag<commit_after>/*!
	Preview script for Campaign Monitor template tags.

	See https://github.com/alexdunae/cm-preview/ for usage and the latest source.

	Copyright (c) 2010 Alex Dunae. See LICENSE for details.

	By Alex Dunae at Dialect (http://dialect.ca/).  Enjoy!
*/

var body = document.getElementsByTagName('body');

// show default text, single and double quoted
repl = body[0].innerHTML.replace(/\<\$(title|description)[\s]+([\w]+='[\w]+'[\s]*)?default='(.[^']+)'[\s]*([\w]+='[\w]+'[\s]*)?\$\>/ig, "$3");
repl = repl.replace(/\<\$(title|description)[\s]+([\w]+="[\w]+"[\s]*)?default="(.[^"]+)"[\s]*([\w]+="[\w]+"[\s]*)?\$\>/ig, "$3");

repl = repl.replace(/\<\$repeatertitle\$\>/ig, 'Table of contents');

// and show personalization fallbacks
repl = repl.replace(/\[(\w+),fallback=(.[^\]]+)\]/ig, "$2");
repl = repl.replace(/\[email\]/ig, 'example@example.com');

// dates
var dt = new Date(),
    days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
    months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

repl = repl.replace(/\<\$currentday\$\>/ig, dt.getDate());
repl = repl.replace(/\<\$currentdayname\$\>/ig, days[dt.getDay()]);
repl = repl.replace(/\<\$currentmonth\$\>/ig, dt.getMonth() + 1);
repl = repl.replace(/\<\$currentmonthname\$\>/ig, months[dt.getMonth()]);
repl = repl.replace(/\<\$currentyear\$\>/ig, dt.getFullYear());

body[0].innerHTML = repl;

// show default images
var imgs = document.getElementsByTagName('img');
for(var i = 0; i < imgs.length; i++) {
	var src = imgs[i].getAttribute('src', 2);
	if (/\$imagesrc/i.test(src)) {
		// unescape attributes for Firefox
		if (src.indexOf('%') !== false) {
			src = unescape(src);
		}

		src = src.match(/default=['"](.[^'"]+)/i);
		if (src && src[1]) {
			imgs[i].setAttribute('src', src[1]);
		}
	}
}

// show multiple repeaters
var repeaters = document.getElementsByTagName('repeater'),
    clones = [];

for(var i = 0; i < repeaters.length; i++) {
	clones[i] = repeaters[i].cloneNode(true);
}

for(var i = 0; i < clones.length; i++) {
	repeaters[i].insertBefore(clones[i], null);
}

// show multiple tableofcontents entries
var tocs = document.getElementsByTagName('tableofcontents'),
    clones = [];

for(var i = 0; i < tocs.length; i++) {
	clones[i] = tocs[i].cloneNode(true);
}

for(var i = 0; i < clones.length; i++) {
	tocs[i].insertBefore(clones[i], null);
}
<|endoftext|>


<commit_before>var inherits = require('util').inherits
  , f = require('util').format
  , toError = require('./utils').toError
  , getSingleProperty = require('./utils').getSingleProperty
  , formattedOrderClause = require('./utils').formattedOrderClause
  , handleCallback = require('./utils').handleCallback
  , Logger = require('mongodb-core').Logger
  , EventEmitter = require('events').EventEmitter
  , ReadPreference = require('./read_preference')
  , MongoError = require('mongodb-core').MongoError
  , Readable = require('stream').Readable || require('readable-stream').Readable
  // , CoreCursor = require('mongodb-core').Cursor
  , CoreCursor = require('./cursor')
  , Query = require('mongodb-core').Query
  , CoreReadPreference = require('mongodb-core').ReadPreference;

/**
 * @fileOverview The **AggregationCursor** class is an internal class that embodies an aggregation cursor on MongoDB
 * allowing for iteration over the results returned from the underlying query. It supports
 * one by one document iteration, conversion to an array or can be iterated as a Node 0.10.X
 * or higher stream
 *
 * **AGGREGATIONCURSOR Cannot directly be instantiated**
 * @example
 * var MongoClient = require('mongodb').MongoClient,
 *   test = require('assert');
 * // Connection url
 * var url = 'mongodb://localhost:27017/test';
 * // Connect using MongoClient
 * MongoClient.connect(url, function(err, db) {
 *   // Create a collection we want to drop later
 *   var col = db.collection('createIndexExample1');
 *   // Insert a bunch of documents
 *   col.insert([{a:1, b:1}
 *     , {a:2, b:2}, {a:3, b:3}
 *     , {a:4, b:4}], {w:1}, function(err, result) {
 *     test.equal(null, err);
 *
 *     // Show that duplicate records got dropped
 *     col.aggregation({}, {cursor: {}}).toArray(function(err, items) {
 *       test.equal(null, err);
 *       test.equal(4, items.length);
 *       db.close();
 *     });
 *   });
 * });
 */

/**
 * Namespace provided by the browser.
 * @external Readable
 */

/**
 * Creates a new Aggregation Cursor instance (INTERNAL TYPE, do not instantiate directly)
 * @class
 * @extends external:Readable
 * @property {number} cursorBatchSize The current cursorBatchSize for the cursor
 * @property {number} cursorLimit The current cursorLimit for the cursor
 * @property {number} cursorSkip The current cursorSkip for the cursor
 * @fires AggregationCursor#data
 * @fires AggregationCursor#end
 * @fires AggregationCursor#close
 * @fires AggregationCursor#readable
 * @return {AggregationCursor} an AggregationCursor instance.
 */
var AggregationCursor = function(bson, ns, cmd, options, topology, topologyOptions) {
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
  var self = this;
  var state = AggregationCursor.INIT;
  var streamOptions = {};

  // MaxTimeMS
  var maxTimeMS = null;

  // Set up
  Readable.call(this, {objectMode: true});

  /**
   * Set the batch size for the cursor.
   * @method
   * @param {number} value The batchSize for the cursor.
   * @throws {MongoError}
   * @return {AggregationCursor}
   */
  this.batchSize = function(value) {
    if(state == AggregationCursor.CLOSED || self.isDead()) throw new MongoError("Cursor is closed");
    if(typeof value != 'number') throw new MongoError("batchSize requires an integer");
    if(cmd.cursor) cmd.cursor.batchSize = value;
    this.cursorBatchSize = value;
    return self;
  }

  /**
   * Add a geoNear stage to the aggregation pipeline
   * @method
   * @param {object} document The geoNear stage document.
   * @return {AggregationCursor}
   */
  this.geoNear = function(document) {
    cmd.pipeline.push({$geoNear: document});
    return self;
  }

  /**
   * Add a group stage to the aggregation pipeline
   * @method
   * @param {object} document The group stage document.
   * @return {AggregationCursor}
   */
  this.group = function(document) {
    cmd.pipeline.push({$group: document});
    return self;
  }

  /**
   * Add a limit stage to the aggregation pipeline
   * @method
   * @param {number} value The state limit value.
   * @return {AggregationCursor}
   */
  this.limit = function(value) {
    cmd.pipeline.push({$limit: value});
    return self;
  }

  /**
   * Add a match stage to the aggregation pipeline
   * @method
   * @param {object} document The match stage document.
   * @return {AggregationCursor}
   */
  this.match = function(document) {
    cmd.pipeline.push({$match: document});
    return self;
  }

  /**
   * Add a maxTimeMS stage to the aggregation pipeline
   * @method
   * @param {number} value The state maxTimeMS value.
   * @return {AggregationCursor}
   */
  this.maxTimeMS = function(value) {
    if(topology.lastIsMaster().minWireVersion > 2) {
      cmd.maxTimeMS = value;
    }
    return self;
  }

  /**
   * Add a out stage to the aggregation pipeline
   * @method
   * @param {number} destination The destination name.
   * @return {AggregationCursor}
   */
  this.out = function(destination) {
    cmd.pipeline.push({$out: destination});
    return self;
  }

  /**
   * Add a project stage to the aggregation pipeline
   * @method
   * @param {object} document The project stage document.
   * @return {AggregationCursor}
   */
  this.project = function(document) {
    cmd.pipeline.push({$project: document});
    return self;
  }

  /**
   * Add a redact stage to the aggregation pipeline
   * @method
   * @param {object} document The redact stage document.
   * @return {AggregationCursor}
   */
  this.redact = function(document) {
    cmd.pipeline.push({$redact: document});
    return self;
  }

  /**
   * Add a skip stage to the aggregation pipeline
   * @method
   * @param {number} value The state skip value.
   * @return {AggregationCursor}
   */
  this.skip = function(value) {
    cmd.pipeline.push({$skip: value});
    return self;
  }

  /**
   * Add a sort stage to the aggregation pipeline
   * @method
   * @param {object} document The sort stage document.
   * @return {AggregationCursor}
   */
  this.sort = function(document) {
    cmd.pipeline.push({$sort: document});
    return self;
  }

  /**
   * Add a unwind stage to the aggregation pipeline
   * @method
   * @param {number} field The unwind field name.
   * @return {AggregationCursor}
   */
  this.unwind = function(field) {
    cmd.pipeline.push({$unwind: field});
    return self;
  }

  this.get = this.toArray;

  /**
   * Get the next available document from the cursor, returns null if no more documents are available.
   * @function AggregationCursor.prototype.next
   * @param {AggregationCursor~resultCallback} callback The result callback.
   * @throws {MongoError}
   * @return {null}
   */

  /**
   * The callback format for results
   * @callback AggregationCursor~toArrayResultCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   * @param {object[]} documents All the documents the satisfy the cursor.
   */

  /**
   * Returns an array of documents. The caller is responsible for making sure that there
   * is enough memory to store the results. Note that the array only contain partial
   * results when this cursor had been previouly accessed. In that case,
   * cursor.rewind() can be used to reset the cursor.
   * @method AggregationCursor.prototype.toArray
   * @param {AggregationCursor~toArrayResultCallback} callback The result callback.
   * @throws {MongoError}
   * @return {null}
   */

  /**
   * The callback format for results
   * @callback AggregationCursor~resultCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   * @param {(object|null)} result The result object if the command was executed successfully.
   */

  /**
   * Iterates over all the documents for this cursor. As with **{cursor.toArray}**,
   * not all of the elements will be iterated if this cursor had been previouly accessed.
   * In that case, **{cursor.rewind}** can be used to reset the cursor. However, unlike
   * **{cursor.toArray}**, the cursor will only hold a maximum of batch size elements
   * at any given time if batch size is specified. Otherwise, the caller is responsible
   * for making sure that the entire result can fit the memory.
   * @method AggregationCursor.prototype.each
   * @param {AggregationCursor~resultCallback} callback The result callback.
   * @throws {MongoError}
   * @return {null}
   */

  /**
   * Close the cursor, sending a KillCursor command and emitting close.
   * @method AggregationCursor.prototype.close
   * @param {AggregationCursor~resultCallback} [callback] The result callback.
   * @return {null}
   */

  /**
   * Is the cursor closed
   * @method AggregationCursor.prototype.isClosed
   * @return {boolean}
   */

  /**
   * Execute the explain for the cursor
   * @method AggregationCursor.prototype.explain
   * @param {AggregationCursor~resultCallback} [callback] The result callback.
   * @return {null}
   */

  /**
   * Clone the cursor
   * @function AggregationCursor.prototype.clone
   * @return {AggregationCursor}
   */

  /**
   * Resets the cursor
   * @function AggregationCursor.prototype.rewind
   * @return {AggregationCursor}
   */

  /**
   * The callback format for the forEach iterator method
   * @callback AggregationCursor~iteratorCallback
   * @param {Object} doc An emitted document for the iterator
   */

  /**
   * The callback error format for the forEach iterator method
   * @callback AggregationCursor~endCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   */

  /*
   * Iterates over all the documents for this cursor using the iterator, callback pattern.
   * @method AggregationCursor.prototype.forEach
   * @param {AggregationCursor~iteratorCallback} iterator The iteration callback.
   * @param {AggregationCursor~endCallback} callback The end callback.
   * @throws {MongoError}
   * @return {null}
   */
}

/**
 * AggregationCursor stream data event, fired for each document in the cursor.
 *
 * @event AggregationCursor#data
 * @type {object}
 */

/**
 * AggregationCursor stream end event
 *
 * @event AggregationCursor#end
 * @type {null}
 */

/**
 * AggregationCursor stream close event
 *
 * @event AggregationCursor#close
 * @type {null}
 */

/**
 * AggregationCursor stream readable event
 *
 * @event AggregationCursor#readable
 * @type {null}
 */

// // Extend the Cursor
// inherits(AggregationCursor, CoreCursor);

// Inherit from Readable
inherits(AggregationCursor, Readable);

// Extend the Cursor
for(var name in CoreCursor.prototype) {
  AggregationCursor.prototype[name] = CoreCursor.prototype[name];
}

AggregationCursor.INIT = 0;
AggregationCursor.OPEN = 1;
AggregationCursor.CLOSED = 2;

module.exports = AggregationCursor;<commit_msg>Changed aggregation cursor to be prototype based<commit_after>var inherits = require('util').inherits
  , f = require('util').format
  , toError = require('./utils').toError
  , getSingleProperty = require('./utils').getSingleProperty
  , formattedOrderClause = require('./utils').formattedOrderClause
  , handleCallback = require('./utils').handleCallback
  , Logger = require('mongodb-core').Logger
  , EventEmitter = require('events').EventEmitter
  , ReadPreference = require('./read_preference')
  , MongoError = require('mongodb-core').MongoError
  , Readable = require('stream').Readable || require('readable-stream').Readable
  // , CoreCursor = require('mongodb-core').Cursor
  , CoreCursor = require('./cursor')
  , Query = require('mongodb-core').Query
  , CoreReadPreference = require('mongodb-core').ReadPreference;

/**
 * @fileOverview The **AggregationCursor** class is an internal class that embodies an aggregation cursor on MongoDB
 * allowing for iteration over the results returned from the underlying query. It supports
 * one by one document iteration, conversion to an array or can be iterated as a Node 0.10.X
 * or higher stream
 *
 * **AGGREGATIONCURSOR Cannot directly be instantiated**
 * @example
 * var MongoClient = require('mongodb').MongoClient,
 *   test = require('assert');
 * // Connection url
 * var url = 'mongodb://localhost:27017/test';
 * // Connect using MongoClient
 * MongoClient.connect(url, function(err, db) {
 *   // Create a collection we want to drop later
 *   var col = db.collection('createIndexExample1');
 *   // Insert a bunch of documents
 *   col.insert([{a:1, b:1}
 *     , {a:2, b:2}, {a:3, b:3}
 *     , {a:4, b:4}], {w:1}, function(err, result) {
 *     test.equal(null, err);
 *
 *     // Show that duplicate records got dropped
 *     col.aggregation({}, {cursor: {}}).toArray(function(err, items) {
 *       test.equal(null, err);
 *       test.equal(4, items.length);
 *       db.close();
 *     });
 *   });
 * });
 */

/**
 * Namespace provided by the browser.
 * @external Readable
 */

/**
 * Creates a new Aggregation Cursor instance (INTERNAL TYPE, do not instantiate directly)
 * @class
 * @extends external:Readable
 * @property {number} cursorBatchSize The current cursorBatchSize for the cursor
 * @property {number} cursorLimit The current cursorLimit for the cursor
 * @property {number} cursorSkip The current cursorSkip for the cursor
 * @fires AggregationCursor#data
 * @fires AggregationCursor#end
 * @fires AggregationCursor#close
 * @fires AggregationCursor#readable
 * @return {AggregationCursor} an AggregationCursor instance.
 */
var AggregationCursor = function(bson, ns, cmd, options, topology, topologyOptions) {
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
  var self = this;
  var state = AggregationCursor.INIT;
  var streamOptions = {};

  // MaxTimeMS
  var maxTimeMS = null;

  // Set up
  Readable.call(this, {objectMode: true});

  // Internal state
  this.s = {
    // MaxTimeMS
      maxTimeMS: maxTimeMS
    // State
    , state: state
    // Stream options
    , streamOptions: streamOptions
    // BSON
    , bson: bson
    // Namespae
    , ns: ns
    // Command
    , cmd: cmd
    // Options
    , options: options
    // Topology
    , topology: topology
    // Topology Options
    , topologyOptions: topologyOptions
  }

  /**
   * Set the batch size for the cursor.
   * @method
   * @param {number} value The batchSize for the cursor.
   * @throws {MongoError}
   * @return {AggregationCursor}
   */
  this.batchSize = function(value) {
    if(this.s.state == AggregationCursor.CLOSED || this.isDead()) throw new MongoError("Cursor is closed");
    if(typeof value != 'number') throw new MongoError("batchSize requires an integer");
    if(this.s.cmd.cursor) this.s.cmd.cursor.batchSize = value;
    this.cursorBatchSize = value;
    return this;
  }

  /**
   * Add a geoNear stage to the aggregation pipeline
   * @method
   * @param {object} document The geoNear stage document.
   * @return {AggregationCursor}
   */
  this.geoNear = function(document) {
    this.s.cmd.pipeline.push({$geoNear: document});
    return this;
  }

  /**
   * Add a group stage to the aggregation pipeline
   * @method
   * @param {object} document The group stage document.
   * @return {AggregationCursor}
   */
  this.group = function(document) {
    this.s.cmd.pipeline.push({$group: document});
    return this;
  }

  /**
   * Add a limit stage to the aggregation pipeline
   * @method
   * @param {number} value The state limit value.
   * @return {AggregationCursor}
   */
  this.limit = function(value) {
    this.s.cmd.pipeline.push({$limit: value});
    return this;
  }

  /**
   * Add a match stage to the aggregation pipeline
   * @method
   * @param {object} document The match stage document.
   * @return {AggregationCursor}
   */
  this.match = function(document) {
    this.s.cmd.pipeline.push({$match: document});
    return this;
  }

  /**
   * Add a maxTimeMS stage to the aggregation pipeline
   * @method
   * @param {number} value The state maxTimeMS value.
   * @return {AggregationCursor}
   */
  this.maxTimeMS = function(value) {
    if(this.s.topology.lastIsMaster().minWireVersion > 2) {
      this.s.cmd.maxTimeMS = value;
    }
    return this;
  }

  /**
   * Add a out stage to the aggregation pipeline
   * @method
   * @param {number} destination The destination name.
   * @return {AggregationCursor}
   */
  this.out = function(destination) {
    this.s.cmd.pipeline.push({$out: destination});
    return this;
  }

  /**
   * Add a project stage to the aggregation pipeline
   * @method
   * @param {object} document The project stage document.
   * @return {AggregationCursor}
   */
  this.project = function(document) {
    this.s.cmd.pipeline.push({$project: document});
    return this;
  }

  /**
   * Add a redact stage to the aggregation pipeline
   * @method
   * @param {object} document The redact stage document.
   * @return {AggregationCursor}
   */
  this.redact = function(document) {
    this.s.cmd.pipeline.push({$redact: document});
    return this;
  }

  /**
   * Add a skip stage to the aggregation pipeline
   * @method
   * @param {number} value The state skip value.
   * @return {AggregationCursor}
   */
  this.skip = function(value) {
    this.s.cmd.pipeline.push({$skip: value});
    return this;
  }

  /**
   * Add a sort stage to the aggregation pipeline
   * @method
   * @param {object} document The sort stage document.
   * @return {AggregationCursor}
   */
  this.sort = function(document) {
    this.s.cmd.pipeline.push({$sort: document});
    return this;
  }

  /**
   * Add a unwind stage to the aggregation pipeline
   * @method
   * @param {number} field The unwind field name.
   * @return {AggregationCursor}
   */
  this.unwind = function(field) {
    this.s.cmd.pipeline.push({$unwind: field});
    return this;
  }

  this.get = this.toArray;

  /**
   * Get the next available document from the cursor, returns null if no more documents are available.
   * @function AggregationCursor.prototype.next
   * @param {AggregationCursor~resultCallback} callback The result callback.
   * @throws {MongoError}
   * @return {null}
   */

  /**
   * The callback format for results
   * @callback AggregationCursor~toArrayResultCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   * @param {object[]} documents All the documents the satisfy the cursor.
   */

  /**
   * Returns an array of documents. The caller is responsible for making sure that there
   * is enough memory to store the results. Note that the array only contain partial
   * results when this cursor had been previouly accessed. In that case,
   * cursor.rewind() can be used to reset the cursor.
   * @method AggregationCursor.prototype.toArray
   * @param {AggregationCursor~toArrayResultCallback} callback The result callback.
   * @throws {MongoError}
   * @return {null}
   */

  /**
   * The callback format for results
   * @callback AggregationCursor~resultCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   * @param {(object|null)} result The result object if the command was executed successfully.
   */

  /**
   * Iterates over all the documents for this cursor. As with **{cursor.toArray}**,
   * not all of the elements will be iterated if this cursor had been previouly accessed.
   * In that case, **{cursor.rewind}** can be used to reset the cursor. However, unlike
   * **{cursor.toArray}**, the cursor will only hold a maximum of batch size elements
   * at any given time if batch size is specified. Otherwise, the caller is responsible
   * for making sure that the entire result can fit the memory.
   * @method AggregationCursor.prototype.each
   * @param {AggregationCursor~resultCallback} callback The result callback.
   * @throws {MongoError}
   * @return {null}
   */

  /**
   * Close the cursor, sending a KillCursor command and emitting close.
   * @method AggregationCursor.prototype.close
   * @param {AggregationCursor~resultCallback} [callback] The result callback.
   * @return {null}
   */

  /**
   * Is the cursor closed
   * @method AggregationCursor.prototype.isClosed
   * @return {boolean}
   */

  /**
   * Execute the explain for the cursor
   * @method AggregationCursor.prototype.explain
   * @param {AggregationCursor~resultCallback} [callback] The result callback.
   * @return {null}
   */

  /**
   * Clone the cursor
   * @function AggregationCursor.prototype.clone
   * @return {AggregationCursor}
   */

  /**
   * Resets the cursor
   * @function AggregationCursor.prototype.rewind
   * @return {AggregationCursor}
   */

  /**
   * The callback format for the forEach iterator method
   * @callback AggregationCursor~iteratorCallback
   * @param {Object} doc An emitted document for the iterator
   */

  /**
   * The callback error format for the forEach iterator method
   * @callback AggregationCursor~endCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   */

  /*
   * Iterates over all the documents for this cursor using the iterator, callback pattern.
   * @method AggregationCursor.prototype.forEach
   * @param {AggregationCursor~iteratorCallback} iterator The iteration callback.
   * @param {AggregationCursor~endCallback} callback The end callback.
   * @throws {MongoError}
   * @return {null}
   */
}

/**
 * AggregationCursor stream data event, fired for each document in the cursor.
 *
 * @event AggregationCursor#data
 * @type {object}
 */

/**
 * AggregationCursor stream end event
 *
 * @event AggregationCursor#end
 * @type {null}
 */

/**
 * AggregationCursor stream close event
 *
 * @event AggregationCursor#close
 * @type {null}
 */

/**
 * AggregationCursor stream readable event
 *
 * @event AggregationCursor#readable
 * @type {null}
 */

// // Extend the Cursor
// inherits(AggregationCursor, CoreCursor);

// Inherit from Readable
inherits(AggregationCursor, Readable);

// Extend the Cursor
for(var name in CoreCursor.prototype) {
  AggregationCursor.prototype[name] = CoreCursor.prototype[name];
}

AggregationCursor.INIT = 0;
AggregationCursor.OPEN = 1;
AggregationCursor.CLOSED = 2;

module.exports = AggregationCursor;<|endoftext|>


<commit_before>import React, { Component } from 'react';

// tracking js apparently cannot be imported via import 'tracking'
import '../../node_modules/tracking/build/tracking.js'
import '../../node_modules/tracking/build/data/face.js'

let faces = [];
const IMG_SIDELENGTH=224;
const faceListeners = [];

class FaceTracker extends Component {

  componentDidMount() {
    let video = this.refs.video;
    let canvas = this.refs.canvas;
    let context = canvas.getContext('2d');
    const tracker = new tracking.ObjectTracker('face');

    tracker.setInitialScale(4);
    tracker.setStepSize(2);
    tracker.setEdgesDensity(0.1);

    tracking.track(video, tracker, { camera: true });

    tracker.on('track', (event) => {
      context.clearRect(0, 0, canvas.width, canvas.height);
      faces = event.data;

      event.data.forEach((rect) => {
        context.strokeStyle = '#a64ceb';
        context.strokeRect(rect.x, rect.y, rect.width, rect.height);
        context.font = '11px Helvetica';
        context.fillStyle = "#fff";
        context.fillText('x: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11);
        context.fillText('y: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 22);
      });

      faceListeners.forEach(l=>l())
    });
  }

  addFaceListener(callback){
    faceListeners.push(callback);
  }

  removeFaceListener(callback){
    let index = faceListeners.indexOf(callback);
    console.log(index);
    if(index >= 0) faceListeners.splice(index, 1);
  }

  getNumberOfFaces() {
    return faces.length;
  }

  getFace() {
    if (faces.length === 0) return "no faces on image"

    var border = 17;
    var face = faces[0];

    var canvas = this.refs.cropcanv;
    var ctx = canvas.getContext("2d");

    let cropWidth = (face.width + border * 2) *2;
    let cropHeight = (face.height + border * 2) *2;

    canvas.width = IMG_SIDELENGTH;
    canvas.height = IMG_SIDELENGTH;

    ctx.drawImage(
      this.refs.video,
      Math.max(face.x - border,0)*2,
      Math.max(face.y - border,0)*2,
      cropWidth,
      cropHeight,
      0,
      0,
      IMG_SIDELENGTH,
      IMG_SIDELENGTH
      );

    // return the .toDataURL of the temp canvas
    return canvas.toDataURL("image/png");
  }

  render() {
    return (

            <canvas ref="cropcanv" style={{display:"none"}}/>

            <video ref="video" width="320" height="240" preload autoPlay loop muted></video>
            <canvas ref="canvas" width="320" height="240"></canvas>

    );
  }
}

export default FaceTracker;

<commit_msg>remove pixel text, change rect color to green<commit_after>import React, { Component } from 'react';

// tracking js apparently cannot be imported via import 'tracking'
import '../../node_modules/tracking/build/tracking.js'
import '../../node_modules/tracking/build/data/face.js'

let faces = [];
const IMG_SIDELENGTH=224;
const faceListeners = [];

class FaceTracker extends Component {

  componentDidMount() {
    let video = this.refs.video;
    let canvas = this.refs.canvas;
    let context = canvas.getContext('2d');
    const tracker = new tracking.ObjectTracker('face');

    tracker.setInitialScale(4);
    tracker.setStepSize(2);
    tracker.setEdgesDensity(0.1);

    tracking.track(video, tracker, { camera: true });

    tracker.on('track', (event) => {
      context.clearRect(0, 0, canvas.width, canvas.height);
      faces = event.data;

      event.data.forEach((rect) => {
        context.strokeStyle = '#73d700';
        context.strokeRect(rect.x, rect.y, rect.width, rect.height);
      });

      faceListeners.forEach(l=>l())
    });
  }

  addFaceListener(callback){
    faceListeners.push(callback);
  }

  removeFaceListener(callback){
    let index = faceListeners.indexOf(callback);
    console.log(index);
    if(index >= 0) faceListeners.splice(index, 1);
  }

  getNumberOfFaces() {
    return faces.length;
  }

  getFace() {
    if (faces.length === 0) return "no faces on image"

    var border = 17;
    var face = faces[0];

    var canvas = this.refs.cropcanv;
    var ctx = canvas.getContext("2d");

    let cropWidth = (face.width + border * 2) *2;
    let cropHeight = (face.height + border * 2) *2;

    canvas.width = IMG_SIDELENGTH;
    canvas.height = IMG_SIDELENGTH;

    ctx.drawImage(
      this.refs.video,
      Math.max(face.x - border,0)*2,
      Math.max(face.y - border,0)*2,
      cropWidth,
      cropHeight,
      0,
      0,
      IMG_SIDELENGTH,
      IMG_SIDELENGTH
      );

    // return the .toDataURL of the temp canvas
    return canvas.toDataURL("image/png");
  }

  render() {
    return (

            <canvas ref="cropcanv" style={{display:"none"}}/>

            <video ref="video" width="320" height="240" preload autoPlay loop muted></video>
            <canvas ref="canvas" width="320" height="240"></canvas>

    );
  }
}

export default FaceTracker;

<|endoftext|>


<commit_before>var _ = require('lodash'),
    queryString = require('query-string'),
    Spreadsheet = require('./utilities/Spreadsheet.js'),
    Request = require('./utilities/Request.js');

var Sitecore = function (config) {

    this.data = null;
    this.config = null;
    this.spreadsheet = null;
    this.request = null;

    var _config = require('../config/config.json');

    this.config = _.assign(config || {}, _config);

    this.spreadsheet = new Spreadsheet({
            sitecore: this
        },
        this.config
    );

    this.request = new Request({
            sitecore: this
        },
        this.config
    );

};

Sitecore.prototype = {

    createItem: function (data) {

        var name = data.name || 'No Name',
            database = data.database || 'master',
            sc_itemid = data.sc_itemid || this.config.sc_itemid,
            template = data.template || this.config.template;

        this.post({
            'url': this.config.baseUrl + '?name=' + name + '&sc_itemid=' + sc_itemid + '&template=' + template + '&sc_database=' + database,
            'body': data.body
        });

    },
    /**
     * Retrieve item by id, can be any type of item ( page, media, etc...)
     * @param id
     */
    readItem: function (id, params, callback) {

        var query = null;

        if (params) {
            query = '?' + queryString.stringify(params) + '&sc_itemid=' + id;
        }

        this.get({
            'url': query || '?sc_itemid=' + id
        }, callback);

    },
    updateItem: function (options, data, callback) {

        var database = data.database || 'master';

        this.put({
            'url': this.config.baseUrl + '?sc_itemid=' + options.id + '&name=' + options.name + '&template=' + options.template + '&sc_database=' + database,
            'body': data
        });

    },
    query: function (path, callback) {

        this.get({
            'url': '?query=fast:' + path
        }, callback);

    },
    queryByName: function(path, name) {

        //You can use the following values to define the scope:
        //     s for self — default if nothing is specified
        //     p for parent
        //     c for children

        //Examples:
        //    scope=p
        //    scope=c|p
        //    scope=s|p|c

        // query=/sitecore/content/home//*[@@templatekey='sample item']
        // /sitecore/content/Home/*[@TITLE = 'Welcome to Sitecore']

        //https://qa.beta.arrow.com/-/item/v1/?query=/sitecore/content/Hemstreet/*%5B%40TITLE%20%3D%20%27Test%20title%27%5D
        //                                           /sitecore/Content/Hemstreet/*%5B@TITLE=Test%20title%5D
        //https://qa.beta.arrow.com/-/item/v1/?query=/sitecore/content/Hemstreet/*[@TITLE=Test title]

        var title = 'Test title',
            query = encodeURIComponent("[@TITLE = '" + title + "']").replace(/'/g, '%27');

        this.get({
            'url': '?query=' + path + query,
            'noEncode': true
        });
    },
    updateItemById: function (id, data) {
        this.readItem(id, null, function (response) {

            if (response.items.length > 1) {
                console.log('More than one item found, bail out!');
                return;
            }

            var item = response.items[0],
                options = {
                    'name': item.Name,
                    'template': item.TemplateId,
                    'id': id
                };

            this.updateItem(options, data);

        }.bind(this));
    },
    get: function (options, callback) {

        options.method = "GET";
        this.request.request(options, callback);

    },
    put: function (options, callback) {

        options.method = "PUT";
        this.request.request(options, callback);

    },
    post: function (options, callback) {

        options.method = "POST";
        this.request.request(options, callback);

    },
    importFromSpreadsheet: function (path, sheetName, fields, target) {
        this.spreadsheet.importFromSpreadsheet(path, sheetName, fields, target);
    },
    updateFromSpreadSheet: function (options, config) {

        this.spreadsheet.updateFromSpreadSheet(options, config);
    }
    // findById(id)
    // findByName(name)
    // updateById(id)
    // updateByName(name)
    // getTemplateByKey(key)
    // getTemplateByName(name)
    // alphabetizeByField(field, array)
    // getFieldKeyByName(name, template)
    // getFieldsByTemplateId
    // getFieldsByTemplateName
    // createFolder(name)
    // deleteById(id)
    // deleteByName(name)
    // importFromSpreadsheet(path)
    // updateFromSpreadsheet(path)
    // generateTemplateFieldSchema(id)

};

module.exports = Sitecore;<commit_msg>code cleanup<commit_after>var _ = require('lodash'),
    queryString = require('query-string'),
    Spreadsheet = require('./utilities/Spreadsheet.js'),
    Request = require('./utilities/Request.js');

var Sitecore = function (config) {

    this.data = null;
    this.config = null;
    this.spreadsheet = null;
    this.request = null;

    var _config = require('../config/config.json');

    this.config = _.assign(config || {}, _config);

    this.spreadsheet = new Spreadsheet({
            sitecore: this
        },
        this.config
    );

    this.request = new Request({
            sitecore: this
        },
        this.config
    );

};

Sitecore.prototype = {

    createItem: function (data) {

        var name = data.name || 'No Name',
            database = data.database || 'master',
            sc_itemid = data.sc_itemid || this.config.sc_itemid,
            template = data.template || this.config.template;

        this.post({
            'url': this.config.baseUrl + '?name=' + name + '&sc_itemid=' + sc_itemid + '&template=' + template + '&sc_database=' + database,
            'body': data.body
        });

    },
    /**
     * Retrieve item by id, can be any type of item ( page, media, etc...)
     * @param id
     */
    readItem: function (id, params, callback) {

        var query = null;

        if (params) {
            query = '?' + queryString.stringify(params) + '&sc_itemid=' + id;
        }

        this.get({
            'url': query || '?sc_itemid=' + id
        }, callback);

    },
    updateItem: function (options, data, callback) {

        var database = data.database || 'master';

        this.put({
            'url': this.config.baseUrl + '?sc_itemid=' + options.id + '&name=' + options.name + '&template=' + options.template + '&sc_database=' + database,
            'body': data
        });

    },
    query: function (path, callback) {

        this.get({
            'url': '?query=fast:' + path
        }, callback);

    },
    queryByName: function(path, name) {

        //You can use the following values to define the scope:
        //     s for self — default if nothing is specified
        //     p for parent
        //     c for children

        //Examples:
        //    scope=p
        //    scope=c|p
        //    scope=s|p|c

        var title = 'Test title',
            query = encodeURIComponent("[@TITLE = '" + title + "']").replace(/'/g, '%27');

        this.get({
            'url': '?query=' + path + query,
            'noEncode': true
        });
    },
    updateItemById: function (id, data) {
        this.readItem(id, null, function (response) {

            if (response.items.length > 1) {
                console.log('More than one item found, bail out!');
                return;
            }

            var item = response.items[0],
                options = {
                    'name': item.Name,
                    'template': item.TemplateId,
                    'id': id
                };

            this.updateItem(options, data);

        }.bind(this));
    },
    get: function (options, callback) {

        options.method = "GET";
        this.request.request(options, callback);

    },
    put: function (options, callback) {

        options.method = "PUT";
        this.request.request(options, callback);

    },
    post: function (options, callback) {

        options.method = "POST";
        this.request.request(options, callback);

    },
    importFromSpreadsheet: function (path, sheetName, fields, target) {
        this.spreadsheet.importFromSpreadsheet(path, sheetName, fields, target);
    },
    updateFromSpreadSheet: function (options, config) {

        this.spreadsheet.updateFromSpreadSheet(options, config);
    }
    // findById(id)
    // findByName(name)
    // updateById(id)
    // updateByName(name)
    // getTemplateByKey(key)
    // getTemplateByName(name)
    // alphabetizeByField(field, array)
    // getFieldKeyByName(name, template)
    // getFieldsByTemplateId
    // getFieldsByTemplateName
    // createFolder(name)
    // deleteById(id)
    // deleteByName(name)
    // importFromSpreadsheet(path)
    // updateFromSpreadsheet(path)
    // generateTemplateFieldSchema(id)

};

module.exports = Sitecore;<|endoftext|>