<commit_before>// TODO: sub Segments?

(function(global, undefined) {
    // TODO: test if this is there (i.e. don't just assume, handle it
    // when it's not there)
    var Raphael = global.Raphael;

    // but this is very much a temporary thing until I understand a better
    // way to pass this in
    var r = Raphael("canvas", 600, 400);

    var pub = {};

    /*** Objects ***/

    pub.Point = function(x, y) {
        this.x = x;
        this.y = y;
    };

    pub.Point.prototype.draw = function() {
        if(!this.display)
            this.display = r.circle(this.x, this.y, 2).attr({fill: "#000"});
    };

    pub.Segment = function(pt1, pt2) {
        // ugly, but some other logic depends on left-most being the first point
        if(pt1.x <= pt2.x) {
            this.A = pt1;
            this.B = pt2;
        } else {
            this.A = pt2;
            this.B = pt1;
        }

        this.length = Math.sqrt(Math.pow(this.B.x - this.A.x, 2)
                              + Math.pow(this.B.y - this.A.y, 2));
    };

    pub.Segment.prototype.draw = function() {
        var pt2String = function(pt) {
            return pt.x+", "+pt.y;
        };

        if(!this.display)
            this.display = r.path("M"+pt2String(this.A)+"L"+pt2String(this.B));
    };

    // get change from A to B
    pub.Segment.prototype.getChangePerLength = function(coord) {
        if(coord === "x") {
            return (this.B.x - this.A.x) / this.length;
        } else {
            return (this.B.y - this.A.y) / this.length;
        }
    };

    pub.Segment.prototype.drawA = function() {
        this.A.draw();
    };

    pub.Segment.prototype.drawB = function() {
        this.B.draw();
    };

    pub.Circle = function(point, rad) {
        this.center = point;
        this.cx = point.x;
        this.cy = point.y;
        this.rad = rad;
    };

    pub.Circle.prototype.draw = function() {
        if(!this.display)
            this.display = r.circle(this.cx, this.cy, this.rad);
    };

    // the idea is that instead of having a separate triangle object, im just going
    // to have a proposition function return a group of segments.
    pub.EleGroup = function(eles) {
        for(ele in eles) {
            this[ele] = eles[ele];
        }
    };

    pub.EleGroup.prototype.draw = function() {
        for(var ele in this) {
            if(this.hasOwnProperty(ele)) {
                this[ele].draw();
            }
        }
    };

    /*** Utility funcitons ***/

    pub.circFromSeg = function(segment, endpoint) {
        if(endpoint === "A") {
            return new pub.Circle(segment.A, segment.length);
        } else {
            return new pub.Circle(segment.B, segment.length);
        }
    };

    // The idea is to return just the extension segment, not a segment
    // that includes both the extension and the original segment
    //
    // direction = 1 means A --> B, -1 means B --> A
    pub.extendSegment = function(segment, endpoint, length, direction) {
        var segpt;
        if(endpoint === "A") {
            segpt = segment.A;
        } else {
            segpt = segment.B;
        }

        var y_inc = direction * segment.getChangePerLength("y") * length;
        var x_inc = direction * segment.getChangePerLength("x") * length;
        var extendpt = new pub.Point(segpt.x + x_inc, segpt.y + y_inc);

        return new pub.Segment(segpt, extendpt);

    };

    pub.findCircsIntersection = function(circ1, circ2) {
        var Lcirc, Rcirc;

        if(circ1.cx <= circ2.cx) {
            Lcirc = circ1;
            Rcirc = circ2;
        } else {
            Lcirc = circ2;
            Rcirc = circ1;
        }

        var btwn_centers = new pub.Segment(Lcirc.center, Rcirc.center);

        // x^2 + y^2 = R^2
        // (x-d)^2 + y^2 = r^2
        // solve for x:
        //
        // (x-d)^2 - x = r^2 - R^2
        // x = (d^2 - r^2 + R^2) / 2d
        //
        // Plug it back into the first equation to get y
        // Note that this is from the reference frame that assumes
        // the left-most circle is the origin
        // and the x-axis is along the line between the circle centers
        // so once we get x, y
        var d = btwn_centers.length;
        var x = (Math.pow(d, 2)
               - Math.pow(Rcirc.rad, 2)
               + Math.pow(Lcirc.rad, 2))
               / (2*d);
        var y = Math.sqrt(Math.pow(Lcirc.rad, 2) - Math.pow(x, 2));

        // get the coords of the point at the intersection between
        // btwn_centers and the line drawn from the 2 intersections of the circles
        // TODO: test and handle when there's zero or one intersection points
        // for now I will assume that less than two intersection points is an
        // uninteresting case

        // Also, we will use radians in this household, young man
        var cLineAng = Raphael.rad(
                         Raphael.angle(
                           Rcirc.cx, Rcirc.cy, Lcirc.cx, Lcirc.cy));

        var cLineX = Lcirc.cx + x*Math.cos(cLineAng),
            cLineY = Lcirc.cy + x*Math.sin(cLineAng);

        var cLinePt = new pub.Point(cLineX, cLineY);

        // finally get the coords of the top intersection (since there are
        // generally two)
        var interPtX = cLinePt.x + y*Math.sin(cLineAng),
            interPtY = cLinePt.y - y*Math.cos(cLineAng);

        var interPt = new pub.Point(interPtX, interPtY);

        return interPt;
    };

    // finds intersection of circle and segment for a segment passing
    // through the center of the circle
    pub.findCircCenterSegIntersection = function(circ, seg) {
        // find change per length of the segment, then
        // multiply by the radius of the circle to find the change
        // from circle to intersection
        var interY = circ.cy + circ.rad * seg.getChangePerLength("y"),
            interX = circ.cx + circ.rad * seg.getChangePerLength("x");

        return new pub.Point(interX, interY);

    };

    /*** Proposition logic ***/

    // Prop1 - takes a segment, returns an equilateral triangle with the segment
    // as one of the sides
    pub.Prop1 = function(seg) {
        var c1 = pub.circFromSeg(seg, "A"),
            c2 = pub.circFromSeg(seg, "B");
        var inter = pub.findCircsIntersection(c1,c2);

        var Aside = new pub.Segment(seg.A, inter),
            Bside = new pub.Segment(seg.B, inter);

        return new pub.EleGroup(
            {
                orig: seg,
                sideA: Aside,
                sideB: Bside
            });

    };

    // Prop2 - takes a segment and a point, returns a segment located at the point
    // that's equal in length to the segment
    pub.Prop2 = function(seg, pt) {
        var seg1 = new pub.Segment(seg.B, pt);

        var eqtri = pub.Prop1(seg1);

        // now make the circle based on the original given seg
        var seg_circ = pub.circFromSeg(seg, "B");

        //extend the other side of the equilateral triangle
        var ext_seg = pub.extendSegment(eqtri.sideA, "B", seg.length + 30, 1);

        //find intersection of circle and extended line
        var interpt = pub.findCircCenterSegIntersection(seg_circ, ext_seg);

        // the other point in the eq tri (not B or C)
        var eqtri_otherpt = eqtri.sideA.A;

        // line from other point of the eqtri to the intersection of the
        // extension and the circle
        var ext_inter_seg = new pub.Segment(eqtri_otherpt, interpt);

        var ext_inter_seg_circ = pub.circFromSeg(ext_inter_seg, "A");

        // extend the remaining side of the eq tri
        var last_ext_seg = pub.extendSegment(eqtri.sideB, "B", seg.length + 30, 1) ;

        // find intersection of the newest circle and the last_ext_seg
        var inter2pt = pub.findCircCenterSegIntersection(ext_inter_seg_circ, last_ext_seg);

        return new pub.Segment(pt, inter2pt);

    };

    // register globally
    global.euclib = pub;

})(this);

<commit_msg>fix lack of var statement in EleGroup.draw()<commit_after>// TODO: sub Segments?

(function(global, undefined) {
    "use strict";

    // TODO: test if this is there (i.e. don't just assume, handle it
    // when it's not there)
    var Raphael = global.Raphael;

    // but this is very much a temporary thing until I understand a better
    // way to pass this in
    var r = Raphael("canvas", 600, 400);

    var pub = {};

    /*** Objects ***/

    pub.Point = function(x, y) {
        this.x = x;
        this.y = y;
    };

    pub.Point.prototype.draw = function() {
        if(!this.display)
            this.display = r.circle(this.x, this.y, 2).attr({fill: "#000"});
    };

    pub.Segment = function(pt1, pt2) {
        // ugly, but some other logic depends on left-most being the first point
        if(pt1.x <= pt2.x) {
            this.A = pt1;
            this.B = pt2;
        } else {
            this.A = pt2;
            this.B = pt1;
        }

        this.length = Math.sqrt(Math.pow(this.B.x - this.A.x, 2)
                              + Math.pow(this.B.y - this.A.y, 2));
    };

    pub.Segment.prototype.draw = function() {
        var pt2String = function(pt) {
            return pt.x+", "+pt.y;
        };

        if(!this.display)
            this.display = r.path("M"+pt2String(this.A)+"L"+pt2String(this.B));
    };

    // get change from A to B
    pub.Segment.prototype.getChangePerLength = function(coord) {
        if(coord === "x") {
            return (this.B.x - this.A.x) / this.length;
        } else {
            return (this.B.y - this.A.y) / this.length;
        }
    };

    pub.Segment.prototype.drawA = function() {
        this.A.draw();
    };

    pub.Segment.prototype.drawB = function() {
        this.B.draw();
    };

    pub.Circle = function(point, rad) {
        this.center = point;
        this.cx = point.x;
        this.cy = point.y;
        this.rad = rad;
    };

    pub.Circle.prototype.draw = function() {
        if(!this.display)
            this.display = r.circle(this.cx, this.cy, this.rad);
    };

    // the idea is that instead of having a separate triangle object, im just going
    // to have a proposition function return a group of segments.
    pub.EleGroup = function(eles) {
        for(var ele in eles) {
            this[ele] = eles[ele];
        }
    };

    pub.EleGroup.prototype.draw = function() {
        for(var ele in this) {
            if(this.hasOwnProperty(ele)) {
                this[ele].draw();
            }
        }
    };

    /*** Utility funcitons ***/

    pub.circFromSeg = function(segment, endpoint) {
        if(endpoint === "A") {
            return new pub.Circle(segment.A, segment.length);
        } else {
            return new pub.Circle(segment.B, segment.length);
        }
    };

    // The idea is to return just the extension segment, not a segment
    // that includes both the extension and the original segment
    //
    // direction = 1 means A --> B, -1 means B --> A
    pub.extendSegment = function(segment, endpoint, length, direction) {
        var segpt;
        if(endpoint === "A") {
            segpt = segment.A;
        } else {
            segpt = segment.B;
        }

        var y_inc = direction * segment.getChangePerLength("y") * length;
        var x_inc = direction * segment.getChangePerLength("x") * length;
        var extendpt = new pub.Point(segpt.x + x_inc, segpt.y + y_inc);

        return new pub.Segment(segpt, extendpt);

    };

    pub.findCircsIntersection = function(circ1, circ2) {
        var Lcirc, Rcirc;

        if(circ1.cx <= circ2.cx) {
            Lcirc = circ1;
            Rcirc = circ2;
        } else {
            Lcirc = circ2;
            Rcirc = circ1;
        }

        var btwn_centers = new pub.Segment(Lcirc.center, Rcirc.center);

        // x^2 + y^2 = R^2
        // (x-d)^2 + y^2 = r^2
        // solve for x:
        //
        // (x-d)^2 - x = r^2 - R^2
        // x = (d^2 - r^2 + R^2) / 2d
        //
        // Plug it back into the first equation to get y
        // Note that this is from the reference frame that assumes
        // the left-most circle is the origin
        // and the x-axis is along the line between the circle centers
        // so once we get x, y
        var d = btwn_centers.length;
        var x = (Math.pow(d, 2)
               - Math.pow(Rcirc.rad, 2)
               + Math.pow(Lcirc.rad, 2))
               / (2*d);
        var y = Math.sqrt(Math.pow(Lcirc.rad, 2) - Math.pow(x, 2));

        // get the coords of the point at the intersection between
        // btwn_centers and the line drawn from the 2 intersections of the circles
        // TODO: test and handle when there's zero or one intersection points
        // for now I will assume that less than two intersection points is an
        // uninteresting case

        // Also, we will use radians in this household, young man
        var cLineAng = Raphael.rad(
                         Raphael.angle(
                           Rcirc.cx, Rcirc.cy, Lcirc.cx, Lcirc.cy));

        var cLineX = Lcirc.cx + x*Math.cos(cLineAng),
            cLineY = Lcirc.cy + x*Math.sin(cLineAng);

        var cLinePt = new pub.Point(cLineX, cLineY);

        // finally get the coords of the top intersection (since there are
        // generally two)
        var interPtX = cLinePt.x + y*Math.sin(cLineAng),
            interPtY = cLinePt.y - y*Math.cos(cLineAng);

        var interPt = new pub.Point(interPtX, interPtY);

        return interPt;
    };

    // finds intersection of circle and segment for a segment passing
    // through the center of the circle
    pub.findCircCenterSegIntersection = function(circ, seg) {
        // find change per length of the segment, then
        // multiply by the radius of the circle to find the change
        // from circle to intersection
        var interY = circ.cy + circ.rad * seg.getChangePerLength("y"),
            interX = circ.cx + circ.rad * seg.getChangePerLength("x");

        return new pub.Point(interX, interY);

    };

    /*** Proposition logic ***/

    // Prop1 - takes a segment, returns an equilateral triangle with the segment
    // as one of the sides
    pub.Prop1 = function(seg) {
        var c1 = pub.circFromSeg(seg, "A"),
            c2 = pub.circFromSeg(seg, "B");
        var inter = pub.findCircsIntersection(c1,c2);

        var Aside = new pub.Segment(seg.A, inter),
            Bside = new pub.Segment(seg.B, inter);

        return new pub.EleGroup(
            {
                orig: seg,
                sideA: Aside,
                sideB: Bside
            });

    };

    // Prop2 - takes a segment and a point, returns a segment located at the point
    // that's equal in length to the segment
    pub.Prop2 = function(seg, pt) {
        var seg1 = new pub.Segment(seg.B, pt);

        var eqtri = pub.Prop1(seg1);

        // now make the circle based on the original given seg
        var seg_circ = pub.circFromSeg(seg, "B");

        //extend the other side of the equilateral triangle
        var ext_seg = pub.extendSegment(eqtri.sideA, "B", seg.length + 30, 1);

        //find intersection of circle and extended line
        var interpt = pub.findCircCenterSegIntersection(seg_circ, ext_seg);

        // the other point in the eq tri (not B or C)
        var eqtri_otherpt = eqtri.sideA.A;

        // line from other point of the eqtri to the intersection of the
        // extension and the circle
        var ext_inter_seg = new pub.Segment(eqtri_otherpt, interpt);

        var ext_inter_seg_circ = pub.circFromSeg(ext_inter_seg, "A");

        // extend the remaining side of the eq tri
        var last_ext_seg = pub.extendSegment(eqtri.sideB, "B", seg.length + 30, 1) ;

        // find intersection of the newest circle and the last_ext_seg
        var inter2pt = pub.findCircCenterSegIntersection(ext_inter_seg_circ, last_ext_seg);

        return new pub.Segment(pt, inter2pt);

    };

    // register globally
    global.euclib = pub;

})(this);

<|endoftext|>


<commit_before>/********************************
 * Create the Hall of Shame panel
 *********************************/
function drawHOS(svg)
{

    var hosProducts= svg.append("g")
                        .attr("id","hos-products")

    // hall of shame panel config
    var hos_lastButton = null;
    var hos_bWidth= 80; //button width
    var hos_bHeight=80; //button height
    var hos_bSpace= 2; //space between buttons
    var hos_x0= 20; //x offset
    var hos_y0= 20; //y offset
    var hos_borderWidth= 1; // border thickness
    var hos_borderColor = "#29ABE2";
    var hos_focusBorderColor = "#29ABE2";
    var hos_focusBorderWidth = 4;

    // filter elements from product set for HOS panel
    var hosData = data.data.filter(function(d) {
        if (d.hos == 1) return d;
    })

    var fullData = data.data

    //groups for each button (which will hold a rect and text)
    var buttonGroups= hosProducts.selectAll("g.button")
                            .data(hosData)
                            .enter()
                            .append("g")
                            .attr("class","button")
                            .style("cursor","pointer")
                            .on("click",function(d,i) {
                                updateButtonColors(d3.select(this), d3.select(this.parentNode));

                                $("#chart1").empty();
                                // update left charts panel
                                var uc1=parallel_chart("#chart1");
                                console.log("At button click, d.code is", d.code)
                                filtered = find_and_rank_comparables(fullData, d.code, "sugars_100g");
                                uc1.update(filtered);

                            });

    //adding a rect to each button group
    //rx and ry give the rect rounded corner
    buttonGroups.append("rect")
                .attr("class","buttonRect")
                .attr("width",hos_bWidth)
                .attr("height",hos_bHeight)
                .attr("x",function(d,i) {return hos_x0+(hos_bWidth+hos_bSpace)*i;})
                .attr("y",hos_y0)
                .attr("border",hos_borderWidth)
                .style("stroke", hos_borderColor)
                .style("fill", "none")
                .style("stroke-width", hos_borderWidth);

    //adding image to each toggle button group
    buttonGroups.append("svg:image")
        .attr("class","buttonImage")
        .attr("x",function(d,i) {return hos_x0+(hos_bWidth+hos_bSpace)*i;})
        .attr("y",hos_y0)
        .attr("xlink:href", function(d) {return d.image_url;})
        .attr("dominant-baseline","central")
        .attr("fill","white")
        .attr("width", hos_bWidth)
        .attr("height", hos_bHeight);

    function updateButtonColors(button, parent) {
        if  (hos_lastButton == null){
            hos_lastButton = button;
        }
        else{
            hos_lastButton.select("rect")
                .style("stroke",hos_borderColor)
                .style("stroke-width", hos_borderWidth);
        }

        button.select("rect")
                .style("stroke",hos_focusBorderColor)
                .style("stroke-width", hos_focusBorderWidth);

        // save the last button
        hos_lastButton = button;
    }
}
<commit_msg>added comment in hos_panel<commit_after>/********************************
 * Create the Hall of Shame panel
 *********************************/
function drawHOS(svg)
{

    var hosProducts= svg.append("g")
                        .attr("id","hos-products")

    // hall of shame panel config
    var hos_lastButton = null;
    var hos_bWidth= 80; //button width
    var hos_bHeight=80; //button height
    var hos_bSpace= 2; //space between buttons
    var hos_x0= 20; //x offset
    var hos_y0= 20; //y offset
    var hos_borderWidth= 1; // border thickness
    var hos_borderColor = "#29ABE2";
    var hos_focusBorderColor = "#29ABE2";
    var hos_focusBorderWidth = 4;

    // filter elements from product set for HOS panel
    var hosData = data.data.filter(function(d) {
        if (d.hos == 1) return d;
    })

    // identify FULL data list for passing to find_and_rank_comparables
    var fullData = data.data

    //groups for each button (which will hold a rect and text)
    var buttonGroups= hosProducts.selectAll("g.button")
                            .data(hosData)
                            .enter()
                            .append("g")
                            .attr("class","button")
                            .style("cursor","pointer")
                            .on("click",function(d,i) {
                                updateButtonColors(d3.select(this), d3.select(this.parentNode));
                                $("#chart1").empty();
                                // update left charts panel
                                var uc1=parallel_chart("#chart1");
                                console.log("At button click, d.code is", d.code)
                                filtered = find_and_rank_comparables(fullData, d.code, "sugars_100g");
                                uc1.update(filtered);

                            });

    //adding a rect to each button group
    //rx and ry give the rect rounded corner
    buttonGroups.append("rect")
                .attr("class","buttonRect")
                .attr("width",hos_bWidth)
                .attr("height",hos_bHeight)
                .attr("x",function(d,i) {return hos_x0+(hos_bWidth+hos_bSpace)*i;})
                .attr("y",hos_y0)
                .attr("border",hos_borderWidth)
                .style("stroke", hos_borderColor)
                .style("fill", "none")
                .style("stroke-width", hos_borderWidth);

    //adding image to each toggle button group
    buttonGroups.append("svg:image")
        .attr("class","buttonImage")
        .attr("x",function(d,i) {return hos_x0+(hos_bWidth+hos_bSpace)*i;})
        .attr("y",hos_y0)
        .attr("xlink:href", function(d) {return d.image_url;})
        .attr("dominant-baseline","central")
        .attr("fill","white")
        .attr("width", hos_bWidth)
        .attr("height", hos_bHeight);

    function updateButtonColors(button, parent) {
        if  (hos_lastButton == null){
            hos_lastButton = button;
        }
        else{
            hos_lastButton.select("rect")
                .style("stroke",hos_borderColor)
                .style("stroke-width", hos_borderWidth);
        }

        button.select("rect")
                .style("stroke",hos_focusBorderColor)
                .style("stroke-width", hos_focusBorderWidth);

        // save the last button
        hos_lastButton = button;
    }
}
<|endoftext|>


<commit_before>'use strict';
/**
 * Created by Greg Rozmarynowycz 6/15/2015
 * @description ArraySearch lib
 */

/**
 * Finds objects in a array with given properties
 * @returns {Finder}
 * @constructor
 */
var Finder = function Finder(){
	/* jshint validthis:true */
	//Determine necessary features exist
	if(!Array.prototype.some || !Array.prototype.every || !Array.prototype.forEach) {
		throw new Error('Array prototype does not contain necessary "some","every", and "forEach" functions');
	}

	/**
	 * Return one result
	 * @type {{in: arraySetter}}
	 */
	this.one = null;
	/**
	 * Return all results
	 * @type {object}
	 * @returns {{in: arraySetter}}
	 */
	this.all = null;

	//create basic search context
	var context = {
		array: [],
		returnMany: null,
		map: false
	};

	/* Construct the finder function */

	//find.[all|one].in(array)
	//sets whether we should return any array or a single object
	function setReturnType(many){
		return function(){
			context.returnMany = many;
			return {'in': arraySetter};
		};
	}

	//find.[all|one]
	Object.defineProperty(this, 'one', {get: setReturnType(false)});
	Object.defineProperty(this, 'all', {get: setReturnType(true)});

	function convertToArray(map){
		return Object.keys(map).map(function(key){
			var obj =  map[key];

			if(typeof(obj) === 'object' && obj !== null){
				Object.defineProperty(obj, '__arrSearchObjKey', {
					value: key,
					configurable: true
				});
			}

			return obj;
		});
	}

	//find.[all|one].in(array).with(predicate)
	/**
	 * registers the array in the finder
	 * @name arraySetter
	 * @param {object} collection
	 * @returns {{with: findWith, having: setSearchPath}}
	 */
	function arraySetter(collection) {
		context.map = false;
		if(!(collection && collection.constructor === Array)) {

			if(collection && typeof(collection) === 'object'){
				context.map = true;
				collection = convertToArray(collection);
			}
			else {
				throw new TypeError('Search context is not an array or object map');
			}
		}
		context.array = collection;
		return  {with: attachKeysSearch(findWith.bind(context)), having: setSearchPath};
	}

	//find.[all|one].in(array).with.keys(keys)
	//find.[all|one].in(array).with.any.keys(keys)
	function attachKeysSearch(func){
		func.keys = findWithKeys.bind(context, false);
		Object.defineProperty(func, 'any', {
			get: function(){
				return {keys: findWithKeys.bind(context, true)};
			}
		});
		return func;
	}

	//find.[all|one].in(array).having(searchPath).with(predicate)
	//curries the search path for findHaving
	/**
	 * sets search path for nested search
	 * @name setSearchPath
	 * @param searchPath
	 * @returns {{with: findHaving}}
	 */
	function setSearchPath(searchPath) {
		searchPath = resolveToSearchPath(searchPath);
		return {with: attachHavingKeysSearch(findHaving.bind(context, searchPath, findWith), searchPath)};
	}

	function findWithAnyKeys(hasAny){
		return function(keys){
			return findWithKeys.call(this, hasAny, keys);
		};
	}

	//find.[all|one].in(array).having(searchPath).with.keys(keys)
	//find.[all|one].in(array).having(searchPath).with.any.keys(keys)
	function attachHavingKeysSearch(func, searchPath){
		func.keys = findHaving.bind(context, searchPath, findWithAnyKeys(false));
		Object.defineProperty(func, 'any', {
			get: function(){
				return {keys: findHaving.bind(context, searchPath, findWithAnyKeys(true))};
			}
		});
		return func;
	}

	/* Finder functions */

	//creates a predicate from property and value
	function createPredicate(prop, value){
		var predicate = {};
		predicate[prop] = value;
		return predicate;
	}

	function buildResult(results){

		if(this.map){
			var resultOjb = {};

			if(!this.returnMany){
				if(!results){
					return results;
				}

				var result = results.shift();
				if(result && result.hasOwnProperty('__arrSearchObjKey')){
					delete result.__arrSearchObjKey;
				}
				return result;
			}

			results.map(function(result){
				var key = result.__arrSearchObjKey;
				delete result.__arrSearchObjKey;
				resultOjb[key] = result;
			});

			return resultOjb;
		}
		else {
			return (this.returnMany) ? results : results[0];
		}

	}

	/**
	 * filters the array by the predicate
	 * @name findWith
	 * @param {object} filter
	 * @returns {*}
	 */
	function findWith(filter) {
		var result = [],
			pool = this.array;
		for(var prop in filter){
			result = [];
			if(filter.hasOwnProperty(prop)){
				pool.forEach(function search(elem){
					if(deepPartialCompare(elem, createPredicate(prop, filter[prop]))){
						result.push(elem);
					}
				});
			}
			pool = result.slice();
		}
		return buildResult.call(this, result);
	}

	//find.[all|one].in(array).with.keys(keys)
	/**
	 * searches an object for the given keys, returning true or false
	 * @param hasAny
	 * @param keys
	 * @returns {*}
	 */
	function findWithKeys(hasAny, keys){
		if(typeof(keys) === 'string') keys = [keys];
		if(!(keys && keys.constructor === Array)) {
			throw new TypeError(JSON.stringify(keys) + ' is not array');
		}
		var result = [];
		this.array.forEach(function(elem){
			var searchFunc = (hasAny === true) ? 'some' : 'every';
			if(keys[searchFunc](elem.hasOwnProperty, elem)){
				result.push(elem);
			}
		});
		return buildResult.call(this, result);
	}

	//Allows the object search path to be defined in variety of ways
	function resolveToSearchPath(value){
		if(value.constructor === Array) return value;

		if(typeof(value) === 'string') {
			return value.split(/[\.\>]/);
		}
		else if(typeof(value) === 'object' || value !== null) {
			var key, keys = [], targetPath = value;
			do {
				key = Object.keys(targetPath)[0];
				keys.push(key);
				targetPath = targetPath[key];
			}
			while(targetPath[key]);
			return keys;
		}
		throw new Error(JSON.stringify(value) + ' is not a valid search path');
	}

	//filters the array based on  a given array property contains elements that match the predicate
	//example search path: {leve1 : {level2: {level3: []}}}
	function findHaving(searchPath, searchFunc, predicate) {
		var result = [];
		this.array.forEach(function searchIn(elem){
			var targetArray = elem;
			searchPath.forEach(function navigateObject(key){
				targetArray = targetArray[key];
			});

			var havingContext = {array: targetArray, returnMany: true};
			if(searchFunc.call(havingContext, predicate).length > 0) {
				result.push(elem);
			}
		});

		return buildResult.call(this, result);
	}

	/**
	 * Recursively determines if the predicate exists in the given object
	 * @param {object} entity Object to search in
	 * @param {object} partial Object with similar structure to entity
	 * @returns {boolean} If the partial is contained in the entity
	 */
	function deepPartialCompare(entity, partial){
		var key = Object.keys(partial)[0],
			value = partial[key];

		if(!key) { throw new Error("Invalid filter: " + JSON.stringify(partial)); }
		if(Boolean(entity) !== Boolean(partial)) {return false; } //prevents property checking on null values
		if(entity[key] === value) return true;
		if(JSON.stringify(entity[key]) === JSON.stringify(value)) return true;

		if(typeof(value) === 'object'  && value !== null) {
			var contains = true;
			for(var valueKey in value){
				//if any of the comparisons return false, "contains" will be false
				contains &= deepPartialCompare(entity[key], createPredicate(valueKey, value[valueKey]));
			}
			//convert to boolean
			return !!contains;
		}
		return false;
	}

	return this;
};

if(typeof(module) !== 'undefined'){
	/**
	 * Returns an array finder instance
	 * @type {{Finder: Finder}}
	 */
	module.exports = {Finder: new Finder()};
}<commit_msg>refactor findWith<commit_after>'use strict';
/**
 * Created by Greg Rozmarynowycz 6/15/2015
 * @description ArraySearch lib
 */

/**
 * Finds objects in a array with given properties
 * @returns {Finder}
 * @constructor
 */
var Finder = function Finder(){
	/* jshint validthis:true */
	//Determine necessary features exist
	if(!Array.prototype.some || !Array.prototype.every || !Array.prototype.forEach) {
		throw new Error('Array prototype does not contain necessary "some","every", and "forEach" functions');
	}

	/**
	 * Return one result
	 * @type {{in: arraySetter}}
	 */
	this.one = null;
	/**
	 * Return all results
	 * @type {object}
	 * @returns {{in: arraySetter}}
	 */
	this.all = null;

	//create basic search context
	var context = {
		array: [],
		returnMany: null,
		map: false
	};

	/* Construct the finder function */

	//find.[all|one].in(array)
	//sets whether we should return any array or a single object
	function setReturnType(many){
		return function(){
			context.returnMany = many;
			return {'in': arraySetter};
		};
	}

	//find.[all|one]
	Object.defineProperty(this, 'one', {get: setReturnType(false)});
	Object.defineProperty(this, 'all', {get: setReturnType(true)});

	function convertToArray(map){
		return Object.keys(map).map(function(key){
			var obj =  map[key];

			if(typeof(obj) === 'object' && obj !== null){
				Object.defineProperty(obj, '__arrSearchObjKey', {
					value: key,
					configurable: true
				});
			}

			return obj;
		});
	}

	//find.[all|one].in(array).with(predicate)
	/**
	 * registers the array in the finder
	 * @name arraySetter
	 * @param {object} collection
	 * @returns {{with: findWith, having: setSearchPath}}
	 */
	function arraySetter(collection) {
		context.map = false;
		if(!(collection && collection.constructor === Array)) {

			if(collection && typeof(collection) === 'object'){
				context.map = true;
				collection = convertToArray(collection);
			}
			else {
				throw new TypeError('Search context is not an array or object map');
			}
		}
		context.array = collection;
		return  {with: attachKeysSearch(findWith.bind(context)), having: setSearchPath};
	}

	//find.[all|one].in(array).with.keys(keys)
	//find.[all|one].in(array).with.any.keys(keys)
	function attachKeysSearch(func){
		func.keys = findWithKeys.bind(context, false);
		Object.defineProperty(func, 'any', {
			get: function(){
				return {keys: findWithKeys.bind(context, true)};
			}
		});
		return func;
	}

	//find.[all|one].in(array).having(searchPath).with(predicate)
	//curries the search path for findHaving
	/**
	 * sets search path for nested search
	 * @name setSearchPath
	 * @param searchPath
	 * @returns {{with: findHaving}}
	 */
	function setSearchPath(searchPath) {
		searchPath = resolveToSearchPath(searchPath);
		return {with: attachHavingKeysSearch(findHaving.bind(context, searchPath, findWith), searchPath)};
	}

	function findWithAnyKeys(hasAny){
		return function(keys){
			return findWithKeys.call(this, hasAny, keys);
		};
	}

	//find.[all|one].in(array).having(searchPath).with.keys(keys)
	//find.[all|one].in(array).having(searchPath).with.any.keys(keys)
	function attachHavingKeysSearch(func, searchPath){
		func.keys = findHaving.bind(context, searchPath, findWithAnyKeys(false));
		Object.defineProperty(func, 'any', {
			get: function(){
				return {keys: findHaving.bind(context, searchPath, findWithAnyKeys(true))};
			}
		});
		return func;
	}

	/* Finder functions */

	//creates a predicate from property and value
	function createPredicate(prop, value){
		var predicate = {};
		predicate[prop] = value;
		return predicate;
	}

	function buildResult(results){

		if(this.map){
			var resultOjb = {};

			if(!this.returnMany){
				if(!results){
					return results;
				}

				var result = results.shift();
				if(result && result.hasOwnProperty('__arrSearchObjKey')){
					delete result.__arrSearchObjKey;
				}
				return result;
			}

			results.map(function(result){
				var key = result.__arrSearchObjKey;
				delete result.__arrSearchObjKey;
				resultOjb[key] = result;
			});

			return resultOjb;
		}
		else {
			return (this.returnMany) ? results : results[0];
		}

	}

	/**
	 * filters the array by the predicate
	 * @name findWith
	 * @param {object} filter
	 * @returns {*}
	 */
	function findWith(filter) {
		var pool = Object.keys(this.array),
			i,
			key,
			filterCount = 0,
			result;

		for(var prop in filter){
			result = [];
			if(filter.hasOwnProperty(prop)){
				filterCount++;
				var predicate = createPredicate(prop, filter[prop]);

				for(i = 0; i < pool.length; ++i){
					key = pool[i];

					if(this.array.hasOwnProperty(key)) {
						var elem = this.array[key];
						if (deepPartialCompare(elem, predicate)) {
							result.push(key);
						}
					}
				}
			}

			pool = result.slice();
		}

		var aggResult = new this.array.constructor();
		for(i = 0; i < pool.length; ++i){
			key = pool[i];
			aggResult[i] = this.array[key]
		}

		return buildResult.call(this, aggResult);
	}

	//find.[all|one].in(array).with.keys(keys)
	/**
	 * searches an object for the given keys, returning true or false
	 * @param hasAny
	 * @param keys
	 * @returns {*}
	 */
	function findWithKeys(hasAny, keys){
		if(typeof(keys) === 'string') keys = [keys];
		if(!(keys && keys.constructor === Array)) {
			throw new TypeError(JSON.stringify(keys) + ' is not array');
		}
		var result = [];
		this.array.forEach(function(elem){
			var searchFunc = (hasAny === true) ? 'some' : 'every';
			if(keys[searchFunc](elem.hasOwnProperty, elem)){
				result.push(elem);
			}
		});
		return buildResult.call(this, result);
	}

	//Allows the object search path to be defined in variety of ways
	function resolveToSearchPath(value){
		if(value.constructor === Array) return value;

		if(typeof(value) === 'string') {
			return value.split(/[\.\>]/);
		}
		else if(typeof(value) === 'object' || value !== null) {
			var key, keys = [], targetPath = value;
			do {
				key = Object.keys(targetPath)[0];
				keys.push(key);
				targetPath = targetPath[key];
			}
			while(targetPath[key]);
			return keys;
		}
		throw new Error(JSON.stringify(value) + ' is not a valid search path');
	}

	//filters the array based on  a given array property contains elements that match the predicate
	//example search path: {leve1 : {level2: {level3: []}}}
	function findHaving(searchPath, searchFunc, predicate) {
		var result = [];
		this.array.forEach(function searchIn(elem){
			var targetArray = elem;
			searchPath.forEach(function navigateObject(key){
				targetArray = targetArray[key];
			});

			var havingContext = {array: targetArray, returnMany: true};
			if(searchFunc.call(havingContext, predicate).length > 0) {
				result.push(elem);
			}
		});

		return buildResult.call(this, result);
	}

	/**
	 * Recursively determines if the predicate exists in the given object
	 * @param {object} entity Object to search in
	 * @param {object} partial Object with similar structure to entity
	 * @returns {boolean} If the partial is contained in the entity
	 */
	function deepPartialCompare(entity, partial){
		var key = Object.keys(partial)[0],
			value = partial[key];

		if(!key) { throw new Error("Invalid filter: " + JSON.stringify(partial)); }
		if(Boolean(entity) !== Boolean(partial)) {return false; } //prevents property checking on null values
		if(entity[key] === value) return true;
		if(JSON.stringify(entity[key]) === JSON.stringify(value)) return true;

		if(typeof(value) === 'object'  && value !== null) {
			var contains = true;
			for(var valueKey in value){
				//if any of the comparisons return false, "contains" will be false
				contains &= deepPartialCompare(entity[key], createPredicate(valueKey, value[valueKey]));
			}
			//convert to boolean
			return !!contains;
		}
		return false;
	}

	return this;
};

if(typeof(module) !== 'undefined'){
	/**
	 * Returns an array finder instance
	 * @type {{Finder: Finder}}
	 */
	module.exports = {Finder: new Finder()};
}<|endoftext|>


<commit_before>App.Controllers.Registro = function(){
    this.$nombreUsuario = $("#nombre_usuario");
    this.$email = $("#email");
    this.$region = $("#region");
    this.$comuna = $("#comuna");
    this.$password = $("#password");
    this.$repeat = $("#repeat");
    this.$dia = $("#dia");
    this.$mes = $("#mes");
    this.$anio = $("#anio");

    this.addEventos();
};

App.Controllers.Registro.mixin({
    addEventos: function(){
        $("#region").on("change", this.redibujarComunas);
        $("#f_registro").on("submit", this.validarEnvio());
    },

    redibujarComunas: function(){
        var regionId = $(this).val();

        if(!regionId){
            return;
        }

        $.get("/registro/obtener_comunas/",{ region : regionId }, function(data){
            var plantilla = templateLoader.render("/templates/registro/comunas/",{
                comunas : JSON.parse(data)
            });

            $("#comuna").empty().html(plantilla);
        });
    },

    validarEnvio: function(){
        var _this = this;

        return function(evt){
            evt.preventDefault();
            $(".error_message").removeClass("error_message").text("");

            var valido, $span, me, json;

            me = this;
            valido = _this.validacionCampo(_this.$nombreUsuario, "nombre_usuario");
            valido = valido && _this.validacionCampo(_this.$email, "email");
            valido = valido && _this.validacionCampo(_this.$region, "region");
            valido = valido && _this.validacionCampo(_this.$comuna, "comuna");
            valido = valido && _this.validacionCampo(_this.$password, "contraseña");
            valido = valido && _this.validacionCampo(_this.$repeat,"repetir contraseña");

            if(!/^[(a-z0-9\_\-\.)]+@[(a-z0-9\_\-\.)]+\.[(a-z)]{2,4}$/.test(_this.$email.val())){
                valido = false;
                _this.$email.parent().find("span").addClass("error_message").text("El email es inválido");
            }

            if(_this.$password.val() !== _this.$repeat.val()){
                valido = false;
                _this.$password.parent().find("span").addClass("error_message").text("Las contraseñas deben ser iguales");
                _this.$repeat.parent().find("span").addClass("error_message").text("Las contraseñas deben ser iguales");
            }else if(_this.$password.val().length < 6 || _this.$password.val().length > 50){
                valido = false;
                _this.$password.parent().find("span").addClass("error_message").text("La contraseña debe tener entre 6 y 50 caracteres");
            }

            if(!$("input[type=radio]:checked").length){
                valido = false;
                $("input[type=radio]:first").parent().find("span").addClass("error_message").text("Debe indicar si es hombre o mujer");
            }

            if(!_this.$dia.val() || !_this.$mes.val() || !_this.$anio.val()){
                valido = false;
                _this.$dia.parent().find("span").addClass("error_message").text("Debe seleccionar una fecha válida");
            }else if(!_this.isFechaValida(_this.$dia.val(), _this.$mes.val(), _this.$anio.val())){
                valido = false;
                _this.$dia.parent().find("span").addClass("error_message").text("Debe seleccionar una fecha válida");
            }

            if(valido){
                $.get("/registro/verificar_usuario/",{ username : _this.$nombreUsuario.val() }, function(res){
                    res = JSON.parse(res);
                    if(!res.existe){
                        me.submit();
                    }else{
                        valido = false;
                        _this.$nombreUsuario.parent().find("span").addClass("error_message").text("Lo sentimos, el usuario ya existe, intente con otro");
                    }
                });
            }
        };
    },

    validacionCampo: function(campo, tipo){
        var valido = true, $span;

        if(campo.val().trim() === ""){
            valido = false;
            $span = campo.parent().find("span");
            $span.addClass("error_message").text("Debe agregar " + tipo);
        }else if(campo.val().trim().length > 140){
            valido = false;
            $span = campo.parent().find("span");
            $span.addClass("error_message").text("El " + tipo + " debe poseer menos de 140 caracteres");
        }

        return valido;
    },

    isFechaValida: function(dia, mes, anio){
        function getDiaMaximo(m){
            var days = 31;

            if(m == 4 || m == 6 || m == 9 || m == 11){
                days = 30;
            }else if(m == 2){
                days = ((anio % 4 == 0) && (!(anio % 100 === 0) || anio % 400 === 0 )) ? 29 : 28;
            }

            return days;
        }

        return  parseInt(dia, 10) <= getDiaMaximo(mes);
    }
});
<commit_msg>cambio para json en registro<commit_after>App.Controllers.Registro = function(){
    this.$nombreUsuario = $("#nombre_usuario");
    this.$email = $("#email");
    this.$region = $("#region");
    this.$comuna = $("#comuna");
    this.$password = $("#password");
    this.$repeat = $("#repeat");
    this.$dia = $("#dia");
    this.$mes = $("#mes");
    this.$anio = $("#anio");

    this.addEventos();
};

App.Controllers.Registro.mixin({
    addEventos: function(){
        $("#region").on("change", this.redibujarComunas);
        $("#f_registro").on("submit", this.validarEnvio());
    },

    redibujarComunas: function(){
        var regionId = $(this).val();

        if(!regionId){
            return;
        }

        $.get("/registro/obtener_comunas/",{ region : regionId }, function(data){
            var plantilla = templateLoader.render("/templates/registro/comunas/",{
                comunas : JSON.parse(data)
            });

            $("#comuna").empty().html(plantilla);
        });
    },

    validarEnvio: function(){
        var _this = this;

        return function(evt){
            evt.preventDefault();
            $(".error_message").removeClass("error_message").text("");

            var valido, $span, me, json;

            me = this;
            valido = _this.validacionCampo(_this.$nombreUsuario, "nombre_usuario");
            valido = valido && _this.validacionCampo(_this.$email, "email");
            valido = valido && _this.validacionCampo(_this.$region, "region");
            valido = valido && _this.validacionCampo(_this.$comuna, "comuna");
            valido = valido && _this.validacionCampo(_this.$password, "contraseña");
            valido = valido && _this.validacionCampo(_this.$repeat,"repetir contraseña");

            if(!/^[(a-z0-9\_\-\.)]+@[(a-z0-9\_\-\.)]+\.[(a-z)]{2,4}$/.test(_this.$email.val())){
                valido = false;
                _this.$email.parent().find("span").addClass("error_message").text("El email es inválido");
            }

            if(_this.$password.val() !== _this.$repeat.val()){
                valido = false;
                _this.$password.parent().find("span").addClass("error_message").text("Las contraseñas deben ser iguales");
                _this.$repeat.parent().find("span").addClass("error_message").text("Las contraseñas deben ser iguales");
            }else if(_this.$password.val().length < 6 || _this.$password.val().length > 50){
                valido = false;
                _this.$password.parent().find("span").addClass("error_message").text("La contraseña debe tener entre 6 y 50 caracteres");
            }

            if(!$("input[type=radio]:checked").length){
                valido = false;
                $("input[type=radio]:first").parent().find("span").addClass("error_message").text("Debe indicar si es hombre o mujer");
            }

            if(!_this.$dia.val() || !_this.$mes.val() || !_this.$anio.val()){
                valido = false;
                _this.$dia.parent().find("span").addClass("error_message").text("Debe seleccionar una fecha válida");
            }else if(!_this.isFechaValida(_this.$dia.val(), _this.$mes.val(), _this.$anio.val())){
                valido = false;
                _this.$dia.parent().find("span").addClass("error_message").text("Debe seleccionar una fecha válida");
            }

            if(valido){
                json = {
                    username : _this.$nombreUsuario.val(),
                    email : _this.$email.val()
                };

                $.get("/registro/verificar_usuario/", json, function(res){
                    res = JSON.parse(res);
                    if(!res.existe){
                        me.submit();
                    }else{
                        valido = false;
                        _this.$nombreUsuario.parent().find("span").addClass("error_message").text(res.mensaje);
                    }
                });
            }
        };
    },

    validacionCampo: function(campo, tipo){
        var valido = true, $span;

        if(campo.val().trim() === ""){
            valido = false;
            $span = campo.parent().find("span");
            $span.addClass("error_message").text("Debe agregar " + tipo);
        }else if(campo.val().trim().length > 140){
            valido = false;
            $span = campo.parent().find("span");
            $span.addClass("error_message").text("El " + tipo + " debe poseer menos de 140 caracteres");
        }

        return valido;
    },

    isFechaValida: function(dia, mes, anio){
        function getDiaMaximo(m){
            var days = 31;

            if(m == 4 || m == 6 || m == 9 || m == 11){
                days = 30;
            }else if(m == 2){
                days = ((anio % 4 == 0) && (!(anio % 100 === 0) || anio % 400 === 0 )) ? 29 : 28;
            }

            return days;
        }

        return  parseInt(dia, 10) <= getDiaMaximo(mes);
    }
});
<|endoftext|>


<commit_before>// Copyright (c) 2020, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Document Naming Rule', {
	refresh: function(frm) {
		frm.trigger('document_type');
		frm.trigger("add_button");
	},
	document_type: (frm) => {
		// update the select field options with fieldnames
		if (frm.doc.document_type) {
			frappe.model.with_doctype(frm.doc.document_type, () => {
				let fieldnames = frappe.get_meta(frm.doc.document_type).fields
					.filter((d) => {
						return frappe.model.no_value_type.indexOf(d.fieldtype) === -1;
					}).map((d) => {
						return {label: `${d.label} (${d.fieldname})`, value: d.fieldname};
					});
				frm.fields_dict.conditions.grid.update_docfield_property(
					'field', 'options', fieldnames
				);
			});
		}
	},
	add_button: (frm) => {
		frm.add_custom_button(__('Update Counter'), function() {

			const fields = [{
				fieldtype: 'Data',
				fieldname: 'new_counter',
				label: __('New Counter'),
				default: frm.doc.counter,
				reqd: 1,
				description: __('This will update the counter and will affect all documents that will be created')
			}];

			let primary_action_label = __('Save');

			let primary_action = (fields) => {
				frappe.call({
					method: 'frappe.core.doctype.document_naming_rule.document_naming_rule.update_current',
					args: {
						name: frm.doc.name,
						new_counter: fields.new_counter
					},
					callback: function() {
						frm.set_value("counter",  fields.new_counter);
						dialog.hide();
					}
				});
			};

			var dialog = new frappe.ui.Dialog({
				title: __('Update Counter Value for Prefix: ' + frm.doc.prefix),
				fields,
				primary_action_label,
				primary_action
			});

			dialog.show();

		});
	}
});
<commit_msg>fix: do not show counter on unsaved forms<commit_after>// Copyright (c) 2020, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Document Naming Rule', {
	refresh: function(frm) {
		frm.trigger('document_type');
		if(!frm.doc.__islocal) frm.trigger("add_button");
	},
	document_type: (frm) => {
		// update the select field options with fieldnames
		if (frm.doc.document_type) {
			frappe.model.with_doctype(frm.doc.document_type, () => {
				let fieldnames = frappe.get_meta(frm.doc.document_type).fields
					.filter((d) => {
						return frappe.model.no_value_type.indexOf(d.fieldtype) === -1;
					}).map((d) => {
						return {label: `${d.label} (${d.fieldname})`, value: d.fieldname};
					});
				frm.fields_dict.conditions.grid.update_docfield_property(
					'field', 'options', fieldnames
				);
			});
		}
	},
	add_button: (frm) => {
		frm.add_custom_button(__('Update Counter'), function() {

			const fields = [{
				fieldtype: 'Data',
				fieldname: 'new_counter',
				label: __('New Counter'),
				default: frm.doc.counter,
				reqd: 1,
				description: __('This will update the counter and will affect all documents that will be created')
			}];

			let primary_action_label = __('Save');

			let primary_action = (fields) => {
				frappe.call({
					method: 'frappe.core.doctype.document_naming_rule.document_naming_rule.update_current',
					args: {
						name: frm.doc.name,
						new_counter: fields.new_counter
					},
					callback: function() {
						frm.set_value("counter",  fields.new_counter);
						dialog.hide();
					}
				});
			};

			var dialog = new frappe.ui.Dialog({
				title: __('Update Counter Value for Prefix: ' + frm.doc.prefix),
				fields,
				primary_action_label,
				primary_action
			});

			dialog.show();

		});
	}
});
<|endoftext|>


<commit_before>$(document).ready(function () {
    var sitemap_toggle = $('#sitemap-button');
    var sitemap_block = $('#footer');

    sitemap_toggle.click(function (e) {
        e.preventDefault();
        sitemap_block.toggleClass('sitemap-expanded');
    });

    var trigger_query   = ".EXLFacetList h3"; // Expand Trigger
    var parent_query    = ".EXLFacetList"; // Parent Block

    $(trigger_query).bind('click', function (e){
        $(this).closest(parent_query).toggleClass('expanded');
        e.preventDefault();
    });
});
<commit_msg>Deploying changes<commit_after>$(document).ready(function () {
    // Sitemap Expand
    var sitemap_toggle = $('#sitemap-button');
    var sitemap_block = $('#footer');

    sitemap_toggle.click(function (e) {
        e.preventDefault();
        sitemap_block.toggleClass('sitemap-expanded');
    });

    // Search Facets Expand
    var trigger_query   = ".EXLFacetList h3";
    var parent_query    = ".EXLFacetList";

    $(trigger_query).bind('click', function (e){
        $(this).closest(parent_query).toggleClass('expanded');
        e.preventDefault();
    });
});
<|endoftext|>


<commit_before>/*
 * Pointer Events Polyfill: Adds support for the style attribute "pointer-events: none" to browsers without this feature (namely, IE).
 * (c) 2013, Kent Mewhort, licensed under BSD. See LICENSE.txt for details.
 */

// constructor
function PointerEventsPolyfill(options){
    // set defaults
    this.options = {
        selector: '*',
        mouseEvents: ['click','dblclick','mousedown','mouseup'],
        usePolyfillIf: function(){
            if(navigator.appName == 'Microsoft Internet Explorer')
            {
                var agent = navigator.userAgent;
                if (agent.match(/MSIE ([0-9]{1,}[\.0-9]{0,})/) != null){
                    var version = parseFloat( RegExp.$1 );
                    if(version < 11)
                      return true;
                }
            }
            return false;
        }
    };
    if(options){
        var obj = this;
        $.each(options, function(k,v){
          obj.options[k] = v;
        });
    }

    if(this.options.usePolyfillIf())
      this.register_mouse_events();
}

// singleton initializer
PointerEventsPolyfill.initialize = function(options){
    if(PointerEventsPolyfill.singleton == null)
      PointerEventsPolyfill.singleton = new PointerEventsPolyfill(options);
    return PointerEventsPolyfill.singleton;
}

// handle mouse events w/ support for pointer-events: none
PointerEventsPolyfill.prototype.register_mouse_events = function(){
    // register on all elements (and all future elements) matching the selector
    $(document).on(this.options.mouseEvents.join(" "), this.options.selector, function(e){
       if($(this).css('pointer-events') == 'none'){
             // peak at the element below
             var origDisplayAttribute = $(this).css('display');
             $(this).css('display','none');

             var underneathElem = document.elementFromPoint(e.clientX, e.clientY);

            if(origDisplayAttribute)
                $(this)
                    .css('display', origDisplayAttribute);
            else
                $(this).css('display','');

             // fire the mouse event on the element below
            e.target = underneathElem;
            $(underneathElem).trigger(e);

            return false;
        }
        return true;
    });
}
<commit_msg>Close last declaration<commit_after>/*
 * Pointer Events Polyfill: Adds support for the style attribute "pointer-events: none" to browsers without this feature (namely, IE).
 * (c) 2013, Kent Mewhort, licensed under BSD. See LICENSE.txt for details.
 */

// constructor
function PointerEventsPolyfill(options){
    // set defaults
    this.options = {
        selector: '*',
        mouseEvents: ['click','dblclick','mousedown','mouseup'],
        usePolyfillIf: function(){
            if(navigator.appName == 'Microsoft Internet Explorer')
            {
                var agent = navigator.userAgent;
                if (agent.match(/MSIE ([0-9]{1,}[\.0-9]{0,})/) != null){
                    var version = parseFloat( RegExp.$1 );
                    if(version < 11)
                      return true;
                }
            }
            return false;
        }
    };
    if(options){
        var obj = this;
        $.each(options, function(k,v){
          obj.options[k] = v;
        });
    }

    if(this.options.usePolyfillIf())
      this.register_mouse_events();
}

// singleton initializer
PointerEventsPolyfill.initialize = function(options){
    if(PointerEventsPolyfill.singleton == null)
      PointerEventsPolyfill.singleton = new PointerEventsPolyfill(options);
    return PointerEventsPolyfill.singleton;
};

// handle mouse events w/ support for pointer-events: none
PointerEventsPolyfill.prototype.register_mouse_events = function(){
    // register on all elements (and all future elements) matching the selector
    $(document).on(this.options.mouseEvents.join(" "), this.options.selector, function(e){
       if($(this).css('pointer-events') == 'none'){
             // peak at the element below
             var origDisplayAttribute = $(this).css('display');
             $(this).css('display','none');

             var underneathElem = document.elementFromPoint(e.clientX, e.clientY);

            if(origDisplayAttribute)
                $(this)
                    .css('display', origDisplayAttribute);
            else
                $(this).css('display','');

             // fire the mouse event on the element below
            e.target = underneathElem;
            $(underneathElem).trigger(e);

            return false;
        }
        return true;
    });
};
<|endoftext|>


<commit_before>let books = [];

let Book = class Book {
	constructor(title, description) {
		this.id = newIndex();
		this.title = title;
		this.description = description;
	}
}

books.push(new Book('SUEÑOS DE ACERO Y NEON',
'Los personajes que protagonizan este relato sobreviven en una sociedad en decadencia a la que, no obstante, lograrán devolver la posibilidad de un futuro. Año 2484. En un mundo dominado por las grandes corporaciones, solo un hombre, Jordi Thompson, detective privado deslenguado y vividor, pero de gran talento y sentido d...'));
books.push(new Book('LA VIDA SECRETA DE LA MENTE',
'La vida secreta de la mentees un viaje especular que recorre el cerebro y el pensamiento: se trata de descubrir nuestra mente para entendernos hasta en los más pequeños rincones que componen lo que somos, cómo forjamos las ideas en los primeros días de vida, cómo damos forma a las decisiones que nos constituyen, cómo soñamos y cómo imaginamos, por qué sentimos ciertas emociones hacia los demás, cómo los demás influyen en nosotros, y cómo el cerebro se transforma y, con él, lo que somos.'));
books.push(new Book('CASI SIN QUERER',
'El amor algunas veces es tan complicado como impredecible. Pero al final lo que más valoramos son los detalles más simples, los más bonitos, los que llegan sin avisar. Y a la hora de escribir sobre sentimientos, no hay nada más limpio que hacerlo desde el corazón. Y eso hace Defreds en este libro.'));
books.push(new Book('TERMINAMOS Y OTROS POEMAS SIN TERMINAR',
'Recopilación de nuevos poemas, textos en prosa y pensamientos del autor. Un sabio dijo una vez: «Pocas cosas hipnotizan tanto en este mundo como una llama y como la luna, será porque no podemos cogerlas o porque nos iluminan en la penumbra». Realmente no sé si alguien dijo esta cita o me la acabo de inventar pero deberían de haberla escrito porque el poder hipnótico que ejercen esa mujer de rojo y esa dama blanca sobre el ser humano es digna de estudio.'));
books.push(new Book('LA LEGIÓN PERDIDA',
'En el año 53 a. C. el cónsul Craso cruzó el Éufrates para conquistar Oriente, pero su ejército fue destrozado en Carrhae. Una legión entera cayó prisionera de los partos. Nadie sabe a ciencia cierta qué pasó con aquella legión perdida.150 años después, Trajano está a punto de volver a cruzar el Éufrates. ...'));

function getBooks() {
	return books;
}

function getBook(id) {
	return books.find(book => book.id === +id);
}

function saveBook(book) {
	const index = books.findIndex(book => book.id === book.id);
	if (index) {
		books.splice(index, 1);
		books.splice(index, 0, book);
	} else {
		book.id = newIndex();
		books.push(book);
	}
	return book;
}

function newIndex() {
	let lastIndex = 0;

	for (const book of books) {
		lastIndex = book.id;
	}
	return lastIndex + 1;
}

function deleteBook(id) {
	const index = books.findIndex(book => book.id == id);
	console.log(index);
	books.splice(index, 1);
	return {};
}

function updateBook(id, title, description) {
	const index = books.findIndex(book => book.id == id);
	books[index].title = title;
	books[index].description = description;
	return books[index];
}

module.exports = Book;
module.exports.getBooks = getBooks;
module.exports.saveBook = saveBook;
module.exports.deleteBook = deleteBook;
module.exports.updateBook = updateBook;<commit_msg>Added GET /book/:id<commit_after>let books = [];

let Book = class Book {
	constructor(title, description) {
		this.id = newIndex();
		this.title = title;
		this.description = description;
	}
}

books.push(new Book('SUEÑOS DE ACERO Y NEON',
'Los personajes que protagonizan este relato sobreviven en una sociedad en decadencia a la que, no obstante, lograrán devolver la posibilidad de un futuro. Año 2484. En un mundo dominado por las grandes corporaciones, solo un hombre, Jordi Thompson, detective privado deslenguado y vividor, pero de gran talento y sentido d...'));
books.push(new Book('LA VIDA SECRETA DE LA MENTE',
'La vida secreta de la mentees un viaje especular que recorre el cerebro y el pensamiento: se trata de descubrir nuestra mente para entendernos hasta en los más pequeños rincones que componen lo que somos, cómo forjamos las ideas en los primeros días de vida, cómo damos forma a las decisiones que nos constituyen, cómo soñamos y cómo imaginamos, por qué sentimos ciertas emociones hacia los demás, cómo los demás influyen en nosotros, y cómo el cerebro se transforma y, con él, lo que somos.'));
books.push(new Book('CASI SIN QUERER',
'El amor algunas veces es tan complicado como impredecible. Pero al final lo que más valoramos son los detalles más simples, los más bonitos, los que llegan sin avisar. Y a la hora de escribir sobre sentimientos, no hay nada más limpio que hacerlo desde el corazón. Y eso hace Defreds en este libro.'));
books.push(new Book('TERMINAMOS Y OTROS POEMAS SIN TERMINAR',
'Recopilación de nuevos poemas, textos en prosa y pensamientos del autor. Un sabio dijo una vez: «Pocas cosas hipnotizan tanto en este mundo como una llama y como la luna, será porque no podemos cogerlas o porque nos iluminan en la penumbra». Realmente no sé si alguien dijo esta cita o me la acabo de inventar pero deberían de haberla escrito porque el poder hipnótico que ejercen esa mujer de rojo y esa dama blanca sobre el ser humano es digna de estudio.'));
books.push(new Book('LA LEGIÓN PERDIDA',
'En el año 53 a. C. el cónsul Craso cruzó el Éufrates para conquistar Oriente, pero su ejército fue destrozado en Carrhae. Una legión entera cayó prisionera de los partos. Nadie sabe a ciencia cierta qué pasó con aquella legión perdida.150 años después, Trajano está a punto de volver a cruzar el Éufrates. ...'));

function getBooks() {
	return books;
}

function getBook(id) {
	return books.find(book => book.id == +id);
}

function saveBook(book) {
	const index = books.findIndex(book => book.id === book.id);
	if (index) {
		books.splice(index, 1);
		books.splice(index, 0, book);
	} else {
		book.id = newIndex();
		books.push(book);
	}
	return book;
}

function newIndex() {
	let lastIndex = 0;

	for (const book of books) {
		lastIndex = book.id;
	}
	return lastIndex + 1;
}

function deleteBook(id) {
	const index = books.findIndex(book => book.id == id);
	console.log(index);
	books.splice(index, 1);
	return {};
}

function updateBook(id, title, description) {
	const index = books.findIndex(book => book.id == id);
	books[index].title = title;
	books[index].description = description;
	return books[index];
}

module.exports = Book;
module.exports.getBooks = getBooks;
module.exports.getBook = getBook;
module.exports.saveBook = saveBook;
module.exports.deleteBook = deleteBook;
module.exports.updateBook = updateBook;<|endoftext|>


<commit_before>import React from 'react';
import ComponentsSideBar from '../ComponentsSideBar';
import ComponentReview from '../ComponentReview';
import ModulePreview from '../ModulePreview';
import ComponentsHome from '../ComponentsHome';
import { clearErrors } from '../../services/errorReporter';
import _ from 'underscore';
import './index.scss';
import cloneRegExp from 'babel-standalone';

/**
 * @class App
 * Main component
 */
export default class App extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            showSideBar: true,
            usages: this.findComponentUsages(),
        };

        this.goToUrl = this.goToUrl.bind(this);
    }

    /**
     * update error state to blank when switching routes to only show errors of the current component
     * @param {object} nextProps
     */
    componentWillReceiveProps(nextProps) {
        if (this.props.match !== nextProps.match) {
            clearErrors();
        }
    }

    /**
     * Iterates every component documentation, and aggregates thier usages
     */
    findComponentUsages() {
        const { documentations } = this.props;

        return _.values(documentations).reduce((usages, { name, requires: currRequires = [] }) => {
            const childName = name[0].name;

            currRequires.forEach(currRequire => {
                if (!usages[currRequire.name]) {
                    usages[currRequire.name] = [];
                }
                usages[currRequire.name].push(childName);
            });

            return usages;
        }, {});
    }

    /**
     * Render component review for a specific component
     * @param {String} componentName
     */
    renderComponentReview(componentName) {
        const { documentations, components, componentsByModule, match, compiler, baseRoute } = this.props;
        const { usages } = this.state;

        return (

                <ComponentReview
                    components={components}
                    documentations={documentations}
                    usages={usages}
                    componentName={componentName}
                    compiler={compiler}
                    exampleIndex={match.params.exampleIndex}
                    changeExampleIndexInUrl={exampleIndexParam => this.props.history.push(`${baseRoute}${componentName}${exampleIndexParam}`)}
                    goToUrl={this.goToUrl} />

        );
    }

    /**
     * Render module preview showing all module's components,
     * or all library modules with their components
     * @param {String} moduleName
     * @param {Boolean} doesDataExist
     */
    renderModulePreview(moduleName, doesDataExist) {
        const { componentsByModule, components, documentations, compiler } = this.props;

        return (

                <ModulePreview
                    moduleName={doesDataExist ? moduleName : 'All'}
                    componentsByModule={componentsByModule}
                    components={components}
                    documentations={documentations}
                    compiler={compiler}
                    isMainModule={!doesDataExist}
                    goToUrl={this.goToUrl} />

        );
    }

    goToUrl(url) {
        const { baseRoute } = this.props;
        this.props.history.push(`${baseRoute}${encodeURIComponent(url)}`);
    }

    render() {
        const { documentations, components, componentsByModule, match, compiler, baseRoute } = this.props;
        const componentName = match.params.componentName;
        const showSideBarClassName = this.state.showSideBar ? ' show-side-bar' : '';

        const moduleName = decodeURIComponent(componentName);
        const isModule = _.includes(_.keys(componentsByModule), moduleName);
        const isComponent = (components[componentName] && !isModule);
        const isHome = !components[componentName] && !isModule;

        return (
            <div className={`library-_-app${showSideBarClassName}`}>
                <button
                    className="library-_-toggle-side-bar"
                    onClick={() => this.setState({ showSideBar: !this.state.showSideBar })} />

                    <ComponentsSideBar
                        components={components}
                        componentsByModule={componentsByModule}
                        selectedComponentName={componentName}
                        goToUrl={this.goToUrl} />

                {!!isComponent && this.renderComponentReview(componentName)}
                {!!isModule && this.renderModulePreview(moduleName, true)}
                {!!isHome && <ComponentsHome />}

        );
    }
}<commit_msg>#goodnessSquad<commit_after>import React from 'react';
import ComponentsSideBar from '../ComponentsSideBar';
import ComponentReview from '../ComponentReview';
import ModulePreview from '../ModulePreview';
import ComponentsHome from '../ComponentsHome';
import { clearErrors } from '../../services/errorReporter';
import _ from 'underscore';
import './index.scss';
import cloneRegExp from 'babel-standalone';

/**
 * @class App
 * Main component
 */
export default class App extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            showSideBar: true,
            usages: this.findComponentUsages(),
        };

        this.goToUrl = this.goToUrl.bind(this);
    }

    /**
     * update error state to blank when switching routes to only show errors of the current component
     * @param {object} nextProps
     */
    componentWillReceiveProps(nextProps) {
        if (this.props.match !== nextProps.match) {
            clearErrors();
        }
    }

    /**
     * Iterates every component documentation, and aggregates thier usages
     */
    findComponentUsages() {
        const { documentations } = this.props;

        return _.values(documentations).reduce((usages, { name, requires: currRequires = [] }) => {
            const childName = name[0].name;

            currRequires.forEach(currRequire => {
                if (!usages[currRequire.name]) {
                    usages[currRequire.name] = [];
                }
                usages[currRequire.name].push(childName);
            });

            return usages;
        }, {});
    }

    /**
     * Render component review for a specific component
     * @param {String} componentName
     */
    renderComponentReview(componentName) {
        const { documentations, components, componentsByModule, match, compiler, baseRoute } = this.props;
        const { usages } = this.state;

        return (

                <ComponentReview
                    components={components}
                    documentations={documentations}
                    usages={usages}
                    componentName={componentName}
                    compiler={compiler}
                    exampleIndex={match.params.exampleIndex}
                    changeExampleIndexInUrl={exampleIndexParam => this.props.history.push(`${baseRoute}${componentName}${exampleIndexParam}`)}
                    goToUrl={this.goToUrl} />

        );
    }

    /**
     * Render module preview showing all module's components,
     * or all library modules with their components
     * @param {String} moduleName
     * @param {Boolean} doesDataExist
     */
    renderModulePreview(moduleName, doesDataExist) {
        const { componentsByModule, components, documentations, compiler } = this.props;

        return (

                <ModulePreview
                    moduleName={doesDataExist ? moduleName : 'All'}
                    componentsByModule={componentsByModule}
                    components={components}
                    documentations={documentations}
                    compiler={compiler}
                    isMainModule={!doesDataExist}
                    goToUrl={this.goToUrl} />

        );
    }

    goToUrl(url) {
        const { baseRoute } = this.props;
        this.props.history.push(`${baseRoute}${encodeURIComponent(url)}`);
    }

    render() {
        const { documentations, components, componentsByModule, match, compiler, baseRoute } = this.props;
        const componentName = match.params.componentName;
        const showSideBarClassName = this.state.showSideBar ? ' show-side-bar' : '';

        const moduleName = decodeURIComponent(componentName);
        const isModule = _.includes(_.keys(componentsByModule), moduleName);
        const isComponent = (components[componentName] && !isModule);
        const isHome = !components[componentName] && !isModule;

        return (
            <div className={`library-_-app${showSideBarClassName}`}>
                <button
                    className="library-_-toggle-side-bar"
                    onClick={() => this.setState({ showSideBar: !this.state.showSideBar })} />

                    <ComponentsSideBar
                        components={components}
                        componentsByModule={componentsByModule}
                        selectedComponentName={componentName}
                        goToUrl={this.goToUrl} />

                {!!isComponent && this.renderComponentReview(componentName)}
                {!!isModule && this.renderModulePreview(moduleName, true)}
                {!!isHome && <ComponentsHome/>}

        );
    }
}<|endoftext|>


<commit_before>$(document).ready(function () {
    var sitemap_toggle = $('#sitemap-button');
    var sitemap_block = $('#footer');

    sitemap_toggle.click(function (e) {
        e.preventDefault();
        sitemap_block.toggleClass('sitemap-expanded');
    });

    var trigger_query   = ".some-class"; // Expand Trigger
    var parent_query    = ".parent-class"; // Parent Block

    $(trigger_query).bind('click', function (e){
        $(this).parent().find(parent_query).toggleClass('expanded');
        e.preventDefault();
    });
});
<commit_msg>Deploying changes<commit_after>$(document).ready(function () {
    var sitemap_toggle = $('#sitemap-button');
    var sitemap_block = $('#footer');

    sitemap_toggle.click(function (e) {
        e.preventDefault();
        sitemap_block.toggleClass('sitemap-expanded');
    });

    var trigger_query   = ".EXLFacetList h3"; // Expand Trigger
    var parent_query    = ".EXLFacetList"; // Parent Block

    $(trigger_query).bind('click', function (e){
        $(this).closest(parent_query).toggleClass('expanded');
        e.preventDefault();
    });
});
<|endoftext|>


<commit_before>import React from 'react'
import databaseUtils from './utils/DatabaseUtils'
import {userRequired} from '../ValidationUtils'

export default class User extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      errors: [],
      userInfo: null,
      userBooks: null,
      userComments: null,
    }
  }

  componentDidUpdate(prevProps, prevState){
    if (prevProps.userId !== this.props.userId){
      databaseUtils.getUserInfo(this.props.userId, this.userInfoCallback.bind(this))
    }
  }

  componentDidMount() {
    databaseUtils.getUserInfo(this.props.userId, this.userInfoCallback.bind(this))
  }

  userInfoCallback(userInfo){
    databaseUtils.getUserBooks(this.props.userId, this.userBooksCallback.bind(this), userInfo)
  }

  userBooksCallback(userBooks, userInfo){
    databaseUtils.getUserComments(this.props.userId, this.userCommentsCallback.bind(this), userInfo, userBooks)
  }

  userCommentsCallback(userComments, userInfo, userBooks){
    this.setState({
      userInfo: userInfo,
      userBooks: userBooks,
      userComments: userComments
    })
  }

  render() {
    const { showBookFn, deleteBookFn, loggedUser, userId, showUser} = this.props
    let userBooks = this.state.userBooks
    let userComments = this.state.userComments
    let userInfo = this.state.userInfo

    if (!userInfo || !userComments || !userBooks) {
      return (

)
    }

    return (

          {userInfo.username}

          Basic information

{'Username:'} {userInfo.username}

{'Email:'} {userInfo.email}

          Books

            {
              Object.keys(userBooks) && Object.keys(userBooks).length != 0 ?

                  {
                    Object.keys(userBooks).map(key =>
                      <UserBook
                        key={key}
                        data={userBooks[key]}
                        showFn={() => showBookFn(key)}
                        loggedUserId={loggedUser()}
                        deleteFn={() => {if (confirm('Are you sure?')) deleteBookFn(key)}}
                        showUser={showUser}
                      />
                    )
                  }

              :

{'You don\'t have any book yet...'}

            }

          Comments

        {
          Object.keys(userComments) && Object.keys(userComments).length != 0 ?
              Object.keys(userComments).map(key =>
                <UsrComment
                  key = {key}
                  data ={userComments[key]}
                  showFn = {() => showBookFn(userComments[key].bookId)}
                />
              )
            :

{'You don\'t have any comment of book yet...'}

        }

    )
  }
}

const UserBook = ({key, data: {title, author, year, description, userId, lend: {lend, lendUserName, lendUserId}}, showFn, deleteFn, loggedUserId, showUser}) => (

          {title} {lend ?   {showUser(lendUserId)}} style={{color: '#ed6c63'}}> {': is lend to'} - {lendUserName}     : ''}

{author} - {year}

          {description}

        Show
        Edit
        {userId === loggedUserId && !lend &&
        Delete
        }

)

const UsrComment = ({key, data: {authorId, date, text, book: {title, author}}, showFn}) => (

         {author} - {title}

{text}

{new Date(date).toLocaleString()}

)<commit_msg>only owner of book can see who have his book<commit_after>import React from 'react'
import databaseUtils from './utils/DatabaseUtils'
import {userRequired} from '../ValidationUtils'

export default class User extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      errors: [],
      userInfo: null,
      userBooks: null,
      userComments: null,
    }
  }

  componentDidUpdate(prevProps, prevState){
    if (prevProps.userId !== this.props.userId){
      databaseUtils.getUserInfo(this.props.userId, this.userInfoCallback.bind(this))
    }
  }

  componentDidMount() {
    databaseUtils.getUserInfo(this.props.userId, this.userInfoCallback.bind(this))
  }

  userInfoCallback(userInfo){
    databaseUtils.getUserBooks(this.props.userId, this.userBooksCallback.bind(this), userInfo)
  }

  userBooksCallback(userBooks, userInfo){
    databaseUtils.getUserComments(this.props.userId, this.userCommentsCallback.bind(this), userInfo, userBooks)
  }

  userCommentsCallback(userComments, userInfo, userBooks){
    this.setState({
      userInfo: userInfo,
      userBooks: userBooks,
      userComments: userComments
    })
  }

  render() {
    const { showBookFn, deleteBookFn, loggedUser, userId, showUser} = this.props
    let userBooks = this.state.userBooks
    let userComments = this.state.userComments
    let userInfo = this.state.userInfo

    if (!userInfo || !userComments || !userBooks) {
      return (

)
    }

    return (

          {userInfo.username}

          Basic information

{'Username:'} {userInfo.username}

{'Email:'} {userInfo.email}

          Books

            {
              Object.keys(userBooks) && Object.keys(userBooks).length != 0 ?

                  {
                    Object.keys(userBooks).map(key =>
                      <UserBook
                        key={key}
                        data={userBooks[key]}
                        showFn={() => showBookFn(key)}
                        loggedUserId={loggedUser()}
                        deleteFn={() => {if (confirm('Are you sure?')) deleteBookFn(key)}}
                        showUser={showUser}
                      />
                    )
                  }

              :

{'You don\'t have any book yet...'}

            }

          Comments

        {
          Object.keys(userComments) && Object.keys(userComments).length != 0 ?
              Object.keys(userComments).map(key =>
                <UsrComment
                  key = {key}
                  data ={userComments[key]}
                  showFn = {() => showBookFn(userComments[key].bookId)}
                />
              )
            :

{'You don\'t have any comment of book yet...'}

        }

    )
  }
}

const UserBook = ({key, data: {title, author, year, description, userId, lend: {lend, lendUserName, lendUserId}}, showFn, deleteFn, loggedUserId, showUser}) => (

          {title} {lend && loggedUserId == userId ?   {showUser(lendUserId)}} style={{color: '#ed6c63'}}> {': is lend to'} - {lendUserName}     : ''}

{author} - {year}

          {description}

        Show
        Edit
        {userId === loggedUserId && !lend &&
        Delete
        }

)

const UsrComment = ({key, data: {authorId, date, text, book: {title, author}}, showFn}) => (

         {author} - {title}

{text}

{new Date(date).toLocaleString()}

)<|endoftext|>