<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2016, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

import React, { PropTypes, PureComponent } from 'react';
import _ from 'lodash';
import * as bolusUtils from '../../../utils/bolus';
import { formatLocalizedFromUTC, formatDuration } from '../../../utils/datetime';
import { formatDecimalNumber } from '../../../utils/format';
import Tooltip from '../../common/tooltips/Tooltip';
import colors from '../../../styles/colors.css';
import styles from './BolusTooltip.css';

class BolusTooltip extends PureComponent {
  getTarget() {
    const wizardTarget = _.get(this.props.bolus, 'bgTarget');
    const target = _.get(wizardTarget, 'target', null);
    const targetLow = _.get(wizardTarget, 'low', null);
    const targetHigh = _.get(wizardTarget, 'high', null);
    const targetRange = _.get(wizardTarget, 'range', null);
    if (targetLow) {
      // medtronic
      return (

Target

{`${targetLow}-${targetHigh}`}

      );
    }
    if (targetRange) {
      // animas
      return [

Target

{`${target}`}

,

Range

{`${targetRange}`}

,
      ];
    }
    if (targetHigh) {
      // insulet
      return [

Target

{`${target}`}

,

High

{`${targetHigh}`}

,
      ];
    }
    // tandem
    return (

Target

{`${target}`}

    );
  }

  renderWizard() {
    const wizard = this.props.bolus;
    const recommended = bolusUtils.getRecommended(wizard);
    const suggested = !_.isNaN(recommended) ? `${recommended}` : null;
    const bg = _.get(wizard, 'bgInput', null);
    const iob = _.get(wizard, 'insulinOnBoard', null);
    const carbsInput = !_.isNaN(bolusUtils.getCarbs(wizard)) && bolusUtils.getCarbs(wizard) > 0;
    const carbRatio = _.get(wizard, 'insulinCarbRatio', null);
    const isf = _.get(wizard, 'insulinSensitivity', null);
    const delivered = bolusUtils.getDelivered(wizard);
    const isInterrupted = bolusUtils.isInterruptedBolus(wizard);
    const programmed = bolusUtils.getProgrammed(wizard);
    const hasExtended = bolusUtils.hasExtended(wizard);
    const normal = _.get(wizard, 'bolus.normal', NaN);
    const normalPercentage = bolusUtils.getNormalPercentage(wizard);
    const extendedPercentage = _.isNaN(bolusUtils.getExtendedPercentage(wizard))
      ? ''
      : `(${bolusUtils.getExtendedPercentage(wizard)})`;

    let overrideLine = null;
    if (bolusUtils.isOverride(wizard)) {
      overrideLine = (

Override

            {`+${formatDecimalNumber(programmed - recommended, 1)}`}

U

      );
    }
    if (bolusUtils.isUnderride(wizard)) {
      overrideLine = (

Underride

            {`-${formatDecimalNumber(recommended - programmed, 1)}`}

U

      );
    }
    const deliveredAtTop = !isInterrupted && !overrideLine;
    const deliveredLine = !!delivered && (

Delivered

{`${formatDecimalNumber(delivered, 1)}`}

U

    );
    const suggestedLine = (isInterrupted || overrideLine) && !!suggested && (

Suggested

{formatDecimalNumber(suggested, 1)}

U

    );
    const bgLine = !!bg && (

BG

{bg}

    );
    const iobLine = !!iob && (

IOB

{`${formatDecimalNumber(iob, 1)}`}

U

    );
    const interruptedLine = isInterrupted && (

Interrupted

{`-${formatDecimalNumber(programmed - delivered, 1)}`}

U

    );
    const icRatioLine = !!carbsInput && !!carbRatio && (

I:C Ratio

{`1:${carbRatio}`}

    );
    const isfLine = !!isf && !!bg && (

ISF

{`${isf}`}

    );
    const extendedLine = hasExtended && [
      !!normal && (

{`Up Front (${normalPercentage})`}

{`${formatDecimalNumber(normal, 1)}`}

U

      ),

          {`Over ${formatDuration(bolusUtils.getDuration(wizard))} ${extendedPercentage}`}

          {`${formatDecimalNumber(bolusUtils.getExtended(wizard), 1)}`}

U

,
    ];

    return (

        {suggestedLine}
        {deliveredAtTop && deliveredLine}
        {bgLine}
        {iobLine}
        {(isInterrupted || overrideLine || hasExtended) &&
}
        {interruptedLine}
        {overrideLine}
        {!deliveredAtTop && deliveredLine}
        {(icRatioLine || isfLine || bg || hasExtended) &&
}
        {extendedLine}
        {icRatioLine}
        {isfLine}
        {!!bg && this.getTarget()}

    );
  }

  renderNormal() {
    const bolus = this.props.bolus;
    const delivered = bolusUtils.getDelivered(bolus);
    const isInterrupted = bolusUtils.isInterruptedBolus(bolus);
    const programmed = bolusUtils.getProgrammed(bolus);
    const hasExtended = bolusUtils.hasExtended(bolus);
    const normal = _.get(bolus, 'normal', NaN);
    const normalPercentage = bolusUtils.getNormalPercentage(bolus);
    const extendedPercentage = _.isNaN(bolusUtils.getExtendedPercentage(bolus))
      ? ''
      : `(${bolusUtils.getExtendedPercentage(bolus)})`;
    const deliveredLine = !!delivered && (

Delivered

{`${formatDecimalNumber(delivered, 1)}`}

U

    );
    const interruptedLine = isInterrupted && (

Interrupted

{`-${formatDecimalNumber(programmed - delivered, 1)}`}

U

    );
    const programmedLine = isInterrupted &&
      !!programmed && (

Programmed

{`${formatDecimalNumber(programmed, 1)}`}

U

    );
    const extendedLine = hasExtended && [
      !!normal && (

{`Up Front (${normalPercentage})`}

{`${formatDecimalNumber(normal, 1)}`}

U

      ),

          {`Over ${formatDuration(bolusUtils.getDuration(bolus))} ${extendedPercentage}`}

          {`${formatDecimalNumber(bolusUtils.getExtended(bolus), 1)}`}

U

,
    ];

    return (

        {programmedLine}
        {!isInterrupted && deliveredLine}
        {(isInterrupted || hasExtended) &&
}
        {interruptedLine}
        {extendedLine}
        {isInterrupted && deliveredLine}

    );
  }

  renderBolus() {
    let content;
    if (this.props.bolus.type === 'wizard') {
      content = this.renderWizard();
    } else {
      content = this.renderNormal();
    }
    return content;
  }

  render() {
    const title = (

        {formatLocalizedFromUTC(this.props.bolus.normalTime, this.props.timePrefs, 'h:mm a')}

    );
    return <Tooltip {...this.props} title={title} content={this.renderBolus()} />;
  }
}

BolusTooltip.propTypes = {
  position: PropTypes.shape({
    top: PropTypes.number.isRequired,
    left: PropTypes.number.isRequired,
  }).isRequired,
  offset: PropTypes.shape({
    top: PropTypes.number.isRequired,
    left: PropTypes.number,
    horizontal: PropTypes.number,
  }),
  tail: PropTypes.bool.isRequired,
  side: PropTypes.oneOf(['top', 'right', 'bottom', 'left']).isRequired,
  tailWidth: PropTypes.number.isRequired,
  tailHeight: PropTypes.number.isRequired,
  backgroundColor: PropTypes.string,
  borderColor: PropTypes.string.isRequired,
  borderWidth: PropTypes.number.isRequired,
  bolus: PropTypes.shape({
    type: PropTypes.string.isRequired,
  }).isRequired,
  timePrefs: PropTypes.object.isRequired,
};

BolusTooltip.defaultProps = {
  tail: true,
  side: 'right',
  tailWidth: 9,
  tailHeight: 17,
  tailColor: colors.bolus,
  borderColor: colors.bolus,
  borderWidth: 2,
};

export default BolusTooltip;
<commit_msg>add proptype definition<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2016, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

import React, { PropTypes, PureComponent } from 'react';
import _ from 'lodash';
import * as bolusUtils from '../../../utils/bolus';
import { formatLocalizedFromUTC, formatDuration } from '../../../utils/datetime';
import { formatDecimalNumber } from '../../../utils/format';
import Tooltip from '../../common/tooltips/Tooltip';
import colors from '../../../styles/colors.css';
import styles from './BolusTooltip.css';

class BolusTooltip extends PureComponent {
  getTarget() {
    const wizardTarget = _.get(this.props.bolus, 'bgTarget');
    const target = _.get(wizardTarget, 'target', null);
    const targetLow = _.get(wizardTarget, 'low', null);
    const targetHigh = _.get(wizardTarget, 'high', null);
    const targetRange = _.get(wizardTarget, 'range', null);
    if (targetLow) {
      // medtronic
      return (

Target

{`${targetLow}-${targetHigh}`}

      );
    }
    if (targetRange) {
      // animas
      return [

Target

{`${target}`}

,

Range

{`${targetRange}`}

,
      ];
    }
    if (targetHigh) {
      // insulet
      return [

Target

{`${target}`}

,

High

{`${targetHigh}`}

,
      ];
    }
    // tandem
    return (

Target

{`${target}`}

    );
  }

  renderWizard() {
    const wizard = this.props.bolus;
    const recommended = bolusUtils.getRecommended(wizard);
    const suggested = !_.isNaN(recommended) ? `${recommended}` : null;
    const bg = _.get(wizard, 'bgInput', null);
    const iob = _.get(wizard, 'insulinOnBoard', null);
    const carbsInput = !_.isNaN(bolusUtils.getCarbs(wizard)) && bolusUtils.getCarbs(wizard) > 0;
    const carbRatio = _.get(wizard, 'insulinCarbRatio', null);
    const isf = _.get(wizard, 'insulinSensitivity', null);
    const delivered = bolusUtils.getDelivered(wizard);
    const isInterrupted = bolusUtils.isInterruptedBolus(wizard);
    const programmed = bolusUtils.getProgrammed(wizard);
    const hasExtended = bolusUtils.hasExtended(wizard);
    const normal = _.get(wizard, 'bolus.normal', NaN);
    const normalPercentage = bolusUtils.getNormalPercentage(wizard);
    const extendedPercentage = _.isNaN(bolusUtils.getExtendedPercentage(wizard))
      ? ''
      : `(${bolusUtils.getExtendedPercentage(wizard)})`;

    let overrideLine = null;
    if (bolusUtils.isOverride(wizard)) {
      overrideLine = (

Override

            {`+${formatDecimalNumber(programmed - recommended, 1)}`}

U

      );
    }
    if (bolusUtils.isUnderride(wizard)) {
      overrideLine = (

Underride

            {`-${formatDecimalNumber(recommended - programmed, 1)}`}

U

      );
    }
    const deliveredAtTop = !isInterrupted && !overrideLine;
    const deliveredLine = !!delivered && (

Delivered

{`${formatDecimalNumber(delivered, 1)}`}

U

    );
    const suggestedLine = (isInterrupted || overrideLine) && !!suggested && (

Suggested

{formatDecimalNumber(suggested, 1)}

U

    );
    const bgLine = !!bg && (

BG

{bg}

    );
    const iobLine = !!iob && (

IOB

{`${formatDecimalNumber(iob, 1)}`}

U

    );
    const interruptedLine = isInterrupted && (

Interrupted

{`-${formatDecimalNumber(programmed - delivered, 1)}`}

U

    );
    const icRatioLine = !!carbsInput && !!carbRatio && (

I:C Ratio

{`1:${carbRatio}`}

    );
    const isfLine = !!isf && !!bg && (

ISF

{`${isf}`}

    );
    const extendedLine = hasExtended && [
      !!normal && (

{`Up Front (${normalPercentage})`}

{`${formatDecimalNumber(normal, 1)}`}

U

      ),

          {`Over ${formatDuration(bolusUtils.getDuration(wizard))} ${extendedPercentage}`}

          {`${formatDecimalNumber(bolusUtils.getExtended(wizard), 1)}`}

U

,
    ];

    return (

        {suggestedLine}
        {deliveredAtTop && deliveredLine}
        {bgLine}
        {iobLine}
        {(isInterrupted || overrideLine || hasExtended) &&
}
        {interruptedLine}
        {overrideLine}
        {!deliveredAtTop && deliveredLine}
        {(icRatioLine || isfLine || bg || hasExtended) &&
}
        {extendedLine}
        {icRatioLine}
        {isfLine}
        {!!bg && this.getTarget()}

    );
  }

  renderNormal() {
    const bolus = this.props.bolus;
    const delivered = bolusUtils.getDelivered(bolus);
    const isInterrupted = bolusUtils.isInterruptedBolus(bolus);
    const programmed = bolusUtils.getProgrammed(bolus);
    const hasExtended = bolusUtils.hasExtended(bolus);
    const normal = _.get(bolus, 'normal', NaN);
    const normalPercentage = bolusUtils.getNormalPercentage(bolus);
    const extendedPercentage = _.isNaN(bolusUtils.getExtendedPercentage(bolus))
      ? ''
      : `(${bolusUtils.getExtendedPercentage(bolus)})`;
    const deliveredLine = !!delivered && (

Delivered

{`${formatDecimalNumber(delivered, 1)}`}

U

    );
    const interruptedLine = isInterrupted && (

Interrupted

{`-${formatDecimalNumber(programmed - delivered, 1)}`}

U

    );
    const programmedLine = isInterrupted &&
      !!programmed && (

Programmed

{`${formatDecimalNumber(programmed, 1)}`}

U

    );
    const extendedLine = hasExtended && [
      !!normal && (

{`Up Front (${normalPercentage})`}

{`${formatDecimalNumber(normal, 1)}`}

U

      ),

          {`Over ${formatDuration(bolusUtils.getDuration(bolus))} ${extendedPercentage}`}

          {`${formatDecimalNumber(bolusUtils.getExtended(bolus), 1)}`}

U

,
    ];

    return (

        {programmedLine}
        {!isInterrupted && deliveredLine}
        {(isInterrupted || hasExtended) &&
}
        {interruptedLine}
        {extendedLine}
        {isInterrupted && deliveredLine}

    );
  }

  renderBolus() {
    let content;
    if (this.props.bolus.type === 'wizard') {
      content = this.renderWizard();
    } else {
      content = this.renderNormal();
    }
    return content;
  }

  render() {
    const title = (

        {formatLocalizedFromUTC(this.props.bolus.normalTime, this.props.timePrefs, 'h:mm a')}

    );
    return <Tooltip {...this.props} title={title} content={this.renderBolus()} />;
  }
}

BolusTooltip.propTypes = {
  position: PropTypes.shape({
    top: PropTypes.number.isRequired,
    left: PropTypes.number.isRequired,
  }).isRequired,
  offset: PropTypes.shape({
    top: PropTypes.number.isRequired,
    left: PropTypes.number,
    horizontal: PropTypes.number,
  }),
  tail: PropTypes.bool.isRequired,
  side: PropTypes.oneOf(['top', 'right', 'bottom', 'left']).isRequired,
  tailColor: PropTypes.string.isRequired,
  tailWidth: PropTypes.number.isRequired,
  tailHeight: PropTypes.number.isRequired,
  backgroundColor: PropTypes.string,
  borderColor: PropTypes.string.isRequired,
  borderWidth: PropTypes.number.isRequired,
  bolus: PropTypes.shape({
    type: PropTypes.string.isRequired,
  }).isRequired,
  timePrefs: PropTypes.object.isRequired,
};

BolusTooltip.defaultProps = {
  tail: true,
  side: 'right',
  tailWidth: 9,
  tailHeight: 17,
  tailColor: colors.bolus,
  borderColor: colors.bolus,
  borderWidth: 2,
};

export default BolusTooltip;
<|endoftext|>


<commit_before>/**
 * @fileoverview Lays out a log event according to some regexp-like format
 * string.
 *
 * Patterns supported are based on those defined by the log4j documentation:
 * http://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout
 *
 * This implementation only supports basic patterns (see below for a full list)
 *
 * Supported patterns:
 * - %logger or %c: the logger name
 * - %date or %d: the date
 * - %highlight or %h: add ANSI colors to the enclosed pattern based on the
 *  underlying level of the log event. This typically outputs \u001b[31m
 *  messages to set the color in usual consoles. In particular, this won't
 *  work if the console does not support colors set this way (Web browser
 *  consoles do not support this for instance).
 * - %message or %m: the actual message
 * - %n: a newline (note loggers always end up a message with a newline)
 * - %level or %p: trace level name in uppercase letters
 * - %relative or %r: number of milliseconds elapsed since the logger context
 *  was created
 * - %%: to output a single percent sign
 *
 * Note the "%highlight" pattern is a simplified version of the one defined in
 * log4j.
 *
 * Most of the code is a minor adaptation of similar code in the
 * log4javascript library. Copyright and license:
 *  Copyright 2012 Tim Down.
 *  Licensed under the Apache License, Version 2.0 (the "License")
 *  http://log4javascript.org/
 *
 * Copyright and license of updated code:
 * Copyright (c) 2013 Joshfire
 * MIT license (see LICENSE file)
 */
/*global module*/

// Ensure "define" is defined in node.js in the absence of require.js
// See: https://github.com/jrburke/amdefine
if (typeof define !== 'function') {
  var define = require('amdefine')(module);
}

define(function (require) {
  var utils = require('../utils');
  var Layout = require('../layout');
  var SimpleDateFormat = require('./simpledateformat');

  /**
   * Basic date formats
   */
  var dateFormats = {
    absolute: 'HH:mm:ss,SSS',
    compact: 'yyyyMMddHHmmssSSS',
    date: 'dd MMM yyyy HH:mm:ss,SSS',
    iso8601: 'yyyy-MM-dd HH:mm:ss,SSS',
    iso8601_basic: 'yyyy-MM-DD HHmmss,SSS'
  };

  /**
   * Trace level CSI codes
   */
  var csiCodes = {
    trace: 37,
    log: 30,
    info: 36,
    warn: 33,
    error: 31
  };

  /**
   * Lays out a log event using a regexp-like format string.
   *
   * @constructor
   * @extends {Layout}
   * @param {Object} config Layout configuration. Structure depends on concrete
   *  layout class being used
   * @param {LoggerContext} loggerContext Reference to the logger context that
   *  gave birth to this layout.
   */
  var PatternLayout = function (config, loggerContext) {
    Layout.call(this, config, loggerContext);
    this.pattern = this.config.pattern ||
      PatternLayout.DEFAULT_CONVERSION_PATTERN;
    this.compactObjects = this.config.compactObjects || false;
  };
  PatternLayout.prototype = new Layout();

  /**
   * Public basic patterns.
   */
  PatternLayout.DEFAULT_CONVERSION_PATTERN = '%m%n';
  PatternLayout.TTCC_CONVERSION_PATTERN = '%r %p %c - %m%n';
  PatternLayout.SIMPLE_CONVERSION_PATTERN = '%d %p %c - %m%n';

  /**
   * Formats the given log event as a string following the pattern set
   * when the layout was created.
   */
  PatternLayout.prototype.toMessageBits = function (evt, options) {
    return this.formatLogEvent(evt, this.pattern, options);
  };

  /**
   * Formats the log event according to the given pattern.
   *
   * This function implements the core functionality of the
   * pattern layout.
   *
   * @function
   * @param {LogEvent} evt Log event to format
   * @param {string} pattern The format pattern to follow
   * @param {string} options Serialization options. The only supported option
   *   is "preserveObjects" to preserve the objects as-is
   * @return {Array<object|string>} The event formatted according to the
   *  pattern. The function returns an array with only one item when the
   *  "preserveObjects" option flag is not set. It returns an array with as
   *  many bits as needed when the "preserveObjects" option flag is set.
   */
  PatternLayout.prototype.formatLogEvent = function (evt, pattern, options) {
    var formattedBits = [];
    var formattedString = '';
    var rawResult = [];
    var result = {
      matched: '',
      padding: '',
      truncation: '',
      pattern: '',
      params: '',
      text: ''
    };
    var str = '';
    var nb = 0;
    var messages = [];
    var replacement = '';
    var i = 0;
    var len = 0;
    var dateFormat = '';
    var date = null;
    var bitIndex = 0;

    options = options || {};

    /**
     * Regular expressions used to extract a pattern in a pattern string
     *
     * A pattern has the following pseudo format:
     *   %[padding?][truncation?][pattern identifier]{[pattern parameters]}?
     * The expression also matches non-pattern pieces of string.
     *
     * Running the regular expression once with "exec" yields the following array:
     * 0 - the entire matched string
     * 1 - the padding if defined, "undefined" otherwise
     * 2 - the truncation if defined, "undefined" otherwise
     * 3 - the pattern identifier character, "undefined" when pattern is a regular
     * piece of text.
     * 4 - pattern parameters if defined, "undefined", otherwise.
     * 5 - the text when the extracted substring  is a regular piece of text and
     * not a real pattern. All other items (save 0) are "undefined".
     */
    var rPattern = /%(-?[0-9]+)?(\.?[0-9]+)?(logger|date|highlight|message|level|relative|[cdhmnpr%])(?:\{([^\}]+)\})?|([^%]+)/g;

    while ((rawResult = rPattern.exec(pattern))) {
      result.matched = rawResult[0];
      result.padding = rawResult[1];
      result.truncation = rawResult[2];
      result.pattern = rawResult[3];
      result.params = rawResult[4];
      result.text = rawResult[5];
      replacement = '';

      // Check if the pattern matched was just normal text
      if (result.text) {
        formattedString += result.text;
        continue;
      }

      // Complete the formatted string with the requested information
      switch (result.pattern) {
      case 'logger':
      case 'c':
        // Output the logger's name, applying precision parameter if defined
        str = evt.getLoggerName();
        if (result.params) {
          nb = parseInt(result.params, 10);
          messages = str.split('.');
          if (nb >= messages.length) {
            replacement = str;
          } else {
            replacement = messages
              .slice(messages.length - nb)
              .join('.');
          }
        } else {
          replacement = str;
        }
        break;

      case 'date':
      case 'd':
        // Output the date
        dateFormat = result.params || 'ISO8601';

        // Convert predefined formats in real date formats
        if (dateFormat === 'ISO8601') {
          dateFormat = dateFormats.iso8601;
        }
        else if (dateFormat === 'ABSOLUTE') {
          dateFormat = dateFormats.absolute;
        }
        else if (dateFormat === 'COMPACT') {
          dateFormat = dateFormats.compact;
        }
        else if (dateFormat === 'DATE') {
          dateFormat = dateFormats.date;
        }
        else if (dateFormat === 'ISO8601_BASIC') {
          dateFormat = dateFormats.iso8601_basic;
        }

        // Format the date
        date = new Date(evt.getMillis());
        replacement = (new SimpleDateFormat(dateFormat)).format(date);
        break;

      case 'highlight':
      case 'h':
        // Add ANSI colors to the rules of the enclosed pattern
        str = result.params || '';
        replacement = '\u001b[' + (csiCodes[evt.getLevel()] || 0) + 'm' +
          this.formatLogEvent(evt, str) +
          '\u001b[0m';
        break;

      case 'message':
      case 'm':
        // Output the messages as a space-separated string (or using the
        // provided parameter if it exists as separation character)
        if (options.preserveObjects) {
          replacement = evt.getMessage().getFormattedParams({
            separator: result.params,
            compactObjects: this.compactObjects,
            preserveObjects: true
          });
        }
        else {
          replacement = evt.getMessage().getFormattedMessage({
            separator: result.params,
            compactObjects: this.compactObjects
          });
        }
        break;

      case 'n':
        // Output a new line
        replacement = '\n';
        break;

      case 'level':
      case 'p':
        // Output the trace level
        replacement = evt.getLevel();
        break;

      case 'relative':
      case 'r':
        // Output the number of milliseconds since logger context
        // was created.
        nb = evt.getMillis() - this.loggerContext.getStartTime();
        replacement = '' + nb;
        break;

      case '%':
        // Output the literal % sign
        replacement = '%';
        break;

      default:
        // Unknown pattern, treat the pattern as regular text
        replacement = result.matched;
        break;
      }

      if (utils.isArray(replacement)) {
        // Replacement is an array when options.preserveObjects is set and
        // when the current bit is the serialization of the message
        if (formattedString) {
          // Concatenate the first bit if it is a string because the
          // console would add a non desired string otherwise
          if (utils.isString(replacement[0])) {
            formattedString += replacement[0];
            replacement = replacement.slice(1);
          }
          formattedBits.push(formattedString);
          formattedString = '';
        }
        if (replacement.length > 0) {
          formattedBits = formattedBits.concat(replacement);
        }
      }
      else {
        // Apply truncation if requested
        // (note truncation is from the beginning in log4j)
        if (result.truncation) {
          nb = parseInt(result.truncation.substr(1), 10);
          len = replacement.length;
          if (nb < len) {
            replacement = replacement.substring(len - nb, len);
          }
        }

        // Pad the result with spaces if requested
        if (result.padding) {
          if (result.padding.charAt(0) === '-') {
            // Right padding
            len = parseInt(result.padding.substr(1), 10);
            for (i = replacement.length; i < len; i++) {
              replacement += ' ';
            }
          } else {
            // Left padding
            len = parseInt(result.padding, 10);
            str = '';
            for (i = replacement.length; i < len; i++) {
              str += ' ';
            }
            replacement = str + replacement;
          }
        }
        formattedString += replacement;
      }
    }

    if (formattedString) {
      formattedBits.push(formattedString);
    }
    return formattedBits;
  };

  // Expose the constructor
  return PatternLayout;
});<commit_msg>Removed unused variable from pattern layout<commit_after>/**
 * @fileoverview Lays out a log event according to some regexp-like format
 * string.
 *
 * Patterns supported are based on those defined by the log4j documentation:
 * http://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout
 *
 * This implementation only supports basic patterns (see below for a full list)
 *
 * Supported patterns:
 * - %logger or %c: the logger name
 * - %date or %d: the date
 * - %highlight or %h: add ANSI colors to the enclosed pattern based on the
 *  underlying level of the log event. This typically outputs \u001b[31m
 *  messages to set the color in usual consoles. In particular, this won't
 *  work if the console does not support colors set this way (Web browser
 *  consoles do not support this for instance).
 * - %message or %m: the actual message
 * - %n: a newline (note loggers always end up a message with a newline)
 * - %level or %p: trace level name in uppercase letters
 * - %relative or %r: number of milliseconds elapsed since the logger context
 *  was created
 * - %%: to output a single percent sign
 *
 * Note the "%highlight" pattern is a simplified version of the one defined in
 * log4j.
 *
 * Most of the code is a minor adaptation of similar code in the
 * log4javascript library. Copyright and license:
 *  Copyright 2012 Tim Down.
 *  Licensed under the Apache License, Version 2.0 (the "License")
 *  http://log4javascript.org/
 *
 * Copyright and license of updated code:
 * Copyright (c) 2013 Joshfire
 * MIT license (see LICENSE file)
 */
/*global module*/

// Ensure "define" is defined in node.js in the absence of require.js
// See: https://github.com/jrburke/amdefine
if (typeof define !== 'function') {
  var define = require('amdefine')(module);
}

define(function (require) {
  var utils = require('../utils');
  var Layout = require('../layout');
  var SimpleDateFormat = require('./simpledateformat');

  /**
   * Basic date formats
   */
  var dateFormats = {
    absolute: 'HH:mm:ss,SSS',
    compact: 'yyyyMMddHHmmssSSS',
    date: 'dd MMM yyyy HH:mm:ss,SSS',
    iso8601: 'yyyy-MM-dd HH:mm:ss,SSS',
    iso8601_basic: 'yyyy-MM-DD HHmmss,SSS'
  };

  /**
   * Trace level CSI codes
   */
  var csiCodes = {
    trace: 37,
    log: 30,
    info: 36,
    warn: 33,
    error: 31
  };

  /**
   * Lays out a log event using a regexp-like format string.
   *
   * @constructor
   * @extends {Layout}
   * @param {Object} config Layout configuration. Structure depends on concrete
   *  layout class being used
   * @param {LoggerContext} loggerContext Reference to the logger context that
   *  gave birth to this layout.
   */
  var PatternLayout = function (config, loggerContext) {
    Layout.call(this, config, loggerContext);
    this.pattern = this.config.pattern ||
      PatternLayout.DEFAULT_CONVERSION_PATTERN;
    this.compactObjects = this.config.compactObjects || false;
  };
  PatternLayout.prototype = new Layout();

  /**
   * Public basic patterns.
   */
  PatternLayout.DEFAULT_CONVERSION_PATTERN = '%m%n';
  PatternLayout.TTCC_CONVERSION_PATTERN = '%r %p %c - %m%n';
  PatternLayout.SIMPLE_CONVERSION_PATTERN = '%d %p %c - %m%n';

  /**
   * Formats the given log event as a string following the pattern set
   * when the layout was created.
   */
  PatternLayout.prototype.toMessageBits = function (evt, options) {
    return this.formatLogEvent(evt, this.pattern, options);
  };

  /**
   * Formats the log event according to the given pattern.
   *
   * This function implements the core functionality of the
   * pattern layout.
   *
   * @function
   * @param {LogEvent} evt Log event to format
   * @param {string} pattern The format pattern to follow
   * @param {string} options Serialization options. The only supported option
   *   is "preserveObjects" to preserve the objects as-is
   * @return {Array<object|string>} The event formatted according to the
   *  pattern. The function returns an array with only one item when the
   *  "preserveObjects" option flag is not set. It returns an array with as
   *  many bits as needed when the "preserveObjects" option flag is set.
   */
  PatternLayout.prototype.formatLogEvent = function (evt, pattern, options) {
    var formattedBits = [];
    var formattedString = '';
    var rawResult = [];
    var result = {
      matched: '',
      padding: '',
      truncation: '',
      pattern: '',
      params: '',
      text: ''
    };
    var str = '';
    var nb = 0;
    var messages = [];
    var replacement = '';
    var i = 0;
    var len = 0;
    var dateFormat = '';
    var date = null;

    options = options || {};

    /**
     * Regular expressions used to extract a pattern in a pattern string
     *
     * A pattern has the following pseudo format:
     *   %[padding?][truncation?][pattern identifier]{[pattern parameters]}?
     * The expression also matches non-pattern pieces of string.
     *
     * Running the regular expression once with "exec" yields the following array:
     * 0 - the entire matched string
     * 1 - the padding if defined, "undefined" otherwise
     * 2 - the truncation if defined, "undefined" otherwise
     * 3 - the pattern identifier character, "undefined" when pattern is a regular
     * piece of text.
     * 4 - pattern parameters if defined, "undefined", otherwise.
     * 5 - the text when the extracted substring  is a regular piece of text and
     * not a real pattern. All other items (save 0) are "undefined".
     */
    var rPattern = /%(-?[0-9]+)?(\.?[0-9]+)?(logger|date|highlight|message|level|relative|[cdhmnpr%])(?:\{([^\}]+)\})?|([^%]+)/g;

    while ((rawResult = rPattern.exec(pattern))) {
      result.matched = rawResult[0];
      result.padding = rawResult[1];
      result.truncation = rawResult[2];
      result.pattern = rawResult[3];
      result.params = rawResult[4];
      result.text = rawResult[5];
      replacement = '';

      // Check if the pattern matched was just normal text
      if (result.text) {
        formattedString += result.text;
        continue;
      }

      // Complete the formatted string with the requested information
      switch (result.pattern) {
      case 'logger':
      case 'c':
        // Output the logger's name, applying precision parameter if defined
        str = evt.getLoggerName();
        if (result.params) {
          nb = parseInt(result.params, 10);
          messages = str.split('.');
          if (nb >= messages.length) {
            replacement = str;
          } else {
            replacement = messages
              .slice(messages.length - nb)
              .join('.');
          }
        } else {
          replacement = str;
        }
        break;

      case 'date':
      case 'd':
        // Output the date
        dateFormat = result.params || 'ISO8601';

        // Convert predefined formats in real date formats
        if (dateFormat === 'ISO8601') {
          dateFormat = dateFormats.iso8601;
        }
        else if (dateFormat === 'ABSOLUTE') {
          dateFormat = dateFormats.absolute;
        }
        else if (dateFormat === 'COMPACT') {
          dateFormat = dateFormats.compact;
        }
        else if (dateFormat === 'DATE') {
          dateFormat = dateFormats.date;
        }
        else if (dateFormat === 'ISO8601_BASIC') {
          dateFormat = dateFormats.iso8601_basic;
        }

        // Format the date
        date = new Date(evt.getMillis());
        replacement = (new SimpleDateFormat(dateFormat)).format(date);
        break;

      case 'highlight':
      case 'h':
        // Add ANSI colors to the rules of the enclosed pattern
        str = result.params || '';
        replacement = '\u001b[' + (csiCodes[evt.getLevel()] || 0) + 'm' +
          this.formatLogEvent(evt, str) +
          '\u001b[0m';
        break;

      case 'message':
      case 'm':
        // Output the messages as a space-separated string (or using the
        // provided parameter if it exists as separation character)
        if (options.preserveObjects) {
          replacement = evt.getMessage().getFormattedParams({
            separator: result.params,
            compactObjects: this.compactObjects,
            preserveObjects: true
          });
        }
        else {
          replacement = evt.getMessage().getFormattedMessage({
            separator: result.params,
            compactObjects: this.compactObjects
          });
        }
        break;

      case 'n':
        // Output a new line
        replacement = '\n';
        break;

      case 'level':
      case 'p':
        // Output the trace level
        replacement = evt.getLevel();
        break;

      case 'relative':
      case 'r':
        // Output the number of milliseconds since logger context
        // was created.
        nb = evt.getMillis() - this.loggerContext.getStartTime();
        replacement = '' + nb;
        break;

      case '%':
        // Output the literal % sign
        replacement = '%';
        break;

      default:
        // Unknown pattern, treat the pattern as regular text
        replacement = result.matched;
        break;
      }

      if (utils.isArray(replacement)) {
        // Replacement is an array when options.preserveObjects is set and
        // when the current bit is the serialization of the message
        if (formattedString) {
          // Concatenate the first bit if it is a string because the
          // console would add a non desired string otherwise
          if (utils.isString(replacement[0])) {
            formattedString += replacement[0];
            replacement = replacement.slice(1);
          }
          formattedBits.push(formattedString);
          formattedString = '';
        }
        if (replacement.length > 0) {
          formattedBits = formattedBits.concat(replacement);
        }
      }
      else {
        // Apply truncation if requested
        // (note truncation is from the beginning in log4j)
        if (result.truncation) {
          nb = parseInt(result.truncation.substr(1), 10);
          len = replacement.length;
          if (nb < len) {
            replacement = replacement.substring(len - nb, len);
          }
        }

        // Pad the result with spaces if requested
        if (result.padding) {
          if (result.padding.charAt(0) === '-') {
            // Right padding
            len = parseInt(result.padding.substr(1), 10);
            for (i = replacement.length; i < len; i++) {
              replacement += ' ';
            }
          } else {
            // Left padding
            len = parseInt(result.padding, 10);
            str = '';
            for (i = replacement.length; i < len; i++) {
              str += ' ';
            }
            replacement = str + replacement;
          }
        }
        formattedString += replacement;
      }
    }

    if (formattedString) {
      formattedBits.push(formattedString);
    }
    return formattedBits;
  };

  // Expose the constructor
  return PatternLayout;
});<|endoftext|>


<commit_before>/*****************
* Reports Plugin *
* Credits: jd    *
*****************/
var fs = require('fs');
var moment = require('moment');

var Reports = {};
function loadReports() {
	try {
		Reports = JSON.parse(fs.readFileSync('config/reports.json'));
	} catch (e) {
		Reports = {};
	}
}
loadReports();

function saveReports() {
	fs.writeFile('config/reports.json', JSON.stringify(Reports));
}

function pmUpperStaff(message) {
    Gold.pmUpperStaff(message, '~Server');
}

exports.commands = {
    complain: 'requesthelp',
    bitch: 'requesthelp',
	report: 'requesthelp',
	requesthelp: function (target, room, user) {
		if (user.can('pban')) return this.parse('/reports ' + (target || ''));
		if (!this.canTalk()) return this.errorReply("You can't use this command while unable to speak.");
		if (!target) return this.errorReply("Usage: /requesthelp [message] - Requests help from Senior Staff. Please remember to include as much detail as possible with your request.");
		if (target.length < 1) return this.errorReply("Usage: /requesthelp [message] - Requests help from Senior Staff. Please remember to include as much detail as possible with your request.");

		var reportId = (Object.size(Reports) + 1);
		while (Reports[reportId]) reportId--;
		Reports[reportId] = new Object();
		Reports[reportId].reporter = user.name;
		Reports[reportId].message = target.trim();
		Reports[reportId].id = reportId;
		Reports[reportId].status = 'Pending Staff';
		Reports[reportId].reportTime = moment().format('MMMM Do YYYY, h:mm A') + " EST";
		saveReports();
		pmUpperStaff('A new report has been submitted by ' + user.name + '. ID: ' + reportId + ' Message: ' + target.trim());
		Rooms('upperstaff').add('A new report has been submitted by ' + user.name + '. ID: ' + reportId + ' Message: ' + target.trim());
		Rooms('upperstaff').update();
		return this.sendReply("Your report has been sent to Senior Staff.");
	},

	reports: function (target, room, user, connection, cmd) {
		if (!user.can('pban')) return this.errorReply('/reports - Access denied.');
		if (!target) var target = '';
		target = target.trim();

		var cmdParts = target.split(' ');
		var cmd = cmdParts.shift().trim().toLowerCase();
		var params = cmdParts.join(' ').split(',').map(function (param) { return param.trim(); });
		switch (cmd) {
			case '':
			case 'view':
				if (!this.runBroadcast()) return;
				var output = '|raw|

ID	Reporter	Message	Report Time	Status
';
				for (var u in Object.keys(Reports)) {
					var currentReport = Reports[Object.keys(Reports)[u]];
					var date = currentReport.reportTime;
					output += '
' + currentReport.id + '	' + Tools.escapeHTML(currentReport.reporter) + '	' +
						Tools.escapeHTML(currentReport.message) + '	' + date + ' (GMT)	' + (currentReport.status === 'Pending Staff' ? 'Pending Staff' : (~currentReport.status.indexOf('Accepted by') ? '' + Tools.escapeHTML(currentReport.status) + '' : Tools.escapeHTML(currentReport.status))) + '
';
				}
				this.sendReply(output);
				break;
			case 'accept':
				if (params.length < 1) return this.errorReply("Usage: /reports accept [id]");
				var id = params.shift();
				if (!Reports[id]) return this.errorReply("There's no report with that id.");
				if (Reports[id].status !== 'Pending Staff') return this.errorReply("That report isn't pending staff.");
				Reports[id].status = "Accepted by " + user.name;
				saveReports();
				if (Users(Reports[id].reporter) && Users(Reports[id].reporter).connected) {
					Users(Reports[id].reporter).popup("Your report has been accepted by " + user.name);
				}
				this.sendReply("You've accepted the report by "+ Reports[id].reporter);
				pmUpperStaff(user.name + " accepted the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').add(user.name + " accepted the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').update();
				break;
			case 'decline':
			case 'deny':
				if (params.length < 1) return this.errorReply("Usage: /reports deny [id]");
				var id = params.shift();
				if (!Reports[id]) return this.errorReply("There's no report with that id.");
				if (Reports[id].status !== 'Pending Staff') return this.errorReply("That report isn't pending staff.");
				if (Users(Reports[id].reporter) && Users(Reports[id].reporter).connected) {
					Users(Reports[id].reporter).popup("|modal|" + "Your report has been denied by " + user.name);
				}
				this.sendReply("You've denied the report by "+Reports[id].reporter);
				pmUpperStaff(user.name + " denied the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').add(user.name + " denied the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').update();
				delete Reports[id];
				saveReports();
				break;
			case 'del':
			case 'delete':
				if (params.length < 1) return this.errorReply("Usage: /reports delete [id]");
				var id = params.shift();
				if (!Reports[id]) return this.errorReply("There's no report with that id.");
				pmUpperStaff(user.name + " deleted the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').add(user.name + " deleted the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').update();
				delete Reports[id];
				saveReports();
				this.sendReply("That report has been deleted.");
				break;
			case 'help':
				if (!this.runBroadcast()) return;
				this.sendReplyBox("Report commands:
" +
					"/report [message] - Adds a report to the system
" +
					"/reports view - Views all current reports
" +
					"/reports accept [id] - Accepts a report
" +
					"/reports delete [id] - Deletes a report
" +
					"/reports deny [id] - Denies a report"
				);
				break;
			default:
				this.sendReply("/reports " + target + " - Command not found.");
		}
	}
};
<commit_msg>Report system uses EST since the server is in the EST timezone<commit_after>/*****************
* Reports Plugin *
* Credits: jd    *
*****************/
var fs = require('fs');
var moment = require('moment');

var Reports = {};
function loadReports() {
	try {
		Reports = JSON.parse(fs.readFileSync('config/reports.json'));
	} catch (e) {
		Reports = {};
	}
}
loadReports();

function saveReports() {
	fs.writeFile('config/reports.json', JSON.stringify(Reports));
}

function pmUpperStaff(message) {
    Gold.pmUpperStaff(message, '~Server');
}

exports.commands = {
    complain: 'requesthelp',
    bitch: 'requesthelp',
	report: 'requesthelp',
	requesthelp: function (target, room, user) {
		if (user.can('pban')) return this.parse('/reports ' + (target || ''));
		if (!this.canTalk()) return this.errorReply("You can't use this command while unable to speak.");
		if (!target) return this.errorReply("Usage: /requesthelp [message] - Requests help from Senior Staff. Please remember to include as much detail as possible with your request.");
		if (target.length < 1) return this.errorReply("Usage: /requesthelp [message] - Requests help from Senior Staff. Please remember to include as much detail as possible with your request.");

		var reportId = (Object.size(Reports) + 1);
		while (Reports[reportId]) reportId--;
		Reports[reportId] = new Object();
		Reports[reportId].reporter = user.name;
		Reports[reportId].message = target.trim();
		Reports[reportId].id = reportId;
		Reports[reportId].status = 'Pending Staff';
		Reports[reportId].reportTime = moment().format('MMMM Do YYYY, h:mm A') + " EST";
		saveReports();
		pmUpperStaff('A new report has been submitted by ' + user.name + '. ID: ' + reportId + ' Message: ' + target.trim());
		Rooms('upperstaff').add('A new report has been submitted by ' + user.name + '. ID: ' + reportId + ' Message: ' + target.trim());
		Rooms('upperstaff').update();
		return this.sendReply("Your report has been sent to Senior Staff.");
	},

	reports: function (target, room, user, connection, cmd) {
		if (!user.can('pban')) return this.errorReply('/reports - Access denied.');
		if (!target) var target = '';
		target = target.trim();

		var cmdParts = target.split(' ');
		var cmd = cmdParts.shift().trim().toLowerCase();
		var params = cmdParts.join(' ').split(',').map(function (param) { return param.trim(); });
		switch (cmd) {
			case '':
			case 'view':
				if (!this.runBroadcast()) return;
				var output = '|raw|

ID	Reporter	Message	Report Time	Status
';
				for (var u in Object.keys(Reports)) {
					var currentReport = Reports[Object.keys(Reports)[u]];
					var date = currentReport.reportTime;
					output += '
' + currentReport.id + '	' + Tools.escapeHTML(currentReport.reporter) + '	' +
						Tools.escapeHTML(currentReport.message) + '	' + date + ' 	' + (currentReport.status === 'Pending Staff' ? 'Pending Staff' : (~currentReport.status.indexOf('Accepted by') ? '' + Tools.escapeHTML(currentReport.status) + '' : Tools.escapeHTML(currentReport.status))) + '
';
				}
				this.sendReply(output);
				break;
			case 'accept':
				if (params.length < 1) return this.errorReply("Usage: /reports accept [id]");
				var id = params.shift();
				if (!Reports[id]) return this.errorReply("There's no report with that id.");
				if (Reports[id].status !== 'Pending Staff') return this.errorReply("That report isn't pending staff.");
				Reports[id].status = "Accepted by " + user.name;
				saveReports();
				if (Users(Reports[id].reporter) && Users(Reports[id].reporter).connected) {
					Users(Reports[id].reporter).popup("Your report has been accepted by " + user.name);
				}
				this.sendReply("You've accepted the report by "+ Reports[id].reporter);
				pmUpperStaff(user.name + " accepted the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').add(user.name + " accepted the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').update();
				break;
			case 'decline':
			case 'deny':
				if (params.length < 1) return this.errorReply("Usage: /reports deny [id]");
				var id = params.shift();
				if (!Reports[id]) return this.errorReply("There's no report with that id.");
				if (Reports[id].status !== 'Pending Staff') return this.errorReply("That report isn't pending staff.");
				if (Users(Reports[id].reporter) && Users(Reports[id].reporter).connected) {
					Users(Reports[id].reporter).popup("|modal|" + "Your report has been denied by " + user.name);
				}
				this.sendReply("You've denied the report by "+Reports[id].reporter);
				pmUpperStaff(user.name + " denied the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').add(user.name + " denied the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').update();
				delete Reports[id];
				saveReports();
				break;
			case 'del':
			case 'delete':
				if (params.length < 1) return this.errorReply("Usage: /reports delete [id]");
				var id = params.shift();
				if (!Reports[id]) return this.errorReply("There's no report with that id.");
				pmUpperStaff(user.name + " deleted the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').add(user.name + " deleted the report by " + Reports[id].reporter + ". (ID: " + id + ")");
				Rooms('upperstaff').update();
				delete Reports[id];
				saveReports();
				this.sendReply("That report has been deleted.");
				break;
			case 'help':
				if (!this.runBroadcast()) return;
				this.sendReplyBox("Report commands:
" +
					"/report [message] - Adds a report to the system
" +
					"/reports view - Views all current reports
" +
					"/reports accept [id] - Accepts a report
" +
					"/reports delete [id] - Deletes a report
" +
					"/reports deny [id] - Denies a report"
				);
				break;
			default:
				this.sendReply("/reports " + target + " - Command not found.");
		}
	}
};
<|endoftext|>


<commit_before>module.exports = function(grunt) {

    // 1. CONFIG
    grunt.initConfig({
        pkg: grunt.file.readJSON("package.json"),

        jasmine: {
            messageBubble: {
                src: "dist/components/message-bubble/message-bubble.js",
                options: {
                    specs: "spec/specs-js/message-bubble-spec.js",
                    vendor: [
                        "bower_components/jquery/dist/jquery.js",
                        "bower_components/jasmine-jquery/lib/jasmine-jquery.js"
                    ],
                    helpers: "spec/helpers-js/message-bubble-helpers.js",
                    styles: "dist/components/message-bubble/message-bubble.css",
                    template: require("grunt-template-jasmine-istanbul"),
                    templateOptions: {
                        coverage: "bin/coverage/coverage.json",
                        report: [
                            {
                                type: "html",
                                options: {
                                    dir: "bin/coverage/html"
                                }
                            },

                            {
                                type: 'cobertura',
                                options: {
                                    dir: "bin/coverage/cobertura"
                                }
                            },

                            {
                                type: "text-summary"
                            }
                        ]
                    }
                }
            },

            toggleSwitch: {
                src: "dist/components/toggle-switch/toggle-switch.js",
                options: {
                    specs: "spec/specs-js/toggle-switch-spec.js",
                    vendor: [
                        "bower_components/jquery/dist/jquery.js",
                        "bower_components/jasmine-jquery/lib/jasmine-jquery.js"
                    ],
                    helpers: "spec/helpers-js/toggle-switch-helpers.js",
                    styles: "dist/components/toggle-switch/toggle-switch.css",
                    template: require("grunt-template-jasmine-istanbul"),
                    templateOptions: {
                        coverage: "bin/coverage/coverage.json",
                        report: [
                            {
                                type: "html",
                                options: {
                                    dir: "bin/coverage/html"
                                }
                            },

                            {
                                type: 'cobertura',
                                options: {
                                    dir: "bin/coverage/cobertura"
                                }
                            },

                            {
                                type: "text-summary"
                            }
                        ]
                    }
                }
            },

            options: {
                "--web-security" : false,
                "--local-to-remote-url-access" : true,
                "--ignore-ssl-errors" : true
            }
        },

        coffee: {
            build: {
                src: "src/behaviors/springs.coffee",
                dest: "dist/behaviors/springs.js"
            },

            specs: {
                files: [{
                    expand: true,
                    cwd: "spec/specs",
                    src: ["**/*.coffee"],
                    dest: "spec/specs-js",
                    ext: ".js"
                }]
            },

            specHelpers: {
                files: [{
                    expand: true,
                    cwd: "spec/helpers",
                    src: ["**/*.coffee"],
                    dest: "spec/helpers-js",
                    ext: ".js"
                }]
            },

            components: {
                files: [{
                    expand: true,
                    cwd: "src",
                    src: ["components/**/*.coffee"],
                    dest: "dist",
                    ext: ".js"
                }]
            },

            docs: {
                src: "docs/coffee/script.coffee",
                dest: "docs/public/js/script.js"
            }
        },

        uglify: {
            build: {
                src: "dist/behaviors/springs.js",
                dest: "dist/behaviors/springs.min.js"
            },

            components: {
                files: [{
                    expand: true,
                    cwd: "dist",
                    src: ["components/**/*.js"],
                    dest: "dist",
                    ext: ".min.js"
                }]
            }
        },

        concat: {
            options: {
                stripBanners: true,
                banner: "// <%= pkg.name %> - v<%= pkg.version %> - " +
                        "<%= grunt.template.today(\"yyyy.mm.dd\") %>\n\n\n",
                separator: "\n\n\n// -----\n\n\n"
            },

            // js: {
            //     src: "src/*.js",
            //     dest: "dist/springs.js"
            // },

            springs: {
                src: [
                    "src/behaviors/helpers.scss",
                    "src/behaviors/springs.scss"
                ],
                dest: "dist/behaviors/springs.scss"
            },

            components: {
                src: ["src/components/*/*.scss"],
                dest: "docs/scss/components.scss"
            }
        },

        sass: {
            dist: {
                options: {
                    style: "expanded",
                    loadPath: "src/"
                },

                files: {
                    "test/test.css": "test/test.scss"
                }
            },

            components: {
                options: {
                    style: "expanded",
                    loadPath: "src/"
                },

                files: [{
                    expand: true,
                    cwd: "src",
                    src: ["components/**/*.scss"],
                    dest: "dist",
                    ext: ".css"
                }]
            },

            docs: {
                files: {
                    "docs/public/css/style.css": "docs/scss/style.scss"
                }
            }
        },

        csscss: {
            options: {
                ignoreSassMixins: true
            },

            dist: {
                src: ["scss/**/*.scss"]
            }
        },

        autoprefixer: {
            dist: {
                files: {
                    "test/test.css": "test/test.css"
                }
            },

            components: {
                files: [{
                    expand: true,
                    cwd: "dist/components",
                    src: ["*/*.css"],
                    dest: "dist/components",
                    ext: ".css"
                }]
            },

            docs: {
                files: {
                    "docs/public/css/style.css": "docs/public/css/style.css"
                }
            }
        },

        compare_size: {
            files: [
                "dist/*.js",
                "dist/components/*.js"
            ]
        },

        haml: {
            components: {
                files: [{
                    expand: true,
                    cwd: "src/components",
                    src: ["**/*.haml"],
                    dest: "dist/components",
                    ext: ".html"
                }]
            },

            docs: {
                files: [{
                    expand: true,
                    cwd: "docs/haml/components-standalone",
                    src: ["**/*.haml"],
                    dest: "docs/pages/components",
                    ext: ".html"
                }]
            },

            fullDocs: {
                files: {
                    "docs/pages/site.html": "docs/haml/site.haml"
                }
            }
        },

        imagemin: {
            all: {
                options: { optimizationLevel: 1 },

                files: [{
                    expand: true,
                    cwd: "docs/img/",
                    src: ["**/*.{png,jpg,jpeg,gif}"],
                    dest: "docs/public/img/"
                }]
            }
        },

        shell: {
            copyMain: {
                command: [
                    // "cp src/springs.js dist/springs.js",
                    // "cp src/springs.scss dist/springs.scss",
                    "cp src/behaviors/drag.js dist/behaviors/drag.js",
                    "cp src/components/*/*.scss docs/scss/components",
                    "rsync -av src/components/ dist/components/"
                ].join("&&")
            }
        },

        watch: {
            options: { livereload: false },

            js: {
                files: ["dist/**/*.js"],
                tasks: ["concat", "shell"],
                options: { spawn: false }
            },

            coffee: {
                files: ["src/**/*.coffee"],
                tasks: ["coffee:components", "coffee:build"]
            },

            coffeeSpecs: {
                files: "spec/**/*.coffee",
                tasks: ["coffee:tests"],
                options: { spawn: false }
            },

            jsSpecs: {
                files: "spec/**/*.js",
                tasks: ["jasmine"],
                options: { spawn: false }
            },

            scss: {
                files: ["src/**/*.scss", "test/**/*.scss"],
                tasks: ["concat", "sass", "autoprefixer", "csscss", "shell"],
                options: { spawn: false }
            },

            css: {
                files: ["dist/**/*.css", "test/**/*.css"],
                tasks: ["autoprefixer"],
                options: { spawn: false }
            },

            haml: {
                files: ["src/**/*.haml"],
                tasks: ["haml"],
                options: { spawn: false }
            },

            docsHaml: {
                files: ["docs/haml/**/*.haml"],
                tasks: ["haml:docs", "haml:fullDocs"],
                options: { spawn: false }
            },

            docsScss: {
                files: ["docs/scss/**/*.scss"],
                tasks: ["sass:docs", "autoprefixer:docs"],
                options: { spawn: false }
            },

            api: {
                files: ["docs/haml/api/**/*.yaml"],
                tasks: ["haml:docs", "haml:fullDocs"],
                options: { spawn: false }
            },

            docsCoffee: {
                files: ["docs/coffee/**/*.coffee"],
                tasks: ["coffee:docs"],
                options: { spawn: false }
            }
        }
    });

    // 2. TASKS
    require("load-grunt-tasks")(grunt);

    // 3. PERFORM
    grunt.registerTask("default", ["coffee:build", "coffee:components", "concat", "sass", "autoprefixer", "csscss", "uglify", "compare_size", "haml", "shell"]);
    grunt.registerTask("tests", ["coffee:specHelpers", "coffee:specs", "jasmine"]);
    grunt.registerTask("full", ["coffee", "jasmine", "concat", "sass", "autoprefixer", "csscss", "uglify", "compare_size", "slim", "shell"]);
    grunt.registerTask("docs", ["haml:fullDocs", "haml:docs", "sass:docs", "autoprefixer:docs"]);

}<commit_msg>Added a dedicated drag compiler to use it in the iOS notification docs page.<commit_after>module.exports = function(grunt) {

    // 1. CONFIG
    grunt.initConfig({
        pkg: grunt.file.readJSON("package.json"),

        jasmine: {
            messageBubble: {
                src: "dist/components/message-bubble/message-bubble.js",
                options: {
                    specs: "spec/specs-js/message-bubble-spec.js",
                    vendor: [
                        "bower_components/jquery/dist/jquery.js",
                        "bower_components/jasmine-jquery/lib/jasmine-jquery.js"
                    ],
                    helpers: "spec/helpers-js/message-bubble-helpers.js",
                    styles: "dist/components/message-bubble/message-bubble.css",
                    template: require("grunt-template-jasmine-istanbul"),
                    templateOptions: {
                        coverage: "bin/coverage/coverage.json",
                        report: [
                            {
                                type: "html",
                                options: {
                                    dir: "bin/coverage/html"
                                }
                            },

                            {
                                type: 'cobertura',
                                options: {
                                    dir: "bin/coverage/cobertura"
                                }
                            },

                            {
                                type: "text-summary"
                            }
                        ]
                    }
                }
            },

            toggleSwitch: {
                src: "dist/components/toggle-switch/toggle-switch.js",
                options: {
                    specs: "spec/specs-js/toggle-switch-spec.js",
                    vendor: [
                        "bower_components/jquery/dist/jquery.js",
                        "bower_components/jasmine-jquery/lib/jasmine-jquery.js"
                    ],
                    helpers: "spec/helpers-js/toggle-switch-helpers.js",
                    styles: "dist/components/toggle-switch/toggle-switch.css",
                    template: require("grunt-template-jasmine-istanbul"),
                    templateOptions: {
                        coverage: "bin/coverage/coverage.json",
                        report: [
                            {
                                type: "html",
                                options: {
                                    dir: "bin/coverage/html"
                                }
                            },

                            {
                                type: 'cobertura',
                                options: {
                                    dir: "bin/coverage/cobertura"
                                }
                            },

                            {
                                type: "text-summary"
                            }
                        ]
                    }
                }
            },

            options: {
                "--web-security" : false,
                "--local-to-remote-url-access" : true,
                "--ignore-ssl-errors" : true
            }
        },

        coffee: {
            build: {
                src: "src/behaviors/springs.coffee",
                dest: "dist/behaviors/springs.js"
            },

            specs: {
                files: [{
                    expand: true,
                    cwd: "spec/specs",
                    src: ["**/*.coffee"],
                    dest: "spec/specs-js",
                    ext: ".js"
                }]
            },

            specHelpers: {
                files: [{
                    expand: true,
                    cwd: "spec/helpers",
                    src: ["**/*.coffee"],
                    dest: "spec/helpers-js",
                    ext: ".js"
                }]
            },

            components: {
                files: [{
                    expand: true,
                    cwd: "src",
                    src: ["components/**/*.coffee"],
                    dest: "dist",
                    ext: ".js"
                }]
            },

            docs: {
                src: "docs/coffee/script.coffee",
                dest: "docs/public/js/script.js"
            }
        },

        uglify: {
            build: {
                src: "dist/behaviors/springs.js",
                dest: "dist/behaviors/springs.min.js"
            },

            components: {
                files: [{
                    expand: true,
                    cwd: "dist",
                    src: ["components/**/*.js"],
                    dest: "dist",
                    ext: ".min.js"
                }]
            }
        },

        concat: {
            options: {
                stripBanners: true,
                banner: "// <%= pkg.name %> - v<%= pkg.version %> - " +
                        "<%= grunt.template.today(\"yyyy.mm.dd\") %>\n\n\n",
                separator: "\n\n\n// -----\n\n\n"
            },

            // js: {
            //     src: "src/*.js",
            //     dest: "dist/springs.js"
            // },

            drag: {
                src: [
                    "dist/behaviors/drag.js",
                    "dist/components/notification-ios/notification-ios.js"
                ],
                dest: "dist/components/notification-ios/notification-ios-drag.js"
            },

            springs: {
                src: [
                    "src/behaviors/helpers.scss",
                    "src/behaviors/springs.scss"
                ],
                dest: "dist/behaviors/springs.scss"
            },

            components: {
                src: ["src/components/*/*.scss"],
                dest: "docs/scss/components.scss"
            }
        },

        sass: {
            dist: {
                options: {
                    style: "expanded",
                    loadPath: "src/"
                },

                files: {
                    "test/test.css": "test/test.scss"
                }
            },

            components: {
                options: {
                    style: "expanded",
                    loadPath: "src/"
                },

                files: [{
                    expand: true,
                    cwd: "src",
                    src: ["components/**/*.scss"],
                    dest: "dist",
                    ext: ".css"
                }]
            },

            docs: {
                files: {
                    "docs/public/css/style.css": "docs/scss/style.scss"
                }
            }
        },

        csscss: {
            options: {
                ignoreSassMixins: true
            },

            dist: {
                src: ["scss/**/*.scss"]
            }
        },

        autoprefixer: {
            dist: {
                files: {
                    "test/test.css": "test/test.css"
                }
            },

            components: {
                files: [{
                    expand: true,
                    cwd: "dist/components",
                    src: ["*/*.css"],
                    dest: "dist/components",
                    ext: ".css"
                }]
            },

            docs: {
                files: {
                    "docs/public/css/style.css": "docs/public/css/style.css"
                }
            }
        },

        compare_size: {
            files: [
                "dist/*.js",
                "dist/components/*.js"
            ]
        },

        haml: {
            components: {
                files: [{
                    expand: true,
                    cwd: "src/components",
                    src: ["**/*.haml"],
                    dest: "dist/components",
                    ext: ".html"
                }]
            },

            docs: {
                files: [{
                    expand: true,
                    cwd: "docs/haml/components-standalone",
                    src: ["**/*.haml"],
                    dest: "docs/pages/components",
                    ext: ".html"
                }]
            },

            fullDocs: {
                files: {
                    "docs/pages/site.html": "docs/haml/site.haml"
                }
            }
        },

        imagemin: {
            all: {
                options: { optimizationLevel: 1 },

                files: [{
                    expand: true,
                    cwd: "docs/img/",
                    src: ["**/*.{png,jpg,jpeg,gif}"],
                    dest: "docs/public/img/"
                }]
            }
        },

        shell: {
            copyMain: {
                command: [
                    // "cp src/springs.js dist/springs.js",
                    // "cp src/springs.scss dist/springs.scss",
                    "cp src/behaviors/drag.js dist/behaviors/drag.js",
                    "cp src/components/*/*.scss docs/scss/components",
                    "rsync -av src/components/ dist/components/"
                ].join("&&")
            }
        },

        watch: {
            options: { livereload: false },

            js: {
                files: ["dist/**/*.js"],
                tasks: ["concat", "shell"],
                options: { spawn: false }
            },

            coffee: {
                files: ["src/**/*.coffee"],
                tasks: ["coffee:components", "coffee:build"]
            },

            coffeeSpecs: {
                files: "spec/**/*.coffee",
                tasks: ["coffee:tests"],
                options: { spawn: false }
            },

            jsSpecs: {
                files: "spec/**/*.js",
                tasks: ["jasmine"],
                options: { spawn: false }
            },

            scss: {
                files: ["src/**/*.scss", "test/**/*.scss"],
                tasks: ["concat", "sass", "autoprefixer", "csscss", "shell"],
                options: { spawn: false }
            },

            css: {
                files: ["dist/**/*.css", "test/**/*.css"],
                tasks: ["autoprefixer"],
                options: { spawn: false }
            },

            haml: {
                files: ["src/**/*.haml"],
                tasks: ["haml"],
                options: { spawn: false }
            },

            docsHaml: {
                files: ["docs/haml/**/*.haml"],
                tasks: ["haml:docs", "haml:fullDocs"],
                options: { spawn: false }
            },

            docsScss: {
                files: ["docs/scss/**/*.scss"],
                tasks: ["sass:docs", "autoprefixer:docs"],
                options: { spawn: false }
            },

            api: {
                files: ["docs/haml/api/**/*.yaml"],
                tasks: ["haml:docs", "haml:fullDocs"],
                options: { spawn: false }
            },

            docsCoffee: {
                files: ["docs/coffee/**/*.coffee"],
                tasks: ["coffee:docs"],
                options: { spawn: false }
            }
        }
    });

    // 2. TASKS
    require("load-grunt-tasks")(grunt);

    // 3. PERFORM
    grunt.registerTask("default", ["coffee:build", "coffee:components", "concat", "sass", "autoprefixer", "csscss", "uglify", "compare_size", "haml", "shell"]);
    grunt.registerTask("tests", ["coffee:specHelpers", "coffee:specs", "jasmine"]);
    grunt.registerTask("full", ["coffee", "jasmine", "concat", "sass", "autoprefixer", "csscss", "uglify", "compare_size", "slim", "shell"]);
    grunt.registerTask("docs", ["haml:fullDocs", "haml:docs", "sass:docs", "autoprefixer:docs"]);

}<|endoftext|>