<commit_before>/*
 * RSpec WebKit Formatter javascript
 * $Id$
 *
 * Most of these functions were converted from Safari's Web Inspector, which is licensed
 * under the following terms:
 *
 *   Copyright (C) 2007 Apple Inc.  All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *   1.  Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *   2.  Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *   3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 *   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 *   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * All mistakes and modifications are (c) 2009 Michael Granger, and are distributed under
 * the same terms as the original.
 *
 */

var polltimer = null;

function fade_out_rect( ctx, x, y, w, h, a1, a2 ) {
    ctx.save();

    var gradient = ctx.createLinearGradient(x, y, x, y + h);
    gradient.addColorStop(0.0, "rgba(0, 0, 0, " + (1.0 - a1) + ")");
    gradient.addColorStop(0.8, "rgba(0, 0, 0, " + (1.0 - a2) + ")");
    gradient.addColorStop(1.0, "rgba(0, 0, 0, 1.0)");

    ctx.globalCompositeOperation = "destination-out";

    ctx.fillStyle = gradient;
    ctx.fillRect(x, y, w, h);

    ctx.restore();
}

function draw_summary_graph( selector, segments ) {
	var graphElement = $(selector).get( 0 );
	var i = 0;

    if (!graphElement)
        return;

    if (!segments || !segments.length) {
        segments = [{color: "white", value: 1}];
        // this._showingEmptySummaryGraph = true;
    } // else
     //        delete this._showingEmptySummaryGraph;

    // Calculate the total of all segments.
    var total = 0;
    for (i = 0; i < segments.length; ++i)
        total += segments[i].value;

    // Calculate the percentage of each segment, rounded to the nearest percent.
    var percents = segments.map(function(s) {
		return Math.max(Math.round(100 * s.value / total), 1);
	});

    // Calculate the total percentage.
    var percentTotal = 0;
    for (i = 0; i < percents.length; ++i)
        percentTotal += percents[i];

    // Make sure our percentage total is not greater-than 100, it can be greater
    // if we rounded up for a few segments.
    while (percentTotal > 100) {
        for (i = 0; i < percents.length && percentTotal > 100; ++i) {
            if (percents[i] > 1) {
                --percents[i];
                --percentTotal;
            }
        }
    }

    // Make sure our percentage total is not less-than 100, it can be less
    // if we rounded down for a few segments.
    while (percentTotal < 100) {
        for (i = 0; i < percents.length && percentTotal < 100; ++i) {
            ++percents[i];
            ++percentTotal;
        }
    }

    var ctx = graphElement.getContext("2d");

    var x = 0;
    var y = 0;
    var w = 450;
    var h = 19;
    var r = (h / 2);

    function drawPillShadow()
    {
        // This draws a line with a shadow that is offset away from the line. The line is stroked
        // twice with different X shadow offsets to give more feathered edges. Later we erase the
        // line with destination-out 100% transparent black, leaving only the shadow. This only
        // works if nothing has been drawn into the canvas yet.

        ctx.beginPath();
        ctx.moveTo(x + 4, y + h - 3 - 0.5);
        ctx.lineTo(x + w - 4, y + h - 3 - 0.5);
        ctx.closePath();

        ctx.save();

        ctx.shadowBlur = 2;
        ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
        ctx.shadowOffsetX = 3;
        ctx.shadowOffsetY = 5;

        ctx.strokeStyle = "white";
        ctx.lineWidth = 1;

        ctx.stroke();

        ctx.shadowOffsetX = -3;

        ctx.stroke();

        ctx.restore();

        ctx.save();

        ctx.globalCompositeOperation = "destination-out";
        ctx.strokeStyle = "rgba(0, 0, 0, 1)";
        ctx.lineWidth = 1;

        ctx.stroke();

        ctx.restore();
    }

    function drawPill()
    {
        // Make a rounded rect path.
        ctx.beginPath();
        ctx.moveTo(x, y + r);
        ctx.lineTo(x, y + h - r);
        ctx.quadraticCurveTo(x, y + h, x + r, y + h);
        ctx.lineTo(x + w - r, y + h);
        ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - r);
        ctx.lineTo(x + w, y + r);
        ctx.quadraticCurveTo(x + w, y, x + w - r, y);
        ctx.lineTo(x + r, y);
        ctx.quadraticCurveTo(x, y, x, y + r);
        ctx.closePath();

        // Clip to the rounded rect path.
        ctx.save();
        ctx.clip();

        // Fill the segments with the associated color.
        var previousSegmentsWidth = 0;
        for (var i = 0; i < segments.length; ++i) {
            var segmentWidth = Math.round(w * percents[i] / 100);
            ctx.fillStyle = segments[i].color;
            ctx.fillRect(x + previousSegmentsWidth, y, segmentWidth, h);
            previousSegmentsWidth += segmentWidth;
        }

        // Draw the segment divider lines.
        ctx.lineWidth = 1;
        for (i = 1; i < 20; ++i) {
            ctx.beginPath();
            ctx.moveTo(x + (i * Math.round(w / 20)) + 0.5, y);
            ctx.lineTo(x + (i * Math.round(w / 20)) + 0.5, y + h);
            ctx.closePath();

            ctx.strokeStyle = "rgba(0, 0, 0, 0.2)";
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(x + (i * Math.round(w / 20)) + 1.5, y);
            ctx.lineTo(x + (i * Math.round(w / 20)) + 1.5, y + h);
            ctx.closePath();

            ctx.strokeStyle = "rgba(255, 255, 255, 0.2)";
            ctx.stroke();
        }

        // Draw the pill shading.
        var lightGradient = ctx.createLinearGradient(x, y, x, y + (h / 1.5));
        lightGradient.addColorStop(0.0, "rgba(220, 220, 220, 0.6)");
        lightGradient.addColorStop(0.4, "rgba(220, 220, 220, 0.2)");
        lightGradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)");

        var darkGradient = ctx.createLinearGradient(x, y + (h / 3), x, y + h);
        darkGradient.addColorStop(0.0, "rgba(0, 0, 0, 0.0)");
        darkGradient.addColorStop(0.8, "rgba(0, 0, 0, 0.2)");
        darkGradient.addColorStop(1.0, "rgba(0, 0, 0, 0.5)");

        ctx.fillStyle = darkGradient;
        ctx.fillRect(x, y, w, h);

        ctx.fillStyle = lightGradient;
        ctx.fillRect(x, y, w, h);

        ctx.restore();
    }

    ctx.clearRect(x, y, w, (h * 2));

    drawPillShadow();
    drawPill();

    ctx.save();

    ctx.translate(0, (h * 2) + 1);
    ctx.scale(1, -1);

    drawPill();

    ctx.restore();

    fade_out_rect(ctx, x, y + h + 1, w, h, 0.5, 0.0);
}

function draw_swatch( canvas, color ) {
    var ctx = canvas.getContext("2d");

    function draw_swatch_square() {
        ctx.fillStyle = color;
        ctx.fillRect(0, 0, 13, 13);

        var gradient = ctx.createLinearGradient(0, 0, 13, 13);
        gradient.addColorStop(0.0, "rgba(255, 255, 255, 0.2)");
        gradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)");

        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, 13, 13);

        gradient = ctx.createLinearGradient(13, 13, 0, 0);
        gradient.addColorStop(0.0, "rgba(0, 0, 0, 0.2)");
        gradient.addColorStop(1.0, "rgba(0, 0, 0, 0.0)");

        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, 13, 13);

        ctx.strokeStyle = "rgba(0, 0, 0, 0.6)";
        ctx.strokeRect(0.5, 0.5, 12, 12);
    }

    ctx.clearRect(0, 0, 13, 24);

    draw_swatch_square();

    ctx.save();

    ctx.translate(0, 25);
    ctx.scale(1, -1);

    draw_swatch_square();

    ctx.restore();

    fade_out_rect(ctx, 0, 13, 13, 13, 0.5, 0.0);
}

function make_legend_element( label, value, color ) {
    var legendElement = document.createElement("label");
    legendElement.className = "rspec-graph-legend-item " + label.replace(/\W+/g, '-');

    if (color) {
        var swatch = document.createElement("canvas");
        swatch.className = "rspec-graph-legend-swatch";
        swatch.setAttribute("width", "13");
        swatch.setAttribute("height", "24");

        legendElement.appendChild(swatch);

        draw_swatch(swatch, color);
    }

    var labelElement = document.createElement("div");
    labelElement.className = "rspec-graph-legend-label";
    legendElement.appendChild(labelElement);

    var headerElement = document.createElement("div");
    headerElement.className = "rspec-graph-legend-header";
    headerElement.textContent = label;
    labelElement.appendChild(headerElement);

    var valueElement = document.createElement("div");
    valueElement.className = "rspec-graph-legend-value";
    valueElement.textContent = value;
    labelElement.appendChild(valueElement);

    return legendElement;
}

function update_summary_graph() {
	var total = $('#spec-count').eq(0).text();
	var graphInfo = {
		passed: $('.spec.passed'),
		pending: $('.spec.pending'),
		failed: $('.spec.failed'),
		'pending:fixed': $('.spec.pending-fixed'),
		total: parseInt( total, 10 )
	};

    var categoryOrder = ["passed", "pending", "pending:fixed", "failed"];
	var categoryColors = {
		passed: {r: 0, g: 157, b: 37},            // #009D25
		pending: {r: 227, g: 184, b: 0},          // #E3B800
		'pending:fixed': {r: 9, g: 60, b: 226},   // #093CE2
		failed: {r: 192, g: 0, b: 0}              // #C00000
	};
    var fillSegments = [];
	var doneCount = 0;

	var legendElement = $( '#rspec-summary-graph-legend' );
	legendElement.empty();

	jQuery.each( categoryOrder, function(i, val) {
        var category = categoryOrder[i];
        var size = graphInfo[ category ].length;
        if (!size) return true;
		doneCount += size;

        var color = categoryColors[category];
        var colorString = "rgb(" + color.r + ", " + color.g + ", " + color.b + ")";

        var fillSegment = {color: colorString, value: size};
        fillSegments.push(fillSegment);

        var legendLabel = make_legend_element( category, size, colorString );
        legendElement.append( legendLabel );
    });

    if ( graphInfo.total ) {
        var totalLegendLabel = make_legend_element( "remaining", graphInfo.total - doneCount );
        // totalLegendLabel.addStyleClass( "total" );
        legendElement.append( totalLegendLabel );
		if ( doneCount < graphInfo.total ) {
			var fillSegment = { color: 'rgb(254,254,254)', value: graphInfo.total - doneCount };
			console.log( "Adding segment for examples yet to run: " + fillSegment.toString() );
			fillSegments.push( fillSegment );
		}
    }

	draw_summary_graph( '#rspec-summary-graph', fillSegments );
}

function toggle_spec_status() {
	var status = $(this).attr( 'class' ).match( /(passed|pending(?:-fixed)?|failed)/ )[0];
	console.log( "Looking for specs with class '" + status + "'." );
	$(this).toggleClass( 'hidden' );
	$( '.spec.' + status ).toggle('fast');
}

function hook_legend_clickables() {
	$('label.passed').click( toggle_spec_status );
	$('label.pending').click( toggle_spec_status );
	$('label.pending-fixed').click( toggle_spec_status );
	$('label.failed').click( toggle_spec_status );
}

function hook_log_clickables() {
	$('.spec:has(div.log-messages)').each( function() {
		$(this).addClass( 'logged' );
	}).find( '.spec-name' ).click( function(e) {
		$(this).parent().find('div.log-messages').toggle();
	});
}

$(document).ready(function() {
	console.log( "Document is ready." );
	clearInterval( polltimer );

	$('#rspec-summary-stats').html( 'Finished in ' + $('#summary .duration').html() );

	update_summary_graph();
	hook_legend_clickables();
	hook_log_clickables();
});

polltimer = setInterval( update_summary_graph, 250 );

<commit_msg>Invert the sense of toggling with the test status labels<commit_after>/*
 * RSpec WebKit Formatter javascript
 * $Id$
 *
 * Most of these functions were converted from Safari's Web Inspector, which is licensed
 * under the following terms:
 *
 *   Copyright (C) 2007 Apple Inc.  All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *   1.  Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *   2.  Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *   3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 *   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *   DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 *   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * All mistakes and modifications are (c) 2009 Michael Granger, and are distributed under
 * the same terms as the original.
 *
 */

var polltimer = null;

function fade_out_rect( ctx, x, y, w, h, a1, a2 ) {
    ctx.save();

    var gradient = ctx.createLinearGradient(x, y, x, y + h);
    gradient.addColorStop(0.0, "rgba(0, 0, 0, " + (1.0 - a1) + ")");
    gradient.addColorStop(0.8, "rgba(0, 0, 0, " + (1.0 - a2) + ")");
    gradient.addColorStop(1.0, "rgba(0, 0, 0, 1.0)");

    ctx.globalCompositeOperation = "destination-out";

    ctx.fillStyle = gradient;
    ctx.fillRect(x, y, w, h);

    ctx.restore();
}

function draw_summary_graph( selector, segments ) {
	var graphElement = $(selector).get( 0 );
	var i = 0;

    if (!graphElement)
        return;

    if (!segments || !segments.length) {
        segments = [{color: "white", value: 1}];
        // this._showingEmptySummaryGraph = true;
    } // else
     //        delete this._showingEmptySummaryGraph;

    // Calculate the total of all segments.
    var total = 0;
    for (i = 0; i < segments.length; ++i)
        total += segments[i].value;

    // Calculate the percentage of each segment, rounded to the nearest percent.
    var percents = segments.map(function(s) {
		return Math.max(Math.round(100 * s.value / total), 1);
	});

    // Calculate the total percentage.
    var percentTotal = 0;
    for (i = 0; i < percents.length; ++i)
        percentTotal += percents[i];

    // Make sure our percentage total is not greater-than 100, it can be greater
    // if we rounded up for a few segments.
    while (percentTotal > 100) {
        for (i = 0; i < percents.length && percentTotal > 100; ++i) {
            if (percents[i] > 1) {
                --percents[i];
                --percentTotal;
            }
        }
    }

    // Make sure our percentage total is not less-than 100, it can be less
    // if we rounded down for a few segments.
    while (percentTotal < 100) {
        for (i = 0; i < percents.length && percentTotal < 100; ++i) {
            ++percents[i];
            ++percentTotal;
        }
    }

    var ctx = graphElement.getContext("2d");

    var x = 0;
    var y = 0;
    var w = 450;
    var h = 19;
    var r = (h / 2);

    function drawPillShadow()
    {
        // This draws a line with a shadow that is offset away from the line. The line is stroked
        // twice with different X shadow offsets to give more feathered edges. Later we erase the
        // line with destination-out 100% transparent black, leaving only the shadow. This only
        // works if nothing has been drawn into the canvas yet.

        ctx.beginPath();
        ctx.moveTo(x + 4, y + h - 3 - 0.5);
        ctx.lineTo(x + w - 4, y + h - 3 - 0.5);
        ctx.closePath();

        ctx.save();

        ctx.shadowBlur = 2;
        ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
        ctx.shadowOffsetX = 3;
        ctx.shadowOffsetY = 5;

        ctx.strokeStyle = "white";
        ctx.lineWidth = 1;

        ctx.stroke();

        ctx.shadowOffsetX = -3;

        ctx.stroke();

        ctx.restore();

        ctx.save();

        ctx.globalCompositeOperation = "destination-out";
        ctx.strokeStyle = "rgba(0, 0, 0, 1)";
        ctx.lineWidth = 1;

        ctx.stroke();

        ctx.restore();
    }

    function drawPill()
    {
        // Make a rounded rect path.
        ctx.beginPath();
        ctx.moveTo(x, y + r);
        ctx.lineTo(x, y + h - r);
        ctx.quadraticCurveTo(x, y + h, x + r, y + h);
        ctx.lineTo(x + w - r, y + h);
        ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - r);
        ctx.lineTo(x + w, y + r);
        ctx.quadraticCurveTo(x + w, y, x + w - r, y);
        ctx.lineTo(x + r, y);
        ctx.quadraticCurveTo(x, y, x, y + r);
        ctx.closePath();

        // Clip to the rounded rect path.
        ctx.save();
        ctx.clip();

        // Fill the segments with the associated color.
        var previousSegmentsWidth = 0;
        for (var i = 0; i < segments.length; ++i) {
            var segmentWidth = Math.round(w * percents[i] / 100);
            ctx.fillStyle = segments[i].color;
            ctx.fillRect(x + previousSegmentsWidth, y, segmentWidth, h);
            previousSegmentsWidth += segmentWidth;
        }

        // Draw the segment divider lines.
        ctx.lineWidth = 1;
        for (i = 1; i < 20; ++i) {
            ctx.beginPath();
            ctx.moveTo(x + (i * Math.round(w / 20)) + 0.5, y);
            ctx.lineTo(x + (i * Math.round(w / 20)) + 0.5, y + h);
            ctx.closePath();

            ctx.strokeStyle = "rgba(0, 0, 0, 0.2)";
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(x + (i * Math.round(w / 20)) + 1.5, y);
            ctx.lineTo(x + (i * Math.round(w / 20)) + 1.5, y + h);
            ctx.closePath();

            ctx.strokeStyle = "rgba(255, 255, 255, 0.2)";
            ctx.stroke();
        }

        // Draw the pill shading.
        var lightGradient = ctx.createLinearGradient(x, y, x, y + (h / 1.5));
        lightGradient.addColorStop(0.0, "rgba(220, 220, 220, 0.6)");
        lightGradient.addColorStop(0.4, "rgba(220, 220, 220, 0.2)");
        lightGradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)");

        var darkGradient = ctx.createLinearGradient(x, y + (h / 3), x, y + h);
        darkGradient.addColorStop(0.0, "rgba(0, 0, 0, 0.0)");
        darkGradient.addColorStop(0.8, "rgba(0, 0, 0, 0.2)");
        darkGradient.addColorStop(1.0, "rgba(0, 0, 0, 0.5)");

        ctx.fillStyle = darkGradient;
        ctx.fillRect(x, y, w, h);

        ctx.fillStyle = lightGradient;
        ctx.fillRect(x, y, w, h);

        ctx.restore();
    }

    ctx.clearRect(x, y, w, (h * 2));

    drawPillShadow();
    drawPill();

    ctx.save();

    ctx.translate(0, (h * 2) + 1);
    ctx.scale(1, -1);

    drawPill();

    ctx.restore();

    fade_out_rect(ctx, x, y + h + 1, w, h, 0.5, 0.0);
}

function draw_swatch( canvas, color ) {
    var ctx = canvas.getContext("2d");

    function draw_swatch_square() {
        ctx.fillStyle = color;
        ctx.fillRect(0, 0, 13, 13);

        var gradient = ctx.createLinearGradient(0, 0, 13, 13);
        gradient.addColorStop(0.0, "rgba(255, 255, 255, 0.2)");
        gradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)");

        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, 13, 13);

        gradient = ctx.createLinearGradient(13, 13, 0, 0);
        gradient.addColorStop(0.0, "rgba(0, 0, 0, 0.2)");
        gradient.addColorStop(1.0, "rgba(0, 0, 0, 0.0)");

        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, 13, 13);

        ctx.strokeStyle = "rgba(0, 0, 0, 0.6)";
        ctx.strokeRect(0.5, 0.5, 12, 12);
    }

    ctx.clearRect(0, 0, 13, 24);

    draw_swatch_square();

    ctx.save();

    ctx.translate(0, 25);
    ctx.scale(1, -1);

    draw_swatch_square();

    ctx.restore();

    fade_out_rect(ctx, 0, 13, 13, 13, 0.5, 0.0);
}

function make_legend_element( label, value, color ) {
    var legendElement = document.createElement("label");
    legendElement.className = "rspec-graph-legend-item " + label.replace(/\W+/g, '-');

    if (color) {
        var swatch = document.createElement("canvas");
        swatch.className = "rspec-graph-legend-swatch";
        swatch.setAttribute("width", "13");
        swatch.setAttribute("height", "24");

        legendElement.appendChild(swatch);

        draw_swatch(swatch, color);
    }

    var labelElement = document.createElement("div");
    labelElement.className = "rspec-graph-legend-label";
    legendElement.appendChild(labelElement);

    var headerElement = document.createElement("div");
    headerElement.className = "rspec-graph-legend-header";
    headerElement.textContent = label;
    labelElement.appendChild(headerElement);

    var valueElement = document.createElement("div");
    valueElement.className = "rspec-graph-legend-value";
    valueElement.textContent = value;
    labelElement.appendChild(valueElement);

    return legendElement;
}

function update_summary_graph() {
	var total = $('#spec-count').eq(0).text();
	var graphInfo = {
		passed: $('.spec.passed'),
		pending: $('.spec.pending'),
		failed: $('.spec.failed'),
		'pending:fixed': $('.spec.pending-fixed'),
		total: parseInt( total, 10 )
	};

    var categoryOrder = ["passed", "pending", "pending:fixed", "failed"];
	var categoryColors = {
		passed: {r: 0, g: 157, b: 37},            // #009D25
		pending: {r: 227, g: 184, b: 0},          // #E3B800
		'pending:fixed': {r: 9, g: 60, b: 226},   // #093CE2
		failed: {r: 192, g: 0, b: 0}              // #C00000
	};
    var fillSegments = [];
	var doneCount = 0;

	var legendElement = $( '#rspec-summary-graph-legend' );
	legendElement.empty();

	jQuery.each( categoryOrder, function(i, val) {
        var category = categoryOrder[i];
        var size = graphInfo[ category ].length;
        if (!size) return true;
		doneCount += size;

        var color = categoryColors[category];
        var colorString = "rgb(" + color.r + ", " + color.g + ", " + color.b + ")";

        var fillSegment = {color: colorString, value: size};
        fillSegments.push(fillSegment);

        var legendLabel = make_legend_element( category, size, colorString );
        legendElement.append( legendLabel );
    });

    if ( graphInfo.total ) {
        var totalLegendLabel = make_legend_element( "remaining", graphInfo.total - doneCount );
        // totalLegendLabel.addStyleClass( "total" );
        legendElement.append( totalLegendLabel );
		if ( doneCount < graphInfo.total ) {
			var fillSegment = { color: 'rgb(254,254,254)', value: graphInfo.total - doneCount };
			console.log( "Adding segment for examples yet to run: " + fillSegment.toString() );
			fillSegments.push( fillSegment );
		}
    }

	draw_summary_graph( '#rspec-summary-graph', fillSegments );
}

function toggle_spec_status() {
	var status = $(this).attr( 'class' ).match( /(passed|pending(?:-fixed)?|failed)/ )[0];
	console.log( "Looking for specs with class '" + status + "'." );

	$( this ).removeClass( 'hidden' );
	$( 'label' ).not( this ).toggleClass( 'hidden' );
	$( '.spec.' + status ).show('fast');
	$( '.spec' ).not( '.' + status ).toggle('fast');
}

function hook_legend_clickables() {
	$('label.passed').click( toggle_spec_status );
	$('label.pending').click( toggle_spec_status );
	$('label.pending-fixed').click( toggle_spec_status );
	$('label.failed').click( toggle_spec_status );
}

function hook_log_clickables() {
	$('.spec:has(div.log-messages)').each( function() {
		$(this).addClass( 'logged' );
	}).find( '.spec-name' ).click( function(e) {
		$(this).parent().find('div.log-messages').toggle();
	});
}

$(document).ready(function() {
	console.log( "Document is ready." );
	clearInterval( polltimer );

	$('#rspec-summary-stats').html( 'Finished in ' + $('#summary .duration').html() );

	update_summary_graph();
	hook_legend_clickables();
	hook_log_clickables();
});

polltimer = setInterval( update_summary_graph, 250 );

<|endoftext|>


<commit_before>/*
 * General (idempotent) DB migration script goes here.
 * Please try to avoid using migration script and instead make special treatment in the code!
 */

// =============================== Helper functions ============================

function addFieldToConfig(lastConf, fieldConf, entityName) {
	var fields = lastConf[entityName]['fields'];
	var found = false;
	for (var field_key in fields) {
		if (fields[field_key].field_name === fieldConf.field_name) {
			found = true;
		}
	}
	if(!found) {
		fields.push(fieldConf);
	}
	lastConf[entityName]['fields'] = fields;

	return lastConf;
}

// =============================================================================

// BRCD-1077 Add new custom 'tariff_category' field to Products(Rates).
var lastConfig = db.config.find().sort({_id: -1}).limit(1).pretty()[0];
delete lastConfig['_id'];
var fields = lastConfig['rates']['fields'];
var found = false;
for (var field_key in fields) {
	if (fields[field_key].field_name === "tariff_category") {
		found = true;
	}
}
if(!found) {
	fields.push({
		"system":false,
		"select_list":true,
		"display":true,
		"editable":true,
		"field_name":"tariff_category",
		"default_value":"retail",
		"show_in_list":true,
		"title":"Tariff category",
		"mandatory":true,
		"select_options":"retail",
		"changeable_props": ["select_options"]
	});
}
lastConfig['rates']['fields'] = fields;

// BRCD-1078: add rate categories
for (var i in lastConfig['file_types']) {
	var firstKey = Object.keys(lastConfig['file_types'][i]['rate_calculators'])[0];
	var secKey = Object.keys(lastConfig['file_types'][i]['rate_calculators'][firstKey])[0];
	if (secKey == 0) {
		lastConfig['file_types'][i]['rate_calculators']['retail'] = {};
	for (var usaget in lastConfig['file_types'][i]['rate_calculators']) {
			if (usaget === 'retail') {
				continue;
			}
			lastConfig['file_types'][i]['rate_calculators']['retail'][usaget] = lastConfig['file_types'][i]['rate_calculators'][usaget];
			delete lastConfig['file_types'][i]['rate_calculators'][usaget];
		}
	}
}

// BRCD-1077 update all products(Rates) tariff_category field.
db.rates.update({'tariff_category': {$exists: false}},{$set:{'tariff_category':'retail'}},{multi:1});

// BRCD-938: Option to not generate pdfs for the cycle
if (typeof lastConfig['billrun']['generate_pdf']  === 'undefined') {
	lastConfig['billrun']['generate_pdf'] = {"v": true ,"t" : "Boolean"};
}

// BRCD-441 -Add plugin support
if (!lastConfig['plugins']) {
	lastConfig.plugins = ["calcCpuPlugin", "csiPlugin", "autorenewPlugin"];
}

//-------------------------------------------------------------------
// BRCD-1278 - backward support for new template
if(lastConfig.invoice_export) {
	lastConfig.invoice_export.header = "/application/views/invoices/header/header_tpl.html";
	lastConfig.invoice_export.footer = "/application/views/invoices/footer/footer_tpl.html";
}

//BRCD-1229 - Input processor re-enabled when not requested
for (var i in lastConfig['file_types']) {
	if (lastConfig['file_types'][i]['enabled'] === undefined) {
		lastConfig['file_types'][i]['enabled'] = true;
	}
}

// BRCD-1278 : add minutes:seconds support  for time display
var found =false;
for(var i in lastConfig["property_types"][0]["uom"]) {
		if(lastConfig["property_types"][0]["uom"][i]['name'] == "mmss" ) {
				found = true;
		}
}
if(!found) {
		lastConfig["property_types"][0]["uom"].push({"name":"mmss","label":"mm:ss","function_name":"parseTime","arguments":{"format":"_I:s"}});
}
lastConfig["property_types"][0]['invoice_uom'] = "mmss";

// BRCD-1152: Add service activation date to each cdr generated on the billing cycle
if(!lastConfig['lines']) {
	lastConfig['lines'] = {};
}
if(!lastConfig['lines']['fields']) {
	lastConfig['lines']['fields'] = [];
}
var idx = 0;
for (var i in lastConfig['lines']['fields']) {
	if (lastConfig['lines']['fields'][i]['field_name'] == 'foreign.activation_date') {
		idx = i;
		break;
	}
	idx = i+1;
}
var addField = {
	field_name : "foreign.activation_date",
	foreign : {
		entity : "service",
		field  :"start",
		translate : {
			type : "unixTimeToString",
			format : "Y-m-d H:i:s"
		}
	}
};
if(lastConfig['lines']['fields'].length > idx) {
	lastConfig['lines']['fields'][idx] = addField;
} else {
	lastConfig['lines']['fields'].push(addField);
}

//BRCD-1324 - Save CreditGuard last 4 digits in the account active payment gateway field
db.subscribers.find({type:"account", 'payment_gateway.active.name':"CreditGuard"}).forEach(
		function(obj) {
			var activeGateway = obj.payment_gateway.active;
			var token = activeGateway.card_token;
			var fourDigits = token.substring(token.length - 4, token.length);
			activeGateway.four_digits = fourDigits;
			db.subscribers.save(obj)
		}
)

// BRCD-1353: CreditGuard fixes
var paymentGateways = lastConfig['payment_gateways'];
for (var paymentGateway in paymentGateways) {
	if (paymentGateways[paymentGateway].name === "CreditGuard" && paymentGateways[paymentGateway]['params']['terminal_id'] !== undefined) {
		if (paymentGateways[paymentGateway]['params']['redirect_terminal'] === undefined || paymentGateways[paymentGateway]['params']['charging_terminal'] === undefined) {
			paymentGateways[paymentGateway]['params']['redirect_terminal'] = paymentGateways[paymentGateway]['params']['terminal_id'];
			paymentGateways[paymentGateway]['params']['charging_terminal'] = paymentGateways[paymentGateway]['params']['terminal_id'];
			delete paymentGateways[paymentGateway]['params']['terminal_id'];
		}
	}
}

// BRCD-1390 - Add activation_date field to subscriber
db.subscribers.find({activation_date:{$exists:0}, type:'subscriber'}).forEach(
	function(obj) {
		var activationDate = -1;
		db.subscribers.find({sid:obj.sid, aid:obj.aid, activation_date:{$exists:0}}).sort({'from': 1}).forEach(
			function(obj2) {
				if (activationDate == -1) {
					activationDate = obj2.from;
				}
				obj2.activation_date = activationDate;
				db.subscribers.save(obj2);
			}
		);
	}
);

// BRCD-1402 - Add activation_date field to subscriber
if(lastConfig.invoice_export) {
	if(lastConfig.invoice_export.header && lastConfig.invoice_export.header.match(/^\/application\/views\/invoices/)) {
		lastConfig.invoice_export.header = lastConfig.invoice_export.header.replace(/^\/application\/views\/invoices/,'');
	}
	if(lastConfig.invoice_export.footer && lastConfig.invoice_export.footer.match(/^\/application\/views\/invoices/)) {
		lastConfig.invoice_export.footer =lastConfig.invoice_export.footer.replace(/^\/application\/views\/invoices/,'');
	}
}

//BRCD-1374 : Add taxation support services
var vatableField ={
					"system":true,
					"select_list" : false,
					"display" : true,
					"editable" : true,
					"multiple" : false,
					"field_name" : "vatable",
					"unique" : false,
					"default_value" : "1",
					"title" : "This service is taxable",
					"mandatory" : false,
					"type" : "boolean",
					"select_options" : ""
	};
lastConfig = addFieldToConfig(lastConfig, vatableField, 'services')

//BRCD-1272 - Generate Creditguard transactions in csv file + handle rejections file
for (var i in lastConfig['payment_gateways']) {
	if (lastConfig["payment_gateways"][i]['name'] == "CreditGuard") {
		if (typeof lastConfig['payment_gateways'][i]['receiver']  === 'undefined' && typeof lastConfig['payment_gateways'][i]['export']  === 'undefined' ) {
			lastConfig["payment_gateways"][i].receiver = {};
			lastConfig["payment_gateways"][i].export = {};
		}
	}
}

//BRCD-1411 - Multiple conditions for usage type mapping.
var fileTypes = lastConfig['file_types'];
for (var fileType in fileTypes) {
	if (typeof fileTypes[fileType]['processor']['usaget_mapping'] !== 'undefined') {
		var usagetMapping = fileTypes[fileType]['processor']['usaget_mapping'];
		for (var mapping in usagetMapping) {
			if (typeof fileTypes[fileType]['processor']['usaget_mapping'][mapping]['conditions'] === 'undefined') {
				var conditions = [];
				var condition = {
					"src_field": usagetMapping[mapping]["src_field"],
					"pattern": usagetMapping[mapping]["pattern"],
					"op": "$eq",
				};
				conditions.push(condition);
				fileTypes[fileType]['processor']['usaget_mapping'][mapping]["conditions"] = conditions;
				delete fileTypes[fileType]['processor']['usaget_mapping'][mapping]["src_field"];
				delete fileTypes[fileType]['processor']['usaget_mapping'][mapping]["pattern"];
			}
		}
	}
}

// BRCD-1415 - add invoice when ready email template
if(!lastConfig.email_templates) {
	lastConfig.email_templates = {
    "invoice_ready": {
      "subject": "Your invoice is ready",
      "content": "<pre>\nHello [[customer_firstname]],\n\nThe invoice for [[cycle_range]] is ready and is attached to this email.\nFor any questions, please contact us at [[company_email]].\n\n[[company_name]]</pre>\n",
      "html_translation": [
        "invoice_id",
        "invoice_total",
        "invoice_due_date",
        "cycle_range",
        "company_email",
        "company_name"
      ]
    }
  };
}

// BRCD-1415 - add system field to account (invoice_shipping_method)
var fields = lastConfig['subscribers']['account']['fields'];
var found = false;
for (var field_key in fields) {
	if (fields[field_key].field_name === "invoice_shipping_method") {
		found = true;
	}
}
if(!found) {
	fields.push({
		"system":false,
		"select_list":true,
		"display":true,
		"editable":true,
		"field_name":"invoice_shipping_method",
		"default_value":"email",
		"show_in_list":true,
		"title":"Invoice shipping method",
		"mandatory":false,
		"select_options":"email",
		"changeable_props": ["select_options"]
	});
}
lastConfig['subscribers']['account']['fields'] = fields;

// BRCD-1458 - Add support for hh:mm:ss, mm:ss "units" in input processor volume stage.
var propertyTypes = lastConfig['property_types'];
for (var i in propertyTypes) {
	if (propertyTypes[i]['type'] === 'time') {
		var timeProperty = lastConfig['property_types'][i];
		if (timeProperty['uom']) {
			for (var j in timeProperty['uom']) {
				if (timeProperty['uom'][j]['name'] === 'hhmmss' || timeProperty['uom'][j]['name'] === 'mmss') {
					lastConfig['property_types'][i]['uom'][j]['convertFunction'] = 'formatedTimeToSeconds';
				}
			}
		}
	}
}

db.rebalance_queue.ensureIndex({"creation_date": 1}, {unique: false, "background": true})

// BRCD-1443 - Wrong billrun field after a rebalance
db.billrun.update({'attributes.invoice_type':{$ne:'immediate'}, billrun_key:{$regex:/^[0-9]{14}$/}},{$set:{'attributes.invoice_type': 'immediate'}},{multi:1});

// BRCD-1457 - Fix creation_time field in subscriber services
db.subscribers.find({type: 'subscriber', 'services.creation_time.sec': {$exists:1}}).forEach(
	function(obj) {
		var services = obj.services;
		for (var service in services) {
			if (obj['services'][service]['creation_time'] === undefined) {
				obj['services'][service]['creation_time'] = obj['services'][service]['from'];
			} else if (obj['services'][service]['creation_time']['sec'] !== undefined) {
				var sec = obj['services'][service]['creation_time']['sec'];
				var usec = obj['services'][service]['creation_time']['usec'];
				var milliseconds = sec * 1000 + usec;
				obj['services'][service]['creation_time'] = new Date(milliseconds);
			}
		}

		db.subscribers.save(obj);
	}
);

db.counters.dropIndex("coll_1_oid_1");
db.counters.ensureIndex({coll: 1, key: 1}, { sparse: false, background: true});

db.config.insert(lastConfig);

// BRCD-1512 - Fix bills' linking fields / take into account linking fields when charging
db.bills.ensureIndex({'invoice_id': 1 }, { unique: false, background: true});

// BRCD-1552 collection
db.collection_steps.dropIndex("aid_1");
db.collection_steps.dropIndex("trigger_date_1_done_1");
db.collection_steps.ensureIndex({'trigger_date': 1 }, { unique: false , sparse: true, background: true });<commit_msg>BRCD-1556 fix(MongoDB) - add 'extra_params.aid' MongoDB indexes for collection collection_steps<commit_after>/*
 * General (idempotent) DB migration script goes here.
 * Please try to avoid using migration script and instead make special treatment in the code!
 */

// =============================== Helper functions ============================

function addFieldToConfig(lastConf, fieldConf, entityName) {
	var fields = lastConf[entityName]['fields'];
	var found = false;
	for (var field_key in fields) {
		if (fields[field_key].field_name === fieldConf.field_name) {
			found = true;
		}
	}
	if(!found) {
		fields.push(fieldConf);
	}
	lastConf[entityName]['fields'] = fields;

	return lastConf;
}

// =============================================================================

// BRCD-1077 Add new custom 'tariff_category' field to Products(Rates).
var lastConfig = db.config.find().sort({_id: -1}).limit(1).pretty()[0];
delete lastConfig['_id'];
var fields = lastConfig['rates']['fields'];
var found = false;
for (var field_key in fields) {
	if (fields[field_key].field_name === "tariff_category") {
		found = true;
	}
}
if(!found) {
	fields.push({
		"system":false,
		"select_list":true,
		"display":true,
		"editable":true,
		"field_name":"tariff_category",
		"default_value":"retail",
		"show_in_list":true,
		"title":"Tariff category",
		"mandatory":true,
		"select_options":"retail",
		"changeable_props": ["select_options"]
	});
}
lastConfig['rates']['fields'] = fields;

// BRCD-1078: add rate categories
for (var i in lastConfig['file_types']) {
	var firstKey = Object.keys(lastConfig['file_types'][i]['rate_calculators'])[0];
	var secKey = Object.keys(lastConfig['file_types'][i]['rate_calculators'][firstKey])[0];
	if (secKey == 0) {
		lastConfig['file_types'][i]['rate_calculators']['retail'] = {};
	for (var usaget in lastConfig['file_types'][i]['rate_calculators']) {
			if (usaget === 'retail') {
				continue;
			}
			lastConfig['file_types'][i]['rate_calculators']['retail'][usaget] = lastConfig['file_types'][i]['rate_calculators'][usaget];
			delete lastConfig['file_types'][i]['rate_calculators'][usaget];
		}
	}
}

// BRCD-1077 update all products(Rates) tariff_category field.
db.rates.update({'tariff_category': {$exists: false}},{$set:{'tariff_category':'retail'}},{multi:1});

// BRCD-938: Option to not generate pdfs for the cycle
if (typeof lastConfig['billrun']['generate_pdf']  === 'undefined') {
	lastConfig['billrun']['generate_pdf'] = {"v": true ,"t" : "Boolean"};
}

// BRCD-441 -Add plugin support
if (!lastConfig['plugins']) {
	lastConfig.plugins = ["calcCpuPlugin", "csiPlugin", "autorenewPlugin"];
}

//-------------------------------------------------------------------
// BRCD-1278 - backward support for new template
if(lastConfig.invoice_export) {
	lastConfig.invoice_export.header = "/application/views/invoices/header/header_tpl.html";
	lastConfig.invoice_export.footer = "/application/views/invoices/footer/footer_tpl.html";
}

//BRCD-1229 - Input processor re-enabled when not requested
for (var i in lastConfig['file_types']) {
	if (lastConfig['file_types'][i]['enabled'] === undefined) {
		lastConfig['file_types'][i]['enabled'] = true;
	}
}

// BRCD-1278 : add minutes:seconds support  for time display
var found =false;
for(var i in lastConfig["property_types"][0]["uom"]) {
		if(lastConfig["property_types"][0]["uom"][i]['name'] == "mmss" ) {
				found = true;
		}
}
if(!found) {
		lastConfig["property_types"][0]["uom"].push({"name":"mmss","label":"mm:ss","function_name":"parseTime","arguments":{"format":"_I:s"}});
}
lastConfig["property_types"][0]['invoice_uom'] = "mmss";

// BRCD-1152: Add service activation date to each cdr generated on the billing cycle
if(!lastConfig['lines']) {
	lastConfig['lines'] = {};
}
if(!lastConfig['lines']['fields']) {
	lastConfig['lines']['fields'] = [];
}
var idx = 0;
for (var i in lastConfig['lines']['fields']) {
	if (lastConfig['lines']['fields'][i]['field_name'] == 'foreign.activation_date') {
		idx = i;
		break;
	}
	idx = i+1;
}
var addField = {
	field_name : "foreign.activation_date",
	foreign : {
		entity : "service",
		field  :"start",
		translate : {
			type : "unixTimeToString",
			format : "Y-m-d H:i:s"
		}
	}
};
if(lastConfig['lines']['fields'].length > idx) {
	lastConfig['lines']['fields'][idx] = addField;
} else {
	lastConfig['lines']['fields'].push(addField);
}

//BRCD-1324 - Save CreditGuard last 4 digits in the account active payment gateway field
db.subscribers.find({type:"account", 'payment_gateway.active.name':"CreditGuard"}).forEach(
		function(obj) {
			var activeGateway = obj.payment_gateway.active;
			var token = activeGateway.card_token;
			var fourDigits = token.substring(token.length - 4, token.length);
			activeGateway.four_digits = fourDigits;
			db.subscribers.save(obj)
		}
)

// BRCD-1353: CreditGuard fixes
var paymentGateways = lastConfig['payment_gateways'];
for (var paymentGateway in paymentGateways) {
	if (paymentGateways[paymentGateway].name === "CreditGuard" && paymentGateways[paymentGateway]['params']['terminal_id'] !== undefined) {
		if (paymentGateways[paymentGateway]['params']['redirect_terminal'] === undefined || paymentGateways[paymentGateway]['params']['charging_terminal'] === undefined) {
			paymentGateways[paymentGateway]['params']['redirect_terminal'] = paymentGateways[paymentGateway]['params']['terminal_id'];
			paymentGateways[paymentGateway]['params']['charging_terminal'] = paymentGateways[paymentGateway]['params']['terminal_id'];
			delete paymentGateways[paymentGateway]['params']['terminal_id'];
		}
	}
}

// BRCD-1390 - Add activation_date field to subscriber
db.subscribers.find({activation_date:{$exists:0}, type:'subscriber'}).forEach(
	function(obj) {
		var activationDate = -1;
		db.subscribers.find({sid:obj.sid, aid:obj.aid, activation_date:{$exists:0}}).sort({'from': 1}).forEach(
			function(obj2) {
				if (activationDate == -1) {
					activationDate = obj2.from;
				}
				obj2.activation_date = activationDate;
				db.subscribers.save(obj2);
			}
		);
	}
);

// BRCD-1402 - Add activation_date field to subscriber
if(lastConfig.invoice_export) {
	if(lastConfig.invoice_export.header && lastConfig.invoice_export.header.match(/^\/application\/views\/invoices/)) {
		lastConfig.invoice_export.header = lastConfig.invoice_export.header.replace(/^\/application\/views\/invoices/,'');
	}
	if(lastConfig.invoice_export.footer && lastConfig.invoice_export.footer.match(/^\/application\/views\/invoices/)) {
		lastConfig.invoice_export.footer =lastConfig.invoice_export.footer.replace(/^\/application\/views\/invoices/,'');
	}
}

//BRCD-1374 : Add taxation support services
var vatableField ={
					"system":true,
					"select_list" : false,
					"display" : true,
					"editable" : true,
					"multiple" : false,
					"field_name" : "vatable",
					"unique" : false,
					"default_value" : "1",
					"title" : "This service is taxable",
					"mandatory" : false,
					"type" : "boolean",
					"select_options" : ""
	};
lastConfig = addFieldToConfig(lastConfig, vatableField, 'services')

//BRCD-1272 - Generate Creditguard transactions in csv file + handle rejections file
for (var i in lastConfig['payment_gateways']) {
	if (lastConfig["payment_gateways"][i]['name'] == "CreditGuard") {
		if (typeof lastConfig['payment_gateways'][i]['receiver']  === 'undefined' && typeof lastConfig['payment_gateways'][i]['export']  === 'undefined' ) {
			lastConfig["payment_gateways"][i].receiver = {};
			lastConfig["payment_gateways"][i].export = {};
		}
	}
}

//BRCD-1411 - Multiple conditions for usage type mapping.
var fileTypes = lastConfig['file_types'];
for (var fileType in fileTypes) {
	if (typeof fileTypes[fileType]['processor']['usaget_mapping'] !== 'undefined') {
		var usagetMapping = fileTypes[fileType]['processor']['usaget_mapping'];
		for (var mapping in usagetMapping) {
			if (typeof fileTypes[fileType]['processor']['usaget_mapping'][mapping]['conditions'] === 'undefined') {
				var conditions = [];
				var condition = {
					"src_field": usagetMapping[mapping]["src_field"],
					"pattern": usagetMapping[mapping]["pattern"],
					"op": "$eq",
				};
				conditions.push(condition);
				fileTypes[fileType]['processor']['usaget_mapping'][mapping]["conditions"] = conditions;
				delete fileTypes[fileType]['processor']['usaget_mapping'][mapping]["src_field"];
				delete fileTypes[fileType]['processor']['usaget_mapping'][mapping]["pattern"];
			}
		}
	}
}

// BRCD-1415 - add invoice when ready email template
if(!lastConfig.email_templates) {
	lastConfig.email_templates = {
    "invoice_ready": {
      "subject": "Your invoice is ready",
      "content": "<pre>\nHello [[customer_firstname]],\n\nThe invoice for [[cycle_range]] is ready and is attached to this email.\nFor any questions, please contact us at [[company_email]].\n\n[[company_name]]</pre>\n",
      "html_translation": [
        "invoice_id",
        "invoice_total",
        "invoice_due_date",
        "cycle_range",
        "company_email",
        "company_name"
      ]
    }
  };
}

// BRCD-1415 - add system field to account (invoice_shipping_method)
var fields = lastConfig['subscribers']['account']['fields'];
var found = false;
for (var field_key in fields) {
	if (fields[field_key].field_name === "invoice_shipping_method") {
		found = true;
	}
}
if(!found) {
	fields.push({
		"system":false,
		"select_list":true,
		"display":true,
		"editable":true,
		"field_name":"invoice_shipping_method",
		"default_value":"email",
		"show_in_list":true,
		"title":"Invoice shipping method",
		"mandatory":false,
		"select_options":"email",
		"changeable_props": ["select_options"]
	});
}
lastConfig['subscribers']['account']['fields'] = fields;

// BRCD-1458 - Add support for hh:mm:ss, mm:ss "units" in input processor volume stage.
var propertyTypes = lastConfig['property_types'];
for (var i in propertyTypes) {
	if (propertyTypes[i]['type'] === 'time') {
		var timeProperty = lastConfig['property_types'][i];
		if (timeProperty['uom']) {
			for (var j in timeProperty['uom']) {
				if (timeProperty['uom'][j]['name'] === 'hhmmss' || timeProperty['uom'][j]['name'] === 'mmss') {
					lastConfig['property_types'][i]['uom'][j]['convertFunction'] = 'formatedTimeToSeconds';
				}
			}
		}
	}
}

db.rebalance_queue.ensureIndex({"creation_date": 1}, {unique: false, "background": true})

// BRCD-1443 - Wrong billrun field after a rebalance
db.billrun.update({'attributes.invoice_type':{$ne:'immediate'}, billrun_key:{$regex:/^[0-9]{14}$/}},{$set:{'attributes.invoice_type': 'immediate'}},{multi:1});

// BRCD-1457 - Fix creation_time field in subscriber services
db.subscribers.find({type: 'subscriber', 'services.creation_time.sec': {$exists:1}}).forEach(
	function(obj) {
		var services = obj.services;
		for (var service in services) {
			if (obj['services'][service]['creation_time'] === undefined) {
				obj['services'][service]['creation_time'] = obj['services'][service]['from'];
			} else if (obj['services'][service]['creation_time']['sec'] !== undefined) {
				var sec = obj['services'][service]['creation_time']['sec'];
				var usec = obj['services'][service]['creation_time']['usec'];
				var milliseconds = sec * 1000 + usec;
				obj['services'][service]['creation_time'] = new Date(milliseconds);
			}
		}

		db.subscribers.save(obj);
	}
);

db.counters.dropIndex("coll_1_oid_1");
db.counters.ensureIndex({coll: 1, key: 1}, { sparse: false, background: true});

db.config.insert(lastConfig);

// BRCD-1512 - Fix bills' linking fields / take into account linking fields when charging
db.bills.ensureIndex({'invoice_id': 1 }, { unique: false, background: true});

// BRCD-1552 collection
db.collection_steps.dropIndex("aid_1");
db.collection_steps.dropIndex("trigger_date_1_done_1");
db.collection_steps.ensureIndex({'trigger_date': 1, 'extra_params.aid':1 }, { unique: false , sparse: true, background: true });<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFil<commit_msg>Grammar fix<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile<|endoftext|>


<commit_before>//----------------------------------------------------------------------
//
// This source file is part of the Folktale project.
//
// Licensed under MIT. See LICENCE for full licence information.
// See CONTRIBUTORS for the list of contributors to the project.
//
//----------------------------------------------------------------------

// This tool converts special Markdown files to the JS files that
// provide documentation information for Meta:Magical. This allows
// documentation to stay out of source files, and also allows us to
// support translating documentation to other languages.
const yaml = require('js-yaml');
const marked = require('marked');
const template = require('babel-template');
const babylonParse = require('babylon').parse;
const t = require('babel-types');
const generateJs = require('babel-generator').default;
const fs = require('fs');
const path = require('path');
const glob = require('glob').sync;
const mkdirp = require('mkdirp').sync;

const babelOptions = {
  plugins: [
    'objectRestSpread',
    'functionBind'
  ]
};

// --[ Helpers ]-------------------------------------------------------
const match = ([tag, payload], pattern) => pattern[tag](payload);

const append = (list, item) =>
  item != null ? [...list, item]
: /* _ */        list;

const intoArray = (x) =>
  Array.isArray(x) ?  x
: /* else */          [x]

const parseJs = (source, options = {}) => {
  try {
    return babylonParse(source, options);
  } catch (e) {
    const lines = source.split(/\r\n|\n\r|\r|\n/);
    const prev = lines.slice(Math.max(e.loc.line - 2, 1) - 1, e.loc.line - 1);
    const line = lines[e.loc.line - 1];
    const next = lines.slice(e.loc.line, e.loc.line + 2);

    throw new SyntaxError(`${options.sourceFilename}: Unable to parse JS annotation, ${e.message}

${prev.join('\n')}
${line}
${' '.repeat(e.loc.column)}^
${next.join('\n')}
`);
  }
};

const parseJsExpr = (source, options = {}) => {
  const ast = parseJs(source, options = {});
  t.assertExpressionStatement(ast.program.body[0]);
  return ast.program.body[0].expression;
};

const pairs = (object) =>
  Object.keys(object).map(key => [key, object[key]]);

const merge = (...args) => {
  return Object.assign({}, ...args);
};

const raise = (error) => {
  throw error;
};

const isString = (value) => typeof value === 'string';

const isBoolean = (value) => typeof value === 'boolean';

const isNumber = (value) => typeof value === 'number';

const isObject = (value) => Object(value) === value;

const flatten = (xs) => xs.reduce((a, b) => a.concat(b), []);

function __metamagical_withMeta(object, meta) {
  const parent  = Object.getPrototypeOf(object);
  let oldMeta   = object[Symbol.for('@@meta:magical')] || {};
  if (parent && parent[Symbol.for('@@meta:magical')] === oldMeta) {
    oldMeta = {};
  }

  Object.keys(meta).forEach(function(key) {
    if (/^~/.test(key)) {
      oldMeta[key.slice(1)] = meta[key];
    } else {
      oldMeta[key] = meta[key];
    }
  });
  object[Symbol.for('@@meta:magical')] = oldMeta;

  return object;
}

const withMeta = template(
  `__metamagical_withMeta(OBJECT, META)`
);

const withMetaFD = parseJs(__metamagical_withMeta.toString()).program.body[0];

// --[ Parser ]--------------------------------------------------------
const classifyLine = (line) =>
  /^\@annotate:/.test(line)       ? ['Entity', line.match(/^\@annotate:\s*(.+)/m)[1]]
: /^\@guide:/.test(line)          ? ['Guide', line.match(/^\@guide:\s*(.+)/m)[1]]
: /^---+\s*$/.test(line)          ? ['Separator']
: /* otherwise */                   ['Line', line];

const parse = (source) =>
  append(source.split(/\r\n|\n\r|\r|\n/).map(classifyLine), ['EOF'])
    .reduce((ctx, node, i) => match(node, {
      Entity(ref) {
        if (ctx.annotation) {
          if (ctx.annotation !== 'entity') {
            throw new Error(`Multiple annotations are only supported for entities. At line ${i + 1}`);
          }
          if (!ctx.current.sealed) {
            return {
              annotation: 'entity',
              current: {
                ref: intoArray(ctx.current.ref).concat([ref]),
                meta: '',
                doc: '',
                multi: true,
                sealed: false
              },
              ast: ctx.ast
            };
          } else {
            throw new Error(`Multiple annotations have to follow each other immediately. Annotation in meta found at line ${i + 1}`);
          }
        } else {
          return {
            annotation: 'entity',
            current: { ref, meta: '', doc: '', multi: false, sealed: false },
            ast: append(ctx.ast, ctx.current)
          };
        }
      },

      Guide(title) {
        if (ctx.annotation) {
          throw new Error(`Multiple annotations are not supported for guides. At line ${i + 1}`);
        } else {
          return {
            annotation: 'guide',
            current: {
              title,
              meta: '',
              doc: '',
              multi: false,
              sealed: false
            },
            ast: ctx.ast
          };
        }
      },

      Separator() {
        if (!ctx.current) {
          throw new Error(`Annotation separator found without a matching entity at line ${i + 1}`);
        }
        return {
          annotation: null,
          current: ctx.current,
          ast: ctx.ast
        };
      },

      EOF() {
        return {
          annotation: null,
          current: null,
          ast: append(ctx.ast, ctx.current)
        };
      },

      Line(line) {
        if (!ctx.current) {
          throw new Error(`Documentation found before an entity annotation at line ${i + 1}`);
        }
        if (ctx.annotation) {
          const { title, ref, meta, doc, multi } = ctx.current;
          return {
            annotation: ctx.annotation,
            current: { title, ref, meta: meta + '\n' + line, doc, multi, sealed: true },
            ast: ctx.ast
          };
        } else {
          const { title, ref, meta, doc, multi } = ctx.current;
          return {
            annotation: null,
            current: { title, ref, meta, doc: doc + '\n' + line, multi, sealed: true },
            ast: ctx.ast
          };
        }
      }
    }), {
      current: null,
      annotation: null,
      ast: []
    }).ast;

// --[ Compiler transformations ]--------------------------------------
const analyse = (entities) =>
  flatten(entities.map(parseMeta));

const parseMeta = (entity) => {
  let meta = yaml.safeLoad(entity.meta) || {};
  meta.documentation = entity.doc;
  if (entity.multi) {
    return entity.ref.map(ref => ({ ref, meta }));
  } else {
    return [{ title: entity.title, ref: entity.ref, meta }];
  }
};

class Raw {
  constructor(value) {
    this.value = value;
  }
}

// Examples
const intoExampleFunction = (source, ast, options) => {
  const body = ast.program.body;

  return new Raw(withMeta({
    OBJECT: t.functionExpression(
      null,   // id
      [],     // params
      t.blockStatement(body),
      false,  // generator
      false   // async
    ),
    META: mergeMeta(options, { source })
  }).expression);
};

const makeParser = (options) => (source) => parseJs(source, options || {});

const parseExample = ({ name, source }, options) => {
  let parse = makeParser(options || {});
  return name        ? { name, call: intoExampleFunction(source, parse(source), options), inferred: true }
  :      /* else */    { name: '', call: intoExampleFunction(source, parse(source), options), inferred: true };
};

const isExampleLeadingParagraph = (node) =>
   node
&& (node.type === 'paragraph' || node.type === 'heading')
&& /::\s*$/.test(node.text);

const collectExamples = (documentation) => {
  const ast = marked.lexer(documentation);

  const [xs, x, name] = ast.reduce(([examples, current, heading, nextNodeIsExample], node) => {
    if (node.type === 'code') {
      if (nextNodeIsExample) {
        return [examples, [...current, node.text], heading, false];
      } else {
        return [examples, current, heading, false];
      }
    } else if (node.type === 'heading') {
      return [
        examples.concat({
          name: heading,
          source: current.join('\n\n')
        }),
        [],
        node.text,
        isExampleLeadingParagraph(node)
      ];
    } else if (node.type === 'paragraph') {
      return [examples, current, heading, isExampleLeadingParagraph(node)];
    } else {
      return [examples, current, heading, false];
    }
  }, [[], [], null, false]);

  if (x.length === 0) {
    return xs;
  } else {
    return [...xs, {
      name: name,
      source: x.join('\n;\n')
    }];
  }
};

const inferExamples = (documentation, options) => {
  const examples = collectExamples(documentation || '');

  return examples.length > 0?  { examples: examples.map(e => parseExample(e, options)) }
  :      /* otherwise */       { };
};

const inferDeprecated = (meta) => {
  return meta.deprecated ?  merge(meta, { stability: 'deprecated' })
  :      /* otherwise */    meta;
};

const inferMetadataFromProvidedMetadata = (meta) => {
  return inferDeprecated(meta);
};

const mergeMeta = (options, ...args) => {
  let fullMeta = merge(...args);
  fullMeta = inferMetadataFromProvidedMetadata(fullMeta);

  if (fullMeta.documentation) {
    const doc = fullMeta.documentation;
    fullMeta = merge(fullMeta, inferExamples(doc, options));
    fullMeta.documentation = doc.replace(/^::$/gm, '').replace(/::[ \t]*$/gm, ':');
  }

  return objectToExpression(fullMeta);
};

// --[ Code generation ]-----------------------------------------------
const annotateEntity = template(
  `meta.for(ENTITY).update(OBJECT)`
);

const annotateGuide = template(`
  meta.for(PARENT[GUIDE] = {}).update(OBJECT)
`)

const moduleExport = template(
  `module.exports = VALUE`
);

const lazy = (expr) =>
  t.functionExpression(
    null,
    [],
    t.blockStatement([
      t.returnStatement(expr)
    ])
  );

const specialParsers = {
  '~belongsTo'(value) {
    const ast = parse(value);
    t.assertExpressionStatement(ast.program.body[0]);

    return lazy(ast.program.body[0].expression);
  }
};

const parseSpecialProperty = (value, key) =>
  specialParsers[key](value);

const isSpecial = (value, key) => key && key in specialParsers;

const objectToExpression = (object) =>
  t.objectExpression(
    pairs(object).map(pairToProperty)
  );

const pairToProperty = ([key, value]) =>
  t.objectProperty(
    t.stringLiteral(key),
    valueToLiteral(value, key)
  );

const valueToLiteral = (value, key) =>
  value instanceof Raw  ?  value.value
: Array.isArray(value)  ?  t.arrayExpression(value.map(x => valueToLiteral(x)))
: isSpecial(value, key) ?  parseSpecialProperty(value, key)
: isString(value)       ?  t.stringLiteral(value)
: isBoolean(value)      ?  t.booleanLiteral(value)
: isNumber(value)       ?  t.numericLiteral(value)
: isObject(value)       ?  objectToExpression(value)
: /* otherwise */          raise(new TypeError(`Type of property not supported: ${value}`));

const generate = (entities, options) =>
  generateJs(
    t.program(
      [
        withMetaFD,
        moduleExport({
          VALUE: t.functionExpression(
            null,
            [t.identifier('meta'), t.identifier('folktale')],
            t.blockStatement(
              entities.map(x => generateEntity(x, options))
            )
          )
        })
      ]
    )
  ).code;

const generateEntity = (entity, options) => {
  if (entity.ref) {
    return annotateEntity({
      ENTITY: parseJsExpr(entity.ref, options),
      OBJECT: mergeMeta(options, entity.meta)
    });
  } else if (entity.title) {
    const parent = entity.meta.parent || 'folktale';
    return annotateGuide({
      GUIDE: t.stringLiteral(entity.title),
      PARENT: parseJsExpr(parent, options),
      OBJECT: mergeMeta(options, merge(entity.meta, {
        name: entity.title,
        module: 'guides'
      }))
    });
  }
};

// --[ Main ]----------------------------------------------------------
if (process.argv.length < 4) {
  throw new Error('Usage: node markdown-to-mm.js <INPUT-DIR> <OUTPUT-DIR>');
}
const input = process.argv[2];
const output = process.argv[3];

glob(path.join(input, '**/*.md')).forEach((file, index, files) => {
  const outPath = path.join(output, path.relative(input, file));
  const source = fs.readFileSync(file, 'utf8');
  const js = generate(analyse(parse(source)), merge(babelOptions, {
    sourceFilename: input
  }));
  mkdirp(path.dirname(outPath));
  fs.writeFileSync(outPath, js);
  console.log(`[${index + 1}/${files.length}]`, file, '->', outPath);
});
<commit_msg>(fix build) Generates .js files and pass code through Babel<commit_after>//----------------------------------------------------------------------
//
// This source file is part of the Folktale project.
//
// Licensed under MIT. See LICENCE for full licence information.
// See CONTRIBUTORS for the list of contributors to the project.
//
//----------------------------------------------------------------------

// This tool converts special Markdown files to the JS files that
// provide documentation information for Meta:Magical. This allows
// documentation to stay out of source files, and also allows us to
// support translating documentation to other languages.
const yaml = require('js-yaml');
const marked = require('marked');
const template = require('babel-template');
const babylonParse = require('babylon').parse;
const t = require('babel-types');
const generateJs = require('babel-generator').default;
const babel = require('babel-core');
const fs = require('fs');
const path = require('path');
const glob = require('glob').sync;
const mkdirp = require('mkdirp').sync;

const babelOptions = {
  plugins: [
    'objectRestSpread',
    'functionBind'
  ]
};

const babelCompileOptions = JSON.parse(fs.readFileSync(path.join(__dirname, '../.babelrc'), 'utf8'));

// --[ Helpers ]-------------------------------------------------------
const match = ([tag, payload], pattern) => pattern[tag](payload);

const append = (list, item) =>
  item != null ? [...list, item]
: /* _ */        list;

const intoArray = (x) =>
  Array.isArray(x) ?  x
: /* else */          [x]

const parseJs = (source, options = {}) => {
  try {
    return babylonParse(source, options);
  } catch (e) {
    const lines = source.split(/\r\n|\n\r|\r|\n/);
    const prev = lines.slice(Math.max(e.loc.line - 2, 1) - 1, e.loc.line - 1);
    const line = lines[e.loc.line - 1];
    const next = lines.slice(e.loc.line, e.loc.line + 2);

    throw new SyntaxError(`${options.sourceFilename}: Unable to parse JS annotation, ${e.message}

${prev.join('\n')}
${line}
${' '.repeat(e.loc.column)}^
${next.join('\n')}
`);
  }
};

const parseJsExpr = (source, options = {}) => {
  const ast = parseJs(source, options = {});
  t.assertExpressionStatement(ast.program.body[0]);
  return ast.program.body[0].expression;
};

const pairs = (object) =>
  Object.keys(object).map(key => [key, object[key]]);

const merge = (...args) => {
  return Object.assign({}, ...args);
};

const raise = (error) => {
  throw error;
};

const isString = (value) => typeof value === 'string';

const isBoolean = (value) => typeof value === 'boolean';

const isNumber = (value) => typeof value === 'number';

const isObject = (value) => Object(value) === value;

const flatten = (xs) => xs.reduce((a, b) => a.concat(b), []);

function __metamagical_withMeta(object, meta) {
  const parent  = Object.getPrototypeOf(object);
  let oldMeta   = object[Symbol.for('@@meta:magical')] || {};
  if (parent && parent[Symbol.for('@@meta:magical')] === oldMeta) {
    oldMeta = {};
  }

  Object.keys(meta).forEach(function(key) {
    if (/^~/.test(key)) {
      oldMeta[key.slice(1)] = meta[key];
    } else {
      oldMeta[key] = meta[key];
    }
  });
  object[Symbol.for('@@meta:magical')] = oldMeta;

  return object;
}

const withMeta = template(
  `__metamagical_withMeta(OBJECT, META)`
);

const withMetaFD = parseJs(__metamagical_withMeta.toString()).program.body[0];

// --[ Parser ]--------------------------------------------------------
const classifyLine = (line) =>
  /^\@annotate:/.test(line)       ? ['Entity', line.match(/^\@annotate:\s*(.+)/m)[1]]
: /^\@guide:/.test(line)          ? ['Guide', line.match(/^\@guide:\s*(.+)/m)[1]]
: /^---+\s*$/.test(line)          ? ['Separator']
: /* otherwise */                   ['Line', line];

const parse = (source) =>
  append(source.split(/\r\n|\n\r|\r|\n/).map(classifyLine), ['EOF'])
    .reduce((ctx, node, i) => match(node, {
      Entity(ref) {
        if (ctx.annotation) {
          if (ctx.annotation !== 'entity') {
            throw new Error(`Multiple annotations are only supported for entities. At line ${i + 1}`);
          }
          if (!ctx.current.sealed) {
            return {
              annotation: 'entity',
              current: {
                ref: intoArray(ctx.current.ref).concat([ref]),
                meta: '',
                doc: '',
                multi: true,
                sealed: false
              },
              ast: ctx.ast
            };
          } else {
            throw new Error(`Multiple annotations have to follow each other immediately. Annotation in meta found at line ${i + 1}`);
          }
        } else {
          return {
            annotation: 'entity',
            current: { ref, meta: '', doc: '', multi: false, sealed: false },
            ast: append(ctx.ast, ctx.current)
          };
        }
      },

      Guide(title) {
        if (ctx.annotation) {
          throw new Error(`Multiple annotations are not supported for guides. At line ${i + 1}`);
        } else {
          return {
            annotation: 'guide',
            current: {
              title,
              meta: '',
              doc: '',
              multi: false,
              sealed: false
            },
            ast: ctx.ast
          };
        }
      },

      Separator() {
        if (!ctx.current) {
          throw new Error(`Annotation separator found without a matching entity at line ${i + 1}`);
        }
        return {
          annotation: null,
          current: ctx.current,
          ast: ctx.ast
        };
      },

      EOF() {
        return {
          annotation: null,
          current: null,
          ast: append(ctx.ast, ctx.current)
        };
      },

      Line(line) {
        if (!ctx.current) {
          throw new Error(`Documentation found before an entity annotation at line ${i + 1}`);
        }
        if (ctx.annotation) {
          const { title, ref, meta, doc, multi } = ctx.current;
          return {
            annotation: ctx.annotation,
            current: { title, ref, meta: meta + '\n' + line, doc, multi, sealed: true },
            ast: ctx.ast
          };
        } else {
          const { title, ref, meta, doc, multi } = ctx.current;
          return {
            annotation: null,
            current: { title, ref, meta, doc: doc + '\n' + line, multi, sealed: true },
            ast: ctx.ast
          };
        }
      }
    }), {
      current: null,
      annotation: null,
      ast: []
    }).ast;

// --[ Compiler transformations ]--------------------------------------
const analyse = (entities) =>
  flatten(entities.map(parseMeta));

const parseMeta = (entity) => {
  let meta = yaml.safeLoad(entity.meta) || {};
  meta.documentation = entity.doc;
  if (entity.multi) {
    return entity.ref.map(ref => ({ ref, meta }));
  } else {
    return [{ title: entity.title, ref: entity.ref, meta }];
  }
};

class Raw {
  constructor(value) {
    this.value = value;
  }
}

// Examples
const intoExampleFunction = (source, ast, options) => {
  const body = ast.program.body;

  return new Raw(withMeta({
    OBJECT: t.functionExpression(
      null,   // id
      [],     // params
      t.blockStatement(body),
      false,  // generator
      false   // async
    ),
    META: mergeMeta(options, { source })
  }).expression);
};

const makeParser = (options) => (source) => parseJs(source, options || {});

const parseExample = ({ name, source }, options) => {
  let parse = makeParser(options || {});
  return name        ? { name, call: intoExampleFunction(source, parse(source), options), inferred: true }
  :      /* else */    { name: '', call: intoExampleFunction(source, parse(source), options), inferred: true };
};

const isExampleLeadingParagraph = (node) =>
   node
&& (node.type === 'paragraph' || node.type === 'heading')
&& /::\s*$/.test(node.text);

const collectExamples = (documentation) => {
  const ast = marked.lexer(documentation);

  const [xs, x, name] = ast.reduce(([examples, current, heading, nextNodeIsExample], node) => {
    if (node.type === 'code') {
      if (nextNodeIsExample) {
        return [examples, [...current, node.text], heading, false];
      } else {
        return [examples, current, heading, false];
      }
    } else if (node.type === 'heading') {
      return [
        examples.concat({
          name: heading,
          source: current.join('\n\n')
        }),
        [],
        node.text,
        isExampleLeadingParagraph(node)
      ];
    } else if (node.type === 'paragraph') {
      return [examples, current, heading, isExampleLeadingParagraph(node)];
    } else {
      return [examples, current, heading, false];
    }
  }, [[], [], null, false]);

  if (x.length === 0) {
    return xs;
  } else {
    return [...xs, {
      name: name,
      source: x.join('\n;\n')
    }];
  }
};

const inferExamples = (documentation, options) => {
  const examples = collectExamples(documentation || '');

  return examples.length > 0?  { examples: examples.map(e => parseExample(e, options)) }
  :      /* otherwise */       { };
};

const inferDeprecated = (meta) => {
  return meta.deprecated ?  merge(meta, { stability: 'deprecated' })
  :      /* otherwise */    meta;
};

const inferMetadataFromProvidedMetadata = (meta) => {
  return inferDeprecated(meta);
};

const mergeMeta = (options, ...args) => {
  let fullMeta = merge(...args);
  fullMeta = inferMetadataFromProvidedMetadata(fullMeta);

  if (fullMeta.documentation) {
    const doc = fullMeta.documentation;
    fullMeta = merge(fullMeta, inferExamples(doc, options));
    fullMeta.documentation = doc.replace(/^::$/gm, '').replace(/::[ \t]*$/gm, ':');
  }

  return objectToExpression(fullMeta);
};

// --[ Code generation ]-----------------------------------------------
const annotateEntity = template(
  `meta.for(ENTITY).update(OBJECT)`
);

const annotateGuide = template(`
  meta.for(PARENT[GUIDE] = {}).update(OBJECT)
`)

const moduleExport = template(
  `module.exports = VALUE`
);

const lazy = (expr) =>
  t.functionExpression(
    null,
    [],
    t.blockStatement([
      t.returnStatement(expr)
    ])
  );

const specialParsers = {
  '~belongsTo'(value) {
    const ast = parse(value);
    t.assertExpressionStatement(ast.program.body[0]);

    return lazy(ast.program.body[0].expression);
  }
};

const parseSpecialProperty = (value, key) =>
  specialParsers[key](value);

const isSpecial = (value, key) => key && key in specialParsers;

const objectToExpression = (object) =>
  t.objectExpression(
    pairs(object).map(pairToProperty)
  );

const pairToProperty = ([key, value]) =>
  t.objectProperty(
    t.stringLiteral(key),
    valueToLiteral(value, key)
  );

const valueToLiteral = (value, key) =>
  value instanceof Raw  ?  value.value
: Array.isArray(value)  ?  t.arrayExpression(value.map(x => valueToLiteral(x)))
: isSpecial(value, key) ?  parseSpecialProperty(value, key)
: isString(value)       ?  t.stringLiteral(value)
: isBoolean(value)      ?  t.booleanLiteral(value)
: isNumber(value)       ?  t.numericLiteral(value)
: isObject(value)       ?  objectToExpression(value)
: /* otherwise */          raise(new TypeError(`Type of property not supported: ${value}`));

const generate = (entities, options) =>
  generateJs(
    t.program(
      [
        withMetaFD,
        moduleExport({
          VALUE: t.functionExpression(
            null,
            [t.identifier('meta'), t.identifier('folktale')],
            t.blockStatement(
              entities.map(x => generateEntity(x, options))
            )
          )
        })
      ]
    )
  ).code;

const generateEntity = (entity, options) => {
  if (entity.ref) {
    return annotateEntity({
      ENTITY: parseJsExpr(entity.ref, options),
      OBJECT: mergeMeta(options, entity.meta)
    });
  } else if (entity.title) {
    const parent = entity.meta.parent || 'folktale';
    return annotateGuide({
      GUIDE: t.stringLiteral(entity.title),
      PARENT: parseJsExpr(parent, options),
      OBJECT: mergeMeta(options, merge(entity.meta, {
        name: entity.title,
        module: 'guides'
      }))
    });
  }
};

// --[ Main ]----------------------------------------------------------
if (process.argv.length < 4) {
  throw new Error('Usage: node markdown-to-mm.js <INPUT-DIR> <OUTPUT-DIR>');
}
const input = process.argv[2];
const output = process.argv[3];

glob(path.join(input, '**/*.md')).forEach((file, index, files) => {
  const filename = path.relative(input, file)
  const outPath = path.join(output, path.dirname(filename), path.basename(filename, path.extname(filename)) + '.js');
  const source = fs.readFileSync(file, 'utf8');
  const js = generate(analyse(parse(source)), merge(babelOptions, {
    sourceFilename: input
  }));
  const { code } = babel.transform(js, babelCompileOptions);
  mkdirp(path.dirname(outPath));
  fs.writeFileSync(outPath, code);
  console.log(`[${index + 1}/${files.length}]`, file, '->', outPath);
});
<|endoftext|>


<commit_before>var Select = require('react-select'),
	React = require('react'),
	Field = require('../field'),
	Note = require('../../components/note'),
	superagent = require('superagent'),
	_ = require('underscore');

module.exports = Field.create({

	getInitialState: function() {
		return {
			ready: this.props.value ? false : true,
			simpleValue: this.props.value,
			expandedValue: null
		}
	},

	componentDidMount: function() {
		this.loadValues(this.props.value);
	},

	componentWillReceiveProps: function(newProps) {
		if (newProps.value !== this.state.simpleValue) {
			this.setState({
				ready: false,
				simpleValue: newProps.value,
				expandedValue: null
			});
			this.loadValues(newProps.value);
		}
	},

	loadValues: function(input) {
		var expandedValues = [];
		var inputs = [].concat(input);
		var self = this;

		var finish = function () {
			self.setState({
				ready: true,
				expandedValues: expandedValues
			});
		};

		if (!inputs.length) return finish();

		_.each(inputs, function(input) {
			superagent
				.get('/keystone/api/' + self.props.refList.path + '/get?dataset=simple&id=' + input)
				.set('Accept', 'application/json')
				.end(function (err, res) {
					if (err) throw err;

					var value = res.body;

					expandedValues.push({
						value: value.id,
						label: value.name
					});

					if (expandedValues.length === inputs.length) {
						finish();
					}
				});
		});
	},

	getOptions: function(input, callback) {
		superagent
			.get('/keystone/api/' + this.props.refList.path + '/autocomplete?q=' + input)
			.set('Accept', 'application/json')
			.end(function (err, res) {
				if (err) throw err;

				var data = res.body;

				callback(null, {
					options: data.items.map(function (item) {
						return {
							value: item.id,
							label: item.name
						};
					}),
					complete: true
				});
			});
	},

	renderLoadingUI: function() {
		return
loading...
;
	},

	updateValue: function(simpleValue, expandedValues) {
		this.setState({
			simpleValue: simpleValue,
			expandedValues: expandedValues
		});
		this.props.onChange({
			path: this.props.path,
			value: this.props.many ? _.pluck(expandedValues, 'value') : simpleValue
		});
	},

	renderValue: function() {
		if (!this.state.ready) {
			return this.renderLoadingUI();
		}
		if (field.many) {
			//a(href='/keystone/' + refList.path + '/' + item.get(field.path), data-ref-path=refList.path).ui-related-item= item.get(field.path)
		} else if (field.many && this.props.value.length) {
			var body = [];

			_.each(this.props.value, function (value) {
				body.push({value});
			}, this);

			return value;
		} else {
			return
(not set)
;
		}
	},

	renderField: function() {
		if (!this.state.ready) {
			return this.renderLoadingUI();
		}
		var body = [];

		body.push(<Select multi={this.props.many} onChange={this.updateValue} name={this.props.path} asyncOptions={this.getOptions} value={this.state.expandedValues} />);

		if (!this.props.many && this.props.value) {
			body.push(

					view {this.props.refList.singular.toLowerCase()}

			);
		}

		return body;
	}

});
<commit_msg>Fixing a typo in the Relationship field<commit_after>var Select = require('react-select'),
	React = require('react'),
	Field = require('../field'),
	Note = require('../../components/note'),
	superagent = require('superagent'),
	_ = require('underscore');

module.exports = Field.create({

	getInitialState: function() {
		return {
			ready: this.props.value ? false : true,
			simpleValue: this.props.value,
			expandedValues: null
		}
	},

	componentDidMount: function() {
		this.loadValues(this.props.value);
	},

	componentWillReceiveProps: function(newProps) {
		if (newProps.value !== this.state.simpleValue) {
			this.setState({
				ready: false,
				simpleValue: newProps.value,
				expandedValues: null
			});
			this.loadValues(newProps.value);
		}
	},

	loadValues: function(input) {
		var expandedValues = [];
		var inputs = [].concat(input);
		var self = this;

		var finish = function () {
			self.setState({
				ready: true,
				expandedValues: expandedValues
			});
		};

		if (!inputs.length) return finish();

		_.each(inputs, function(input) {
			superagent
				.get('/keystone/api/' + self.props.refList.path + '/get?dataset=simple&id=' + input)
				.set('Accept', 'application/json')
				.end(function (err, res) {
					if (err) throw err;

					var value = res.body;

					expandedValues.push({
						value: value.id,
						label: value.name
					});

					if (expandedValues.length === inputs.length) {
						finish();
					}
				});
		});
	},

	getOptions: function(input, callback) {
		superagent
			.get('/keystone/api/' + this.props.refList.path + '/autocomplete?q=' + input)
			.set('Accept', 'application/json')
			.end(function (err, res) {
				if (err) throw err;

				var data = res.body;

				callback(null, {
					options: data.items.map(function (item) {
						return {
							value: item.id,
							label: item.name
						};
					}),
					complete: true
				});
			});
	},

	renderLoadingUI: function() {
		return
loading...
;
	},

	updateValue: function(simpleValue, expandedValues) {
		this.setState({
			simpleValue: simpleValue,
			expandedValues: expandedValues
		});
		this.props.onChange({
			path: this.props.path,
			value: this.props.many ? _.pluck(expandedValues, 'value') : simpleValue
		});
	},

	renderValue: function() {
		if (!this.state.ready) {
			return this.renderLoadingUI();
		}
		if (field.many) {
			//a(href='/keystone/' + refList.path + '/' + item.get(field.path), data-ref-path=refList.path).ui-related-item= item.get(field.path)
		} else if (field.many && this.props.value.length) {
			var body = [];

			_.each(this.props.value, function (value) {
				body.push({value});
			}, this);

			return value;
		} else {
			return
(not set)
;
		}
	},

	renderField: function() {
		if (!this.state.ready) {
			return this.renderLoadingUI();
		}
		var body = [];

		body.push(<Select multi={this.props.many} onChange={this.updateValue} name={this.props.path} asyncOptions={this.getOptions} value={this.state.expandedValues} />);

		if (!this.props.many && this.props.value) {
			body.push(

					view {this.props.refList.singular.toLowerCase()}

			);
		}

		return body;
	}

});
<|endoftext|>


<commit_before>var m = require('mithril');
var util = require('../util');
var assert = require('../assert');
var arrow = util.arrow;

var imgUrl = util.assetUrl + 'images/learn/bowman.svg';

module.exports = {
  key: 'capture',
  title: 'Capture',
  subtitle: 'Take the enemy pieces',
  image: imgUrl,
  intro: 'You are ready for combat! In this level, we will be capturing enemy pieces.',
  illustration: util.roundSvg(imgUrl),
  levels: [{ // rook
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '8/2p2p2/8/8/8/2R5/8/8 w - -',
    nbMoves: 2,
    captures: 2,
    shapes: [arrow('c3c7'), arrow('c7f7')],
    success: assert.extinct('black')
  }, { // queen
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '8/2r2p2/8/8/5Q2/8/8/8 w - -',
    nbMoves: 2,
    captures: 2,
    shapes: [arrow('f4c7'), arrow('f4f7', 'red'), arrow('c7f7', 'yellow')],
    success: assert.extinct('black')
  }, { // bishop
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '8/5r2/8/1r3p2/8/3B4/8/8 w - -',
    nbMoves: 5,
    captures: 3,
    success: assert.extinct('black')
  }, { // queen
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '8/5b2/5p2/3n2p1/8/6Q1/8/8 w - -',
    nbMoves: 7,
    captures: 4,
    success: assert.extinct('black')
  }, { // knight
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '8/3b4/2p2q2/8/3p1N2/8/8/8 w - -',
    nbMoves: 6,
    captures: 4,
    success: assert.extinct('black')
  }, {
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '2n1b3/4pp2/5Q2/2R5/8/8/8/8 w - -',
    nbMoves: 5,
    captures: 4,
    success: assert.extinct('black')
  }, {
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '2n5/8/2B3b1/3p4/4p3/8/2q1R3/8 w - -',
    nbMoves: 6,
    captures: 5,
    success: assert.extinct('black')
  }].map(function(l, i) {
    l.pointsForCapture = true;
    return util.toLevel(l, i);
  }),
  complete: 'Congratulations! You know how to fight with chess pieces!'
};
<commit_msg>simplify capture stage<commit_after>var m = require('mithril');
var util = require('../util');
var assert = require('../assert');
var arrow = util.arrow;

var imgUrl = util.assetUrl + 'images/learn/bowman.svg';

module.exports = {
  key: 'capture',
  title: 'Capture',
  subtitle: 'Take the enemy pieces',
  image: imgUrl,
  intro: 'Identify the opponent undefended pieces, and capture them!',
  illustration: util.roundSvg(imgUrl),
  levels: [{ // rook
    goal: 'Take the black pieces!',
    fen: '8/2p2p2/8/8/8/2R5/8/8 w - -',
    nbMoves: 2,
    captures: 2,
    shapes: [arrow('c3c7'), arrow('c7f7')],
    success: assert.extinct('black')
  }, { // queen
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '8/2r2p2/8/8/5Q2/8/8/8 w - -',
    nbMoves: 2,
    captures: 2,
    shapes: [arrow('f4c7'), arrow('f4f7', 'red'), arrow('c7f7', 'yellow')],
    success: assert.extinct('black')
  }, { // bishop
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '8/5r2/8/1r3p2/8/3B4/8/8 w - -',
    nbMoves: 5,
    captures: 3,
    success: assert.extinct('black')
  }, { // queen
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '8/5b2/5p2/3n2p1/8/6Q1/8/8 w - -',
    nbMoves: 7,
    captures: 4,
    success: assert.extinct('black')
  }, { // knight
    goal: 'Take the black pieces!
And don\'t lose yours.',
    fen: '8/3b4/2p2q2/8/3p1N2/8/8/8 w - -',
    nbMoves: 6,
    captures: 4,
    success: assert.extinct('black')
  }].map(function(l, i) {
    l.pointsForCapture = true;
    return util.toLevel(l, i);
  }),
  complete: 'Congratulations! You know how to fight with chess pieces!'
};
<|endoftext|>


<commit_before>var cache = {};

function venfix(property, target){
    var bodyStyle = document.defaultView.getComputedStyle(document.body);

    if(!target && cache[property]){
        return cache[property];
    }

    target = target || bodyStyle;

    var props = [];

    for(var key in target){
        cache[key] = key;
        props.push(key);
    }

    if(property in target){
        return property;
    }

    var propertyRegex = new RegExp('^(' + venfix.prefixes.join('|') + ')' + property + '$', 'i');

    for(var i = 0; i < props.length; i++) {
        if(props[i].match(propertyRegex)){
            if(target === bodyStyle){
                cache[property] = props[i]
            }
            return props[i];
        }
    }
}

// Add extensibility
venfix.prefixes = ['webkit', 'moz', 'ms', 'o'];

module.exports = venfix;<commit_msg>Fixed lookup isse<commit_after>var cache = {},
    bodyStyle;

function getBodyStyleProperties(){
    if(!bodyStyle){
        var values = {},
            items = document.defaultView.getComputedStyle(document.body);

        for(var i = 0; i < items.length; i++){
            values[items[i]] = null;
        }
        bodyStyle = values;
    }
    return bodyStyle;
}

function venfix(property, target){
    var bodyStyle = getBodyStyleProperties();

    if(!target && cache[property]){
        return cache[property];
    }

    target = target || bodyStyle;

    var props = [];

    for(var key in target){
        cache[key] = key;
        props.push(key);
    }

    if(property in target){
        return property;
    }

    var propertyRegex = new RegExp('^(' + venfix.prefixes.join('|') + ')' + property + '$', 'i');

    for(var i = 0; i < props.length; i++) {
        if(props[i].match(propertyRegex)){
            if(target === bodyStyle){
                cache[property] = props[i]
            }
            return props[i];
        }
    }
}

// Add extensibility
venfix.prefixes = ['webkit', 'moz', 'ms', 'o'];

module.exports = venfix;<|endoftext|>