<commit_before>/**
* Copyright 2012-2020, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var dragElement = require('../../dragelement');
var dragHelpers = require('../../dragelement/helpers');
var drawMode = dragHelpers.drawMode;

var Registry = require('../../../registry');
var setCursor = require('../../../lib/setcursor');

var MINSELECT = require('../../../plots/cartesian/constants').MINSELECT;
var constants = require('./constants');
var i000 = constants.i000;
var i090 = constants.i090;
var i180 = constants.i180;
var i270 = constants.i270;

var handleOutline = require('../../../plots/cartesian/handle_outline');
var clearOutlineControllers = handleOutline.clearOutlineControllers;

var helpers = require('./helpers');
var pointsShapeRectangle = helpers.pointsShapeRectangle;
var pointsShapeEllipse = helpers.pointsShapeEllipse;
var writePaths = helpers.writePaths;
var newShapes = require('./newshapes');

module.exports = function displayOutlines(polygons, outlines, dragOptions, nCalls) {
    if(!nCalls) nCalls = 0;

    var gd = dragOptions.gd;

    function redraw() {
        // recursive call
        displayOutlines(polygons, outlines, dragOptions, nCalls++);

        if(pointsShapeEllipse(polygons[0])) {
            update({redrawing: true});
        }
    }

    function update(opts) {
        dragOptions.isActiveShape = false; // i.e. to disable controllers

        var shapes = newShapes(outlines, dragOptions);
        if(shapes) {
            Registry.call(opts && opts.redrawing ? 'relayout' : '_guiRelayout', gd, {
                shapes: shapes // update active shape
            });
        }
    }

    // remove previous controllers - only if there is an active shape
    if(gd._fullLayout._activeShapeIndex >= 0) clearOutlineControllers(gd);

    var isActiveShape = dragOptions.isActiveShape;
    var fullLayout = gd._fullLayout;
    var zoomLayer = fullLayout._zoomlayer;

    var dragmode = dragOptions.dragmode;
    var isDrawMode = drawMode(dragmode);

    if(isDrawMode) gd._fullLayout._drawing = true;

    // make outline
    outlines.attr('d', writePaths(polygons));

    // add controllers
    var rVertexController = MINSELECT * 1.5; // bigger vertex buttons
    var vertexDragOptions;
    var shapeDragOptions;
    var indexI; // cell index
    var indexJ; // vertex or cell-controller index
    var copyPolygons;

    if(isActiveShape) {
        if(!nCalls) copyPolygons = recordPositions([], polygons);

        var g = zoomLayer.append('g').attr('class', 'outline-controllers');
        addVertexControllers(g);
        addShapeControllers();
    }

    function startDragVertex(evt) {
        indexI = +evt.srcElement.getAttribute('data-i');
        indexJ = +evt.srcElement.getAttribute('data-j');

        vertexDragOptions[indexI][indexJ].moveFn = moveVertexController;
    }

    function moveVertexController(dx, dy) {
        if(!polygons.length) return;

        var x0 = copyPolygons[indexI][indexJ][1];
        var y0 = copyPolygons[indexI][indexJ][2];

        var cell = polygons[indexI];
        var len = cell.length;
        if(pointsShapeRectangle(cell)) {
            for(var q = 0; q < len; q++) {
                if(q === indexJ) continue;

                // move other corners of rectangle
                var pos = cell[q];

                if(pos[1] === cell[indexJ][1]) {
                    pos[1] = x0 + dx;
                }

                if(pos[2] === cell[indexJ][2]) {
                    pos[2] = y0 + dy;
                }
            }
            // move the corner
            cell[indexJ][1] = x0 + dx;
            cell[indexJ][2] = y0 + dy;

            if(!pointsShapeRectangle(cell)) {
                // reject result to rectangles with ensure areas
                for(var j = 0; j < len; j++) {
                    for(var k = 0; k < cell[j].length; k++) {
                        cell[j][k] = copyPolygons[indexI][j][k];
                    }
                }
            }
        } else { // other polylines
            cell[indexJ][1] = x0 + dx;
            cell[indexJ][2] = y0 + dy;
        }

        redraw();
    }

    function endDragVertexController() {
        update();
    }

    function removeVertex() {
        if(!polygons.length) return;
        if(!polygons[indexI]) return;
        if(!polygons[indexI].length) return;

        var newPolygon = [];
        for(var j = 0; j < polygons[indexI].length; j++) {
            if(j !== indexJ) {
                newPolygon.push(
                    polygons[indexI][j]
                );
            }
        }

        if(newPolygon.length > 1 && !(
            newPolygon.length === 2 && newPolygon[1][0] === 'Z')
        ) {
            if(indexJ === 0) {
                newPolygon[0][0] = 'M';
            }

            polygons[indexI] = newPolygon;

            redraw();
        }
    }

    function clickVertexController(numClicks, evt) {
        if(numClicks === 2) {
            indexI = +evt.srcElement.getAttribute('data-i');
            indexJ = +evt.srcElement.getAttribute('data-j');

            var cell = polygons[indexI];
            if(
                !pointsShapeRectangle(cell) &&
                !pointsShapeEllipse(cell)
            ) {
                removeVertex();
            }
        }
    }

    function addVertexControllers(g) {
        vertexDragOptions = [];

        for(var i = 0; i < polygons.length; i++) {
            var cell = polygons[i];

            var onRect = pointsShapeRectangle(cell);
            var onEllipse = !onRect && pointsShapeEllipse(cell);

            var minX;
            var minY;
            var maxX;
            var maxY;
            if(onRect) {
                // compute bounding box
                minX = calcMin(cell, 1);
                minY = calcMin(cell, 2);
                maxX = calcMax(cell, 1);
                maxY = calcMax(cell, 2);
            }

            vertexDragOptions[i] = [];
            for(var j = 0; j < cell.length; j++) {
                if(cell[j][0] === 'Z') continue;

                if(onEllipse &&
                    j !== i000 &&
                    j !== i090 &&
                    j !== i180 &&
                    j !== i270
                ) {
                    continue;
                }

                var x = cell[j][1];
                var y = cell[j][2];

                var rIcon = 3;
                var button = g.append(onRect ? 'rect' : 'circle')
                .style({
                    'mix-blend-mode': 'luminosity',
                    fill: 'black',
                    stroke: 'white',
                    'stroke-width': 1
                });

                if(onRect) {
                    button
                        .attr('x', x - rIcon)
                        .attr('y', y - rIcon)
                        .attr('width', 2 * rIcon)
                        .attr('height', 2 * rIcon);
                } else {
                    button
                        .attr('cx', x)
                        .attr('cy', y)
                        .attr('r', rIcon);
                }

                var vertex = g.append(onRect ? 'rect' : 'circle')
                .attr('data-i', i)
                .attr('data-j', j)
                .style({
                    opacity: 0
                });

                if(onRect) {
                    var ratioX = (x - minX) / (maxX - minX);
                    var ratioY = (y - minY) / (maxY - minY);
                    if(isFinite(ratioX) && isFinite(ratioY)) {
                        setCursor(
                            vertex,
                            dragElement.getCursor(ratioX, 1 - ratioY)
                        );
                    }

                    vertex
                        .attr('x', x - rVertexController)
                        .attr('y', y - rVertexController)
                        .attr('width', 2 * rVertexController)
                        .attr('height', 2 * rVertexController);
                } else {
                    vertex
                        .classed('cursor-grab', true)
                        .attr('cx', x)
                        .attr('cy', y)
                        .attr('r', rVertexController);
                }

                vertexDragOptions[i][j] = {
                    element: vertex.node(),
                    gd: gd,
                    prepFn: startDragVertex,
                    doneFn: endDragVertexController,
                    clickFn: clickVertexController
                };

                dragElement.init(vertexDragOptions[i][j]);
            }
        }
    }

    function moveShape(dx, dy) {
        if(!polygons.length) return;

        for(var i = 0; i < polygons.length; i++) {
            for(var j = 0; j < polygons[i].length; j++) {
                for(var k = 0; k + 2 < polygons[i][j].length; k += 2) {
                    polygons[i][j][k + 1] = copyPolygons[i][j][k + 1] + dx;
                    polygons[i][j][k + 2] = copyPolygons[i][j][k + 2] + dy;
                }
            }
        }
    }

    function moveShapeController(dx, dy) {
        moveShape(dx, dy);

        redraw();
    }

    function startDragShapeController(evt) {
        indexI = +evt.srcElement.getAttribute('data-i');
        if(!indexI) indexI = 0; // ensure non-existing move button get zero index

        shapeDragOptions[indexI].moveFn = moveShapeController;
    }

    function endDragShapeController() {
        update();
    }

    function addShapeControllers() {
        shapeDragOptions = [];

        if(!polygons.length) return;

        var i = 0;
        shapeDragOptions[i] = {
            element: outlines[0][0],
            gd: gd,
            prepFn: startDragShapeController,
            doneFn: endDragShapeController
        };

        dragElement.init(shapeDragOptions[i]);
    }
};

function calcMin(cell, dim) {
    var v = Infinity;
    for(var i = 0; i < cell.length; i++) {
        v = Math.min(v, cell[i][dim]);
    }
    return v;
}

function calcMax(cell, dim) {
    var v = -Infinity;
    for(var i = 0; i < cell.length; i++) {
        v = Math.max(v, cell[i][dim]);
    }
    return v;
}

function recordPositions(polygonsOut, polygonsIn) {
    for(var i = 0; i < polygonsIn.length; i++) {
        var cell = polygonsIn[i];
        polygonsOut[i] = [];
        for(var j = 0; j < cell.length; j++) {
            polygonsOut[i][j] = [];
            for(var k = 0; k < cell[j].length; k++) {
                polygonsOut[i][j][k] = cell[j][k];
            }
        }
    }
    return polygonsOut;
}
<commit_msg>correct call to update vertices on click<commit_after>/**
* Copyright 2012-2020, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var dragElement = require('../../dragelement');
var dragHelpers = require('../../dragelement/helpers');
var drawMode = dragHelpers.drawMode;

var Registry = require('../../../registry');
var setCursor = require('../../../lib/setcursor');

var MINSELECT = require('../../../plots/cartesian/constants').MINSELECT;
var constants = require('./constants');
var i000 = constants.i000;
var i090 = constants.i090;
var i180 = constants.i180;
var i270 = constants.i270;

var handleOutline = require('../../../plots/cartesian/handle_outline');
var clearOutlineControllers = handleOutline.clearOutlineControllers;

var helpers = require('./helpers');
var pointsShapeRectangle = helpers.pointsShapeRectangle;
var pointsShapeEllipse = helpers.pointsShapeEllipse;
var writePaths = helpers.writePaths;
var newShapes = require('./newshapes');

module.exports = function displayOutlines(polygons, outlines, dragOptions, nCalls) {
    if(!nCalls) nCalls = 0;

    var gd = dragOptions.gd;

    function redraw() {
        // recursive call
        displayOutlines(polygons, outlines, dragOptions, nCalls++);

        if(pointsShapeEllipse(polygons[0])) {
            update({redrawing: true});
        }
    }

    function update(opts) {
        dragOptions.isActiveShape = false; // i.e. to disable controllers

        var shapes = newShapes(outlines, dragOptions);
        if(shapes) {
            Registry.call(opts && opts.redrawing ? 'relayout' : '_guiRelayout', gd, {
                shapes: shapes // update active shape
            });
        }
    }

    // remove previous controllers - only if there is an active shape
    if(gd._fullLayout._activeShapeIndex >= 0) clearOutlineControllers(gd);

    var isActiveShape = dragOptions.isActiveShape;
    var fullLayout = gd._fullLayout;
    var zoomLayer = fullLayout._zoomlayer;

    var dragmode = dragOptions.dragmode;
    var isDrawMode = drawMode(dragmode);

    if(isDrawMode) gd._fullLayout._drawing = true;

    // make outline
    outlines.attr('d', writePaths(polygons));

    // add controllers
    var rVertexController = MINSELECT * 1.5; // bigger vertex buttons
    var vertexDragOptions;
    var shapeDragOptions;
    var indexI; // cell index
    var indexJ; // vertex or cell-controller index
    var copyPolygons;

    if(isActiveShape) {
        if(!nCalls) copyPolygons = recordPositions([], polygons);

        var g = zoomLayer.append('g').attr('class', 'outline-controllers');
        addVertexControllers(g);
        addShapeControllers();
    }

    function startDragVertex(evt) {
        indexI = +evt.srcElement.getAttribute('data-i');
        indexJ = +evt.srcElement.getAttribute('data-j');

        vertexDragOptions[indexI][indexJ].moveFn = moveVertexController;
    }

    function moveVertexController(dx, dy) {
        if(!polygons.length) return;

        var x0 = copyPolygons[indexI][indexJ][1];
        var y0 = copyPolygons[indexI][indexJ][2];

        var cell = polygons[indexI];
        var len = cell.length;
        if(pointsShapeRectangle(cell)) {
            for(var q = 0; q < len; q++) {
                if(q === indexJ) continue;

                // move other corners of rectangle
                var pos = cell[q];

                if(pos[1] === cell[indexJ][1]) {
                    pos[1] = x0 + dx;
                }

                if(pos[2] === cell[indexJ][2]) {
                    pos[2] = y0 + dy;
                }
            }
            // move the corner
            cell[indexJ][1] = x0 + dx;
            cell[indexJ][2] = y0 + dy;

            if(!pointsShapeRectangle(cell)) {
                // reject result to rectangles with ensure areas
                for(var j = 0; j < len; j++) {
                    for(var k = 0; k < cell[j].length; k++) {
                        cell[j][k] = copyPolygons[indexI][j][k];
                    }
                }
            }
        } else { // other polylines
            cell[indexJ][1] = x0 + dx;
            cell[indexJ][2] = y0 + dy;
        }

        redraw();
    }

    function endDragVertexController() {
        update();
    }

    function removeVertex() {
        if(!polygons.length) return;
        if(!polygons[indexI]) return;
        if(!polygons[indexI].length) return;

        var newPolygon = [];
        for(var j = 0; j < polygons[indexI].length; j++) {
            if(j !== indexJ) {
                newPolygon.push(
                    polygons[indexI][j]
                );
            }
        }

        if(newPolygon.length > 1 && !(
            newPolygon.length === 2 && newPolygon[1][0] === 'Z')
        ) {
            if(indexJ === 0) {
                newPolygon[0][0] = 'M';
            }

            polygons[indexI] = newPolygon;

            update();
        }
    }

    function clickVertexController(numClicks, evt) {
        if(numClicks === 2) {
            indexI = +evt.srcElement.getAttribute('data-i');
            indexJ = +evt.srcElement.getAttribute('data-j');

            var cell = polygons[indexI];
            if(
                !pointsShapeRectangle(cell) &&
                !pointsShapeEllipse(cell)
            ) {
                removeVertex();
            }
        }
    }

    function addVertexControllers(g) {
        vertexDragOptions = [];

        for(var i = 0; i < polygons.length; i++) {
            var cell = polygons[i];

            var onRect = pointsShapeRectangle(cell);
            var onEllipse = !onRect && pointsShapeEllipse(cell);

            var minX;
            var minY;
            var maxX;
            var maxY;
            if(onRect) {
                // compute bounding box
                minX = calcMin(cell, 1);
                minY = calcMin(cell, 2);
                maxX = calcMax(cell, 1);
                maxY = calcMax(cell, 2);
            }

            vertexDragOptions[i] = [];
            for(var j = 0; j < cell.length; j++) {
                if(cell[j][0] === 'Z') continue;

                if(onEllipse &&
                    j !== i000 &&
                    j !== i090 &&
                    j !== i180 &&
                    j !== i270
                ) {
                    continue;
                }

                var x = cell[j][1];
                var y = cell[j][2];

                var rIcon = 3;
                var button = g.append(onRect ? 'rect' : 'circle')
                .style({
                    'mix-blend-mode': 'luminosity',
                    fill: 'black',
                    stroke: 'white',
                    'stroke-width': 1
                });

                if(onRect) {
                    button
                        .attr('x', x - rIcon)
                        .attr('y', y - rIcon)
                        .attr('width', 2 * rIcon)
                        .attr('height', 2 * rIcon);
                } else {
                    button
                        .attr('cx', x)
                        .attr('cy', y)
                        .attr('r', rIcon);
                }

                var vertex = g.append(onRect ? 'rect' : 'circle')
                .attr('data-i', i)
                .attr('data-j', j)
                .style({
                    opacity: 0
                });

                if(onRect) {
                    var ratioX = (x - minX) / (maxX - minX);
                    var ratioY = (y - minY) / (maxY - minY);
                    if(isFinite(ratioX) && isFinite(ratioY)) {
                        setCursor(
                            vertex,
                            dragElement.getCursor(ratioX, 1 - ratioY)
                        );
                    }

                    vertex
                        .attr('x', x - rVertexController)
                        .attr('y', y - rVertexController)
                        .attr('width', 2 * rVertexController)
                        .attr('height', 2 * rVertexController);
                } else {
                    vertex
                        .classed('cursor-grab', true)
                        .attr('cx', x)
                        .attr('cy', y)
                        .attr('r', rVertexController);
                }

                vertexDragOptions[i][j] = {
                    element: vertex.node(),
                    gd: gd,
                    prepFn: startDragVertex,
                    doneFn: endDragVertexController,
                    clickFn: clickVertexController
                };

                dragElement.init(vertexDragOptions[i][j]);
            }
        }
    }

    function moveShape(dx, dy) {
        if(!polygons.length) return;

        for(var i = 0; i < polygons.length; i++) {
            for(var j = 0; j < polygons[i].length; j++) {
                for(var k = 0; k + 2 < polygons[i][j].length; k += 2) {
                    polygons[i][j][k + 1] = copyPolygons[i][j][k + 1] + dx;
                    polygons[i][j][k + 2] = copyPolygons[i][j][k + 2] + dy;
                }
            }
        }
    }

    function moveShapeController(dx, dy) {
        moveShape(dx, dy);

        redraw();
    }

    function startDragShapeController(evt) {
        indexI = +evt.srcElement.getAttribute('data-i');
        if(!indexI) indexI = 0; // ensure non-existing move button get zero index

        shapeDragOptions[indexI].moveFn = moveShapeController;
    }

    function endDragShapeController() {
        update();
    }

    function addShapeControllers() {
        shapeDragOptions = [];

        if(!polygons.length) return;

        var i = 0;
        shapeDragOptions[i] = {
            element: outlines[0][0],
            gd: gd,
            prepFn: startDragShapeController,
            doneFn: endDragShapeController
        };

        dragElement.init(shapeDragOptions[i]);
    }
};

function calcMin(cell, dim) {
    var v = Infinity;
    for(var i = 0; i < cell.length; i++) {
        v = Math.min(v, cell[i][dim]);
    }
    return v;
}

function calcMax(cell, dim) {
    var v = -Infinity;
    for(var i = 0; i < cell.length; i++) {
        v = Math.max(v, cell[i][dim]);
    }
    return v;
}

function recordPositions(polygonsOut, polygonsIn) {
    for(var i = 0; i < polygonsIn.length; i++) {
        var cell = polygonsIn[i];
        polygonsOut[i] = [];
        for(var j = 0; j < cell.length; j++) {
            polygonsOut[i][j] = [];
            for(var k = 0; k < cell[j].length; k++) {
                polygonsOut[i][j][k] = cell[j][k];
            }
        }
    }
    return polygonsOut;
}
<|endoftext|>


<commit_before>'use strict';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace pareto1
*/
var pareto = {};

/**
* @name cdf
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/cdf}
*/
setReadOnly( pareto, 'cdf', require( '@stdlib/math/base/dist/pareto-type1/cdf' ) );

/**
* @name median
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/mean}
*/
setReadOnly( pareto, 'mean', require( '@stdlib/math/base/dist/pareto-type1/mean' ) );

/**
* @name median
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/median}
*/
setReadOnly( pareto, 'median', require( '@stdlib/math/base/dist/pareto-type1/median' ) );

/**
* @name pdf
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/pdf}
*/
setReadOnly( pareto, 'pdf', require( '@stdlib/math/base/dist/pareto-type1/pdf' ) );

/**
* @name quantile
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/quantile}
*/
setReadOnly( pareto, 'quantile', require( '@stdlib/math/base/dist/pareto-type1/quantile' ) );

// EXPORTS //

module.exports = pareto;
<commit_msg>Export packages to Pareto namespace<commit_after>'use strict';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace pareto1
*/
var pareto = {};

/**
* @name cdf
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/cdf}
*/
setReadOnly( pareto, 'cdf', require( '@stdlib/math/base/dist/pareto-type1/cdf' ) );

/**
* @name entropy
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/entropy}
*/
setReadOnly( pareto, 'entropy', require( '@stdlib/math/base/dist/pareto-type1/entropy' ) );

/**
* @name kurtosis
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/kurtosis}
*/
setReadOnly( pareto, 'kurtosis', require( '@stdlib/math/base/dist/pareto-type1/kurtosis' ) );

/**
* @name mean
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/mean}
*/
setReadOnly( pareto, 'mean', require( '@stdlib/math/base/dist/pareto-type1/mean' ) );

/**
* @name median
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/median}
*/
setReadOnly( pareto, 'median', require( '@stdlib/math/base/dist/pareto-type1/median' ) );

/**
* @name mode
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/mode}
*/
setReadOnly( pareto, 'mode', require( '@stdlib/math/base/dist/pareto-type1/mode' ) );

/**
* @name pdf
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/pdf}
*/
setReadOnly( pareto, 'pdf', require( '@stdlib/math/base/dist/pareto-type1/pdf' ) );

/**
* @name quantile
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/quantile}
*/
setReadOnly( pareto, 'quantile', require( '@stdlib/math/base/dist/pareto-type1/quantile' ) );

/**
* @name skewness
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/skewness}
*/
setReadOnly( pareto, 'skewness', require( '@stdlib/math/base/dist/pareto-type1/skewness' ) );

/**
* @name variance
* @memberof pareto1
* @readonly
* @type {Function}
* @see {@link module:@stdlib/math/base/dist/pareto-type1/variance}
*/
setReadOnly( pareto, 'variance', require( '@stdlib/math/base/dist/pareto-type1/variance' ) );

// EXPORTS //

module.exports = pareto;
<|endoftext|>


<commit_before>'use strict';

// VARIABLES //

// 4294901760 => 0xFFFF0000 => 11111111111111110000000000000000
var B4 = 0xFFFF0000 >>> 0; // asm type annotation

// 65280 => 0xFF00 => 00000000000000001111111100000000
var B3 = 0xFF00 >>> 0; // asm type annotation

// 240 => 0xF0 => 00000000000000000000000011110000
var B2 = 0xF0 >>> 0; // asm type annotation

// 12 => 0xC => 00000000000000000000000000001100
var B1 = 0xC >>> 0; // asm type annotation

// 2 => 0x2 => 00000000000000000000000000000010
var B0 = 0x2 >>> 0; // asm type annotation

// 16 => 00000000000000000000000000010000
var S4 = 16 >>> 0; // asm type annotation

// 8 => 00000000000000000000000000001000
var S3 = 8 >>> 0; // asm type annotation

// 4 => 00000000000000000000000000000100
var S2 = 4 >>> 0; // asm type annotation

// 2 => 00000000000000000000000000000010
var S1 = 2 >>> 0; // asm type annotation

// 1 => 00000000000000000000000000000001
var S0 = 1 >>> 0; // asm type annotation

// MAIN //

/**
* Computes an integer binary logarithm (base two).
*
* #### Method
*
* 1. Note that the largest unsigned 32-bit integer is `4294967295`, which is `2^{32}-1`. Hence, the integer binary logarithm cannot exceed `31` (i.e., `16 + 8 + 4 + 2 + 1`), which corresponds to the bit sequence
*
*   ``` binarystring
*   00000000000000000000000000011111
*   ```
*
* 2. Initialize a return variable with the value zero.
*
* 3. If at least one of the first sixteen most significant bits of the input 32-bit integer `x` is turned on, we know that the power to which the number `2` must be raised to obtain `x` is at least `16` (i.e., `x > 65536`). Hence, activate the corresponding bit of the return variable. Mutate `x` by shifting sixteen bits to the right, discarding the bits shifted off.
*
* 4. Carry out the following steps with `B` in `[ 8, 4, 2, 1 ]`:
*
*    - If at least one of the next `B` most significant bits of the current `x` is turned on, we know that the power to which the number `2` must be raised to obtain `x` has to be increased by `B`.
*    - Activate the bit of the return variable that corresponds to `B`.
*    - Mutate `x` by shifting `B` bits to the right, discarding the bits shifted off.
*
* 5. The final value of the return variable is the integer binary logarithm of `x`.
*
*
* @param {uinteger32} x - input value
* @returns {uinteger32} integer binary logarithm
*
* @example
* var v = llog2( 4 );
* // returns 2
* @example
* var v = llog2( 8 );
* // returns 3
* @example
* var v = llog2( 9 );
* // returns 3
*/
function llog2( x ) {
	var out = 0 >>> 0; // asm type annotation

	// `x >= 65536`:
	if ( x & B4 ) {
		x >>>= S4;
		out |= S4;
	}
	// `x >= 256`:
	if ( x & B3 ) {
		x >>>= S3;
		out |= S3;
	}
	// `x >= 16`:
	if ( x & B2 ) {
		x >>>= S2;
		out |= S2;
	}
	// `x >= 4`:
	if ( x & B1 ) {
		x >>>= S1;
		out |= S1;
	}
	// `x >= 2`:
	if ( x & B0 ) {
		x >>>= S0;
		out |= S0;
	}
	return out;
} // end FUNCTION llog2()

// EXPORTS //

module.exports = llog2;
<commit_msg>Rename function<commit_after>'use strict';

// VARIABLES //

// 4294901760 => 0xFFFF0000 => 11111111111111110000000000000000
var B4 = 0xFFFF0000 >>> 0; // asm type annotation

// 65280 => 0xFF00 => 00000000000000001111111100000000
var B3 = 0xFF00 >>> 0; // asm type annotation

// 240 => 0xF0 => 00000000000000000000000011110000
var B2 = 0xF0 >>> 0; // asm type annotation

// 12 => 0xC => 00000000000000000000000000001100
var B1 = 0xC >>> 0; // asm type annotation

// 2 => 0x2 => 00000000000000000000000000000010
var B0 = 0x2 >>> 0; // asm type annotation

// 16 => 00000000000000000000000000010000
var S4 = 16 >>> 0; // asm type annotation

// 8 => 00000000000000000000000000001000
var S3 = 8 >>> 0; // asm type annotation

// 4 => 00000000000000000000000000000100
var S2 = 4 >>> 0; // asm type annotation

// 2 => 00000000000000000000000000000010
var S1 = 2 >>> 0; // asm type annotation

// 1 => 00000000000000000000000000000001
var S0 = 1 >>> 0; // asm type annotation

// MAIN //

/**
* Computes an integer binary logarithm (base two).
*
* #### Method
*
* 1. Note that the largest unsigned 32-bit integer is `4294967295`, which is `2^{32}-1`. Hence, the integer binary logarithm cannot exceed `31` (i.e., `16 + 8 + 4 + 2 + 1`), which corresponds to the bit sequence
*
*   ``` binarystring
*   00000000000000000000000000011111
*   ```
*
* 2. Initialize a return variable with the value zero.
*
* 3. If at least one of the first sixteen most significant bits of the input 32-bit integer `x` is turned on, we know that the power to which the number `2` must be raised to obtain `x` is at least `16` (i.e., `x > 65536`). Hence, activate the corresponding bit of the return variable. Mutate `x` by shifting sixteen bits to the right, discarding the bits shifted off.
*
* 4. Carry out the following steps with `B` in `[ 8, 4, 2, 1 ]`:
*
*    - If at least one of the next `B` most significant bits of the current `x` is turned on, we know that the power to which the number `2` must be raised to obtain `x` has to be increased by `B`.
*    - Activate the bit of the return variable that corresponds to `B`.
*    - Mutate `x` by shifting `B` bits to the right, discarding the bits shifted off.
*
* 5. The final value of the return variable is the integer binary logarithm of `x`.
*
*
* @param {uinteger32} x - input value
* @returns {uinteger32} integer binary logarithm
*
* @example
* var v = log2( 4 );
* // returns 2
* @example
* var v = log2( 8 );
* // returns 3
* @example
* var v = log2( 9 );
* // returns 3
*/
function log2( x ) {
	var out = 0 >>> 0; // asm type annotation

	// `x >= 65536`:
	if ( x & B4 ) {
		x >>>= S4;
		out |= S4;
	}
	// `x >= 256`:
	if ( x & B3 ) {
		x >>>= S3;
		out |= S3;
	}
	// `x >= 16`:
	if ( x & B2 ) {
		x >>>= S2;
		out |= S2;
	}
	// `x >= 4`:
	if ( x & B1 ) {
		x >>>= S1;
		out |= S1;
	}
	// `x >= 2`:
	if ( x & B0 ) {
		x >>>= S0;
		out |= S0;
	}
	return out;
} // end FUNCTION log2()

// EXPORTS //

module.exports = log2;
<|endoftext|>


<commit_before><commit_msg>Fix missing throws annotation<commit_after><|endoftext|>


<commit_before>/**
 * conjoon
 * (c) 2002-2009 siteartwork.de/conjoon.org
 * licensing@conjoon.org
 *
 * $Author$
 * $Id$
 * $Date$
 * $Revision$
 * $LastChangedDate$
 * $LastChangedBy$
 * $URL$
 */

Ext.namespace('com.conjoon.groupware.email');

com.conjoon.groupware.email.EmailViewPanel = Ext.extend(Ext.Panel, {

    /**
     * @cfg {Object} viewConfig
     * Used only if no custom view is specified using the config.
     */
    viewConfig : {},

    /**
     * @cfg {Boolean} refreshFrame
     * Will totally rebuild the iframe when the panel gets hidden/shown for any
     * other browser than IE.
     */

    /**
     * @cfg {Boolean} autoLoad
     * Overrides completely the functionality of the parents implementation.
     * If autoLoad is true, the according message will be loaded immediately
     * after this component was rendered, otherwise a blank panel will be rendered.
     * Set this to <tt>false</tt> to load messages manually for this panel.
     */

    /**
     * @cfg {com.conjoon.groupware.email.EmailItemRecord} emailItem The email item this
     * panel represents. Will load the message body according to the record's id.
     * If provided, the title will be automatically set to the value of the record's
     * subject field.
     */

    /**
     * @param {com.conjoon.groupware.email._EmailView} view the view for this panel
     * to render the message
     */
    view : null,

    /**
     * @param {Object} requestId The id of the current ajax request in progress
     */
    requestId : null,

    /**
     * @param {Ext.data.JsonReader} reader The reader for the email message loaded
     */
    reader : null,

    /**
     * @param {Boolean} viewReady Flag to help determine wether the view is rendered
     * or not.
     */
    viewReady : false,

    /**
     * @param {com.conjoon.groupware.email.EmailRecord} emailRecord The last loaded
     * email record as specified in the id of emailItem
     */
    emailRecord : null,

    /**
     * @type {String} _orgIconCls stores the original icon class that was used so that
     * it can be restored with iconCls after the pending icon class was showed
     */
    _orgIconCls : null,

    initComponent : function()
    {
        this.addEvents(
            /**
             * Gets fired before the ajax request for this panel gets started.
             */
            'beforeemailload',
            /**
             * Gets fired before when loading the emails contents failed.
             */
            'emailloadfailure',
            /**
             * Gets fired when the ajax request for this panel successfully
             * loaded the email message which is about to be displayed.
             */
            'emailload'
        );

        if (this.emailItem) {
            this.title = this.autoLoad
                         ? ' '
                         : (this.emailItem.data.subject || ' ');
        }

        if (this.refreshFrame === true && !Ext.isIE) {
            this.on('hide', this._onHide, this);
            this.on('show', this._onShow, this);
        }

        this._orgIconCls = this.iconCls || 'com-conjoon-groupware-email-EmailView-Icon';
        this.iconCls     = this.autoLoad
                           ? 'com-conjoon-groupware-pending-icon'
                           : (this.iconCls || 'com-conjoon-groupware-email-EmailView-Icon');

        Ext.apply(this, {
            closable  : true,
            hideMode  : 'offsets'
            /**
             * @bug adding listeners via listeners property does not work in initComponent!!!
             */

        });

        Ext.ux.util.MessageBus.subscribe(
            'com.conjoon.groupware.email.editor.draftSave',
            this._onDraftSave,
            this
        );

        this.on('render',  com.conjoon.groupware.util.LinkInterceptor.getListener().render);
        this.on('destroy', this.abortRequest, this);

        com.conjoon.groupware.email.EmailViewPanel.superclass.initComponent.call(this);
    },

    getView : function()
    {
        if (!this.view) {
            this.view = new com.conjoon.groupware.email.view.DefaultViewRenderer(
                this.viewConfig
            );
        }

        return this.view;
    },

    /**
     * Renders the view to display the current message.
     *
     */
    renderView : function()
    {
        if (this.emailRecord != null && this.viewReady) {
            this.view.onEmailLoad(this.emailRecord);
        }
    },

    clearView : function()
    {
        if (this.viewReady) {
            this.view.clear();
        }

        if (this.requestId) {
            Ext.Ajax.abort(this.requestId);
            this.requestId = null;
        }
    },

    onRender : function(ct, position)
    {
        com.conjoon.groupware.email.EmailViewPanel.superclass.onRender.apply(this, arguments);

        var view = this.getView()
        view.init(this);

        this.view.render();

        this.viewReady = true;
    },

    // private
    onDestroy : function()
    {
        if(this.rendered){
            var c = this.body;
            c.removeAllListeners();
            this.view.destroy();
            c.update("");
        }

        com.conjoon.groupware.email.EmailViewPanel.superclass.onDestroy.call(this);
    },

    // private
    onResize : function()
    {
        com.conjoon.groupware.email.EmailViewPanel.superclass.onResize.apply(this, arguments);

        if(this.viewReady){
            this.view.layout();
        }
    },

    /**
     * Sets a new email item to for this panel to display
     *
     * @param {com.conjoon.groupware.email.EmailItemRecord} emailItem The
     * new emailItem this panel should represent
     * @param {Boolean} suspendAutoLoad If set to true, the according message will not be loaded
     *
     */
    setEmailItem : function(emailItem, suspendAutoLoad)
    {
        // check first if the requested email item is already being loaded
        if (emailItem && this.emailItem && this.requestId != null && emailItem.id == this.emailItem.id) {
            return;
        }

        this.emailItem = emailItem;

        if (emailItem != null)  {
            this.setTitle((emailItem.data.subject || ' '));
        } else {
            this.emailRecord = null;
            this.view.clear();
        }

        if (suspendAutoLoad === true) {
            return;
        }

        this.load();
    },

    /**
     * Loads an email message into this panel.
     *
     * @param {Number} id The id of the email to load. If not provided,
     * the id of the emailItem that was passed to the constriuctor will be used
     * instead
     */
    load : function(id)
    {
        if (id == undefined) {
            if (this.emailItem) {
                id = this.emailItem.id;
            } else {
                return;
            }
        }

        this.abortRequest();

        this.view.clear();

        if (this.fireEvent('beforeemailload', id) === false) {
            return;
        }

        this.setTitle(
            com.conjoon.Gettext.gettext("Loading...")
        );

        this.setIconClass('com-conjoon-groupware-pending-icon');

        this.emailRecord = null;

        this.requestId = Ext.Ajax.request({
            url            : './groupware/email/get.email/format/json',
            params         : {
                id  : id
            },
            success        : this.onEmailLoadSuccess,
            failure        : this.onEmailLoadFailure,
            scope          : this,
            disableCaching : true
        });
    },

    abortRequest : function()
    {
        if (this.requestId) {
            Ext.Ajax.abort(this.requestId);
        }
        this.requestId = null;
    },

    // private
    doAutoLoad : function()
    {
        this.load();
    },

    /**
     * Callback for successfully retrieving an email
     */
    onEmailLoadSuccess : function(response, parameters)
    {
        var data = com.conjoon.groupware.ResponseInspector.isSuccess(response);
        if (data === null || (data && !data.item)) {
            this.onEmailLoadFailure(response, parameters);
            return;
        }

        var record = com.conjoon.util.Record.convertTo(
            com.conjoon.groupware.email.EmailRecord,
            data.item,
            data.item.id
        );

        this.emailRecord = record;

        this.setTitle((record.data.subject || ' '));
        this.setIconClass(this._orgIconCls);
        this.renderView();

        this.fireEvent('emailload', record);

        Ext.ux.util.MessageBus.publish('com.conjoon.groupware.email.view.onEmailLoad', {
            emailRecord : record,
            panelId     : this.id
        });

        this.requestId = null;
    },

    /**
     * Callback for any error that occured during loading an email
     */
    onEmailLoadFailure : function(response, options)
    {
        this.fireEvent('emailloadfailure', response, options);

        Ext.ux.util.MessageBus.publish('com.conjoon.groupware.email.view.onEmailLoadFailure', {
            response : response,
            options  : options,
            panelId  : id
        });

        this.requestId = null;
    },

    _onDraftSave : function(subject, message)
    {
        var emailItem = message.itemRecord;

        if (!this.emailItem || emailItem.get('id') != this.emailItem.get('id')) {
            return;
        }

        this.setEmailItem(emailItem.copy(), true);
        this.emailRecord = message.emailRecord.copy();
        this.renderView();
    },

    _onHide : function()
    {
        if (this.viewReady) {
            this.view._removeIframe();
            this.view.clear();
            this.viewReady = false;
        }
    },

    _onShow : function()
    {
        if (!this.view) {
            return;
        }
        this.view._createIframe();
        this.viewReady = true;
        this.renderView();
    }

});

<commit_msg> - fixed: setting a new emailItem for the component would not abort a pending request<commit_after>/**
 * conjoon
 * (c) 2002-2009 siteartwork.de/conjoon.org
 * licensing@conjoon.org
 *
 * $Author$
 * $Id$
 * $Date$
 * $Revision$
 * $LastChangedDate$
 * $LastChangedBy$
 * $URL$
 */

Ext.namespace('com.conjoon.groupware.email');

com.conjoon.groupware.email.EmailViewPanel = Ext.extend(Ext.Panel, {

    /**
     * @cfg {Object} viewConfig
     * Used only if no custom view is specified using the config.
     */
    viewConfig : {},

    /**
     * @cfg {Boolean} refreshFrame
     * Will totally rebuild the iframe when the panel gets hidden/shown for any
     * other browser than IE.
     */

    /**
     * @cfg {Boolean} autoLoad
     * Overrides completely the functionality of the parents implementation.
     * If autoLoad is true, the according message will be loaded immediately
     * after this component was rendered, otherwise a blank panel will be rendered.
     * Set this to <tt>false</tt> to load messages manually for this panel.
     */

    /**
     * @cfg {com.conjoon.groupware.email.EmailItemRecord} emailItem The email item this
     * panel represents. Will load the message body according to the record's id.
     * If provided, the title will be automatically set to the value of the record's
     * subject field.
     */

    /**
     * @param {com.conjoon.groupware.email._EmailView} view the view for this panel
     * to render the message
     */
    view : null,

    /**
     * @param {Object} requestId The id of the current ajax request in progress
     */
    requestId : null,

    /**
     * @param {Ext.data.JsonReader} reader The reader for the email message loaded
     */
    reader : null,

    /**
     * @param {Boolean} viewReady Flag to help determine wether the view is rendered
     * or not.
     */
    viewReady : false,

    /**
     * @param {com.conjoon.groupware.email.EmailRecord} emailRecord The last loaded
     * email record as specified in the id of emailItem
     */
    emailRecord : null,

    /**
     * @type {String} _orgIconCls stores the original icon class that was used so that
     * it can be restored with iconCls after the pending icon class was showed
     */
    _orgIconCls : null,

    initComponent : function()
    {
        this.addEvents(
            /**
             * Gets fired before the ajax request for this panel gets started.
             */
            'beforeemailload',
            /**
             * Gets fired before when loading the emails contents failed.
             */
            'emailloadfailure',
            /**
             * Gets fired when the ajax request for this panel successfully
             * loaded the email message which is about to be displayed.
             */
            'emailload'
        );

        if (this.emailItem) {
            this.title = this.autoLoad
                         ? ' '
                         : (this.emailItem.data.subject || ' ');
        }

        if (this.refreshFrame === true && !Ext.isIE) {
            this.on('hide', this._onHide, this);
            this.on('show', this._onShow, this);
        }

        this._orgIconCls = this.iconCls || 'com-conjoon-groupware-email-EmailView-Icon';
        this.iconCls     = this.autoLoad
                           ? 'com-conjoon-groupware-pending-icon'
                           : (this.iconCls || 'com-conjoon-groupware-email-EmailView-Icon');

        Ext.apply(this, {
            closable  : true,
            hideMode  : 'offsets'
            /**
             * @bug adding listeners via listeners property does not work in initComponent!!!
             */

        });

        Ext.ux.util.MessageBus.subscribe(
            'com.conjoon.groupware.email.editor.draftSave',
            this._onDraftSave,
            this
        );

        this.on('render',  com.conjoon.groupware.util.LinkInterceptor.getListener().render);
        this.on('destroy', this.abortRequest, this);

        com.conjoon.groupware.email.EmailViewPanel.superclass.initComponent.call(this);
    },

    getView : function()
    {
        if (!this.view) {
            this.view = new com.conjoon.groupware.email.view.DefaultViewRenderer(
                this.viewConfig
            );
        }

        return this.view;
    },

    /**
     * Renders the view to display the current message.
     *
     */
    renderView : function()
    {
        if (this.emailRecord != null && this.viewReady) {
            this.view.onEmailLoad(this.emailRecord);
        }
    },

    clearView : function()
    {
        if (this.viewReady) {
            this.view.clear();
        }

        if (this.requestId) {
            Ext.Ajax.abort(this.requestId);
            this.requestId = null;
        }
    },

    onRender : function(ct, position)
    {
        com.conjoon.groupware.email.EmailViewPanel.superclass.onRender.apply(this, arguments);

        var view = this.getView()
        view.init(this);

        this.view.render();

        this.viewReady = true;
    },

    // private
    onDestroy : function()
    {
        if(this.rendered){
            var c = this.body;
            c.removeAllListeners();
            this.view.destroy();
            c.update("");
        }

        com.conjoon.groupware.email.EmailViewPanel.superclass.onDestroy.call(this);
    },

    // private
    onResize : function()
    {
        com.conjoon.groupware.email.EmailViewPanel.superclass.onResize.apply(this, arguments);

        if(this.viewReady){
            this.view.layout();
        }
    },

    /**
     * Sets a new email item to for this panel to display
     *
     * @param {com.conjoon.groupware.email.EmailItemRecord} emailItem The
     * new emailItem this panel should represent
     * @param {Boolean} suspendAutoLoad If set to true, the according message will not be loaded
     *
     */
    setEmailItem : function(emailItem, suspendAutoLoad)
    {
        // check first if the requested email item is already being loaded
        if (emailItem && this.emailItem && this.requestId != null && emailItem.id == this.emailItem.id) {
            return;
        }

        this.emailItem = emailItem;

        if (emailItem != null)  {
            this.setTitle((emailItem.data.subject || ' '));
        } else {
            this.emailRecord = null;
            this.view.clear();
        }

        this.abortRequest();

        if (suspendAutoLoad === true) {
            return;
        }

        this.load();
    },

    /**
     * Loads an email message into this panel.
     *
     * @param {Number} id The id of the email to load. If not provided,
     * the id of the emailItem that was passed to the constriuctor will be used
     * instead
     */
    load : function(id)
    {
        if (id == undefined) {
            if (this.emailItem) {
                id = this.emailItem.id;
            } else {
                return;
            }
        }

        this.abortRequest();

        this.view.clear();

        if (this.fireEvent('beforeemailload', id) === false) {
            return;
        }

        this.setTitle(
            com.conjoon.Gettext.gettext("Loading...")
        );

        this.setIconClass('com-conjoon-groupware-pending-icon');

        this.emailRecord = null;

        this.requestId = Ext.Ajax.request({
            url            : './groupware/email/get.email/format/json',
            params         : {
                id  : id
            },
            success        : this.onEmailLoadSuccess,
            failure        : this.onEmailLoadFailure,
            scope          : this,
            disableCaching : true
        });
    },

    abortRequest : function()
    {
        if (this.requestId) {
            Ext.Ajax.abort(this.requestId);
        }
        this.requestId = null;
    },

    // private
    doAutoLoad : function()
    {
        this.load();
    },

    /**
     * Callback for successfully retrieving an email
     */
    onEmailLoadSuccess : function(response, parameters)
    {
        var data = com.conjoon.groupware.ResponseInspector.isSuccess(response);
        if (data === null || (data && !data.item)) {
            this.onEmailLoadFailure(response, parameters);
            return;
        }

        var record = com.conjoon.util.Record.convertTo(
            com.conjoon.groupware.email.EmailRecord,
            data.item,
            data.item.id
        );

        this.emailRecord = record;

        this.setTitle((record.data.subject || ' '));
        this.setIconClass(this._orgIconCls);
        this.renderView();

        this.fireEvent('emailload', record);

        Ext.ux.util.MessageBus.publish('com.conjoon.groupware.email.view.onEmailLoad', {
            emailRecord : record,
            panelId     : this.id
        });

        this.requestId = null;
    },

    /**
     * Callback for any error that occured during loading an email
     */
    onEmailLoadFailure : function(response, options)
    {
        this.fireEvent('emailloadfailure', response, options);

        Ext.ux.util.MessageBus.publish('com.conjoon.groupware.email.view.onEmailLoadFailure', {
            response : response,
            options  : options,
            panelId  : id
        });

        this.requestId = null;
    },

    _onDraftSave : function(subject, message)
    {
        var emailItem = message.itemRecord;

        if (!this.emailItem || emailItem.get('id') != this.emailItem.get('id')) {
            return;
        }

        this.setEmailItem(emailItem.copy(), true);
        this.emailRecord = message.emailRecord.copy();
        this.renderView();
    },

    _onHide : function()
    {
        if (this.viewReady) {
            this.view._removeIframe();
            this.view.clear();
            this.viewReady = false;
        }
    },

    _onShow : function()
    {
        if (!this.view) {
            return;
        }
        this.view._createIframe();
        this.viewReady = true;
        this.renderView();
    }

});

<|endoftext|>


<commit_before>if (!window.console) {
	window.console = {
		log: function() {},
		warn: function() {},
		error: function() {},
	};
} else {
	var debug = Spontaneous.debug;
	window.__console = window.console;
	window.console = {
		log: function() {
			if (debug) { window.__console.log.apply(window.__console, arguments); }
		},
		warn: function() {
			if (debug) { window.__console.warn.apply(window.__console, arguments); }
		},
		error: function() {
			if (debug) { window.__console.error.apply(window.__console, arguments); }
		}
	};
}

console.log('Loading Extensions...')

// Thank you Prototype
function $A(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
};

jQuery.extend(Function.prototype, {
	bind: function() {
		var __method = this, args = $A(arguments), object = args.shift();
		return function() {
      return __method.apply(object, args.concat($A(arguments)));
    };
	}
});

<commit_msg>removed console overrides to re-enable logging of file & linenumber of log command<commit_after>if (!window.console) {
	window.console = {
		log: function() {},
		warn: function() {},
		error: function() {},
	};
} else {
	// var debug = Spontaneous.debug;
	// window.__console = window.console;
	// window.console = {
	// 	log: function() {
	// 		if (debug) { window.__console.log.apply(window.__console, arguments); }
	// 	},
	// 	warn: function() {
	// 		if (debug) { window.__console.warn.apply(window.__console, arguments); }
	// 	},
	// 	error: function() {
	// 		if (debug) { window.__console.error.apply(window.__console, arguments); }
	// 	}
	// };
}

console.log('Loading Extensions...')

// Thank you Prototype
function $A(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
};

jQuery.extend(Function.prototype, {
	bind: function() {
		var __method = this, args = $A(arguments), object = args.shift();
		return function() {
      return __method.apply(object, args.concat($A(arguments)));
    };
	}
});

<|endoftext|>


<commit_before>/*
Exercise 4.1: The sum of a range

The introduction of this book alluded to the following as a nice way to compute the sum of a range of numbers:

console.log(sum(range(1, 10)));

Write a range function that takes two arguments, start and end, and returns an array containing all the numbers from start up to (and including) end.

Next, write a sum function that takes an array of numbers and returns the sum of these numbers. Run the previous program and see whether it does indeed return 55.

As a bonus assignment, modify your range function to take an optional third argument that indicates the “step” value used to build up the array. If no step is given, the array elements go up by increments of one, corresponding to the old behavior. The function call range(1, 10, 2) should return [1, 3, 5, 7, 9]. Make sure it also works with negative step values so that range(5, 2, -1) produces [5, 4, 3, 2].

console.log(range(1, 10));
// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(range(5, 2, -1));
// → [5, 4, 3, 2]
console.log(sum(range(1, 10)));
// → 55

Hints:

Building up an array is most easily done by first initializing a variable to [] (a fresh, empty array) and repeatedly calling its push method to add a value. Don’t forget to return the array at the end of the function.

Since the end boundary is inclusive, you’ll need to use the <= operator rather than simply < to check for the end of your loop.

To check whether the optional step argument was given, either check arguments.length or compare the value of the argument to undefined. If it wasn’t given, simply set it to its default value (1) at the top of the function.

Having range understand negative step values is probably best done by writing two separate loops—one for counting up and one for counting down—because the comparison that checks whether the loop is finished needs to be >= rather than <= when counting downward.

It might also be worthwhile to use a different default step, namely, -1, when the end of the range is smaller than the start. That way, range(5, 2) returns something meaningful, rather than getting stuck in an infinite loop.
*/

// Part 1:
function range(start, end) {
  var arr = [];
  for (var i = start; i <= end; i++)
    arr.push(i);

  return arr;
}

// Part 2:
function sum(array) {
  var total = 0;
  for (var i = 0; i < array.length; i++)
    total += array[i];
  return total;
}

console.log(range(1, 10)); // → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(sum(range(1, 10))); // → 55
<commit_msg>Added part 3 solution for prob4.1SumOfRange.js<commit_after>/*
Exercise 4.1: The sum of a range

The introduction of this book alluded to the following as a nice way to compute the sum of a range of numbers:

console.log(sum(range(1, 10)));

Write a range function that takes two arguments, start and end, and returns an array containing all the numbers from start up to (and including) end.

Next, write a sum function that takes an array of numbers and returns the sum of these numbers. Run the previous program and see whether it does indeed return 55.

As a bonus assignment, modify your range function to take an optional third argument that indicates the “step” value used to build up the array. If no step is given, the array elements go up by increments of one, corresponding to the old behavior. The function call range(1, 10, 2) should return [1, 3, 5, 7, 9]. Make sure it also works with negative step values so that range(5, 2, -1) produces [5, 4, 3, 2].

console.log(range(1, 10));
// → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(range(5, 2, -1));
// → [5, 4, 3, 2]
console.log(sum(range(1, 10)));
// → 55

Hints:

Building up an array is most easily done by first initializing a variable to [] (a fresh, empty array) and repeatedly calling its push method to add a value. Don’t forget to return the array at the end of the function.

Since the end boundary is inclusive, you’ll need to use the <= operator rather than simply < to check for the end of your loop.

To check whether the optional step argument was given, either check arguments.length or compare the value of the argument to undefined. If it wasn’t given, simply set it to its default value (1) at the top of the function.

Having range understand negative step values is probably best done by writing two separate loops—one for counting up and one for counting down—because the comparison that checks whether the loop is finished needs to be >= rather than <= when counting downward.

It might also be worthwhile to use a different default step, namely, -1, when the end of the range is smaller than the start. That way, range(5, 2) returns something meaningful, rather than getting stuck in an infinite loop.
*/

// Part 1:
function range(start, end) {
  var arr = [];
  for (var i = start; i <= end; i++)
    arr.push(i);

  return arr;
}

// Part 2:
function sum(array) {
  var total = 0;
  for (var i = 0; i < array.length; i++)
    total += array[i];
  return total;
}

// Part 3:
function range(start, end, step) {
  if (step == null) step = 1;
  var array = [];

  if (step > 0) {
    for (var i = start; i <= end; i += step)
      array.push(i);
  } else {
    for (var i = start; i >= end; i += step)
      array.push(i);
  }
  return array;
}

console.log(range(1, 10)); // → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(sum(range(1, 10))); // → 55
console.log(range(5, 2, -1)); // → [5, 4, 3, 2]
<|endoftext|>


<commit_before>function arrayToList(arr) {
    var list = null;
    for (var i = arr.length - 1; i >= 0; --i)
        list = { value: arr[i], rest: list };
    return list;
}

function listToArray(list) {
    var arr = [];
    var myList = list;
    while (myList) {            // More elegant: for (var node = list; node; node = node.rest)
        arr.push(myList.value);
        myList = myList.rest;
    }
    return arr;
}

function prepend(num, list) {
    return { value: num, rest: list };
}

function nth(list, num) {
    var node = list;
    for (var i = 0; i < num; ++i) {
        if (! node.rest)
            return undefined;
        node = node.rest;
    }
    return node.value;
}

function nthRecurse(list, num) {
    if (! list)
        return undefined;
    if (num === 0)
        return list.value;
    return nthRecurse(list.rest, num - 1);
}
<commit_msg>Add Chapter 04, exercise 3<commit_after>function arrayToList(array) {
    let list = null;
    for (let i = array.length - 1; i >= 0; --i) {
        list = {value: array[i], rest: list};
    }
    return list;
}

function listToArray(list) {
    let array = [];
    for (let node = list; node; node = node.rest) {
        array.push(node.value);
    }
    return array;
}

function prepend(element, list) {
    return arrayToList([element, ...listToArray(list)]);
}

function nth(list, n) {
    if (n === 0) return list.value;
    else return list.rest ? nth(list.rest, n-1) : null;
}
<|endoftext|>


<commit_before>function skipSpace(string) {
    let toRemove = string.match(/(?:\s|#.*?\n)*/);
    return string.slice(toRemove[0].length);
}
<commit_msg>Anchor at start, realize that . does not match \n<commit_after>function skipSpace(string) {
    let toRemove = string.match(/^(?:\s|#.*)*/);
    return string.slice(toRemove[0].length);
}
<|endoftext|>


<commit_before>'use strict';

let db = requireDb();
let userInfo = require('./../userInfo');
let pinwallFilter = require('./pinwallFilter');
let pinwallElement = require('./pinwallElement/pinwallElement');
let pinwallSelector = require('./pinwallSelector');
let contacting = require('./contacting');
let recommendedUser = require('./recommendedUser');
let recommendedUserSetting = require('./../setting/recommendedUser');
let logger = require('elyoos-server-lib').logging.getLogger(__filename);

let getRecommendationOfUser = function (userId, request) {
    return db.cypher().match("(user:User {userId: {userId}})-[:RECOMMENDS]->(pinwall:Recommendation)-[:RECOMMENDS]->(pinwallData)")
        .optionalMatch("(pinwallData)<-[:WRITTEN]-(writer:User)")
        .return(`user, pinwall, pinwallData, LABELS(pinwall) AS pinwallType, writer, true AS recommendedByUser, true AS thisRecommendationByUser,
                 pinwall.recommendationId AS userRecommendationId, NOT EXISTS(pinwall.visible) AS isPublic,
                 EXISTS((user)-[:IS_ADMIN]->(pinwallData)) AS isAdmin,
                 SIZE((pinwallData)<-[:RECOMMENDS]-(:Recommendation)) AS numberOfPinwallDataRecommendations`)
        .orderBy("pinwall.created DESC")
        .skip("{skip}")
        .limit("{maxItems}")
        .end({userId: userId, skip: request.skip, maxItems: request.maxItems})
        .send()
        .then(function (resp) {
            return {pinwall: pinwallElement.getPinwallElements(resp)};
        });
};

let getPagesOfUserOrder = function (type) {
    if(type === 'adminPopular') {
        return "totalNumberOfRecommendations DESC";
    }
    return "page.created DESC";
};

let getPagesOfUser = function (userId, request) {
    return db.cypher().match("(user:User {userId: {userId}})-[:IS_ADMIN|WRITTEN]->(page:Page)")
        .optionalMatch("(user)-[:RECOMMENDS]->(recommendation:Recommendation)-[:RECOMMENDS]->(page)")
        .return(`user, page, LABELS(page) AS pinwallType, true AS isAdminType, NOT EXISTS(page.visible) AS isPublic,
                 recommendation.recommendationId AS userRecommendationId,
                 SIZE((page)<-[:RECOMMENDS]-(:Recommendation)) AS totalNumberOfRecommendations`)
        .orderBy(getPagesOfUserOrder(request.type))
        .skip("{skip}")
        .limit("{maxItems}")
        .end({userId: userId, skip: request.skip, maxItems: request.maxItems})
        .send()
        .then(function (resp) {
            return {pinwall: pinwallElement.getPinwallElements(resp)};
        });
};

let getPinwallOfDetailUser = function (userId, request) {

    return db.cypher().match("(user:User {userId: {userId}}), " +
        "(privacyNoContact:Privacy)<-[:HAS_PRIVACY_NO_CONTACT]-(otherUser:User {userId: {detailUserId}})" +
        "-[:WRITTEN|:RECOMMENDS]->(pinwall:PinwallElement)")
        .optionalMatch("(pinwall)-[:PINWALL_DATA]->(pinwallData)")
        .optionalMatch("(user)<-[isContact:IS_CONTACT]-(otherUser)-[relPrivacy:HAS_PRIVACY]->(privacy:Privacy)")
        .where("isContact.type = relPrivacy.type")
        .optionalMatch("(pinwallData)<-[:WRITTEN]-(writer:User)")
        .where("pinwallData:Blog")
        .optionalMatch("(writer)-[writerContact:IS_CONTACT]->(user)")
        .optionalMatch("(user)-[:RECOMMENDS]->(userRec:Recommendation)-[:RECOMMENDS]->(pinwallData)")
        .optionalMatch("(user)-[:RECOMMENDS]->(userRecBlog:Recommendation)-[:RECOMMENDS]->(pinwall)")
        .where("pinwall:Blog")
        .with(`user, pinwall, pinwallData, otherUser, isContact, privacy, privacyNoContact, false AS isAdmin, userRec, userRecBlog, writer,
               writerContact`)
        .where(`(NOT EXISTS(pinwall.visible) AND ANY(l IN LABELS(pinwall) WHERE l = 'Blog'))
                 OR (ANY(v IN pinwall.visible WHERE v = isContact.type) AND ANY(l IN LABELS(pinwall) WHERE l = 'Blog'))
                 OR ((NONE(l IN LABELS(pinwall) WHERE l = 'Blog') AND
                (writer is null OR (writer is not null AND
                (NOT EXISTS(pinwallData.visible) OR (ANY(v IN pinwallData.visible WHERE v = writerContact.type)) OR writer.userId = user.userId))) AND
                (privacy.pinwall = true OR (NOT (otherUser)-[:IS_CONTACT]->(user) AND privacyNoContact.pinwall = true)) AND
                (privacy.profile = true OR (NOT (otherUser)-[:IS_CONTACT]->(user) AND privacyNoContact.profile = true))))
                 AND NOT (otherUser)-[:IS_BLOCKED]->(user)`)
        .return(`user, pinwall, pinwallData, otherUser AS contact, LABELS(pinwall) AS pinwallType, privacy, privacyNoContact, writer,
                 NOT EXISTS(pinwall.visible) AS isPublic , isAdmin, userRec.recommendationId AS userRecommendationId,
                 userRecBlog.recommendationId AS userBlogRecommendationId,
                 EXISTS((user)-[:RECOMMENDS]->(pinwall)) AS thisRecommendationByUser,
                 SIZE((pinwallData)<-[:RECOMMENDS]-(:Recommendation)) AS numberOfPinwallDataRecommendations,
                 SIZE((pinwall)<-[:RECOMMENDS]-(:Recommendation)) AS numberOfPinwallRecommendations`)
        .orderBy("pinwall.created DESC")
        .skip("{skip}")
        .limit("{maxItems}")
        .end({userId: userId, detailUserId: request.userId, skip: request.skip, maxItems: request.maxItems})
        .send()
        .then(function (resp) {
            return {pinwall: pinwallElement.getPinwallElements(resp)};
        });
};

let getBlogOnlyContactFilter = function (onlyContact) {
    if (onlyContact) {
        return "(user:User {userId: {userId}})-[:IS_CONTACT|:WRITTEN*1..2]->(pinwall:Blog)";
    }
    return "(user:User {userId: {userId}}), (contact:User)-[:WRITTEN]->(pinwall:Blog)";
};

let getBlogs = function (userId, request) {
    let filters = pinwallFilter.getFilters(request, 'pinwall');
    return db.cypher().match(getBlogOnlyContactFilter(request.onlyContact))
        .where(filters)
        .optionalMatch("(user)-[hasContact:IS_CONTACT]->(contact)-[:WRITTEN]->(pinwall)")
        .optionalMatch("(user)<-[isContact:IS_CONTACT]-(contact)-[relPrivacy:HAS_PRIVACY]->(privacy:Privacy)")
        .where("isContact.type = relPrivacy.type")
        .optionalMatch("(contact)-[:HAS_PRIVACY_NO_CONTACT]->(privacyNoContact:Privacy)")
        .where("privacy is NULL")
        .optionalMatch("(pinwall)<-[:RECOMMENDS]-(recommendation:Recommendation)")
        .with(`user, contact, pinwall, isContact, hasContact, privacy, privacyNoContact, count(recommendation) AS totalNumberOfRecommendations,
               EXISTS((user)-[:WRITTEN]->(pinwall)) AS isAdmin`)
        .where(`((user)-[:WRITTEN]->(pinwall) OR (contact IS NOT null AND NOT EXISTS(pinwall.visible)) OR
                (contact IS NOT null AND ANY(v IN pinwall.visible WHERE v = isContact.type))) AND NOT (contact)-[:IS_BLOCKED]->(user)`)
        .optionalMatch("(user)-[:RECOMMENDS]->(userRec:Recommendation)-[:RECOMMENDS]->(pinwall)")
        .return(`user, totalNumberOfRecommendations, pinwall, contact, LABELS(pinwall) AS pinwallType, privacy, privacyNoContact, isAdmin,
                 NOT EXISTS(pinwall.visible) AS isPublic, userRec.recommendationId AS userRecommendationId,
                 EXISTS((user)-[:RECOMMENDS]->(:Recommendation)-[:RECOMMENDS]->(pinwall)) AS recommendedByUser`)
        .orderBy("pinwall.created DESC")
        .skip("{skip}")
        .limit("{maxItems}")
        .end({
            userId: userId, skip: request.skipBlog, maxItems: request.maxItems, language: request.language, topic: request.topic,
            recommendationType: request.recommendationType
        });
};

let getRecommendationPrivacyString = function (withCondition) {
    return db.cypher().optionalMatch("(user)-[hasContact:IS_CONTACT]->(contact)-[:RECOMMENDS]->(pinwall)")
        .optionalMatch("(user)<-[isContact:IS_CONTACT]-(contact)-[relPrivacy:HAS_PRIVACY]->(privacy:Privacy)")
        .where("isContact.type = relPrivacy.type")
        .optionalMatch("(contact)-[:HAS_PRIVACY_NO_CONTACT]->(privacyNoContact:Privacy)")
        .where("isContact is NULL")
        .with("user, contact, pinwall, pinwallData, isContact, hasContact, privacy, privacyNoContact" + withCondition)
        .where(`(user)-[:RECOMMENDS]->(pinwall) OR
                ((privacy.pinwall = true OR (privacy is null AND privacyNoContact.pinwall = true)) AND
                (privacy.profile = true OR (privacy is null AND privacyNoContact.profile = true)) AND NOT (contact)-[:IS_BLOCKED]->(user))`)
        .getCommandString();
};

let getBlogRecommendationPrivacyString = function () {
    return db.cypher().optionalMatch("(pinwallData)<-[:WRITTEN]-(writer:User)")
        .where("pinwallData:Blog")
        .optionalMatch("(writer)-[writerContact:IS_CONTACT]->(user)")
        .with(`user, contact, pinwall, pinwallData, isContact, hasContact, privacy, privacyNoContact, numberOfRecommendations, writer, writerContact`)
        .where(`writer is null OR (writer is not null AND
               (NOT EXISTS(pinwallData.visible) OR (ANY(v IN pinwallData.visible WHERE v = writerContact.type)) OR writer.userId = user.userId)
                AND NOT EXISTS((writer)-[:IS_BLOCKED]->(user)))`)
        .getCommandString();
};

let getRecommendationOnlyContactFilter = function (onlyContact) {
    if (onlyContact) {
        return "(user:User {userId: {userId}})-[:IS_CONTACT|:RECOMMENDS*1..2]->(pinwall:Recommendation)-[:PINWALL_DATA]->(pinwallData)";
    }
    return "(user:User {userId: {userId}}), (contact:User)-[:RECOMMENDS]->(pinwall:Recommendation)-[:PINWALL_DATA]->(pinwallData)";
};

let getRecommendationOrderFilter = function (order) {
    if (order === 'popular') {
        return "numberOfRecommendations DESC, created DESC";
    }
    return "created DESC";
};

let getFilterWithCreatedCondition = function (filters) {
    if (filters) {
        return `pinwall.created = created AND ${filters}`;
    }
    return `pinwall.created = created`;
};

let getRecommendations = function (userId, request) {
    let filters = pinwallFilter.getFilters(request, 'pinwallData');
    return db.cypher().match(getRecommendationOnlyContactFilter(request.onlyContact))
        .where(filters)
        .addCommand(getRecommendationPrivacyString(''))
        .with("count(pinwall) AS numberOfRecommendations, max(pinwall.created) AS created, pinwallData")
        .orderBy(getRecommendationOrderFilter(request.order))
        .skip("{skip}")
        .limit("{maxItems}")
        .match(getRecommendationOnlyContactFilter(request.onlyContact))
        .where(getFilterWithCreatedCondition(filters))
        .addCommand(getRecommendationPrivacyString(', numberOfRecommendations '))
        .addCommand(getBlogRecommendationPrivacyString())
        .optionalMatch("(user)-[:RECOMMENDS]->(userRec:Recommendation)-[:RECOMMENDS]->(pinwallData)")
        .return(`user, pinwall, pinwallData, contact, LABELS(pinwall) AS pinwallType, privacy, privacyNoContact, numberOfRecommendations, writer,
            userRec.recommendationId AS userRecommendationId,
            EXISTS((user)-[:RECOMMENDS]->(pinwall)) AS thisRecommendationByUser,
            SIZE((pinwallData)<-[:RECOMMENDS]-(:Recommendation)) AS totalNumberOfRecommendations`)
        .end({
            userId: userId, skip: request.skipRecommendation, maxItems: request.maxItems, language: request.language, topic: request.topic,
            recommendationType: request.recommendationType
        });
};

let sortPinwall = function (resp, skipRecommendation, skipBlog, maxItems, order, showUserRecommendation, request) {
    let pinwall;
    if (showUserRecommendation && order === 'popular') {
        pinwall = pinwallSelector.sortPinwall(null, resp[4], skipRecommendation, skipBlog, maxItems);
    } else if (showUserRecommendation) {
        pinwall = pinwallSelector.sortPinwall(resp[4], resp[5], skipRecommendation, skipBlog, maxItems);
    } else if (!showUserRecommendation && order === 'popular') {
        pinwall = pinwallSelector.sortPinwall(null, resp[2], skipRecommendation, skipBlog, maxItems);
    } else if (!showUserRecommendation) {
        pinwall = pinwallSelector.sortPinwall(resp[2], resp[3], skipRecommendation, skipBlog, maxItems);
    } else {
        logger.error(`Error sorting pinwall (Order:${order}, showUserRecommendation: ${showUserRecommendation})`, request);
    }
    return pinwall;
};

let getPinwall = function (userId, request) {
    let commands = [];

    return recommendedUserSetting.showUserRecommendationOnHome(userId).then(function (showUserRecommendation) {
        commands.push(contacting.getContacting(userId).getCommand());
        commands.push(contacting.getNumberOfContacting(userId).getCommand());
        if (showUserRecommendation) {
            commands.push(recommendedUser.getRecommendedByContactUsers(userId, 10).getCommand());
            commands.push(recommendedUser.getRecommendedUsers(userId, 10).getCommand());
        }
        if (request.order !== 'popular') {
            commands.push(getBlogs(userId, request).getCommand());
        }

        return getRecommendations(userId, request)
            .send(commands)
            .then(function (resp) {
                let pinwall, recommendedUserResult = [];
                userInfo.addImageForThumbnail(resp[0]);
                if (showUserRecommendation) {
                    userInfo.addImageForThumbnail(resp[2]);
                    userInfo.addImageForThumbnail(resp[3]);
                    recommendedUserResult = resp[2].concat(resp[3]);
                }
                pinwall = sortPinwall(resp, request.skipRecommendation, request.skipBlog, request.maxItems, request.order, showUserRecommendation);

                return {
                    contacting: {users: resp[0], numberOfContacting: resp[1][0].numberOfContacting},
                    recommendedUser: recommendedUserResult,
                    pinwall: pinwallElement.getPinwallElements(pinwall.pinwall),
                    skipRecommendation: pinwall.skipRecommendation,
                    skipBlog: pinwall.skipBlog
                };
            });
    });
};

module.exports = {
    getPinwall: getPinwall,
    getRecommendationOfUser: getRecommendationOfUser,
    getPagesOfUser: getPagesOfUser,
    getPinwallOfDetailUser: getPinwallOfDetailUser
};
<commit_msg>Second order added "page.created DESC" for adminPopular<commit_after>'use strict';

let db = requireDb();
let userInfo = require('./../userInfo');
let pinwallFilter = require('./pinwallFilter');
let pinwallElement = require('./pinwallElement/pinwallElement');
let pinwallSelector = require('./pinwallSelector');
let contacting = require('./contacting');
let recommendedUser = require('./recommendedUser');
let recommendedUserSetting = require('./../setting/recommendedUser');
let logger = require('elyoos-server-lib').logging.getLogger(__filename);

let getRecommendationOfUser = function (userId, request) {
    return db.cypher().match("(user:User {userId: {userId}})-[:RECOMMENDS]->(pinwall:Recommendation)-[:RECOMMENDS]->(pinwallData)")
        .optionalMatch("(pinwallData)<-[:WRITTEN]-(writer:User)")
        .return(`user, pinwall, pinwallData, LABELS(pinwall) AS pinwallType, writer, true AS recommendedByUser, true AS thisRecommendationByUser,
                 pinwall.recommendationId AS userRecommendationId, NOT EXISTS(pinwall.visible) AS isPublic,
                 EXISTS((user)-[:IS_ADMIN]->(pinwallData)) AS isAdmin,
                 SIZE((pinwallData)<-[:RECOMMENDS]-(:Recommendation)) AS numberOfPinwallDataRecommendations`)
        .orderBy("pinwall.created DESC")
        .skip("{skip}")
        .limit("{maxItems}")
        .end({userId: userId, skip: request.skip, maxItems: request.maxItems})
        .send()
        .then(function (resp) {
            return {pinwall: pinwallElement.getPinwallElements(resp)};
        });
};

let getPagesOfUserOrder = function (type) {
    if(type === 'adminPopular') {
        return "totalNumberOfRecommendations DESC, page.created DESC";
    }
    return "page.created DESC";
};

let getPagesOfUser = function (userId, request) {
    return db.cypher().match("(user:User {userId: {userId}})-[:IS_ADMIN|WRITTEN]->(page:Page)")
        .optionalMatch("(user)-[:RECOMMENDS]->(recommendation:Recommendation)-[:RECOMMENDS]->(page)")
        .return(`user, page, LABELS(page) AS pinwallType, true AS isAdminType, NOT EXISTS(page.visible) AS isPublic,
                 recommendation.recommendationId AS userRecommendationId,
                 SIZE((page)<-[:RECOMMENDS]-(:Recommendation)) AS totalNumberOfRecommendations`)
        .orderBy(getPagesOfUserOrder(request.type))
        .skip("{skip}")
        .limit("{maxItems}")
        .end({userId: userId, skip: request.skip, maxItems: request.maxItems})
        .send()
        .then(function (resp) {
            return {pinwall: pinwallElement.getPinwallElements(resp)};
        });
};

let getPinwallOfDetailUser = function (userId, request) {

    return db.cypher().match("(user:User {userId: {userId}}), " +
        "(privacyNoContact:Privacy)<-[:HAS_PRIVACY_NO_CONTACT]-(otherUser:User {userId: {detailUserId}})" +
        "-[:WRITTEN|:RECOMMENDS]->(pinwall:PinwallElement)")
        .optionalMatch("(pinwall)-[:PINWALL_DATA]->(pinwallData)")
        .optionalMatch("(user)<-[isContact:IS_CONTACT]-(otherUser)-[relPrivacy:HAS_PRIVACY]->(privacy:Privacy)")
        .where("isContact.type = relPrivacy.type")
        .optionalMatch("(pinwallData)<-[:WRITTEN]-(writer:User)")
        .where("pinwallData:Blog")
        .optionalMatch("(writer)-[writerContact:IS_CONTACT]->(user)")
        .optionalMatch("(user)-[:RECOMMENDS]->(userRec:Recommendation)-[:RECOMMENDS]->(pinwallData)")
        .optionalMatch("(user)-[:RECOMMENDS]->(userRecBlog:Recommendation)-[:RECOMMENDS]->(pinwall)")
        .where("pinwall:Blog")
        .with(`user, pinwall, pinwallData, otherUser, isContact, privacy, privacyNoContact, false AS isAdmin, userRec, userRecBlog, writer,
               writerContact`)
        .where(`(NOT EXISTS(pinwall.visible) AND ANY(l IN LABELS(pinwall) WHERE l = 'Blog'))
                 OR (ANY(v IN pinwall.visible WHERE v = isContact.type) AND ANY(l IN LABELS(pinwall) WHERE l = 'Blog'))
                 OR ((NONE(l IN LABELS(pinwall) WHERE l = 'Blog') AND
                (writer is null OR (writer is not null AND
                (NOT EXISTS(pinwallData.visible) OR (ANY(v IN pinwallData.visible WHERE v = writerContact.type)) OR writer.userId = user.userId))) AND
                (privacy.pinwall = true OR (NOT (otherUser)-[:IS_CONTACT]->(user) AND privacyNoContact.pinwall = true)) AND
                (privacy.profile = true OR (NOT (otherUser)-[:IS_CONTACT]->(user) AND privacyNoContact.profile = true))))
                 AND NOT (otherUser)-[:IS_BLOCKED]->(user)`)
        .return(`user, pinwall, pinwallData, otherUser AS contact, LABELS(pinwall) AS pinwallType, privacy, privacyNoContact, writer,
                 NOT EXISTS(pinwall.visible) AS isPublic , isAdmin, userRec.recommendationId AS userRecommendationId,
                 userRecBlog.recommendationId AS userBlogRecommendationId,
                 EXISTS((user)-[:RECOMMENDS]->(pinwall)) AS thisRecommendationByUser,
                 SIZE((pinwallData)<-[:RECOMMENDS]-(:Recommendation)) AS numberOfPinwallDataRecommendations,
                 SIZE((pinwall)<-[:RECOMMENDS]-(:Recommendation)) AS numberOfPinwallRecommendations`)
        .orderBy("pinwall.created DESC")
        .skip("{skip}")
        .limit("{maxItems}")
        .end({userId: userId, detailUserId: request.userId, skip: request.skip, maxItems: request.maxItems})
        .send()
        .then(function (resp) {
            return {pinwall: pinwallElement.getPinwallElements(resp)};
        });
};

let getBlogOnlyContactFilter = function (onlyContact) {
    if (onlyContact) {
        return "(user:User {userId: {userId}})-[:IS_CONTACT|:WRITTEN*1..2]->(pinwall:Blog)";
    }
    return "(user:User {userId: {userId}}), (contact:User)-[:WRITTEN]->(pinwall:Blog)";
};

let getBlogs = function (userId, request) {
    let filters = pinwallFilter.getFilters(request, 'pinwall');
    return db.cypher().match(getBlogOnlyContactFilter(request.onlyContact))
        .where(filters)
        .optionalMatch("(user)-[hasContact:IS_CONTACT]->(contact)-[:WRITTEN]->(pinwall)")
        .optionalMatch("(user)<-[isContact:IS_CONTACT]-(contact)-[relPrivacy:HAS_PRIVACY]->(privacy:Privacy)")
        .where("isContact.type = relPrivacy.type")
        .optionalMatch("(contact)-[:HAS_PRIVACY_NO_CONTACT]->(privacyNoContact:Privacy)")
        .where("privacy is NULL")
        .optionalMatch("(pinwall)<-[:RECOMMENDS]-(recommendation:Recommendation)")
        .with(`user, contact, pinwall, isContact, hasContact, privacy, privacyNoContact, count(recommendation) AS totalNumberOfRecommendations,
               EXISTS((user)-[:WRITTEN]->(pinwall)) AS isAdmin`)
        .where(`((user)-[:WRITTEN]->(pinwall) OR (contact IS NOT null AND NOT EXISTS(pinwall.visible)) OR
                (contact IS NOT null AND ANY(v IN pinwall.visible WHERE v = isContact.type))) AND NOT (contact)-[:IS_BLOCKED]->(user)`)
        .optionalMatch("(user)-[:RECOMMENDS]->(userRec:Recommendation)-[:RECOMMENDS]->(pinwall)")
        .return(`user, totalNumberOfRecommendations, pinwall, contact, LABELS(pinwall) AS pinwallType, privacy, privacyNoContact, isAdmin,
                 NOT EXISTS(pinwall.visible) AS isPublic, userRec.recommendationId AS userRecommendationId,
                 EXISTS((user)-[:RECOMMENDS]->(:Recommendation)-[:RECOMMENDS]->(pinwall)) AS recommendedByUser`)
        .orderBy("pinwall.created DESC")
        .skip("{skip}")
        .limit("{maxItems}")
        .end({
            userId: userId, skip: request.skipBlog, maxItems: request.maxItems, language: request.language, topic: request.topic,
            recommendationType: request.recommendationType
        });
};

let getRecommendationPrivacyString = function (withCondition) {
    return db.cypher().optionalMatch("(user)-[hasContact:IS_CONTACT]->(contact)-[:RECOMMENDS]->(pinwall)")
        .optionalMatch("(user)<-[isContact:IS_CONTACT]-(contact)-[relPrivacy:HAS_PRIVACY]->(privacy:Privacy)")
        .where("isContact.type = relPrivacy.type")
        .optionalMatch("(contact)-[:HAS_PRIVACY_NO_CONTACT]->(privacyNoContact:Privacy)")
        .where("isContact is NULL")
        .with("user, contact, pinwall, pinwallData, isContact, hasContact, privacy, privacyNoContact" + withCondition)
        .where(`(user)-[:RECOMMENDS]->(pinwall) OR
                ((privacy.pinwall = true OR (privacy is null AND privacyNoContact.pinwall = true)) AND
                (privacy.profile = true OR (privacy is null AND privacyNoContact.profile = true)) AND NOT (contact)-[:IS_BLOCKED]->(user))`)
        .getCommandString();
};

let getBlogRecommendationPrivacyString = function () {
    return db.cypher().optionalMatch("(pinwallData)<-[:WRITTEN]-(writer:User)")
        .where("pinwallData:Blog")
        .optionalMatch("(writer)-[writerContact:IS_CONTACT]->(user)")
        .with(`user, contact, pinwall, pinwallData, isContact, hasContact, privacy, privacyNoContact, numberOfRecommendations, writer, writerContact`)
        .where(`writer is null OR (writer is not null AND
               (NOT EXISTS(pinwallData.visible) OR (ANY(v IN pinwallData.visible WHERE v = writerContact.type)) OR writer.userId = user.userId)
                AND NOT EXISTS((writer)-[:IS_BLOCKED]->(user)))`)
        .getCommandString();
};

let getRecommendationOnlyContactFilter = function (onlyContact) {
    if (onlyContact) {
        return "(user:User {userId: {userId}})-[:IS_CONTACT|:RECOMMENDS*1..2]->(pinwall:Recommendation)-[:PINWALL_DATA]->(pinwallData)";
    }
    return "(user:User {userId: {userId}}), (contact:User)-[:RECOMMENDS]->(pinwall:Recommendation)-[:PINWALL_DATA]->(pinwallData)";
};

let getRecommendationOrderFilter = function (order) {
    if (order === 'popular') {
        return "numberOfRecommendations DESC, created DESC";
    }
    return "created DESC";
};

let getFilterWithCreatedCondition = function (filters) {
    if (filters) {
        return `pinwall.created = created AND ${filters}`;
    }
    return `pinwall.created = created`;
};

let getRecommendations = function (userId, request) {
    let filters = pinwallFilter.getFilters(request, 'pinwallData');
    return db.cypher().match(getRecommendationOnlyContactFilter(request.onlyContact))
        .where(filters)
        .addCommand(getRecommendationPrivacyString(''))
        .with("count(pinwall) AS numberOfRecommendations, max(pinwall.created) AS created, pinwallData")
        .orderBy(getRecommendationOrderFilter(request.order))
        .skip("{skip}")
        .limit("{maxItems}")
        .match(getRecommendationOnlyContactFilter(request.onlyContact))
        .where(getFilterWithCreatedCondition(filters))
        .addCommand(getRecommendationPrivacyString(', numberOfRecommendations '))
        .addCommand(getBlogRecommendationPrivacyString())
        .optionalMatch("(user)-[:RECOMMENDS]->(userRec:Recommendation)-[:RECOMMENDS]->(pinwallData)")
        .return(`user, pinwall, pinwallData, contact, LABELS(pinwall) AS pinwallType, privacy, privacyNoContact, numberOfRecommendations, writer,
            userRec.recommendationId AS userRecommendationId,
            EXISTS((user)-[:RECOMMENDS]->(pinwall)) AS thisRecommendationByUser,
            SIZE((pinwallData)<-[:RECOMMENDS]-(:Recommendation)) AS totalNumberOfRecommendations`)
        .end({
            userId: userId, skip: request.skipRecommendation, maxItems: request.maxItems, language: request.language, topic: request.topic,
            recommendationType: request.recommendationType
        });
};

let sortPinwall = function (resp, skipRecommendation, skipBlog, maxItems, order, showUserRecommendation, request) {
    let pinwall;
    if (showUserRecommendation && order === 'popular') {
        pinwall = pinwallSelector.sortPinwall(null, resp[4], skipRecommendation, skipBlog, maxItems);
    } else if (showUserRecommendation) {
        pinwall = pinwallSelector.sortPinwall(resp[4], resp[5], skipRecommendation, skipBlog, maxItems);
    } else if (!showUserRecommendation && order === 'popular') {
        pinwall = pinwallSelector.sortPinwall(null, resp[2], skipRecommendation, skipBlog, maxItems);
    } else if (!showUserRecommendation) {
        pinwall = pinwallSelector.sortPinwall(resp[2], resp[3], skipRecommendation, skipBlog, maxItems);
    } else {
        logger.error(`Error sorting pinwall (Order:${order}, showUserRecommendation: ${showUserRecommendation})`, request);
    }
    return pinwall;
};

let getPinwall = function (userId, request) {
    let commands = [];

    return recommendedUserSetting.showUserRecommendationOnHome(userId).then(function (showUserRecommendation) {
        commands.push(contacting.getContacting(userId).getCommand());
        commands.push(contacting.getNumberOfContacting(userId).getCommand());
        if (showUserRecommendation) {
            commands.push(recommendedUser.getRecommendedByContactUsers(userId, 10).getCommand());
            commands.push(recommendedUser.getRecommendedUsers(userId, 10).getCommand());
        }
        if (request.order !== 'popular') {
            commands.push(getBlogs(userId, request).getCommand());
        }

        return getRecommendations(userId, request)
            .send(commands)
            .then(function (resp) {
                let pinwall, recommendedUserResult = [];
                userInfo.addImageForThumbnail(resp[0]);
                if (showUserRecommendation) {
                    userInfo.addImageForThumbnail(resp[2]);
                    userInfo.addImageForThumbnail(resp[3]);
                    recommendedUserResult = resp[2].concat(resp[3]);
                }
                pinwall = sortPinwall(resp, request.skipRecommendation, request.skipBlog, request.maxItems, request.order, showUserRecommendation);

                return {
                    contacting: {users: resp[0], numberOfContacting: resp[1][0].numberOfContacting},
                    recommendedUser: recommendedUserResult,
                    pinwall: pinwallElement.getPinwallElements(pinwall.pinwall),
                    skipRecommendation: pinwall.skipRecommendation,
                    skipBlog: pinwall.skipBlog
                };
            });
    });
};

module.exports = {
    getPinwall: getPinwall,
    getRecommendationOfUser: getRecommendationOfUser,
    getPagesOfUser: getPagesOfUser,
    getPinwallOfDetailUser: getPinwallOfDetailUser
};
<|endoftext|>