<commit_before>// config.js

'use strict';

var CONFIG = {
  region: 'eu-west-1',
  dynamodb: {
    tableNames: {
        hotbits: 'LambdaRandom',
        counters: 'Lotto_Counters'
    },
    types: {
      counter: 'Counter',
      item: 'Item',
      hotbitsCounter: 'Hotbits'
    },
    counter: {
      type: 'Counter', // or 'Hotbits'
      id: '1',
      attr: 'Val'
    },
    blockLen: 16 // hotbit numbers in a single DB item
  },
  hotbits: {
    blockLen: 256 // hotbit numbers in a single hotbits Request. Do not ever set it to 402, it happens to be the length of the "quota exceeded response"
    // hotbits.blockLen / dynamodb.blockLen ratio must not exceed 25 (items are written in a single batchWriteItem)
  }
};

global.NODE_CONFIG = global.NODE_CONFIG ? global.NODE_CONFIG : CONFIG;

module.exports = global.NODE_CONFIG;

// database table

/*
aws --profile lambda dynamodb describe-table --table-name LambdaRandom
{
    "Table": {
        "LocalSecondaryIndexes": [
            {
                "KeySchema": [
                    {
                        "KeyType": "HASH",
                        "AttributeName": "Type"
                    },
                    {
                        "KeyType": "RANGE",
                        "AttributeName": "HotId"
                    }
                ],
                "IndexSizeBytes": 0,
                "ItemCount": 0,
                "IndexName": "HotId-index",
                "Projection": {
                    "ProjectionType": "KEYS_ONLY"
                }
            }
        ],
        "AttributeDefinitions": [
            {
                "AttributeName": "HotId",
                "AttributeType": "N"
            },
            {
                "AttributeName": "Id",
                "AttributeType": "N"
            },
            {
                "AttributeName": "Type",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 5,
            "ReadCapacityUnits": 5
        },
        "TableSizeBytes": 0,
        "TableName": "LambdaRandom",
        "TableStatus": "ACTIVE",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Type"
            },
            {
                "KeyType": "RANGE",
                "AttributeName": "Id"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1420406213.19
    }
}

aws --profile lambda dynamodb describe-table --table-name Lotto_Counters
{
    "Table": {
        "AttributeDefinitions": [
            {
                "AttributeName": "Type",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 2,
            "ReadCapacityUnits": 2
        },
        "TableSizeBytes": 0,
        "TableName": "Lotto_Counters",
        "TableStatus": "ACTIVE",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Type"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1420883840.277
    }
}

aws --profile lambda dynamodb scan --table-name Lotto_Counters
{
    "Count": 2,
    "Items": [
        {
            "Type": {
                "S": "Hotbits"
            },
            "Val": {
                "N": "2"
            }
        },
        {
            "Type": {
                "S": "Counter"
            },
            "Val": {
                "N": "17"
            }
        }
    ],
    "ScannedCount": 2,
    "ConsumedCapacity": null
}

*/<commit_msg>preparing to simplify the hotbits table<commit_after>// config.js

'use strict';

var CONFIG = {
  region: 'eu-west-1',
  dynamodb: {
    tableNames: {
        hotbits: 'LambdaRandom', // 'Lotto_Hotbits', // 'LambdaRandom',
        counters: 'Lotto_Counters'
    },
    types: { // to be obsoleted
      counter: 'Counter',
      item: 'Item',
      hotbitsCounter: 'Hotbits'
    },
    counter: {
      type: 'Counter', // or 'Hotbits'
      id: '1',
      attr: 'Val'
    },
    blockLen: 16 // hotbit numbers in a single DB item
  },
  hotbits: {
    blockLen: 256 // hotbit numbers in a single hotbits Request. Do not ever set it to 402, it happens to be the length of the "quota exceeded response"
    // hotbits.blockLen / dynamodb.blockLen ratio must not exceed 25 (items are written in a single batchWriteItem)
  }
};

global.NODE_CONFIG = global.NODE_CONFIG ? global.NODE_CONFIG : CONFIG;

module.exports = global.NODE_CONFIG;

// database table

/*
aws --profile lambda dynamodb describe-table --table-name LambdaRandom
{
    "Table": {
        "LocalSecondaryIndexes": [
            {
                "KeySchema": [
                    {
                        "KeyType": "HASH",
                        "AttributeName": "Type"
                    },
                    {
                        "KeyType": "RANGE",
                        "AttributeName": "HotId"
                    }
                ],
                "IndexSizeBytes": 0,
                "ItemCount": 0,
                "IndexName": "HotId-index",
                "Projection": {
                    "ProjectionType": "KEYS_ONLY"
                }
            }
        ],
        "AttributeDefinitions": [
            {
                "AttributeName": "HotId",
                "AttributeType": "N"
            },
            {
                "AttributeName": "Id",
                "AttributeType": "N"
            },
            {
                "AttributeName": "Type",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 5,
            "ReadCapacityUnits": 5
        },
        "TableSizeBytes": 0,
        "TableName": "LambdaRandom",
        "TableStatus": "ACTIVE",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Type"
            },
            {
                "KeyType": "RANGE",
                "AttributeName": "Id"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1420406213.19
    }
}

aws --profile lambda dynamodb describe-table --table-name Lotto_Counters
{
    "Table": {
        "AttributeDefinitions": [
            {
                "AttributeName": "Type",
                "AttributeType": "S"
            }
        ],
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0,
            "WriteCapacityUnits": 2,
            "ReadCapacityUnits": 2
        },
        "TableSizeBytes": 0,
        "TableName": "Lotto_Counters",
        "TableStatus": "ACTIVE",
        "KeySchema": [
            {
                "KeyType": "HASH",
                "AttributeName": "Type"
            }
        ],
        "ItemCount": 0,
        "CreationDateTime": 1420883840.277
    }
}

aws --profile lambda dynamodb scan --table-name Lotto_Counters
{
    "Count": 2,
    "Items": [
        {
            "Type": {
                "S": "Hotbits"
            },
            "Val": {
                "N": "2"
            }
        },
        {
            "Type": {
                "S": "Counter"
            },
            "Val": {
                "N": "17"
            }
        }
    ],
    "ScannedCount": 2,
    "ConsumedCapacity": null
}

*/<|endoftext|>


<commit_before>var ADULT_KEYWORDS = require('./config.json').adultKeywords;
var ADULT_KEYWORDS_REGEX = new RegExp(ADULT_KEYWORDS.join('|'), 'gim');

var isExplicitText = function (tweet) {
  return ADULT_KEYWORDS_REGEX.test(tweet.text);
};

var isExplicitName = function (tweet) {
  return ADULT_KEYWORDS_REGEX.test(tweet.user.name);
};

var isExplicitLocation = function (tweet) {
  return ADULT_KEYWORDS_REGEX.test(tweet.user.location);
};

var isExplicitDescription = function (tweet) {
  return ADULT_KEYWORDS_REGEX.test(tweet.user.description);
};

module.exports = function (tweet) {
  if (
    tweet.possibly_sensitive
    || isExplicitText(tweet)
    || isExplicitName(tweet)
    || isExplicitLocation(tweet)
    || isExplicitDescription(tweet)
  ) {
    return true;
  } else {
    return false;
  }
};<commit_msg>Simplify code<commit_after>var ADULT_KEYWORDS = require('./config.json').adultKeywords;
var ADULT_KEYWORDS_REGEX = new RegExp(ADULT_KEYWORDS.join('|'), 'gim');

var isExplicitText = function (tweet) {
  return ADULT_KEYWORDS_REGEX.test(tweet.text);
};

var isExplicitName = function (tweet) {
  return ADULT_KEYWORDS_REGEX.test(tweet.user.name);
};

var isExplicitLocation = function (tweet) {
  return ADULT_KEYWORDS_REGEX.test(tweet.user.location);
};

var isExplicitDescription = function (tweet) {
  return ADULT_KEYWORDS_REGEX.test(tweet.user.description);
};

module.exports = function (tweet) {
  if (
    tweet.possibly_sensitive
    || isExplicitText(tweet)
    || isExplicitName(tweet)
    || isExplicitLocation(tweet)
    || isExplicitDescription(tweet)
  ) {
    return true;
  }

  return false;
};<|endoftext|>


<commit_before>/**
 ## Heat Map

 Includes: [Color Mixin](#color-mixin), [Margin Mixin](#margin-mixin), [Base Mixin](#base-mixin)

 A heat map is matrix that represents the values of two dimensions of data using colors.

 #### dc.heatMap(parent[, chartGroup])
 Create a heat map instance and attach it to the given parent element.

 Parameters:
* parent : string | node | selection - any valid
 [d3 single selector](https://github.com/mbostock/d3/wiki/Selections#selecting-elements) specifying
 a dom block element such as a div; or a dom element or d3 selection.

* chartGroup : string (optional) - name of the chart group this chart instance should be placed in.
 Interaction with a chart will only trigger events and redraws within the chart's group.

 Returns:
 A newly created heat map instance

 ```js
 // create a heat map under #chart-container1 element using the default global chart group
 var heatMap1 = dc.heatMap('#chart-container1');
 // create a heat map under #chart-container2 element using chart group A
 var heatMap2 = dc.heatMap('#chart-container2', 'chartGroupA');
 ```

 **/
dc.heatMap = function (parent, chartGroup) {

    var DEFAULT_BORDER_RADIUS = 6.75;

    var _chartBody;

    var _cols;
    var _rows;
    var _colOrdering = d3.ascending;
    var _rowOrdering = d3.ascending;
    var _colScale = d3.scale.ordinal();
    var _rowScale = d3.scale.ordinal();

    var _xBorderRadius = DEFAULT_BORDER_RADIUS;
    var _yBorderRadius = DEFAULT_BORDER_RADIUS;

    var _chart = dc.colorMixin(dc.marginMixin(dc.baseMixin({})));
    _chart._mandatoryAttributes(['group']);
    _chart.title(_chart.colorAccessor());

    var _colsLabel = function (d) {
        return d;
    };
    var _rowsLabel = function (d) {
        return d;
    };

   /**
    #### .colsLabel([labelFunction])
    Set or get the column label function. The chart class uses this function to render
    column labels on the X axis. It is passed the column name.
    ```js
    // the default label function just returns the name
    chart.colsLabel(function(d) { return d; });
    ```
    **/
    _chart.colsLabel = function (_) {
        if (!arguments.length) {
            return _colsLabel;
        }
        _colsLabel = _;
        return _chart;
    };

   /**
    #### .rowsLabel([labelFunction])
    Set or get the row label function. The chart class uses this function to render
    row labels on the Y axis. It is passed the row name.
    ```js
    // the default label function just returns the name
    chart.rowsLabel(function(d) { return d; });
    ```
    **/
    _chart.rowsLabel = function (_) {
        if (!arguments.length) {
            return _rowsLabel;
        }
        _rowsLabel = _;
        return _chart;
    };

    var _xAxisOnClick = function (d) { filterAxis(0, d); };
    var _yAxisOnClick = function (d) { filterAxis(1, d); };
    var _boxOnClick = function (d) {
        var filter = d.key;
        dc.events.trigger(function () {
            _chart.filter(filter);
            _chart.redrawGroup();
        });
    };

    function filterAxis(axis, value) {
        var cellsOnAxis = _chart.selectAll('.box-group').filter(function (d) {
            return d.key[axis] === value;
        });
        var unfilteredCellsOnAxis = cellsOnAxis.filter(function (d) {
            return !_chart.hasFilter(d.key);
        });
        dc.events.trigger(function () {
            if (unfilteredCellsOnAxis.empty()) {
                cellsOnAxis.each(function (d) {
                    _chart.filter(d.key);
                });
            } else {
                unfilteredCellsOnAxis.each(function (d) {
                    _chart.filter(d.key);
                });
            }
            _chart.redrawGroup();
        });
    }

    dc.override(_chart, 'filter', function (filter) {
        if (!arguments.length) {
            return _chart._filter();
        }

        return _chart._filter(dc.filters.TwoDimensionalFilter(filter));
    });

    /**
     #### .rows([values])
     Gets or sets the values used to create the rows of the heatmap, as an array. By default, all
     the values will be fetched from the data using the value accessor, and they will be sorted in
     ascending order.
     **/

    _chart.rows = function (_) {
        if (!arguments.length) {
            return _rows;
        }
        _rows = _;
        return _chart;
    };

    /**
     #### .rowOrdering([orderFunction])
     Get or set an accessor to order the rows
     */
    _chart.rowOrdering = function (_) {
        if (!arguments.length) {
            return _rowOrdering;
        }
        _rowOrdering = _;
        return _chart;
    };

    /**
     #### .cols([keys])
     Gets or sets the keys used to create the columns of the heatmap, as an array. By default, all
     the values will be fetched from the data using the key accessor, and they will be sorted in
     ascending order.
     **/
    _chart.cols = function (_) {
        if (!arguments.length) {
            return _cols;
        }
        _cols = _;
        return _chart;
    };

    /**
     #### .colOrdering([orderFunction])
     Get or set an accessor to order the cols
     */
    _chart.colOrdering = function (_) {
        if (!arguments.length) {
            return _colOrdering;
        }
        _colOrdering = _;
        return _chart;
    };

    _chart._doRender = function () {
        _chart.resetSvg();

        _chartBody = _chart.svg()
            .append('g')
            .attr('class', 'heatmap')
            .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart.margins().top + ')');

        return _chart._doRedraw();
    };

    _chart._doRedraw = function () {
        var data = _chart.data(),
            rows = _chart.rows() || data.map(_chart.valueAccessor()),
            cols = _chart.cols() || data.map(_chart.keyAccessor());
        if (_rowOrdering) {
            rows = rows.sort(_rowOrdering);
        }
        if (_colOrdering) {
            cols = cols.sort(_colOrdering);
        }
        rows = _rowScale.domain(rows);
        cols = _colScale.domain(cols);

        var rowCount = rows.domain().length,
            colCount = cols.domain().length,
            boxWidth = Math.floor(_chart.effectiveWidth() / colCount),
            boxHeight = Math.floor(_chart.effectiveHeight() / rowCount);

        cols.rangeRoundBands([0, _chart.effectiveWidth()]);
        rows.rangeRoundBands([_chart.effectiveHeight(), 0]);

        var boxes = _chartBody.selectAll('g.box-group').data(_chart.data(), function (d, i) {
            return _chart.keyAccessor()(d, i) + '\0' + _chart.valueAccessor()(d, i);
        });
        var gEnter = boxes.enter().append('g')
            .attr('class', 'box-group');

        gEnter.append('rect')
            .attr('class', 'heat-box')
            .attr('fill', 'white')
            .on('click', _chart.boxOnClick());

        if (_chart.renderTitle()) {
            gEnter.append('title')
                .text(_chart.title());
        }

        dc.transition(boxes.selectAll('rect'), _chart.transitionDuration())
            .attr('x', function (d, i) { return cols(_chart.keyAccessor()(d, i)); })
            .attr('y', function (d, i) { return rows(_chart.valueAccessor()(d, i)); })
            .attr('rx', _xBorderRadius)
            .attr('ry', _yBorderRadius)
            .attr('fill', _chart.getColor)
            .attr('width', boxWidth)
            .attr('height', boxHeight);

        boxes.exit().remove();

        var gCols = _chartBody.selectAll('g.cols');
        if (gCols.empty()) {
            gCols = _chartBody.append('g').attr('class', 'cols axis');
        }
        var gColsText = gCols.selectAll('text').data(cols.domain());
        gColsText.enter().append('text')
              .attr('x', function (d) { return cols(d) + boxWidth / 2; })
              .style('text-anchor', 'middle')
              .attr('y', _chart.effectiveHeight())
              .attr('dy', 12)
              .on('click', _chart.xAxisOnClick())
              .text(_chart.colsLabel());
        dc.transition(gColsText, _chart.transitionDuration())
               .text(_chart.colsLabel())
               .attr('x', function (d) { return cols(d) + boxWidth / 2; });
        gColsText.exit().remove();
        var gRows = _chartBody.selectAll('g.rows');
        if (gRows.empty()) {
            gRows = _chartBody.append('g').attr('class', 'rows axis');
        }
        var gRowsText = gRows.selectAll('text').data(rows.domain());
        gRowsText.enter().append('text')
              .attr('dy', 6)
              .style('text-anchor', 'end')
              .attr('x', 0)
              .attr('dx', -2)
              .on('click', _chart.yAxisOnClick())
              .text(_chart.rowsLabel());
        dc.transition(gRowsText, _chart.transitionDuration())
              .text(_chart.rowsLabel())
              .attr('y', function (d) { return rows(d) + boxHeight / 2; });
        gRowsText.exit().remove();

        if (_chart.hasFilter()) {
            _chart.selectAll('g.box-group').each(function (d) {
                if (_chart.isSelectedNode(d)) {
                    _chart.highlightSelected(this);
                } else {
                    _chart.fadeDeselected(this);
                }
            });
        } else {
            _chart.selectAll('g.box-group').each(function () {
                _chart.resetHighlight(this);
            });
        }
        return _chart;
    };
    /**
     #### .boxOnClick([handler])
     Gets or sets the handler that fires when an individual cell is clicked in the heatmap.
     By default, filtering of the cell will be toggled.
     **/
    _chart.boxOnClick = function (f) {
        if (!arguments.length) {
            return _boxOnClick;
        }
        _boxOnClick = f;
        return _chart;
    };

    /**
     #### .xAxisOnClick([handler])
     Gets or sets the handler that fires when a column tick is clicked in the x axis.
     By default, if any cells in the column are unselected, the whole column will be selected,
     otherwise the whole column will be unselected.
     **/
    _chart.xAxisOnClick = function (f) {
        if (!arguments.length) {
            return _xAxisOnClick;
        }
        _xAxisOnClick = f;
        return _chart;
    };

    /**
     #### .yAxisOnClick([handler])
     Gets or sets the handler that fires when a row tick is clicked in the y axis.
     By default, if any cells in the row are unselected, the whole row will be selected,
     otherwise the whole row will be unselected.
     **/
    _chart.yAxisOnClick = function (f) {
        if (!arguments.length) {
            return _yAxisOnClick;
        }
        _yAxisOnClick = f;
        return _chart;
    };

    /**
     #### .xBorderRadius([value])
     Gets or sets the X border radius.  Set to 0 to get full rectangles.  Default: 6.75
     */
    _chart.xBorderRadius = function (d) {
        if (!arguments.length) {
            return _xBorderRadius;
        }
        _xBorderRadius = d;
        return _chart;
    };

    /**
     #### .xBorderRadius([value])
     Gets or sets the Y border radius.  Set to 0 to get full rectangles.  Default: 6.75
     */
    _chart.yBorderRadius = function (d) {
        if (!arguments.length) {
            return _yBorderRadius;
        }
        _yBorderRadius = d;
        return _chart;
    };

    _chart.isSelectedNode = function (d) {
        return _chart.hasFilter(d.key);
    };

    return _chart.anchor(parent, chartGroup);
};
<commit_msg>Fixed documentation<commit_after>/**
 ## Heat Map

 Includes: [Color Mixin](#color-mixin), [Margin Mixin](#margin-mixin), [Base Mixin](#base-mixin)

 A heat map is matrix that represents the values of two dimensions of data using colors.

 #### dc.heatMap(parent[, chartGroup])
 Create a heat map instance and attach it to the given parent element.

 Parameters:
* parent : string | node | selection - any valid
 [d3 single selector](https://github.com/mbostock/d3/wiki/Selections#selecting-elements) specifying
 a dom block element such as a div; or a dom element or d3 selection.

* chartGroup : string (optional) - name of the chart group this chart instance should be placed in.
 Interaction with a chart will only trigger events and redraws within the chart's group.

 Returns:
 A newly created heat map instance

 ```js
 // create a heat map under #chart-container1 element using the default global chart group
 var heatMap1 = dc.heatMap('#chart-container1');
 // create a heat map under #chart-container2 element using chart group A
 var heatMap2 = dc.heatMap('#chart-container2', 'chartGroupA');
 ```

 **/
dc.heatMap = function (parent, chartGroup) {

    var DEFAULT_BORDER_RADIUS = 6.75;

    var _chartBody;

    var _cols;
    var _rows;
    var _colOrdering = d3.ascending;
    var _rowOrdering = d3.ascending;
    var _colScale = d3.scale.ordinal();
    var _rowScale = d3.scale.ordinal();

    var _xBorderRadius = DEFAULT_BORDER_RADIUS;
    var _yBorderRadius = DEFAULT_BORDER_RADIUS;

    var _chart = dc.colorMixin(dc.marginMixin(dc.baseMixin({})));
    _chart._mandatoryAttributes(['group']);
    _chart.title(_chart.colorAccessor());

    var _colsLabel = function (d) {
        return d;
    };
    var _rowsLabel = function (d) {
        return d;
    };

   /**
    #### .colsLabel([labelFunction])
    Set or get the column label function. The chart class uses this function to render
    column labels on the X axis. It is passed the column name.
    ```js
    // the default label function just returns the name
    chart.colsLabel(function(d) { return d; });
    ```
    **/
    _chart.colsLabel = function (_) {
        if (!arguments.length) {
            return _colsLabel;
        }
        _colsLabel = _;
        return _chart;
    };

   /**
    #### .rowsLabel([labelFunction])
    Set or get the row label function. The chart class uses this function to render
    row labels on the Y axis. It is passed the row name.
    ```js
    // the default label function just returns the name
    chart.rowsLabel(function(d) { return d; });
    ```
    **/
    _chart.rowsLabel = function (_) {
        if (!arguments.length) {
            return _rowsLabel;
        }
        _rowsLabel = _;
        return _chart;
    };

    var _xAxisOnClick = function (d) { filterAxis(0, d); };
    var _yAxisOnClick = function (d) { filterAxis(1, d); };
    var _boxOnClick = function (d) {
        var filter = d.key;
        dc.events.trigger(function () {
            _chart.filter(filter);
            _chart.redrawGroup();
        });
    };

    function filterAxis(axis, value) {
        var cellsOnAxis = _chart.selectAll('.box-group').filter(function (d) {
            return d.key[axis] === value;
        });
        var unfilteredCellsOnAxis = cellsOnAxis.filter(function (d) {
            return !_chart.hasFilter(d.key);
        });
        dc.events.trigger(function () {
            if (unfilteredCellsOnAxis.empty()) {
                cellsOnAxis.each(function (d) {
                    _chart.filter(d.key);
                });
            } else {
                unfilteredCellsOnAxis.each(function (d) {
                    _chart.filter(d.key);
                });
            }
            _chart.redrawGroup();
        });
    }

    dc.override(_chart, 'filter', function (filter) {
        if (!arguments.length) {
            return _chart._filter();
        }

        return _chart._filter(dc.filters.TwoDimensionalFilter(filter));
    });

    /**
     #### .rows([values])
     Gets or sets the values used to create the rows of the heatmap, as an array. By default, all
     the values will be fetched from the data using the value accessor.
     **/

    _chart.rows = function (_) {
        if (!arguments.length) {
            return _rows;
        }
        _rows = _;
        return _chart;
    };

    /**
     #### .rowOrdering([orderFunction])
     Get or set an accessor to order the rows.  Default is d3.ascending.
     */
    _chart.rowOrdering = function (_) {
        if (!arguments.length) {
            return _rowOrdering;
        }
        _rowOrdering = _;
        return _chart;
    };

    /**
     #### .cols([keys])
     Gets or sets the keys used to create the columns of the heatmap, as an array. By default, all
     the values will be fetched from the data using the key accessor.
     **/
    _chart.cols = function (_) {
        if (!arguments.length) {
            return _cols;
        }
        _cols = _;
        return _chart;
    };

    /**
     #### .colOrdering([orderFunction])
     Get or set an accessor to order the cols.  Default is ascending.
     */
    _chart.colOrdering = function (_) {
        if (!arguments.length) {
            return _colOrdering;
        }
        _colOrdering = _;
        return _chart;
    };

    _chart._doRender = function () {
        _chart.resetSvg();

        _chartBody = _chart.svg()
            .append('g')
            .attr('class', 'heatmap')
            .attr('transform', 'translate(' + _chart.margins().left + ',' + _chart.margins().top + ')');

        return _chart._doRedraw();
    };

    _chart._doRedraw = function () {
        var data = _chart.data(),
            rows = _chart.rows() || data.map(_chart.valueAccessor()),
            cols = _chart.cols() || data.map(_chart.keyAccessor());
        if (_rowOrdering) {
            rows = rows.sort(_rowOrdering);
        }
        if (_colOrdering) {
            cols = cols.sort(_colOrdering);
        }
        rows = _rowScale.domain(rows);
        cols = _colScale.domain(cols);

        var rowCount = rows.domain().length,
            colCount = cols.domain().length,
            boxWidth = Math.floor(_chart.effectiveWidth() / colCount),
            boxHeight = Math.floor(_chart.effectiveHeight() / rowCount);

        cols.rangeRoundBands([0, _chart.effectiveWidth()]);
        rows.rangeRoundBands([_chart.effectiveHeight(), 0]);

        var boxes = _chartBody.selectAll('g.box-group').data(_chart.data(), function (d, i) {
            return _chart.keyAccessor()(d, i) + '\0' + _chart.valueAccessor()(d, i);
        });
        var gEnter = boxes.enter().append('g')
            .attr('class', 'box-group');

        gEnter.append('rect')
            .attr('class', 'heat-box')
            .attr('fill', 'white')
            .on('click', _chart.boxOnClick());

        if (_chart.renderTitle()) {
            gEnter.append('title')
                .text(_chart.title());
        }

        dc.transition(boxes.selectAll('rect'), _chart.transitionDuration())
            .attr('x', function (d, i) { return cols(_chart.keyAccessor()(d, i)); })
            .attr('y', function (d, i) { return rows(_chart.valueAccessor()(d, i)); })
            .attr('rx', _xBorderRadius)
            .attr('ry', _yBorderRadius)
            .attr('fill', _chart.getColor)
            .attr('width', boxWidth)
            .attr('height', boxHeight);

        boxes.exit().remove();

        var gCols = _chartBody.selectAll('g.cols');
        if (gCols.empty()) {
            gCols = _chartBody.append('g').attr('class', 'cols axis');
        }
        var gColsText = gCols.selectAll('text').data(cols.domain());
        gColsText.enter().append('text')
              .attr('x', function (d) { return cols(d) + boxWidth / 2; })
              .style('text-anchor', 'middle')
              .attr('y', _chart.effectiveHeight())
              .attr('dy', 12)
              .on('click', _chart.xAxisOnClick())
              .text(_chart.colsLabel());
        dc.transition(gColsText, _chart.transitionDuration())
               .text(_chart.colsLabel())
               .attr('x', function (d) { return cols(d) + boxWidth / 2; });
        gColsText.exit().remove();
        var gRows = _chartBody.selectAll('g.rows');
        if (gRows.empty()) {
            gRows = _chartBody.append('g').attr('class', 'rows axis');
        }
        var gRowsText = gRows.selectAll('text').data(rows.domain());
        gRowsText.enter().append('text')
              .attr('dy', 6)
              .style('text-anchor', 'end')
              .attr('x', 0)
              .attr('dx', -2)
              .on('click', _chart.yAxisOnClick())
              .text(_chart.rowsLabel());
        dc.transition(gRowsText, _chart.transitionDuration())
              .text(_chart.rowsLabel())
              .attr('y', function (d) { return rows(d) + boxHeight / 2; });
        gRowsText.exit().remove();

        if (_chart.hasFilter()) {
            _chart.selectAll('g.box-group').each(function (d) {
                if (_chart.isSelectedNode(d)) {
                    _chart.highlightSelected(this);
                } else {
                    _chart.fadeDeselected(this);
                }
            });
        } else {
            _chart.selectAll('g.box-group').each(function () {
                _chart.resetHighlight(this);
            });
        }
        return _chart;
    };
    /**
     #### .boxOnClick([handler])
     Gets or sets the handler that fires when an individual cell is clicked in the heatmap.
     By default, filtering of the cell will be toggled.
     **/
    _chart.boxOnClick = function (f) {
        if (!arguments.length) {
            return _boxOnClick;
        }
        _boxOnClick = f;
        return _chart;
    };

    /**
     #### .xAxisOnClick([handler])
     Gets or sets the handler that fires when a column tick is clicked in the x axis.
     By default, if any cells in the column are unselected, the whole column will be selected,
     otherwise the whole column will be unselected.
     **/
    _chart.xAxisOnClick = function (f) {
        if (!arguments.length) {
            return _xAxisOnClick;
        }
        _xAxisOnClick = f;
        return _chart;
    };

    /**
     #### .yAxisOnClick([handler])
     Gets or sets the handler that fires when a row tick is clicked in the y axis.
     By default, if any cells in the row are unselected, the whole row will be selected,
     otherwise the whole row will be unselected.
     **/
    _chart.yAxisOnClick = function (f) {
        if (!arguments.length) {
            return _yAxisOnClick;
        }
        _yAxisOnClick = f;
        return _chart;
    };

    /**
     #### .xBorderRadius([value])
     Gets or sets the X border radius.  Set to 0 to get full rectangles.  Default: 6.75
     */
    _chart.xBorderRadius = function (d) {
        if (!arguments.length) {
            return _xBorderRadius;
        }
        _xBorderRadius = d;
        return _chart;
    };

    /**
     #### .xBorderRadius([value])
     Gets or sets the Y border radius.  Set to 0 to get full rectangles.  Default: 6.75
     */
    _chart.yBorderRadius = function (d) {
        if (!arguments.length) {
            return _yBorderRadius;
        }
        _yBorderRadius = d;
        return _chart;
    };

    _chart.isSelectedNode = function (d) {
        return _chart.hasFilter(d.key);
    };

    return _chart.anchor(parent, chartGroup);
};
<|endoftext|>


<commit_before>/*-----------------------------------------------------------------

Modular - JS Extension
Made by @esr360
http://github.com/esr360/Modular/

-----------------------------------------------------------------*/

//-----------------------------------------------------------------
// Convert CSS config to JS
//-----------------------------------------------------------------

// Get styles' configuration
var stylesConfigJSON = document.getElementById("stylesConfigJSON");

// Remove quotes from computed JSON
function removeQuotes(json) {
    json = json.replace(/^['"]+|\s+|\\|(;\s?})+|['"]$/g, '');
    return json;
}

// Convert computed JSON to camelCase
function camelCase(json) {
	json = json.replace(/-([a-z])/g, function (g) {
		return g[1].toUpperCase();
	});
	return json;
}

// Convert the config to JS
function getStylesConfig(camelCase) {
    var style = null;
    style = window.getComputedStyle(stylesConfigJSON, '::before');
    style = style.content;
	style = removeQuotes(style);
	if(camelCase) {
		style = camelCase(style);
	}
    return JSON.parse(style);
}

// Store configuartion data in a variable
var module = getStylesConfig();

// CamelCase the config
var moduleCC = getStylesConfig(camelCase);

// Create a global variable to select each main component in the DOM
var componentIndex = 0;
$.each(module, function(component) {
	var componentCC = moduleCC[Object.keys(moduleCC)[componentIndex]]['name'];
	window['_' + componentCC] = '.' + component + ', [class*="' + component + '-"]';
	componentIndex++;
});

//-----------------------------------------------------------------
// Functions
//-----------------------------------------------------------------

//	1. setting()

//-----------------------------------------------------------------

// 1. Setting
//-----------------------------------------------------------------

//	Usage
//
//	if(setting(navigation, 'sticky')) {
//		...
//	}

function setting(component, setting) {

	var $setting  = module[component][setting],
		$id = $('.' + component + ', [class*="' + component + '-"]'),
		$selector = $id.is('[class*="-' + setting + '"]') == true;

	if (typeof($setting[Object.keys($setting)[0]]) == 'boolean') {
		return $selector || $setting['default'] != false;
	} else {
		return $selector || $setting != false;
	}

}<commit_msg>adding '_' prefix to global 'module' var<commit_after>/*-----------------------------------------------------------------

Modular - JS Extension
Made by @esr360
http://github.com/esr360/Modular/

-----------------------------------------------------------------*/

//-----------------------------------------------------------------
// Convert CSS config to JS
//-----------------------------------------------------------------

// Get styles' configuration
var stylesConfigJSON = document.getElementById("stylesConfigJSON");

// Remove quotes from computed JSON
function removeQuotes(json) {
    json = json.replace(/^['"]+|\s+|\\|(;\s?})+|['"]$/g, '');
    return json;
}

// Convert computed JSON to camelCase
function camelCase(json) {
	json = json.replace(/-([a-z])/g, function (g) {
		return g[1].toUpperCase();
	});
	return json;
}

// Convert the config to JS
function getStylesConfig(camelCase) {
    var style = null;
    style = window.getComputedStyle(stylesConfigJSON, '::before');
    style = style.content;
	style = removeQuotes(style);
	if(camelCase) {
		style = camelCase(style);
	}
    return JSON.parse(style);
}

// Store configuartion data in a variable
var _module = getStylesConfig();

// CamelCase the config
var moduleCC = getStylesConfig(camelCase);

// Create a global variable to select each main component in the DOM
var componentIndex = 0;
$.each(_module, function(component) {
	var componentCC = moduleCC[Object.keys(moduleCC)[componentIndex]]['name'];
	window['_' + componentCC] = '.' + component + ', [class*="' + component + '-"]';
	componentIndex++;
});

//-----------------------------------------------------------------
// Functions
//-----------------------------------------------------------------

//	1. setting()

//-----------------------------------------------------------------

// 1. Setting
//-----------------------------------------------------------------

//	Usage
//
//	if(setting(navigation, 'sticky')) {
//		...
//	}

function setting(component, setting) {

	var $setting  = _module[component][setting],
		$id = $('.' + component + ', [class*="' + component + '-"]'),
		$selector = $id.is('[class*="-' + setting + '"]') == true;

	if (typeof($setting[Object.keys($setting)[0]]) == 'boolean') {
		return $selector || $setting['default'] != false;
	} else {
		return $selector || $setting != false;
	}

}<|endoftext|>


<commit_before>module.exports = {
  presets: [require("foundation-design-system/tailwind.config")],
  content: [
    "./node_modules/foundation-design-system/**/*.js",
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
};
<commit_msg>tailwind.config: pass markdown content thru markdoc for tailwind classes<commit_after>const markdoc = require("@markdoc/markdoc");

module.exports = {
  presets: [require("foundation-design-system/tailwind.config")],
  content: {
    files: [
      "./node_modules/foundation-design-system/**/*.js",
      "./pages/**/*.{js,ts,jsx,tsx}",
      "./components/**/*.{js,ts,jsx,tsx}",
      "./content/**/*.md",
    ],
    transform: {
      md: (content) => {
        const parsed = markdoc.parse(content);
        const transform = markdoc.transform(parsed);
        return markdoc.renderers.html(transform);
      },
    },
  },
};
<|endoftext|>


<commit_before>'use strict';

/**
* Test you can visit the Higlass Display page.
*/
describe("HiGlass Display pages", function(){

    context('Higlass Display summary page', function(){
        it('Can visit HiGlass Display collection page without login', function(){

            // Visit the page and confirm you can see the table and facet properties.
            cy.visit('/higlass-view-configs').wait(100).end()
                .get(".search-headers-column-block span.column-title").should('have.text', ['Title', 'Creator'].join('')).end()
                .get(".facets-header .facets-title").should('have.text', 'Properties');

            // All of the higlass displays you can view should have the "released" status.
            cy.request("/higlass-view-configs/?format=json").then((response)=>{
                const nonReleasedFound = Cypress._.some(response.body["@graph"], (higlassViewConf)=>{
                    if (higlassViewConf.status && higlassViewConf.status === "released") {
                        return false;
                    }
                    return true;
                });
                expect(nonReleasedFound).to.not.be.ok;
            });

        });

        it('Have permission to create new displays & view own drafts', function(){

            // You should be able to visit the higlass view that is still in "draft" status.
            const draftUrl = "/higlass-view-configs/00000000-1111-0000-1111-000000000002/";

            // Log in, visit the page and look for the create button to assert ability to create.
            cy.visit('/higlass-view-configs/').login4DN({'email': 'ud4dntest@gmail.com', 'useEnvToken' : true}).wait(500).end()
                .get(".above-results-table-row a.btn.btn-xs span").should('have.text', 'Create');

            cy.visit(draftUrl).end().logout4DN();

        });

    });

    // Being skipped until Foursight is set up to collect & purge any Items created here.
    // We should manually add "tag" : "created_via_cypress_test" or something maybe when we perform edit? Idk
    // Or PATCH/request to it separately right after 'clone' test? Idk
    // Or in the afterEach logic we also patch that maybe?
    // Or in foursight we can find "modified_by" : "ud4dntest@gmail.com" + "status":"deleted" ?
    // Many options... maybe already setup/working ?
    context.skip('Share/Create and Edit Individual Higlass display page', function() {

        // Tracks uuids across tests so they can be deleted afterwards.
        var testUuidsToDelete = [];

        beforeEach(function() {
            // Log in.
            cy.visit('/higlass-view-configs/').login4DN({'email': 'ud4dntest@gmail.com', 'useEnvToken' : true}).wait(500);
        });

        afterEach(function(){
            if (testUuidsToDelete.length === 0) return;

            // Log in.
            cy.visit('/higlass-view-configs/').login4DN({'email': 'ud4dntest@gmail.com', 'useEnvToken' : true}).wait(500);

            // Delete all newly created higlass views.
            Cypress._.forEach(testUuidsToDelete, (newUuid) => {
                cy.request("DELETE", newUuid).wait(100);
            });

            // Empty the array now that we're done.
            testUuidsToDelete = [];
        });

        it('Can clone new draft views', function() {
            // Verify logged in users can save higlass displays.

            // Go to the display for the draft display.
            const draftUrl = "/higlass-view-configs/00000000-1111-0000-1111-000000000002/";
            cy.visit(draftUrl);

            // When the app creates a new HiGlass display, we'll capture the JSON of the POST call.
            cy.server();
            cy.route('POST', '/higlass-view-configs/').as('newHiglassDisplay');

            // Click the save as button.
            cy.get('.text-right.inline-block .inline-block:nth-child(2) button.btn.btn-success').click().then(() => {
                // Confirm there is a success message.
                cy.get('.alert div').should('have.text', 'Saved new display.');
            });

            // Inspect POST response.
            cy.get('@newHiglassDisplay').then(function (xhr) {
                // Expect a 201 response.
                expect(xhr.status).to.eq(201);

                // Expect a new uuid.
                expect(xhr.responseBody["@graph"][0]["uuid"]).to.not.equal("00000000-1111-0000-1111-000000000002");

                // Add the test uuid so we can delete it later.
                testUuidsToDelete.push(xhr.responseBody["@graph"][0]["uuid"]);
            });
        });

        it('Can edit the title and description', function() {
            // Verify logged in users can save higlass displays.

            // Go to the display for the draft display.
            const draftUrl = "/higlass-view-configs/00000000-1111-0000-1111-000000000002/";
            cy.visit(draftUrl);

            // There will be an AJAX response to a POST for the new Higlass display, so capture it here.
            cy.server();
            cy.route('POST', '/higlass-view-configs/').as('newHiglassDisplay');

            // Click the save as button.
            cy.get('.text-right.inline-block .inline-block:nth-child(2) button.btn.btn-success').click().then(() => {
                // Confirm there is a success message.
                cy.get('.alert div').should('have.text', 'Saved new display.');
            });

            // Inspect the AJAX response so we can capture the new uuid.
            cy.get('@newHiglassDisplay').then(function (xhr) {
                // Expect a 201 response.
                expect(xhr.status).to.eq(201);

                // Expect a new uuid.
                expect(xhr.responseBody["@graph"][0]["uuid"]).to.not.equal("00000000-1111-0000-1111-000000000002");

                // Add the test uuid so we can delete it later.
                testUuidsToDelete.push(xhr.responseBody["@graph"][0]["uuid"]);
                const newUuid = xhr.responseBody["@graph"][0]["uuid"];
                expect(newUuid).to.be.ok;

                // Clicking the SaveAs button should redirect us to the new page
                cy.location('pathname').should('eq', "/higlass-view-configs/" + newUuid + "/");

                // Click on the edit button and wait for the page load.
                cy.get(".action-button[data-action='edit'] a").click();

                // Change the title and description, then save.
                const newTitle = "Cypress Cool Display";
                const newDescription = "Look at the description";

                cy.get("input#field_for_title").clear().type(newTitle).then(() => {
                    cy.get('#field_for_description').clear().type(newDescription).then(() => {
                        // Click validate then click submit
                        cy.get(".action-buttons-container button.btn-info").click().then(() => {
                            cy.get(".action-buttons-container button.btn-success").click().wait(1000).then(() => {
                                // Once the page reloads, look for the updated title/description
                                cy.request("/higlass-view-configs/" + newUuid + "/?format=json&datastore=database").then((resp)=>{
                                    expect(resp.body.title).to.equal(newTitle);
                                    expect(resp.body.description).to.equal(newDescription);
                                });
                            });
                        });
                    });
                });

            });
        });
    });

    context('Sharing on the Individual Higlass display page', function() {

        after(function(){
            // Edit the higlass display back to draft status.
            cy.visit('/higlass-view-configs/').login4DN({'email': 'ud4dntest@gmail.com', 'useEnvToken' : true}).wait(500).end().request(
                'PATCH',
                "/higlass-view-configs/00000000-1111-0000-1111-000000000002/",
                {
                    "status":"draft"
                }
            ).logout4DN();
        });

        it('Can release HiGlass Item to public.', function() {

            // Log in as the sharing user.
            // N.B. Cypress had/has some gotchas/issues in terms of preserving cookies between test, so had previously
            // been attempting to login and out in same test.
            // They might have updated/improved their mechanisms and maybe could refactor code later.
            cy.visit('/higlass-view-configs/').login4DN({'email': 'ud4dntest@gmail.com', 'useEnvToken' : true}).wait(500);

            // Go to the draft higlass display.
            // Click on the Share button.
            // Wait for the message popup to indicate success.
            const draftUrl = "/higlass-view-configs/00000000-1111-0000-1111-000000000002/";

            // This also tests the mobile menu as well as the share/release dropdown btn functionality.
            cy.visit(draftUrl).get('.item-view-header .indicator-item.item-status').should('have.text', 'draft').end()
                .get(".tab-section-title button.toggle-open-button").click().wait(500).end()
                .get(".tab-section-title .inner-panel.collapse.in button.btn-info.dropdown-toggle").click().wait(300).end()
                .get(".tab-section-title .inner-panel.collapse.in button.btn-info.dropdown-toggle + ul").within(($ul) => {
                    return cy.contains("Visible by Everyone").click().wait(1000).end();
                }).end()
                .get('.alert div').should('contain', 'Changed Display status to released.').end()

                // Download the JSON to see if the higlass display is released
                .request(draftUrl + "?format=json&datastore=database").then((newJson)=>{
                    expect(newJson.body.status).to.equal("released");
                }).logout4DN();
        });
    });
});
<commit_msg>Cypress: Update & enable HiGlass "Clone" test<commit_after>'use strict';

/**
* Test you can visit the Higlass Display page.
*/
describe("HiGlass Display pages", function(){

    context('Higlass Display summary page', function(){
        it('Can visit HiGlass Display collection page without login', function(){

            // Visit the page and confirm you can see the table and facet properties.
            cy.visit('/higlass-view-configs').wait(100).end()
                .get(".search-headers-column-block span.column-title").should('have.text', ['Title', 'Creator'].join('')).end()
                .get(".facets-header .facets-title").should('have.text', 'Properties');

            // All of the higlass displays you can view should have the "released" status.
            cy.request("/higlass-view-configs/?format=json").then((response)=>{
                const nonReleasedFound = Cypress._.some(response.body["@graph"], (higlassViewConf)=>{
                    if (higlassViewConf.status && higlassViewConf.status === "released") {
                        return false;
                    }
                    return true;
                });
                expect(nonReleasedFound).to.not.be.ok;
            });

        });

        it('Have permission to create new displays & view own drafts', function(){

            // You should be able to visit the higlass view that is still in "draft" status.
            const draftUrl = "/higlass-view-configs/00000000-1111-0000-1111-000000000002/";

            // Log in, visit the page and look for the create button to assert ability to create.
            cy.visit('/higlass-view-configs/').login4DN({'email': 'ud4dntest@gmail.com', 'useEnvToken' : true}).wait(500).end()
                .get(".above-results-table-row a.btn.btn-xs span").should('have.text', 'Create');

            cy.visit(draftUrl).end().logout4DN();

        });

    });

    context('Share/Create and Edit Individual Higlass display page', function() {

        // Tracks uuids across tests so they can be deleted afterwards.
        var testItemsToDelete = [];

        beforeEach(function() {
            // Log in.
            cy.visit('/higlass-view-configs/').login4DN({'email': 'ud4dntest@gmail.com', 'useEnvToken' : true}).wait(500);
        });

        afterEach(function(){
            if (testItemsToDelete.length === 0) return;

            // Log in _as admin_.
            cy.visit('/higlass-view-configs/').login4DN({'email': '4dndcic@gmail.com', 'useEnvToken' : true}).wait(500);

            // Delete all newly created higlass views.
            cy.wrap(testItemsToDelete).each(function(testItem){ // Synchronously process async stuff.
                cy.request({
                    method: "DELETE",
                    url: testItem['@id'],
                    headers: {
                        "Content-Type" : "application/json",
                        "Accept" : "application/json"
                    }
                }).end()
                    .request({
                        method: "PATCH",
                        url: testItem['@id'],
                        headers: {
                            "Content-Type" : "application/json",
                            "Accept" : "application/json"
                        },
                        body: JSON.stringify({ "tags" : ["deleted_by_cypress_test"] })
                    });
            });

            // Empty the array now that we're done.
            testItemsToDelete = [];
        });

        it('Can clone new draft views', function() {
            // Verify logged in users can save higlass displays.

            // Go to the display for the draft display.
            const draftUrl = "/higlass-view-configs/00000000-1111-0000-1111-000000000002/";
            cy.visit(draftUrl);

            // When the app creates a new HiGlass display, we'll capture the JSON of the POST call.
            cy.server();
            cy.route('POST', '/higlass-view-configs/').as('newHiglassDisplay');

            // Click the 'Clone' button.
            cy.get(".tab-section-title button.toggle-open-button").click().wait(500).end()
                .get(".tab-section-title .inner-panel.collapse.in").within(($panel)=>{
                    return cy.contains('Clone').click().wait(300).end();
                }).end()
                .get('.alert div').should('have.text', 'Saved new display.').end()
                // Inspect POST response.
                .get('@newHiglassDisplay').then(function (xhr) {

                    // Expect a 201 response.
                    expect(xhr.status).to.eq(201);

                    // Expect a new uuid.
                    expect(xhr.responseBody["@graph"][0]["uuid"]).to.not.equal("00000000-1111-0000-1111-000000000002");

                    // Add the test Item so we can delete it later.
                    testItemsToDelete.push(xhr.responseBody["@graph"][0]);
                });
        });

        it('Can edit the title and description', function() {
            // Verify logged in users can save higlass displays.

            // Go to the display for the draft display.
            const draftUrl = "/higlass-view-configs/00000000-1111-0000-1111-000000000002/";
            cy.visit(draftUrl);

            // There will be an AJAX response to a POST for the new Higlass display, so capture it here.
            cy.server();
            cy.route('POST', '/higlass-view-configs/').as('newHiglassDisplay');

            // Click the 'Clone' button.
            cy.get(".tab-section-title button.toggle-open-button").click().wait(500).end()
                .get(".tab-section-title .inner-panel.collapse.in").within(($panel)=>{
                    return cy.contains('Clone').click().wait(300).end();
                }).end()
                // Confirm there is a success message.
                .get('.alert div').should('have.text', 'Saved new display.').end()
                // Inspect the AJAX response so we can capture the new uuid.
                .get('@newHiglassDisplay').then(function (xhr) {

                    // Expect a 201 response.
                    expect(xhr.status).to.eq(201);

                    // Expect a new uuid.
                    expect(xhr.responseBody["@graph"][0]["uuid"]).to.not.equal("00000000-1111-0000-1111-000000000002");

                    // Add the test uuid so we can delete it later.
                    testItemsToDelete.push(xhr.responseBody["@graph"][0]);
                    const newID = xhr.responseBody["@graph"][0]["@id"];
                    expect(newID).to.be.ok;

                    // Clicking the SaveAs button should redirect us to the new page
                    cy.location('pathname').should('eq', newID);

                    // Click on the edit button and wait for the page load.
                    cy.get(".action-button[data-action='edit'] a").click();

                    // Change the title and description, then save.
                    const newTitle = "Cypress Cool Display";
                    const newDescription = "Look at the description";

                    cy.get("input#field_for_title").clear().type(newTitle).then(() => {
                        cy.get('#field_for_description').clear().type(newDescription).then(() => {
                            // Click validate then click submit
                            cy.get(".action-buttons-container button.btn-info").click().then(() => {
                                cy.get(".action-buttons-container button.btn-success").click().wait(1000).then(() => {
                                    // Once the page reloads, look for the updated title/description
                                    cy.request(newID + "?format=json&datastore=database").then((resp)=>{
                                        expect(resp.body.title).to.equal(newTitle);
                                        expect(resp.body.description).to.equal(newDescription);
                                    });
                                });
                            });
                        });
                    });

                });
        });
    });

    context('Sharing on the Individual Higlass display page', function() {

        after(function(){
            // Edit the higlass display back to draft status.
            cy.visit('/higlass-view-configs/').login4DN({'email': 'ud4dntest@gmail.com', 'useEnvToken' : true}).wait(500).end().request(
                'PATCH',
                "/higlass-view-configs/00000000-1111-0000-1111-000000000002/",
                {
                    "status":"draft"
                }
            ).logout4DN();
        });

        it('Can release HiGlass Item to public.', function() {

            // Log in as the sharing user.
            // N.B. Cypress had/has some gotchas/issues in terms of preserving cookies between test, so had previously
            // been attempting to login and out in same test.
            // They might have updated/improved their mechanisms and maybe could refactor code later.
            cy.visit('/higlass-view-configs/').login4DN({'email': 'ud4dntest@gmail.com', 'useEnvToken' : true}).wait(500);

            // Go to the draft higlass display.
            // Click on the Share button.
            // Wait for the message popup to indicate success.
            const draftUrl = "/higlass-view-configs/00000000-1111-0000-1111-000000000002/";

            // This also tests the mobile menu as well as the share/release dropdown btn functionality.
            cy.visit(draftUrl).get('.item-view-header .indicator-item.item-status').should('have.text', 'draft').end()
                .get(".tab-section-title button.toggle-open-button").click().wait(500).end()
                .get(".tab-section-title .inner-panel.collapse.in button.btn-info.dropdown-toggle").click().wait(300).end()
                .get(".tab-section-title .inner-panel.collapse.in button.btn-info.dropdown-toggle + ul").within(($ul) => {
                    return cy.contains("Visible by Everyone").click().wait(1000).end();
                }).end()
                .get('.alert div').should('contain', 'Changed Display status to released.').end()

                // Download the JSON to see if the higlass display is released
                .request(draftUrl + "?format=json&datastore=database").then((newJson)=>{
                    expect(newJson.body.status).to.equal("released");
                }).logout4DN();
        });
    });
});
<|endoftext|>


<commit_before>// @ts-check
/* eslint-disable no-param-reassign */
// This file contain any `private` method for the EasyV

import is from 'is_js';
import handlerMatcher from './matchers';

export const NAME_PLACEHOLDER = '#{NAME}#';

/**
 * Return error message for checking the parameters of the constructor.
 *
 * @export
 * @param {string} paramName
 * @param {any} value
 * @returns {string}
 */
export function getConstructorErrorMessage(paramName, value) {
  return `[Form Validation - ${paramName}] Expect: non empty object. Actual: ${value}`;
}

/**
 * Check if an object is a non-empty object
 *
 * @param {any} obj
 * @returns {boolean}
 */
function isNonEmptyObject(obj) {
  return is.object(obj) && is.not.empty(obj);
}

/**
 * Type check for 2 parameters of the constructor
 *
 * @export
 * @param {object} component
 * @param {object} schema
 */
export function typeCheck(component, schema) {
  const isComponentValid = isNonEmptyObject(component);
  const isSchemaValid = isNonEmptyObject(schema);

  if (!isComponentValid) {
    throw new Error(
      getConstructorErrorMessage('Parameter component', component)
    );
  }

  if (!isSchemaValid) {
    throw new Error(getConstructorErrorMessage('Parameter schema', schema));
  }

  Object.keys(schema).forEach(prop => {
    if (!isNonEmptyObject(schema[prop])) {
      throw new Error(
        getConstructorErrorMessage(`schema.${prop}`, schema[prop])
      );
    }
  });
}

/**
 * Create initial value for a field if no default is provided.
 *
 * @export
 * @param {object} schema
 * @returns {boolean | string}
 */
export function createInitialValue(schema) {
  if (is.propertyDefined(schema, 'default')) {
    return schema.default;
  }

  if (is.propertyDefined(schema, 'min') || is.propertyDefined(schema, 'max')) {
    return '0';
  }

  return '';
}

/**
 * Create a new state for a field in componentState.formStatus.fields.field
 *
 * @export
 * @param {boolean} [needValue=false]
 * @param {object} [fieldSchema]
 * @returns {object}
 */
export function createNewFieldState(needValue = false, fieldSchema) {
  const result = {
    status: 'normal',
    errorText: ''
  };
  if (needValue) {
    result.value = createInitialValue(fieldSchema);
  }
  return result;
}

export function createInitialState(schema, userState) {
  const initialState = {
    isFormOK: false,
    ...userState
  };

  Object.keys(schema).forEach(prop => {
    initialState[prop] = createNewFieldState(true, schema[prop]);
  });

  const schemaItems = Object.keys(schema);
  schemaItems.forEach(name => {
    if (is.propertyDefined(userState, name)) {
      initialState[name] = {
        ...initialState[name],
        ...userState[name]
      };
    }
  });

  return initialState;
}

/**
 * Check if we should change the state or not.
 *
 * @export
 * @param {object} oldState
 * @param {object} newState
 * @returns {boolean}
 */
export function shouldChange(oldState, newState) {
  const isErrorDifferent = oldState.status !== newState.status;
  const isValueDifferent = oldState.value !== newState.value;
  return isErrorDifferent || isValueDifferent;
}

/**
 * throw an error with defined text, usually calls by handler.
 *
 * @export
 * @param {any} value
 * @param {string} errorText
 * @returns {never}
 */
export function throwError(value, errorText) {
  const result = createNewFieldState();
  result.value = value;
  result.status = 'error';
  result.errorText = errorText;
  throw result;
}

/**
 * Modify the state from { state } to { fieldName: state }
 *
 * @export
 * @param {string} name
 * @param {object} result
 * @returns {object}
 */
export function addNameToResult(name, result) {
  if (result.status === 'error') {
    result.errorText = result.errorText.replace(NAME_PLACEHOLDER, name);
  }
  return { [name]: result };
}

/**
 * It will run through the user's settings for a field,
 * and try matching to the matchers.js,
 * if according handler could be found,
 * it will then execute the according handler function.
 * For instance:
 * if user sets a `minLength` for a field,
 * This function will invoke the minLengthHandler()
 *
 * @param {object} matcher
 * @param {object} fieldState
 * @param {object} schema
 */
function runMatchers(matcher, fieldState, schema) {
  Object.keys(schema).forEach(ruleInSchema => {
    if (is.propertyDefined(matcher, ruleInSchema)) {
      matcher[ruleInSchema](fieldState.value, schema);
    } else if (ruleInSchema !== 'default') {
      console.warn(`No such rule: ${ruleInSchema}`);
    }
  });
  return fieldState;
}

/**
 * This is the main entry for all validator.
 *
 * @export
 * @param {any} value
 * @param {object} schema
 * @returns {object}
 */
export function validatorRunner(value, schema) {
  const fieldState = createNewFieldState();
  fieldState.value = value;
  return runMatchers(handlerMatcher, fieldState, schema);
}

/**
 * If field's status === `error` and its value isn't empty
 * we will set its status to `ok`
 * then return it.
 * It's fine to mutate here, since it's already a new state here.
 *
 * @export
 * @param {object} fieldState
 * @returns {object}
 */
export function checkFieldIsOK(fieldState) {
  if (
    fieldState.status !== 'error' &&
    is.existy(fieldState.value) &&
    is.not.empty(fieldState.value)
  ) {
    fieldState.status = 'ok';
  }
  return fieldState;
}

/**
 * If all fields in the state has their status !== error
 * Then we will set the isFormOK to true then return the state.
 * Just mutate the value since it's already a new state object
 *
 * @export
 * @param {object} schema
 * @param {object} componentState
 * @returns {object}}
 */
export function checkIsFormOK(schema, componentState) {
  const properties = Object.keys(schema);
  let isError = false;
  properties.some(prop => {
    if (componentState[prop].status === 'error') {
      isError = true;
      return true;
    }
    return false;
  });
  if (!isError) {
    componentState.isFormOK = true;
  }
  return componentState;
}

export function restoreErrorStatus(fieldState) {
  if (fieldState.status === 'error' && is.empty(fieldState.value)) {
    fieldState.status = 'normal';
    fieldState.errorText = '';
  }
  return fieldState;
}

function updateWhenNeeded(
  newFieldState,
  propName,
  update,
  schema,
  formStatus = ''
) {
  const fieldState = addNameToResult(propName, newFieldState);
  if (formStatus === '') {
    update(fieldState);
    return
  }

  const oldFieldState = formStatus[propName];
  const newFieldState1 = {
    ...oldFieldState,
    ...fieldState[propName]
  };

  if (!shouldChange(oldFieldState, newFieldState)) {
    return;
  }

  const finalState = {
    ...formStatus,
    [propName]: { ...newFieldState1 }
  };
  update(checkIsFormOK(schema, finalState));
}

export function startValidating(target, schema, update, allState) {
  const propName = target.name;
  const fieldInfo = {
    value: target.value,
    schema: schema[propName]
  };

  return Promise.resolve(fieldInfo)
    .then(info => validatorRunner(info.value, info.schema))
    .then(result1 => checkFieldIsOK(result1))
    .then(result2 => restoreErrorStatus(result2))
    .catch(errorState => errorState)
    .then(newFieldState =>
      updateWhenNeeded(newFieldState, propName, update, schema, allState)
    );
}
<commit_msg>Prepare for a big refactor<commit_after>// @ts-check
/* eslint-disable no-param-reassign */
// This file contain any `private` method for the EasyV

import is from 'is_js';
import handlerMatcher from './matchers';

export const NAME_PLACEHOLDER = '#{NAME}#';

/**
 * Return error message for checking the parameters of the constructor.
 *
 * @export
 * @param {string} paramName
 * @param {any} value
 * @returns {string}
 */
export function getConstructorErrorMessage(paramName, value) {
  return `[Form Validation - ${paramName}] Expect: non empty object. Actual: ${value}`;
}

/**
 * Check if an object is a non-empty object
 *
 * @param {any} obj
 * @returns {boolean}
 */
function isNonEmptyObject(obj) {
  return is.object(obj) && is.not.empty(obj);
}

/**
 * Type check for 2 parameters of the constructor
 *
 * @export
 * @param {object} component
 * @param {object} schema
 */
export function typeCheck(component, schema) {
  const isComponentValid = isNonEmptyObject(component);
  const isSchemaValid = isNonEmptyObject(schema);

  if (!isComponentValid) {
    throw new Error(
      getConstructorErrorMessage('Parameter component', component)
    );
  }

  if (!isSchemaValid) {
    throw new Error(getConstructorErrorMessage('Parameter schema', schema));
  }

  Object.keys(schema).forEach(prop => {
    if (!isNonEmptyObject(schema[prop])) {
      throw new Error(
        getConstructorErrorMessage(`schema.${prop}`, schema[prop])
      );
    }
  });
}

/**
 * Create initial value for a field if no default is provided.
 *
 * @export
 * @param {object} schema
 * @returns {boolean | string}
 */
export function createInitialValue(schema) {
  if (is.propertyDefined(schema, 'default')) {
    return schema.default;
  }

  if (is.propertyDefined(schema, 'min') || is.propertyDefined(schema, 'max')) {
    return '0';
  }

  return '';
}

/**
 * Create a new state for a field in componentState.formStatus.fields.field
 *
 * @export
 * @param {boolean} [needValue=false]
 * @param {object} [fieldSchema]
 * @returns {object}
 */
export function createNewFieldState(needValue = false, fieldSchema) {
  const result = {
    status: 'normal',
    errorText: ''
  };
  if (needValue) {
    result.value = createInitialValue(fieldSchema);
  }
  return result;
}

export function createInitialState(schema, userState) {
  const initialState = {
    isFormOK: false,
    ...userState
  };

  Object.keys(schema).forEach(prop => {
    initialState[prop] = createNewFieldState(true, schema[prop]);
  });

  const schemaItems = Object.keys(schema);
  schemaItems.forEach(name => {
    if (is.propertyDefined(userState, name)) {
      initialState[name] = {
        ...initialState[name],
        ...userState[name]
      };
    }
  });

  return initialState;
}

/**
 * Check if we should change the state or not.
 *
 * @export
 * @param {object} oldState
 * @param {object} newState
 * @returns {boolean}
 */
export function shouldChange(oldState, newState) {
  const isErrorDifferent = oldState.status !== newState.status;
  const isValueDifferent = oldState.value !== newState.value;
  return isErrorDifferent || isValueDifferent;
}

/**
 * throw an error with defined text, usually calls by handler.
 *
 * @export
 * @param {any} value
 * @param {string} errorText
 * @returns {never}
 */
export function throwError(value, errorText) {
  const result = createNewFieldState();
  result.value = value;
  result.status = 'error';
  result.errorText = errorText;
  throw result;
}

/**
 * Modify the state from { state } to { fieldName: state }
 *
 * @export
 * @param {string} name
 * @param {object} result
 * @returns {object}
 */
export function addNameToResult(name, result) {
  if (result.status === 'error') {
    result.errorText = result.errorText.replace(NAME_PLACEHOLDER, name);
  }
  return { [name]: result };
}

/**
 * It will run through the user's settings for a field,
 * and try matching to the matchers.js,
 * if according handler could be found,
 * it will then execute the according handler function.
 * For instance:
 * if user sets a `minLength` for a field,
 * This function will invoke the minLengthHandler()
 *
 * @param {object} matcher
 * @param {object} fieldState
 * @param {object} schema
 */
function runMatchers(matcher, fieldState, fieldSchema) {
  const fieldName = Object.keys(fieldSchema)[0];
  const schema = fieldSchema[fieldName];
  Object.keys(schema).forEach(ruleInSchema => {
    if (is.propertyDefined(matcher, ruleInSchema)) {
      matcher[ruleInSchema](fieldState.value, schema);
    } else if (ruleInSchema !== 'default') {
      console.warn(`No such rule: ${ruleInSchema}`);
    }
  });
  return fieldState;
}

/**
 * This is the main entry for all validator.
 *
 * @export
 * @param {any} value
 * @param {object} schema
 * @returns {object}
 */
export function validatorRunner(value, schema) {
  const fieldState = createNewFieldState();
  fieldState.value = value;
  return runMatchers(handlerMatcher, fieldState, schema);
}

/**
 * If field's status === `error` and its value isn't empty
 * we will set its status to `ok`
 * then return it.
 * It's fine to mutate here, since it's already a new state here.
 *
 * @export
 * @param {object} fieldState
 * @returns {object}
 */
export function checkFieldIsOK(fieldState) {
  if (
    fieldState.status !== 'error' &&
    is.existy(fieldState.value) &&
    is.not.empty(fieldState.value)
  ) {
    fieldState.status = 'ok';
  }
  return fieldState;
}

/**
 * If all fields in the state has their status !== error
 * Then we will set the isFormOK to true then return the state.
 * Just mutate the value since it's already a new state object
 *
 * @export
 * @param {object} schema
 * @param {object} componentState
 * @returns {object}}
 */
export function checkIsFormOK(schema, componentState) {
  const properties = Object.keys(schema);
  let isError = false;
  properties.some(prop => {
    if (componentState[prop].status === 'error') {
      isError = true;
      return true;
    }
    return false;
  });
  if (!isError) {
    componentState.isFormOK = true;
  }
  return componentState;
}

export function restoreErrorStatus(fieldState) {
  if (fieldState.status === 'error' && is.empty(fieldState.value)) {
    fieldState.status = 'normal';
    fieldState.errorText = '';
  }
  return fieldState;
}

function updateWhenNeeded(
  newFieldState,
  propName,
  update,
  schema,
  formStatus = ''
) {
  const fieldState = addNameToResult(propName, newFieldState);
  if (formStatus === '') {
    update(fieldState);
    return
  }

  const oldFieldState = formStatus[propName];
  const newFieldState1 = {
    ...oldFieldState,
    ...fieldState[propName]
  };

  if (!shouldChange(oldFieldState, newFieldState)) {
    return;
  }

  const finalState = {
    ...formStatus,
    [propName]: { ...newFieldState1 }
  };
  update(checkIsFormOK(schema, finalState));
}

export function startValidating(target, schema, update, allState) {
  const propName = target.name;
  const fieldInfo = {
    value: target.value,
    schema: { [propName]: schema[propName] }
  };

  return Promise.resolve(fieldInfo)
    .then(info => validatorRunner(info.value, info.schema))
    .then(result1 => checkFieldIsOK(result1))
    .then(result2 => restoreErrorStatus(result2))
    .catch(errorState => errorState)
    .then(newFieldState =>
      updateWhenNeeded(newFieldState, propName, update, schema, allState)
    );
}
<|endoftext|>


<commit_before>const defaultTheme = require('tailwindcss/defaultTheme');

module.exports = {
    purge: [
        './resources/**/*.blade.php',
        './resources/**/*.js',
        './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
    ],
    theme: {
        container: {
            screens: {
                sm: '640px',
                md: '768px',
                lg: '1024px',
                xl: '1280px',
                '2xl': '1280px',
            },
        },
        extend: {
            colors: {
                lio: {
                    100: '#e6f6f3',
                    200: '#a3ecde',
                    300: '#6ee2cc',
                    400: '#43d4b8',
                    500: '#18bc9c',
                    600: '#14a88b',
                    700: '#0e8b73',
                    800: '#0a6553',
                    900: '#053c31',
                },
                gray: {
                    100: '#f9fafb',
                    200: '#dfdfdf',
                    300: '#cbcbcb',
                    400: '#b2b2b2',
                    500: '#989898',
                    600: '#7e7e7e',
                    700: '#636363',
                    800: '#484a4a',
                    900: '#343636',
                },
                twitter: '#00aaec',
            },
            fontFamily: {
                sans: ['Inter', ...defaultTheme.fontFamily.sans],
            },
            maxWidth: {
                14: '14rem',
            },
            typography: {
                DEFAULT: {
                    css: {
                        color: '#343636',
                        a: {
                            'text-decoration': 'none',
                            color: '#18bc9c',
                            'border-bottom': '2px solid #e6f6f3',
                            'padding-bottom': '0.125rem',
                            '&:hover': {
                                color: '#0e8b73',
                            },
                        },
                    },
                },
            },
        },
    },
    variants: {},
    plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],
};
<commit_msg>Style inline code (#665)<commit_after>const defaultTheme = require('tailwindcss/defaultTheme');

module.exports = {
    purge: [
        './resources/**/*.blade.php',
        './resources/**/*.js',
        './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
    ],
    theme: {
        container: {
            screens: {
                sm: '640px',
                md: '768px',
                lg: '1024px',
                xl: '1280px',
                '2xl': '1280px',
            },
        },
        extend: {
            colors: {
                lio: {
                    100: '#e6f6f3',
                    200: '#a3ecde',
                    300: '#6ee2cc',
                    400: '#43d4b8',
                    500: '#18bc9c',
                    600: '#14a88b',
                    700: '#0e8b73',
                    800: '#0a6553',
                    900: '#053c31',
                },
                gray: {
                    100: '#f9fafb',
                    200: '#dfdfdf',
                    300: '#cbcbcb',
                    400: '#b2b2b2',
                    500: '#989898',
                    600: '#7e7e7e',
                    700: '#636363',
                    800: '#484a4a',
                    900: '#343636',
                },
                twitter: '#00aaec',
            },
            fontFamily: {
                sans: ['Inter', ...defaultTheme.fontFamily.sans],
            },
            maxWidth: {
                14: '14rem',
            },
            typography: (theme) => ({
                DEFAULT: {
                    css: {
                        color: theme('colors.gray.900'),
                        a: {
                            'text-decoration': 'none',
                            color: theme('colors.lio.500'),
                            'border-bottom': `2px solid ${theme('colors.lio.100')}`,
                            'padding-bottom': theme('padding')['0.5'],
                            '&:hover': {
                                color: theme('colors.lio.700'),
                            },
                        },
                        'code::before': {
                            content: '""',
                        },
                        'code::after': {
                            content: '""',
                        },
                        code: {
                            color: '#ec4073',
                            'background-color': 'rgba(236, 64, 115, 0.1)',
                            'border-radius': theme('borderRadius.DEFAULT'),
                            padding: `${theme('padding')[0.5]}`,
                        },
                    },
                },
            }),
        },
    },
    variants: {},
    plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],
};
<|endoftext|>


<commit_before>/*
 Licensed to Jasig under one or more contributor license
 agreements. See the NOTICE file distributed with this work
 for additional information regarding copyright ownership.
 Jasig licenses this file to you under the Apache License,
 Version 2.0 (the "License"); you may not use this file
 except in compliance with the License. You may obtain a
 copy of the License at:

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing,
 software distributed under the License is distributed on
 an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND, either express or implied. See the License for the
 specific language governing permissions and limitations
 under the License.
 */
// THE CONTENTS OF THIS FILE WILL MOVE INTO bedeworkAttendees.js

// ========================================================================
// free/busy functions
// ========================================================================

/* An attendee
 * name:     String - name of attendee, e.g. "Venerable Bede"
 * uid:      String - attendee's uid with mailto included, e.g. "mailto:vbede@example.com"
 * freebusy: Array of rfc5545 freebusy reply values for the current attendee in the current date range
 * role:     String - Attendee role, e.g. CHAIR, REQ-PARTICIPANT, etc
 * status:   String - participation status (PARTSTAT)
 */
var bwAttendee = function(name, uid, freebusy, role, status) {
  this.name = name;
  this.uid = uid;
  this.freebusy = freebusy;
  this.role = role;
  this.status = status;
}

/* Object to model the freebusy grid
 * displayId:  ID of html block for display output
 * startRange: js date string - start of date range for grid
 * endRange:   js date string - end of date range for grid
 * startDate:  js date string - start date/time for meeting
 * endDate:    date string - end date/time for meeting
 * attendees:  array   - array of attendee objects; MUST include organizer on first instantiation
 * workday:    boolean - true to display workday hours only, false to display all 24 hours
 * zoom:       integer - scalar value for zooming the grid
 */
var bwFreeBusy = function(displayId, startRange, endRange, startDate, endDate, attendees, workday, zoom) {
  this.displayId = displayId;
  this.startRange = new Date(startRange);
  this.endRange = new Date(endRange);
  this.startDate = new Date(startDate);
  this.endDate = new Date(endDate);
  this.zoom = zoom;
  this.workday = workday;
  this.attendees = attendees;

  this.addAttendee = function(name, uid, freebusy, role, status) {
    var newAttendee = new bwAttendee(name, uid, freebusy, role, status);
    attendees.push(newAttendee);

    this.display();
  }

  this.deleteAttendee = function(index) {
    attendees.splice(index, 1);
    this.diplay();
  }

  this.display = function() {
    try {
      // set up parameters

      // build the entire free/busy table first
      var fbDisplay = document.createElement("table");
      fbDisplay.id = "bwScheduleTable";
      var fbDisplayTBody = document.createElement("tbody");
      fbDisplay.appendChild(fbDisplayTBody);

      // generate the date row
      fbDisplayDateRow = fbDisplay.fbDisplayTBody.insertRow(0);

      var td1 = document.createElement("td");
      var txt1 = document.createTextNode(this.startRange.getDay());
      var td2 = document.createElement("td");
      var txt2 = document.createTextNode(this.startRange.getDay() + 1);
      var td3 = document.createElement("td");
      var txt3 = document.createTextNode(this.startRange.getDay() + 2);
      td1.appendChild(txt1);
      td2.appendChild(txt2);
      td3.appendChild(txt3);
      fbDisplayTBody.appendChild(td1);
      fbDisplayTBody.appendChild(td2);
      fbDisplayTBody.appendChild(td3);

      // finally, write the table back to the display
      $("#" + displayId).html(fbDisplay);

    } catch (e) {
      alert(e);
    }

  }
}
var dayRange = function(startDate, endDate) {
  // find difference in milliseconds and return number of days.
  // 86400000 is the number of milliseconds in a day;
  return Math.round((Math.abs(startDate.getTime() - endDate.getTime())) / 86400000)
}

/*

 * The following is an example of a "VFREEBUSY" calendar component

 used to reply to the request with busy time information:

 BEGIN:VFREEBUSY

 UID:19970901T095957Z-76A912@example.com

 ORGANIZER:mailto:jane_doe@example.com

 ATTENDEE:mailto:john_public@example.com

 DTSTAMP:19970901T100000Z

 FREEBUSY:19971015T050000Z/PT8H30M,

 19971015T160000Z/PT5H30M,19971015T223000Z/PT6H30M

 URL:http://example.com/pub/busy/jpublic-01.ifb

 COMMENT:This iCalendar file contains busy time information for

 the next three months.

 END:VFREEBUSY

 The following is an example of a "VFREEBUSY" calendar component

 used to publish busy time information:

 BEGIN:VFREEBUSY

 UID:19970901T115957Z-76A912@example.com

 DTSTAMP:19970901T120000Z

 ORGANIZER:jsmith@example.com

 DTSTART:19980313T141711Z

 DTEND:19980410T141711Z

 FREEBUSY:19980314T233000Z/19980315T003000Z

 FREEBUSY:19980316T153000Z/19980316T163000Z

 FREEBUSY:19980318T030000Z/19980318T040000Z

 URL:http://www.example.com/calendar/busytime/jsmith.ifb

 END:VFREEBUSY

 *

 */

<commit_msg>ongoing update to free/busy mockup<commit_after>/*
 Licensed to Jasig under one or more contributor license
 agreements. See the NOTICE file distributed with this work
 for additional information regarding copyright ownership.
 Jasig licenses this file to you under the Apache License,
 Version 2.0 (the "License"); you may not use this file
 except in compliance with the License. You may obtain a
 copy of the License at:

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing,
 software distributed under the License is distributed on
 an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND, either express or implied. See the License for the
 specific language governing permissions and limitations
 under the License.
 */
// THE CONTENTS OF THIS FILE WILL MOVE INTO bedeworkAttendees.js

// ========================================================================
// free/busy functions
// ========================================================================

/* An attendee
 * name:     String - name of attendee, e.g. "Venerable Bede"
 * uid:      String - attendee's uid with mailto included, e.g. "mailto:vbede@example.com"
 * freebusy: Array of rfc5545 freebusy reply values for the current attendee in the current date range
 * role:     String - Attendee role, e.g. CHAIR, REQ-PARTICIPANT, etc
 * status:   String - participation status (PARTSTAT)
 */
var bwAttendee = function(name, uid, freebusy, role, status) {
  this.name = name;
  this.uid = uid;
  this.freebusy = freebusy;
  this.role = role;
  this.status = status;
}

/* Object to model the freebusy grid
 * displayId:  ID of html block for display output
 * startRange: js date string - start of date range for grid
 * endRange:   js date string - end of date range for grid
 * startDate:  js date string - start date/time for meeting
 * endDate:    date string - end date/time for meeting
 * attendees:  array   - array of attendee objects; MUST include organizer on first instantiation
 * workday:    boolean - true to display workday hours only, false to display all 24 hours
 * zoom:       integer - scalar value for zooming the grid
 */
var bwFreeBusy = function(displayId, startRange, endRange, startDate, endDate, attendees, workday, zoom) {
  this.displayId = displayId;
  this.startRange = new Date(startRange);
  this.endRange = new Date(endRange);
  this.startDate = new Date(startDate);
  this.endDate = new Date(endDate);
  this.zoom = zoom;
  this.workday = workday;
  this.attendees = attendees;

  this.addAttendee = function(name, uid, freebusy, role, status) {
    var newAttendee = new bwAttendee(name, uid, freebusy, role, status);
    attendees.push(newAttendee);

    this.display();
  }

  this.deleteAttendee = function(index) {
    attendees.splice(index, 1);
    this.diplay();
  }

  this.display = function() {
    try {
      // number of days in the range
      var range = dayRange(this.startRange, this.endRange);
      alert(range);

      // build the entire free/busy table first
      var fbDisplay = document.createElement("table");
      fbDisplay.id = "bwScheduleTable";
      var fbDisplayTBody = document.createElement("tbody");
      fbDisplay.appendChild(fbDisplayTBody);

      // generate the date row
      fbDisplayDateRow = fbDisplay.fbDisplayTBody.insertRow(0);

      var td1 = document.createElement("td");
      var txt1 = document.createTextNode(this.startRange.getDay());
      var td2 = document.createElement("td");
      var txt2 = document.createTextNode(this.startRange.getDay() + 1);
      var td3 = document.createElement("td");
      var txt3 = document.createTextNode(this.startRange.getDay() + 2);
      td1.appendChild(txt1);
      td2.appendChild(txt2);
      td3.appendChild(txt3);
      fbDisplayDateRow.appendChild(td1);
      fbDisplayDateRow.appendChild(td2);
      fbDisplayDateRow.appendChild(td3);

      // finally, write the table back to the display
      $("#" + displayId).html(fbDisplay);

    } catch (e) {
      alert(e);
    }

  }
}
var dayRange = function(startDate, endDate) {
  // find difference in milliseconds and return number of days.
  // 86400000 is the number of milliseconds in a day;
  return Math.round((Math.abs(startDate.getTime() - endDate.getTime())) / 86400000)
}

/*

 * The following is an example of a "VFREEBUSY" calendar component

 used to reply to the request with busy time information:

 BEGIN:VFREEBUSY

 UID:19970901T095957Z-76A912@example.com

 ORGANIZER:mailto:jane_doe@example.com

 ATTENDEE:mailto:john_public@example.com

 DTSTAMP:19970901T100000Z

 FREEBUSY:19971015T050000Z/PT8H30M,

 19971015T160000Z/PT5H30M,19971015T223000Z/PT6H30M

 URL:http://example.com/pub/busy/jpublic-01.ifb

 COMMENT:This iCalendar file contains busy time information for

 the next three months.

 END:VFREEBUSY

 The following is an example of a "VFREEBUSY" calendar component

 used to publish busy time information:

 BEGIN:VFREEBUSY

 UID:19970901T115957Z-76A912@example.com

 DTSTAMP:19970901T120000Z

 ORGANIZER:jsmith@example.com

 DTSTART:19980313T141711Z

 DTEND:19980410T141711Z

 FREEBUSY:19980314T233000Z/19980315T003000Z

 FREEBUSY:19980316T153000Z/19980316T163000Z

 FREEBUSY:19980318T030000Z/19980318T040000Z

 URL:http://www.example.com/calendar/busytime/jsmith.ifb

 END:VFREEBUSY

 *

 */

<|endoftext|>


<commit_before>/**
 * @flow
 */

import BaseBuilder from './build/BaseBuilder';
import IOSBuilder from './build/IOSBuilder';
import AndroidBuilder from './build/AndroidBuilder';
import BuildError from './build/BuildError';

export default (program: any) => {
  program
    .command('build:ios [project-dir]')
    .alias('bi')
    .option('-c, --clear-credentials', 'Clear stored credentials.')
    .description('Build a standalone IPA for your project, signed and ready for submission to the Apple App Store.')
    .allowNonInteractive()
    .asyncActionProjectDir((projectDir, options) => {
      const iosBuilder = new IOSBuilder(projectDir, options);
      return iosBuilder.command();
    });

  program
    .command('build:android [project-dir]')
    .alias('ba')
    .option('-c, --clear-credentials', 'Clear stored credentials.')
    .description('Build a standalone APK for your project, signed and ready for submission to the Google Play Store.')
    .allowNonInteractive()
    .asyncActionProjectDir((projectDir, options) => {
      const androidBuilder = new AndroidBuilder(projectDir, options);
      return androidBuilder.command();
    });

  program
    .command('build:status [project-dir]')
    .alias('bs')
    .description(`Gets the status of a current (or most recently finished) build for your project.`)
    .allowNonInteractive()
    .asyncActionProjectDir(async (projectDir, options) => {
      const builder = new BaseBuilder(projectDir, options);
      try {
        return await builder.checkStatus(false);
      } catch (e) {
        if (e instanceof BuildError) {
          return;
        }
        throw e;
      }
    });
};
<commit_msg>Remove -c option from build:android<commit_after>/**
 * @flow
 */

import BaseBuilder from './build/BaseBuilder';
import IOSBuilder from './build/IOSBuilder';
import AndroidBuilder from './build/AndroidBuilder';
import BuildError from './build/BuildError';

export default (program: any) => {
  program
    .command('build:ios [project-dir]')
    .alias('bi')
    .option('-c, --clear-credentials', 'Clear stored credentials.')
    .description('Build a standalone IPA for your project, signed and ready for submission to the Apple App Store.')
    .allowNonInteractive()
    .asyncActionProjectDir((projectDir, options) => {
      const iosBuilder = new IOSBuilder(projectDir, options);
      return iosBuilder.command();
    });

  program
    .command('build:android [project-dir]')
    .alias('ba')
    // TODO: add this option back once we have a better way to download credentials.
    // For now, just handle this manually for people.
    //.option('-c, --clear-credentials', 'Clear stored credentials.')
    .description('Build a standalone APK for your project, signed and ready for submission to the Google Play Store.')
    .allowNonInteractive()
    .asyncActionProjectDir((projectDir, options) => {
      const androidBuilder = new AndroidBuilder(projectDir, options);
      return androidBuilder.command();
    });

  program
    .command('build:status [project-dir]')
    .alias('bs')
    .description(`Gets the status of a current (or most recently finished) build for your project.`)
    .allowNonInteractive()
    .asyncActionProjectDir(async (projectDir, options) => {
      const builder = new BaseBuilder(projectDir, options);
      try {
        return await builder.checkStatus(false);
      } catch (e) {
        if (e instanceof BuildError) {
          return;
        }
        throw e;
      }
    });
};
<|endoftext|>