<commit_before>// <output>
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-button-element.html#the-output-element
// by Addy Osmani
Modernizr.addTest('output', 'value' in document.createElement('output'));
<commit_msg>output elem test rename to avoid class conflicts. (incl our test suite)<commit_after>// <output>
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-button-element.html#the-output-element
// by Addy Osmani
Modernizr.addTest('outputelem', 'value' in document.createElement('output'));
<|endoftext|>


<commit_before>
var regexpHelper = require('../../src/regexpHelper');

module.exports = function RedirectMiddleware() {
  return {
    requestHandler: function(req, res, next, target) {
      if (req.match)
        target = regexpHelper(target, req.match);

      target = target.replace("[path]", req.url.substring(1));
      target = target.replace("[host]", req.unicodeHost.split(':')[0]);
      res.statusCode = 302;
      res.setHeader("Location", target);
      return res.end();
    }
  };
}<commit_msg>Fix tests<commit_after>
var regexpHelper = require('../../src/regexpHelper');

module.exports = function RedirectMiddleware() {
  return {
    requestHandler: function(req, res, next, target) {
      if (req.match)
        target = regexpHelper(target, req.match);

      target = target.replace("[path]", req.url.substring(1));
      if(req.unicodeHost) {
        target = target.replace("[host]", req.unicodeHost.split(':')[0]);
      }
      res.statusCode = 302;
      res.setHeader("Location", target);
      return res.end();
    }
  };
}<|endoftext|>


<commit_before>// -------------------------------------------------------------------------- \\
// File: ModalEventHandler.js                                                 \\
// Module: PanelViews                                                         \\
// Requires: Core, Foundation, DOM, View                                      \\
// Author: Neil Jenkins                                                       \\
// License: © 2010-2015 FastMail Pty Ltd. MIT Licensed.                       \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

var ModalEventHandler = NS.Class({

    Extends: NS.Object,

    init: function ( mixin ) {
        ModalEventHandler.parent.init.call( this, mixin );
        this._seenMouseDown = false;
    },

    inView: function ( event ) {
        var targetView = event.targetView,
            view = this.get( 'view' );
        while ( targetView && targetView !== view ) {
            targetView = targetView.get( 'parentView' );
        }
        return !!targetView;
    },

    // If a user clicks outside the menu we want to close it. But we don't want
    // the mousedown/mouseup/click events to propagate to what's below. The
    // events fire in that order, and not all are guaranteed to fire (the user
    // could mousedown and drag their mouse out of the window before releasing
    // it or vica versa. If there is a drag in between mousedown and mouseup,
    // the click event won't fire).
    //
    // The safest to hide on is click, as we know there are no more events from
    // this user interaction which we need to capture, and it also means the
    // user has clicked and released outside the pop over; a decent indication
    // we should close it. However, if the pop over was triggered on mousedown
    // we may still see a mouseup and a click event from this initial user
    // interaction, but these musn't hide the view. Therefore, we make sure
    // we've seen at least one mousedown event after the popOver view shows
    // before hiding on click.
    handleMouse: function ( event ) {
        var type = event.type,
            view;
        if ( !event.seenByModal && !this.inView( event ) ) {
            event.stopPropagation();
            if ( type === 'mousedown' || type === 'tap' ) {
                this._seenMouseDown = true;
            } else if ( type === 'click' ) {
                event.preventDefault();
                if ( this._seenMouseDown ) {
                    view = this.get( 'view' );
                    if ( view.clickedOutside ) {
                        view.clickedOutside( event );
                    }
                }
            }
        }
        event.seenByModal = true;
    }.on( 'click', 'mousedown', 'mouseup', 'tap' ),

    // If the user clicks on a scroll bar to scroll (I know, who does that
    // these days right?), we don't want to count that as a click. So cancel
    // the seen mousedown on scroll events.
    handleScroll: function () {
        this._seenMouseDown = false;
    }.on( 'scroll' ),

    handleKeys: function ( event ) {
        if ( !event.seenByModal && !this.inView( event ) ) {
            event.stopPropagation();
            // View may be interested in key events:
            var view = this.get( 'view' );
            if ( view.keyOutside ) {
                view.keyOutside( event );
            }
        }
        event.seenByModal = true;
    }.on( 'keypress', 'keydown', 'keyup' ),

    handleTouch: function ( event ) {
        if ( !event.seenByModal && !this.inView( event ) ) {
            event.preventDefault();
            event.stopPropagation();
        }
        event.seenByModal = true;
    }.on( 'touchstart' )
});

NS.ModalEventHandler = ModalEventHandler;

}( O ) );
<commit_msg>Handle correctly browsers that fire mouse+touch events<commit_after>// -------------------------------------------------------------------------- \\
// File: ModalEventHandler.js                                                 \\
// Module: PanelViews                                                         \\
// Requires: Core, Foundation, DOM, View                                      \\
// Author: Neil Jenkins                                                       \\
// License: © 2010-2015 FastMail Pty Ltd. MIT Licensed.                       \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

var ModalEventHandler = NS.Class({

    Extends: NS.Object,

    init: function ( mixin ) {
        ModalEventHandler.parent.init.call( this, mixin );
        this._seenMouseDown = false;
    },

    inView: function ( event ) {
        var targetView = event.targetView,
            view = this.get( 'view' );
        while ( targetView && targetView !== view ) {
            targetView = targetView.get( 'parentView' );
        }
        return !!targetView;
    },

    // If a user clicks outside the menu we want to close it. But we don't want
    // the mousedown/mouseup/click events to propagate to what's below. The
    // events fire in that order, and not all are guaranteed to fire (the user
    // could mousedown and drag their mouse out of the window before releasing
    // it or vica versa. If there is a drag in between mousedown and mouseup,
    // the click event won't fire).
    //
    // The safest to hide on is click, as we know there are no more events from
    // this user interaction which we need to capture, and it also means the
    // user has clicked and released outside the pop over; a decent indication
    // we should close it. However, if the pop over was triggered on mousedown
    // we may still see a mouseup and a click event from this initial user
    // interaction, but these must not hide the view. Therefore, we make sure
    // we've seen at least one mousedown event after the popOver view shows
    // before hiding on click. On Android/iOS, we will not see a mousedown
    // event, so we also count a touchstart event.
    handleMouse: function ( event ) {
        var type = event.type,
            view;
        if ( !event.seenByModal && !this.inView( event ) ) {
            event.stopPropagation();
            if ( type === 'mousedown' ) {
                this._seenMouseDown = true;
            } else if ( type === 'click' ) {
                event.preventDefault();
                if ( this._seenMouseDown ) {
                    view = this.get( 'view' );
                    if ( view.clickedOutside ) {
                        view.clickedOutside( event );
                    }
                }
            }
        }
        event.seenByModal = true;
    }.on( 'click', 'mousedown', 'mouseup', 'tap' ),

    // If the user clicks on a scroll bar to scroll (I know, who does that
    // these days right?), we don't want to count that as a click. So cancel
    // the seen mousedown on scroll events.
    handleScroll: function () {
        this._seenMouseDown = false;
    }.on( 'scroll' ),

    handleKeys: function ( event ) {
        if ( !event.seenByModal && !this.inView( event ) ) {
            event.stopPropagation();
            // View may be interested in key events:
            var view = this.get( 'view' );
            if ( view.keyOutside ) {
                view.keyOutside( event );
            }
        }
        event.seenByModal = true;
    }.on( 'keypress', 'keydown', 'keyup' ),

    handleTouch: function ( event ) {
        if ( !event.seenByModal && !this.inView( event ) ) {
            event.preventDefault();
            event.stopPropagation();
            // Clicks outside should now close the modal.
            this._seenMouseDown = true;
        }
        event.seenByModal = true;
    }.on( 'touchstart' )
});

NS.ModalEventHandler = ModalEventHandler;

}( O ) );
<|endoftext|>


<commit_before>const ARG_WATER_VOLUME = 'water_volume';
const Str = require('./strings');
const util = require('util');

class Conversation {
    constructor(dialogflowApp, userManager, waterLog) {
        this.dialogflowApp = dialogflowApp;
        this.userManager = userManager;
        this.waterLog = waterLog;
    }

    actionWelcomeUser() {
        return this.userManager.isFirstUsage(this._getCurrentUserId())
            .then(isFirstUsage => {
                if (isFirstUsage) {
                    this.userManager.saveAssistantUser(this._getCurrentUserId());
                    this._greetNewUser();
                } else {
                    this._greetExistingUser();
                }

                return isFirstUsage;
            });
    }

    _greetNewUser() {
        this.dialogflowApp.ask(Str.GREETING_NEW_USER, Str.GREETING_NEW_USER_NO_INPUT_PROMPT);
    }

    _greetExistingUser() {
        this.waterLog.getLoggedWaterForUser(this._getCurrentUserId())
            .then(loggedWater => {
                this.dialogflowApp.ask(
                    util.format(Str.GREETING_EXISTING_USER, loggedWater),
                    Str.GREETING_EXISTING_USER_NO_INPUT_PROMPT);
            });
    }

    actionLogWater() {
        let waterToLog = this.dialogflowApp.getArgument(ARG_WATER_VOLUME);
        this.waterLog.saveLoggedWater(this._getCurrentUserId(), waterToLog);
        return this.waterLog.getLoggedWaterForUser(this._getCurrentUserId())
            .then(loggedWater => {
                this.dialogflowApp.tell(
                    util.format(Str.WATER_LOGGED_NOW,
                        waterToLog.amount,
                        waterToLog.unit,
                        loggedWater
                    )
                );
            });
    }

    actionGetLoggedWater() {
        return this.waterLog.getLoggedWaterForUser(this._getCurrentUserId())
            .then(loggedWater => this.dialogflowApp.tell(util.format(Str.WATER_LOGGED_OVERALL, loggedWater)));
    }

    _getCurrentUserId() {
        return this.dialogflowApp.getUser().userId;
    }
}

module.exports = Conversation;
<commit_msg>added some comments<commit_after>const ARG_WATER_VOLUME = 'water_volume';
const Str = require('./strings');
const util = require('util');

class Conversation {
    constructor(dialogflowApp, userManager, waterLog) {
        this.dialogflowApp = dialogflowApp;
        this.userManager = userManager;
        this.waterLog = waterLog;
    }

    //Intent input.welcome
    actionWelcomeUser() {
        return this.userManager.isFirstUsage(this._getCurrentUserId())
            .then(isFirstUsage => {
                if (isFirstUsage) {
                    this.userManager.saveAssistantUser(this._getCurrentUserId());
                    this._greetNewUser();
                } else {
                    this._greetExistingUser();
                }

                return isFirstUsage;
            });
    }

    _greetNewUser() {
        this.dialogflowApp.ask(Str.GREETING_NEW_USER, Str.GREETING_NEW_USER_NO_INPUT_PROMPT);
    }

    _greetExistingUser() {
        this.waterLog.getLoggedWaterForUser(this._getCurrentUserId())
            .then(loggedWater => {
                this.dialogflowApp.ask(
                    util.format(Str.GREETING_EXISTING_USER, loggedWater),
                    Str.GREETING_EXISTING_USER_NO_INPUT_PROMPT);
            });
    }

    //Intent log_water
    actionLogWater() {
        //Get argument extracted by Dialogflow
        let waterToLog = this.dialogflowApp.getArgument(ARG_WATER_VOLUME);
        //Save logged water into Firebase Realtime Database
        this.waterLog.saveLoggedWater(this._getCurrentUserId(), waterToLog);
        //Load sum of logged water for current user and reply user
        //with how much water he or she logged so far.
        //End the conversation.
        return this.waterLog.getLoggedWaterForUser(this._getCurrentUserId())
            .then(loggedWater => {
                this.dialogflowApp.tell(
                    util.format(Str.WATER_LOGGED_NOW,
                        waterToLog.amount,
                        waterToLog.unit,
                        loggedWater
                    )
                );
            });
    }

    //Intent get_logged_water
    actionGetLoggedWater() {
        return this.waterLog.getLoggedWaterForUser(this._getCurrentUserId())
            .then(loggedWater => this.dialogflowApp.tell(util.format(Str.WATER_LOGGED_OVERALL, loggedWater)));
    }

    _getCurrentUserId() {
        return this.dialogflowApp.getUser().userId;
    }
}

module.exports = Conversation;
<|endoftext|>


<commit_before>import React, { useState } from 'react';
import PropTypes from 'prop-types';
import ContentLink from 'components/ContentLink';
import UsernameText from 'components/Texts/UsernameText';
import RoundList from 'components/RoundList';
import Banner from 'components/Banner';
import LoadMoreButton from 'components/Buttons/LoadMoreButton';
import Rankings from './Rankings';
import NotiItem from './NotiItem';
import ErrorBoundary from 'components/Wrappers/ErrorBoundary';
import {
  clearRewards,
  fetchNotifications,
  loadMoreNotifications,
  loadMoreRewards
} from 'redux/actions/NotiActions';
import { timeSince } from 'helpers/timeStampHelpers';
import { Color } from 'constants/css';
import { changeUserXP } from 'redux/actions/UserActions';
import { connect } from 'react-redux';
import { addCommasToNumber } from 'helpers/stringHelpers';
import { notiFeedListItem } from '../Styles';
import { rewardValue } from 'constants/defaultValues';

MainFeeds.propTypes = {
  clearRewards: PropTypes.func.isRequired,
  changeUserXP: PropTypes.func.isRequired,
  fetchNotifications: PropTypes.func.isRequired,
  loadMore: PropTypes.object.isRequired,
  loadMoreNotifications: PropTypes.func.isRequired,
  loadMoreRewards: PropTypes.func.isRequired,
  numNewNotis: PropTypes.number,
  activeTab: PropTypes.string,
  notifications: PropTypes.array.isRequired,
  rewards: PropTypes.array,
  selectNotiTab: PropTypes.func.isRequired,
  style: PropTypes.object,
  totalRewardAmount: PropTypes.number,
  twinkleXP: PropTypes.number,
  userId: PropTypes.number
};

function MainFeeds({
  activeTab,
  changeUserXP,
  clearRewards,
  fetchNotifications,
  loadMore,
  loadMoreNotifications,
  loadMoreRewards,
  notifications,
  numNewNotis,
  rewards,
  selectNotiTab,
  style,
  totalRewardAmount,
  twinkleXP,
  userId
}) {
  const [loading, setLoading] = useState(false);
  const [originalTotalReward, setOriginalTotalReward] = useState(0);
  const [originalTwinkleXP, setOriginalTwinkleXP] = useState(0);

  return (
    <ErrorBoundary style={style}>
      {numNewNotis > 0 && (
        <Banner
          gold
          style={{ marginBottom: '1rem' }}
          onClick={onNewNotiAlertClick}
        >
          Tap to See {numNewNotis} New Notification
          {numNewNotis > 1 ? 's' : ''}
        </Banner>
      )}
      {activeTab === 'reward' && (
        <Banner
          gold={totalRewardAmount > 0}
          success={totalRewardAmount === 0}
          style={{ marginBottom: '1rem' }}
          onClick={totalRewardAmount > 0 ? onCollectReward : null}
        >
          {totalRewardAmount > 0 && (
            <>

Tap to collect all your rewards

                ({totalRewardAmount} Twinkles x {rewardValue.star} XP/Twinkle ={' '}
                {addCommasToNumber(totalRewardAmount * rewardValue.star)} XP)

            </>
          )}
          {totalRewardAmount === 0 && (
            <>

{originalTotalReward * rewardValue.star} XP Collected!

                Your XP went up from {addCommasToNumber(originalTwinkleXP)} to{' '}
                {addCommasToNumber(
                  originalTwinkleXP + originalTotalReward * rewardValue.star
                )}

            </>
          )}
        </Banner>
      )}
      {notifications.length > 0 && (
        <RoundList style={{ marginTop: 0, marginBottom: '1rem' }}>
          {activeTab === 'notification' &&
            userId &&
            notifications.map(notification => {
              return (

                  <NotiItem notification={notification} />

              );
            })}
        </RoundList>
      )}
      {activeTab === 'rankings' && <Rankings />}
      <RoundList style={{ marginTop: 0 }}>
        {activeTab === 'reward' &&
          rewards.map(
            ({
              id,
              contentId,
              contentType,
              rewardAmount,
              rewardType,
              rewarderId,
              rewarderUsername,
              timeStamp
            }) => (

                  <UsernameText
                    user={{ id: rewarderId, username: rewarderUsername }}
                    color={Color.blue()}
                  />{' '}
                  = 10
                          ? Color.rose()
                          : rewardAmount >= 5
                          ? Color.orange()
                          : rewardAmount >= 3
                          ? Color.pink()
                          : Color.lightBlue(),
                      fontWeight: 'bold'
                    }}
                  >
                    rewarded you {rewardAmount === 1 ? 'a' : rewardAmount}{' '}
                    {rewardType}
                    {rewardAmount > 1 ? 's' : ''}
                  {' '}
                  for your{' '}
                  <ContentLink
                    style={{ color: Color.green() }}
                    content={{
                      id: contentId,
                      title: contentType
                    }}
                    type={contentType}
                  />

                {timeSince(timeStamp)}

            )
          )}
      </RoundList>
      {((activeTab === 'notification' && loadMore.notifications) ||
        (activeTab === 'reward' && loadMore.rewards)) && (
        <LoadMoreButton
          loading={loading}
          info
          filled
          stretch
          onClick={onLoadMore}
        />
      )}
    </ErrorBoundary>
  );

  async function onCollectReward() {
    setOriginalTotalReward(totalRewardAmount);
    setOriginalTwinkleXP(twinkleXP);
    await changeUserXP({
      action: 'collect'
    });
    clearRewards();
  }

  async function onNewNotiAlertClick() {
    await fetchNotifications();
    selectNotiTab();
  }

  async function onLoadMore() {
    setLoading(true);
    if (activeTab === 'notification') {
      await loadMoreNotifications(notifications[notifications.length - 1].id);
    } else {
      await loadMoreRewards(rewards[rewards.length - 1].id);
    }
    setLoading(false);
  }
}

export default connect(
  state => ({
    numNewNotis: state.NotiReducer.numNewNotis,
    totalRewardAmount: state.NotiReducer.totalRewardAmount,
    twinkleXP: state.UserReducer.twinkleXP,
    userId: state.UserReducer.userId
  }),
  {
    changeUserXP,
    clearRewards,
    fetchNotifications,
    loadMoreNotifications,
    loadMoreRewards
  }
)(MainFeeds);
<commit_msg>minor fix<commit_after>import React, { useState } from 'react';
import PropTypes from 'prop-types';
import ContentLink from 'components/ContentLink';
import UsernameText from 'components/Texts/UsernameText';
import RoundList from 'components/RoundList';
import Banner from 'components/Banner';
import LoadMoreButton from 'components/Buttons/LoadMoreButton';
import Rankings from './Rankings';
import NotiItem from './NotiItem';
import ErrorBoundary from 'components/Wrappers/ErrorBoundary';
import {
  clearRewards,
  fetchNotifications,
  loadMoreNotifications,
  loadMoreRewards
} from 'redux/actions/NotiActions';
import { timeSince } from 'helpers/timeStampHelpers';
import { Color } from 'constants/css';
import { changeUserXP } from 'redux/actions/UserActions';
import { connect } from 'react-redux';
import { addCommasToNumber } from 'helpers/stringHelpers';
import { notiFeedListItem } from '../Styles';
import { rewardValue } from 'constants/defaultValues';

MainFeeds.propTypes = {
  clearRewards: PropTypes.func.isRequired,
  changeUserXP: PropTypes.func.isRequired,
  fetchNotifications: PropTypes.func.isRequired,
  loadMore: PropTypes.object.isRequired,
  loadMoreNotifications: PropTypes.func.isRequired,
  loadMoreRewards: PropTypes.func.isRequired,
  numNewNotis: PropTypes.number,
  activeTab: PropTypes.string,
  notifications: PropTypes.array.isRequired,
  rewards: PropTypes.array,
  selectNotiTab: PropTypes.func.isRequired,
  style: PropTypes.object,
  totalRewardAmount: PropTypes.number,
  twinkleXP: PropTypes.number,
  userId: PropTypes.number
};

function MainFeeds({
  activeTab,
  changeUserXP,
  clearRewards,
  fetchNotifications,
  loadMore,
  loadMoreNotifications,
  loadMoreRewards,
  notifications,
  numNewNotis,
  rewards,
  selectNotiTab,
  style,
  totalRewardAmount,
  twinkleXP,
  userId
}) {
  const [loading, setLoading] = useState(false);
  const [originalTotalReward, setOriginalTotalReward] = useState(0);
  const [originalTwinkleXP, setOriginalTwinkleXP] = useState(0);

  return (
    <ErrorBoundary style={style}>
      {numNewNotis > 0 && (
        <Banner
          gold
          style={{ marginBottom: '1rem' }}
          onClick={onNewNotiAlertClick}
        >
          Tap to See {numNewNotis} New Notification
          {numNewNotis > 1 ? 's' : ''}
        </Banner>
      )}
      {activeTab === 'reward' && (
        <Banner
          gold={totalRewardAmount > 0}
          success={totalRewardAmount === 0}
          style={{ marginBottom: '1rem' }}
          onClick={totalRewardAmount > 0 ? onCollectReward : null}
        >
          {totalRewardAmount > 0 && (
            <>

Tap to collect all your rewards

                ({totalRewardAmount} Twinkles x {rewardValue.star} XP/Twinkle ={' '}
                {addCommasToNumber(totalRewardAmount * rewardValue.star)} XP)

            </>
          )}
          {totalRewardAmount === 0 && (
            <>

{originalTotalReward * rewardValue.star} XP Collected!

                Your XP went up from {addCommasToNumber(originalTwinkleXP)} to{' '}
                {addCommasToNumber(
                  originalTwinkleXP + originalTotalReward * rewardValue.star
                )}

            </>
          )}
        </Banner>
      )}
      {notifications.length > 0 && (
        <RoundList style={{ marginTop: 0, marginBottom: '1rem' }}>
          {activeTab === 'notification' &&
            userId &&
            notifications.map(notification => {
              return (

                  <NotiItem notification={notification} />

              );
            })}
        </RoundList>
      )}
      {activeTab === 'rankings' && <Rankings />}
      <RoundList style={{ marginTop: 0 }}>
        {activeTab === 'reward' &&
          rewards.map(
            ({
              id,
              contentId,
              contentType,
              rewardAmount,
              rewardType,
              rewarderId,
              rewarderUsername,
              timeStamp
            }) => (

                  <UsernameText
                    user={{ id: rewarderId, username: rewarderUsername }}
                    color={Color.blue()}
                  />{' '}
                  = 10
                          ? Color.rose()
                          : rewardAmount >= 5
                          ? Color.orange()
                          : rewardAmount >= 3
                          ? Color.pink()
                          : Color.lightBlue(),
                      fontWeight: 'bold'
                    }}
                  >
                    rewarded you {rewardAmount === 1 ? 'a' : rewardAmount}{' '}
                    {rewardType}
                    {rewardAmount > 1 ? 's' : ''}
                  {' '}
                  for your{' '}
                  <ContentLink
                    style={{ color: Color.green() }}
                    content={{
                      id: contentId,
                      title: contentType
                    }}
                    type={contentType}
                  />

                {timeSince(timeStamp)}

            )
          )}
      </RoundList>
      {((activeTab === 'notification' && loadMore.notifications) ||
        (activeTab === 'reward' && loadMore.rewards)) &&
        !!userId && (
          <LoadMoreButton
            loading={loading}
            info
            filled
            stretch
            onClick={onLoadMore}
          />
        )}
    </ErrorBoundary>
  );

  async function onCollectReward() {
    setOriginalTotalReward(totalRewardAmount);
    setOriginalTwinkleXP(twinkleXP);
    await changeUserXP({
      action: 'collect'
    });
    clearRewards();
  }

  async function onNewNotiAlertClick() {
    await fetchNotifications();
    selectNotiTab();
  }

  async function onLoadMore() {
    setLoading(true);
    if (activeTab === 'notification') {
      await loadMoreNotifications(notifications[notifications.length - 1].id);
    } else {
      await loadMoreRewards(rewards[rewards.length - 1].id);
    }
    setLoading(false);
  }
}

export default connect(
  state => ({
    numNewNotis: state.NotiReducer.numNewNotis,
    totalRewardAmount: state.NotiReducer.totalRewardAmount,
    twinkleXP: state.UserReducer.twinkleXP,
    userId: state.UserReducer.userId
  }),
  {
    changeUserXP,
    clearRewards,
    fetchNotifications,
    loadMoreNotifications,
    loadMoreRewards
  }
)(MainFeeds);
<|endoftext|>


<commit_before>var workersActions = require('workersActions')
var _ = require('lodash')

var roleGeneralist = {

    /** @param {Creep} creep **/
    run: function(creep) {
	    var state = creep.memory.state;
    	var options = ['upgrading', 'storing', 'building'];

	    workersActions.maybeHarvest(creep);

	    if(state == 'harvesting') {
    	    workersActions.harvest(creep);
    	} else if(state == 'upgrading') {
	        workersActions.upgrade(creep);
	    } else if(state == 'storing') {
	        workersActions.store(creep);
	    } else if(state == 'building') {
	        workersActions.build(creep);
	    } else if(state == 'deciding') {

            // always prioritize upgrading if it has not been done recently.
            // otherwise fill up all energy storage.
            // then randomly pick between build and upgrade, slightly biased towards build.

	        if(Memory.lastUpgraded + 1000 < Game.time) {
	            creep.memory.state = 'upgrading';
	        } else if(Room.energyCapacityAvailable > Room.energyAvailable) {
	            creep.memory.state = 'storing';
	        } else {
	            var next = _.shuffle(['upgrading', 'building', 'building'])[0];
                creep.memory.state = next;
	        }
	    }
    }
};

module.exports = roleGeneralist;
<commit_msg>room is not global, there is more than one!<commit_after>var workersActions = require('workersActions')
var _ = require('lodash')

var roleGeneralist = {

    /** @param {Creep} creep **/
    run: function(creep) {
	    var state = creep.memory.state;
    	var options = ['upgrading', 'storing', 'building'];

	    workersActions.maybeHarvest(creep);

	    if(state == 'harvesting') {
    	    workersActions.harvest(creep);
    	} else if(state == 'upgrading') {
	        workersActions.upgrade(creep);
	    } else if(state == 'storing') {
	        workersActions.store(creep);
	    } else if(state == 'building') {
	        workersActions.build(creep);
	    } else if(state == 'deciding') {

            // always prioritize upgrading if it has not been done recently.
            // otherwise fill up all energy storage.
            // then randomly pick between build and upgrade, slightly biased towards build.

	        if(Memory.lastUpgraded + 1000 < Game.time) {
	            creep.memory.state = 'upgrading';
	        } else if(creep.room.energyCapacityAvailable > creep.room.energyAvailable) {
	            creep.memory.state = 'storing';
	        } else {
	            var next = _.shuffle(['upgrading', 'building', 'building'])[0];
                creep.memory.state = next;
	        }
	    }
    }
};

module.exports = roleGeneralist;
<|endoftext|>


<commit_before>'use strict';

var sensor = require('../index'),
    out = console.log;

out('Welcome to the sample app for sync handling of the ds18x20 driver.\n');

out('First off, let\'s see if the ds18x20 driver is loaded.');
out('This we do by calling "sensor.isDriverLoaded()":');
out('...');

var isLoaded = sensor.isDriverLoaded();

if (!isLoaded) {

    out('The driver is not loaded.');
    out('This means we want to load the driver to be able to continue.');
    out('To do so, we need to be super-user root... Let\'s try!');
    out('"sensor.loadDriver()":');

    try {
        sensor.loadDriver();
    } catch (err) {
        out('Seems like we are not running this script as a super-user...');
        out('Sorry to say, but we will shut down since we cannot continue with the show.');
        out('try to run "sudo node examples/sample-app-sync" to continue.');
        out('or load the driver manually by running: "sudo modprobe w1-gpio && sudo modprobe w1-therm"\n');

        out('Bye bye for now =-)');
        process.exit(0);
    }

    out('Alright. The driver is now loaded. Then let\'s continue. Maybe you have some sensors connected...?');

} else {

    out('Alright, the driver was already loaded. Let\'s see if you have some sensors hooked up.');
}

out('So, first we want to list all sensors you have: "sensor.list()":');
out('...');

var listOfDeviceIds = sensor.list();

if (listOfDeviceIds.length === 0) {

    out('Too bad. You don\'t seem to have any sensors hooked up to your raspberry...');
    out('Please do so you can continue with this awesome sample app.');
    out('Bye bye for now =-)');
    process.exit(0);
}

out('Woohoo, you seem to have some sensors we can play with =-D.');
out('These devices were found:', listOfDeviceIds, '\n');

out('Not only can you list the devices.');
out('You can also measure the temperature of all sensors: "sensor.getAll()":');
out('Each sensor that is hooked up to this raspberry pi is now probed in sequence for their temperature.');

out('...');

var result = sensor.getAll();

out('The result:', result);
out('');
out('All results are presented in Celsius degrees and are rounded to one decimal place.');
out('');
out('');

var oneId = listOfDeviceIds[listOfDeviceIds.length - 1];

out('If you want to check a specific sensor, say:', oneId);
out('Then you call: "sensor.get(\'' + oneId + '\')"');

out('...');

var oneResult = sensor.get(oneId);

out('The result:', oneResult);
out('');

var arrayOfIds = [listOfDeviceIds[0], oneId];

out('There is also the possibility to request several measurements in one request:');
out('Then you call: "sensor.get(' + arrayOfIds.toString() + ')"');

if (listOfDeviceIds.length === 1) {
    out('...since you only had one sensor connected, it will be probed twice!');
}

out('...');

var arrayOfResults = sensor.get(arrayOfIds);

out('And here is the result:', arrayOfResults);
out('Note that the result is presented in the same order as the input array');
out('');

out('And that\'s it. Hope you find this sample app and the sensor library useful!');<commit_msg>Corrected sample app usage example.<commit_after>'use strict';

var sensor = require('../index'),
    out = console.log;

out('Welcome to the sample app for sync handling of the ds18x20 driver.\n');

out('First off, let\'s see if the ds18x20 driver is loaded.');
out('This we do by calling "sensor.isDriverLoaded()":');
out('...');

var isLoaded = sensor.isDriverLoaded();

if (!isLoaded) {

    out('The driver is not loaded.');
    out('This means we want to load the driver to be able to continue.');
    out('To do so, we need to be super-user root... Let\'s try!');
    out('"sensor.loadDriver()":');

    try {
        sensor.loadDriver();
    } catch (err) {
        out('Seems like we are not running this script as a super-user...');
        out('Sorry to say, but we will shut down since we cannot continue with the show.');
        out('try to run "sudo node examples/sample-app-sync" to continue.');
        out('or load the driver manually by running: "sudo modprobe w1-gpio && sudo modprobe w1-therm"\n');

        out('Bye bye for now =-)');
        process.exit(0);
    }

    out('Alright. The driver is now loaded. Then let\'s continue. Maybe you have some sensors connected...?');

} else {

    out('Alright, the driver was already loaded. Let\'s see if you have some sensors hooked up.');
}

out('So, first we want to list all sensors you have: "sensor.list()":');
out('...');

var listOfDeviceIds = sensor.list();

if (listOfDeviceIds.length === 0) {

    out('Too bad. You don\'t seem to have any sensors hooked up to your raspberry...');
    out('Please do so you can continue with this awesome sample app.');
    out('Bye bye for now =-)');
    process.exit(0);
}

out('Woohoo, you seem to have some sensors we can play with =-D.');
out('These devices were found:', listOfDeviceIds, '\n');

out('Not only can you list the devices.');
out('You can also measure the temperature of all sensors: "sensor.getAll()":');
out('Each sensor that is hooked up to this raspberry pi is now probed in sequence for their temperature.');

out('...');

var result = sensor.getAll();

out('The result:', result);
out('');
out('All results are presented in Celsius degrees and are rounded to one decimal place.');
out('');
out('');

var oneId = listOfDeviceIds[listOfDeviceIds.length - 1];

out('If you want to check a specific sensor, say:', oneId);
out('Then you call: "sensor.get(\'' + oneId + '\')"');

out('...');

var oneResult = sensor.get(oneId);

out('The result:', oneResult);
out('');

var arrayOfIds = [listOfDeviceIds[0], oneId];

out('There is also the possibility to request several measurements in one request:');
out('Then you call: "sensor.get(', arrayOfIds, ')"');

if (listOfDeviceIds.length === 1) {
    out('...since you only had one sensor connected, it will be probed twice!');
}

out('...');

var arrayOfResults = sensor.get(arrayOfIds);

out('And here is the result:', arrayOfResults);
out('Note that the result is presented in the same order as the input array');
out('');

out('And that\'s it. Hope you find this sample app and the sensor library useful!');
<|endoftext|>


<commit_before>/* @flow */

import _ from 'lodash'
import {kbfsCommon} from '../constants/types/keybase-v1'
import {getTLF} from '../util/kbfs'
import path from 'path'
import type {FSNotification} from '../constants/types/flow-types'

type DecodedKBFSError = {
  'title': string;
  'body': string;
}

export function decodeKBFSError (user: string, notification: FSNotification): DecodedKBFSError {
  const basedir = notification.filename.split(path.sep)[0]
  const tlf = `/keybase${getTLF(notification.publicTopLevelFolder, basedir)}`
  const errors = {
    [kbfsCommon.FSErrorType.accessDenied]: {
      title: 'Keybase: Access denied',
      body: `${user} does not have ${notification.params.mode} access to ${tlf}`,
    },
    [kbfsCommon.FSErrorType.userNotFound]: {
      title: 'Keybase: User not found',
      body: `${notification.params.username} is not a Keybase user`,
    },
    [kbfsCommon.FSErrorType.revokedDataDetected]: {
      title: 'Keybase: Possibly revoked data detected',
      body: `${tlf} was modified by a revoked or bad device. Use 'keybase log send' to file an issue with the Keybase admins.`,
    },
    [kbfsCommon.FSErrorType.notLoggedIn]: {
      title: `Keybase: Permission denied in ${tlf}`,
      body: "You are not logged into Keybase. Try 'keybase login'.",
    },
    [kbfsCommon.FSErrorType.timeout]: {
      title: `Keybase: ${_.capitalize(notification.params.mode)} timeout in ${tlf}`,
      body: `The ${notification.params.mode} operation took too long and failed. Please run 'keybase log send' so our admins can review.`,
    },
    [kbfsCommon.FSErrorType.rekeyNeeded]: notification.params.rekeyself ? {
      title: 'Keybase: Files need to be rekeyed',
      body: `Please open one of your other computers to unlock ${tlf}`,
    } : {
      title: 'Keybase: Friends needed',
      body: `Please ask another member of ${tlf} to open Keybase on one of their computers to unlock it for you.`,
    },
    [kbfsCommon.FSErrorType.badFolder]: {
      title: 'Keybase: Bad folder',
      body: `${notification.params.tlf} is not a Keybase folder. All folders begin with /keybase/private or /keybase/public.`,
    },
    [kbfsCommon.FSErrorType.overQuota]: {
      title: 'Keybase: Out of space',
      body: `Action needed! You are using ${(parseInt(notification.params.usageBytes, 10) / 1e9).toFixed(1)}GB (${Math.round(100 * parseInt(notification.params.usageBytes, 10) / parseInt(notification.params.limitBytes, 10))}%) of your quota. Please delete some data.`,
    },
  }

  if (notification.errorType in errors) {
    return errors[notification.errorType]
  }

  return ({
    title: 'Keybase: KBFS error',
    body: `${notification.status}`,
  })

  // This code came from the kbfs team but this isn't plumbed through the protocol. Leaving this for now
  // if (notification.errorType === kbfsCommon.FSErrorType.notImplemented) {
    // if (notification.feature === '2gbFileLimit') {
      // return ({
        // title: 'Keybase: Not yet implemented',
        // body: `You just tried to write a file larger than 2GB in ${tlf}. This limitation will be removed soon.`
      // })
    // } else if (notification.feature === '512kbDirLimit') {
      // return ({
        // title: 'Keybase: Not yet implemented',
        // body: `You just tried to write too many files into ${tlf}. This limitation will be removed soon.`
      // })
    // } else {
      // return ({
        // title: 'Keybase: Not yet implemented',
        // body: `You just hit a ${notification.feature} limitation in KBFS. It will be fixed soon.`
      // })
    // }
  // } else {
    // return ({
      // title: 'Keybase: KBFS error',
      // body: `${notification.status}`
    // })
  // }
}

// TODO: Once we have access to the Redux store from the thread running
// notification listeners, store the sentNotifications map in it.
let sentNotifications = {}
let sentError = null
function rateLimitAllowsNotify (action, state, tlf, isError) {
  if (!(action in sentNotifications)) {
    sentNotifications[action] = {}
  }
  if (!(state in sentNotifications[action])) {
    sentNotifications[action][state] = {}
  }

  const now = new Date()

  // If we haven't notified for {action,state,tlf} or it was >20s ago, do it.
  const MSG_DELAY = 20 * 1000
  if (tlf in sentNotifications[action][state] && now - sentNotifications[action][state][tlf] <= MSG_DELAY) {
    return false
  }

  // If we last displayed an error, don't replace it with another notification for 5s.
  const ERROR_DELAY = 5 * 1000
  if (sentError !== null && now - sentError <= ERROR_DELAY) {
    return false
  }

  sentNotifications[action][state][tlf] = now
  if (isError) {
    sentError = now
  }

  return true
}

export function kbfsNotification (notification: FSNotification, notify: any, getState: any) {
  const action = {
    [kbfsCommon.FSNotificationType.encrypting]: 'Encrypting and uploading',
    [kbfsCommon.FSNotificationType.decrypting]: 'Decrypting, verifying, and downloading',
    [kbfsCommon.FSNotificationType.signing]: 'Signing and uploading',
    [kbfsCommon.FSNotificationType.verifying]: 'Verifying and downloading',
    [kbfsCommon.FSNotificationType.rekeying]: 'Rekeying',
  }[notification.notificationType]

  if (action === undefined) {
    // Ignore notification types we don't care about.
    return
  }

  const state = {
    [kbfsCommon.FSStatusCode.start]: 'starting',
    [kbfsCommon.FSStatusCode.finish]: 'finished',
    [kbfsCommon.FSStatusCode.error]: 'errored',
  }[notification.statusCode]

  // KBFS fires a notification when it changes state between connected
  // and disconnected (to the mdserver).  For now we just log it.
  if (notification.notificationType === kbfsCommon.FSNotificationType.connection) {
    const state = (notification.statusCode === kbfsCommon.FSStatusCode.start) ? 'connected' : 'disconnected'
    console.log(`KBFS is ${state}`)
    return
  }

  if (notification.statusCode === kbfsCommon.FSStatusCode.finish) {
    // Since we're aggregating dir operations and not showing state,
    // let's ignore file-finished notifications.
    return
  }

  const basedir = notification.filename.split(path.sep)[0]
  const tlf = getTLF(notification.publicTopLevelFolder, basedir)

  let title = `KBFS: ${action}`
  let body = `Files in ${tlf} ${notification.status}`
  let user = 'You' || getState().config.username

  const isError = notification.statusCode === kbfsCommon.FSStatusCode.error
  // Don't show starting or finished, but do show error.
  if (isError) {
    ({title, body} = decodeKBFSError(user, notification))
  }

  if (rateLimitAllowsNotify(action, state, tlf, isError)) {
    notify(title, {body})
  }
}
<commit_msg>Convert to switch statement<commit_after>/* @flow */

import _ from 'lodash'
import {kbfsCommon} from '../constants/types/keybase-v1'
import {getTLF} from '../util/kbfs'
import path from 'path'
import type {FSNotification} from '../constants/types/flow-types'

type DecodedKBFSError = {
  'title': string;
  'body': string;
}

export function decodeKBFSError (user: string, notification: FSNotification): DecodedKBFSError {
  const basedir = notification.filename.split(path.sep)[0]
  const tlf = `/keybase${getTLF(notification.publicTopLevelFolder, basedir)}`
  switch (notification.errorType) {
    case kbfsCommon.FSErrorType.accessDenied:
      return {
        title: 'Keybase: Access denied',
        body: `${user} does not have ${notification.params.mode} access to ${tlf}`,
      }

    case kbfsCommon.FSErrorType.userNotFound:
      return {
        title: 'Keybase: User not found',
        body: `${notification.params.username} is not a Keybase user`,
      }

    case kbfsCommon.FSErrorType.revokedDataDetected:
      return {
        title: 'Keybase: Possibly revoked data detected',
        body: `${tlf} was modified by a revoked or bad device. Use 'keybase log send' to file an issue with the Keybase admins.`,
      }

    case kbfsCommon.FSErrorType.notLoggedIn:
      return {
        title: `Keybase: Permission denied in ${tlf}`,
        body: "You are not logged into Keybase. Try 'keybase login'.",
      }

    case kbfsCommon.FSErrorType.timeout:
      return {
        title: `Keybase: ${_.capitalize(notification.params.mode)} timeout in ${tlf}`,
        body: `The ${notification.params.mode} operation took too long and failed. Please run 'keybase log send' so our admins can review.`,
      }

    case kbfsCommon.FSErrorType.rekeyNeeded:
      return notification.params.rekeyself ? {
        title: 'Keybase: Files need to be rekeyed',
        body: `Please open one of your other computers to unlock ${tlf}`,
      } : {
        title: 'Keybase: Friends needed',
        body: `Please ask another member of ${tlf} to open Keybase on one of their computers to unlock it for you.`,
      }

    case kbfsCommon.FSErrorType.badFolder:
      return {
        title: 'Keybase: Bad folder',
        body: `${notification.params.tlf} is not a Keybase folder. All folders begin with /keybase/private or /keybase/public.`,
      }

    case kbfsCommon.FSErrorType.overQuota:
      return {
        title: 'Keybase: Out of space',
        body: `Action needed! You are using ${(parseInt(notification.params.usageBytes, 10) / 1e9).toFixed(1)}GB (${Math.round(100 * parseInt(notification.params.usageBytes, 10) / parseInt(notification.params.limitBytes, 10))}%) of your quota. Please delete some data.`,
      }

    default:
      return {
        title: 'Keybase: KBFS error',
        body: `${notification.status}`,
      }
  }

  // This code came from the kbfs team but this isn't plumbed through the protocol. Leaving this for now
  // if (notification.errorType === kbfsCommon.FSErrorType.notImplemented) {
    // if (notification.feature === '2gbFileLimit') {
      // return ({
        // title: 'Keybase: Not yet implemented',
        // body: `You just tried to write a file larger than 2GB in ${tlf}. This limitation will be removed soon.`
      // })
    // } else if (notification.feature === '512kbDirLimit') {
      // return ({
        // title: 'Keybase: Not yet implemented',
        // body: `You just tried to write too many files into ${tlf}. This limitation will be removed soon.`
      // })
    // } else {
      // return ({
        // title: 'Keybase: Not yet implemented',
        // body: `You just hit a ${notification.feature} limitation in KBFS. It will be fixed soon.`
      // })
    // }
  // } else {
    // return ({
      // title: 'Keybase: KBFS error',
      // body: `${notification.status}`
    // })
  // }
}

// TODO: Once we have access to the Redux store from the thread running
// notification listeners, store the sentNotifications map in it.
let sentNotifications = {}
let sentError = null
function rateLimitAllowsNotify (action, state, tlf, isError) {
  if (!(action in sentNotifications)) {
    sentNotifications[action] = {}
  }
  if (!(state in sentNotifications[action])) {
    sentNotifications[action][state] = {}
  }

  const now = new Date()

  // If we haven't notified for {action,state,tlf} or it was >20s ago, do it.
  const MSG_DELAY = 20 * 1000
  if (tlf in sentNotifications[action][state] && now - sentNotifications[action][state][tlf] <= MSG_DELAY) {
    return false
  }

  // If we last displayed an error, don't replace it with another notification for 5s.
  const ERROR_DELAY = 5 * 1000
  if (sentError !== null && now - sentError <= ERROR_DELAY) {
    return false
  }

  sentNotifications[action][state][tlf] = now
  if (isError) {
    sentError = now
  }

  return true
}

export function kbfsNotification (notification: FSNotification, notify: any, getState: any) {
  const action = {
    [kbfsCommon.FSNotificationType.encrypting]: 'Encrypting and uploading',
    [kbfsCommon.FSNotificationType.decrypting]: 'Decrypting, verifying, and downloading',
    [kbfsCommon.FSNotificationType.signing]: 'Signing and uploading',
    [kbfsCommon.FSNotificationType.verifying]: 'Verifying and downloading',
    [kbfsCommon.FSNotificationType.rekeying]: 'Rekeying',
  }[notification.notificationType]

  if (action === undefined) {
    // Ignore notification types we don't care about.
    return
  }

  const state = {
    [kbfsCommon.FSStatusCode.start]: 'starting',
    [kbfsCommon.FSStatusCode.finish]: 'finished',
    [kbfsCommon.FSStatusCode.error]: 'errored',
  }[notification.statusCode]

  // KBFS fires a notification when it changes state between connected
  // and disconnected (to the mdserver).  For now we just log it.
  if (notification.notificationType === kbfsCommon.FSNotificationType.connection) {
    const state = (notification.statusCode === kbfsCommon.FSStatusCode.start) ? 'connected' : 'disconnected'
    console.log(`KBFS is ${state}`)
    return
  }

  if (notification.statusCode === kbfsCommon.FSStatusCode.finish) {
    // Since we're aggregating dir operations and not showing state,
    // let's ignore file-finished notifications.
    return
  }

  const basedir = notification.filename.split(path.sep)[0]
  const tlf = getTLF(notification.publicTopLevelFolder, basedir)

  let title = `KBFS: ${action}`
  let body = `Files in ${tlf} ${notification.status}`
  let user = 'You' || getState().config.username

  const isError = notification.statusCode === kbfsCommon.FSStatusCode.error
  // Don't show starting or finished, but do show error.
  if (isError) {
    ({title, body} = decodeKBFSError(user, notification))
  }

  if (rateLimitAllowsNotify(action, state, tlf, isError)) {
    notify(title, {body})
  }
}
<|endoftext|>


<commit_before>var FS = require('fs');
var events = require('events');
var util = require('util');
var JsonUtils = require("./lib/utils");
var DBParentData = require("./lib/DBParentData");
var DatabaseError = require("./lib/Errors").DatabaseError;
var DataError = require("./lib/Errors").DataError;

var JsonDB = function (filename, saveOnPush) {

    this.filename = filename;

    if (!JsonUtils.strEndWith(filename, ".json")) {
        this.filename += ".json";
    }
    var self = this;
    this.loaded = false;
    this.data = {};
    if (!FS.existsSync(this.filename)) {
        self.save(true);
        self.loaded = true;
        util.log("[JsonDB] DataBase " + self.filename + " created.");
    }
    this.saveOnPush = saveOnPush;

    return this;
};
JsonDB.prototype.processDataPath = function (dataPath) {
    if (dataPath === undefined || !dataPath.trim()) {
        throw new DataError("The Data Path can't be empty", 6);
    }
    if (dataPath == "/") {
        return [];
    }
    var path = dataPath.split("/");
    path.shift();
    return path;
}

JsonDB.prototype.getParentData = function (dataPath, create) {
    var path = this.processDataPath(dataPath);
    var last = path.pop();
    return new DBParentData(last, this._getData(path, create), this);
}

JsonDB.prototype.getData = function (dataPath) {
    var path = this.processDataPath(dataPath);
    return this._getData(path);
}

JsonDB.prototype._getData = function (dataPath, create) {
    this.load();

    create = create || false;
    if (dataPath.length == 0) {
        return this.data;
    }
    var data = this.data;
    for (var i in dataPath) {
        if (dataPath.hasOwnProperty(i)) {
            var property = dataPath[i];
            if (data.hasOwnProperty(property)) {
                data = data[property];
            } else if (create) {
                data[property] = {};
                data = data[property];
            } else {
                throw new DataError("Can't find dataPath: /" + dataPath.join("/") + ". Stopped at " + property, 5);
            }
        }
    }
    return data;

}
JsonDB.prototype.push = function (dataPath, data, override) {
    override = override === undefined ? true : override;
    var dbData = this.getParentData(dataPath, true);
    if (!dbData) {
        throw new Error("Data not found");
    }
    var toSet = data;
    if (!override) {
        if (Array.isArray(data)) {
            var storedData = dbData.getData();
            if (storedData === undefined) {
                storedData = [];
            } else if (!Array.isArray(storedData)) {
                throw new DataError("Can't merge another type of data with an Array", 3);
            }
            toSet = storedData.concat(data);
        } else if (data === Object(data)) {
            if (Array.isArray(dbData.getData())) {
                throw  new DataError("Can't merge an Array with an Object", 4);
            }
            toSet = JsonUtils.mergeObject(dbData.getData(), data);
        }
    }
    dbData.setData(toSet);

    if (this.saveOnPush) {
        this.save();
    }
}
JsonDB.prototype.delete = function (dataPath) {
    var dbData = this.getParentData(dataPath, true);
    if (!dbData) {
        return;
    }
    dbData.delete();
}

JsonDB.prototype.load = function () {
    if (this.loaded) {
        return;
    }
    try {
        var data = FS.readFileSync(this.filename, 'utf8');
        this.data = JSON.parse(data);
        this.loaded = true;
        util.log("[JsonDB] DataBase " + this.filename + " loaded.");
    } catch (err) {
        var error = new DatabaseError("Can't Load Database", 1, err);
        error.inner = err;
        throw error;
    }
}
JsonDB.prototype.save = function (force) {
    force = force || false;
    if (!force && !this.loaded) {
        throw new DatabaseError("DataBase not loaded. Can't write", 7);
    }
    var data = "";
    try {
        data = JSON.stringify(this.data);
        FS.writeFileSync(this.filename, data, 'utf8');
    } catch (err) {
        var error = new DatabaseError("Can't save the database", 2, err);
        error.inner = err;
        throw error;
    }

}

exports = module.exports = JsonDB;<commit_msg>[FIX] jshint warning<commit_after>var FS = require('fs');
var events = require('events');
var util = require('util');
var JsonUtils = require("./lib/utils");
var DBParentData = require("./lib/DBParentData");
var DatabaseError = require("./lib/Errors").DatabaseError;
var DataError = require("./lib/Errors").DataError;

var JsonDB = function (filename, saveOnPush) {

    this.filename = filename;

    if (!JsonUtils.strEndWith(filename, ".json")) {
        this.filename += ".json";
    }
    var self = this;
    this.loaded = false;
    this.data = {};
    if (!FS.existsSync(this.filename)) {
        self.save(true);
        self.loaded = true;
        util.log("[JsonDB] DataBase " + self.filename + " created.");
    }
    this.saveOnPush = saveOnPush;

    return this;
};
JsonDB.prototype.processDataPath = function (dataPath) {
    if (dataPath === undefined || !dataPath.trim()) {
        throw new DataError("The Data Path can't be empty", 6);
    }
    if (dataPath == "/") {
        return [];
    }
    var path = dataPath.split("/");
    path.shift();
    return path;
}

JsonDB.prototype.getParentData = function (dataPath, create) {
    var path = this.processDataPath(dataPath);
    var last = path.pop();
    return new DBParentData(last, this._getData(path, create), this);
}

JsonDB.prototype.getData = function (dataPath) {
    var path = this.processDataPath(dataPath);
    return this._getData(path);
}

JsonDB.prototype._getData = function (dataPath, create) {
    this.load();

    create = create || false;
    if (dataPath.length === 0) {
        return this.data;
    }
    var data = this.data;
    for (var i in dataPath) {
        if (dataPath.hasOwnProperty(i)) {
            var property = dataPath[i];
            if (data.hasOwnProperty(property)) {
                data = data[property];
            } else if (create) {
                data[property] = {};
                data = data[property];
            } else {
                throw new DataError("Can't find dataPath: /" + dataPath.join("/") + ". Stopped at " + property, 5);
            }
        }
    }
    return data;

}
JsonDB.prototype.push = function (dataPath, data, override) {
    override = override === undefined ? true : override;
    var dbData = this.getParentData(dataPath, true);
    if (!dbData) {
        throw new Error("Data not found");
    }
    var toSet = data;
    if (!override) {
        if (Array.isArray(data)) {
            var storedData = dbData.getData();
            if (storedData === undefined) {
                storedData = [];
            } else if (!Array.isArray(storedData)) {
                throw new DataError("Can't merge another type of data with an Array", 3);
            }
            toSet = storedData.concat(data);
        } else if (data === Object(data)) {
            if (Array.isArray(dbData.getData())) {
                throw  new DataError("Can't merge an Array with an Object", 4);
            }
            toSet = JsonUtils.mergeObject(dbData.getData(), data);
        }
    }
    dbData.setData(toSet);

    if (this.saveOnPush) {
        this.save();
    }
}
JsonDB.prototype.delete = function (dataPath) {
    var dbData = this.getParentData(dataPath, true);
    if (!dbData) {
        return;
    }
    dbData.delete();
}

JsonDB.prototype.load = function () {
    if (this.loaded) {
        return;
    }
    try {
        var data = FS.readFileSync(this.filename, 'utf8');
        this.data = JSON.parse(data);
        this.loaded = true;
        util.log("[JsonDB] DataBase " + this.filename + " loaded.");
    } catch (err) {
        var error = new DatabaseError("Can't Load Database", 1, err);
        error.inner = err;
        throw error;
    }
}
JsonDB.prototype.save = function (force) {
    force = force || false;
    if (!force && !this.loaded) {
        throw new DatabaseError("DataBase not loaded. Can't write", 7);
    }
    var data = "";
    try {
        data = JSON.stringify(this.data);
        FS.writeFileSync(this.filename, data, 'utf8');
    } catch (err) {
        var error = new DatabaseError("Can't save the database", 2, err);
        error.inner = err;
        throw error;
    }

}

exports = module.exports = JsonDB;<|endoftext|>


<commit_before>// Copyright 2018, University of Colorado Boulder

/**
 * Model for the 'Standard Form' screen.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const GQModel = require( 'GRAPHING_QUADRATICS/common/model/GQModel' );
  const graphingQuadratics = require( 'GRAPHING_QUADRATICS/graphingQuadratics' );
  const Quadratic = require( 'GRAPHING_QUADRATICS/common/model/Quadratic' );
  const Property = require( 'AXON/Property' );
  const RangeWithValue = require( 'DOT/RangeWithValue' );
  const Vector2 = require( 'DOT/Vector2' );

  // constants
  const P_RANGE = new RangeWithValue( -9, 9, 2 );
  const H_RANGE = new RangeWithValue( -6, 6, 0 );
  const K_RANGE = new RangeWithValue( -6, 6, 0 );
  const POINT_X = 2;

  class FocusAndDirectrixModel extends GQModel {

    /**
     * @param {Object} [options]
     */
    constructor( options ) {

      options = options || {};

      assert && assert( !options.quadratic, 'FocusAndDirectrixModel sets quadratic' );
      options.quadratic = Quadratic.createFromAlternateVertexForm(
        P_RANGE.defaultValue, H_RANGE.defaultValue, K_RANGE.defaultValue, {
          color: 'black'
        } );

      super( options );

      // @public (read-only)
      this.hRange = H_RANGE;
      this.kRange = K_RANGE;
      this.pRange = P_RANGE;

      // @public
      this.pointOnQuadraticProperty =
        new Property( new Vector2( POINT_X, this.quadraticProperty.value.solveY( POINT_X ) ) );

      // update the point
      this.quadraticProperty.link( ( quadratic, oldQuadratic ) => {
        //TODO handle the case of no quadratic
        if ( quadratic && oldQuadratic && quadratic.vertex && oldQuadratic.vertex ) {
          const dx = quadratic.vertex.x - oldQuadratic.vertex.x;
          const x = this.pointOnQuadraticProperty.value.x + dx;
          this.pointOnQuadraticProperty.value = new Vector2( x, quadratic.solveY( x ) );
        }
      } );
    }
  }

  return graphingQuadratics.register( 'FocusAndDirectrixModel', FocusAndDirectrixModel );
} );
<commit_msg>add assertions<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * Model for the 'Standard Form' screen.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const GQModel = require( 'GRAPHING_QUADRATICS/common/model/GQModel' );
  const graphingQuadratics = require( 'GRAPHING_QUADRATICS/graphingQuadratics' );
  const Quadratic = require( 'GRAPHING_QUADRATICS/common/model/Quadratic' );
  const Property = require( 'AXON/Property' );
  const RangeWithValue = require( 'DOT/RangeWithValue' );
  const Vector2 = require( 'DOT/Vector2' );

  // constants
  const P_RANGE = new RangeWithValue( -9, 9, 2 );
  const H_RANGE = new RangeWithValue( -6, 6, 0 );
  const K_RANGE = new RangeWithValue( -6, 6, 0 );
  const POINT_X = 2;

  class FocusAndDirectrixModel extends GQModel {

    /**
     * @param {Object} [options]
     */
    constructor( options ) {

      options = options || {};

      assert && assert( !options.quadratic, 'FocusAndDirectrixModel sets quadratic' );
      options.quadratic = Quadratic.createFromAlternateVertexForm(
        P_RANGE.defaultValue, H_RANGE.defaultValue, K_RANGE.defaultValue, {
          color: 'black'
        } );

      super( options );

      // @public (read-only)
      this.hRange = H_RANGE;
      this.kRange = K_RANGE;
      this.pRange = P_RANGE;

      // @public
      this.pointOnQuadraticProperty =
        new Property( new Vector2( POINT_X, this.quadraticProperty.value.solveY( POINT_X ) ) );

      // update the point
      this.quadraticProperty.lazyLink( ( quadratic, oldQuadratic ) => {

        assert && assert( quadratic.vertex, 'null quadratic.vertex not supported' );
        assert && assert( oldQuadratic.vertex, 'null oldQuadratic.vertex not supported' );

        const dx = quadratic.vertex.x - oldQuadratic.vertex.x;
        const x = this.pointOnQuadraticProperty.value.x + dx;
        this.pointOnQuadraticProperty.value = new Vector2( x, quadratic.solveY( x ) );
      } );
    }
  }

  return graphingQuadratics.register( 'FocusAndDirectrixModel', FocusAndDirectrixModel );
} );
<|endoftext|>


<commit_before>var Downloader = require('downloader');
var InputManager = require('./input');
var $ = require('cheerio');
var Q = require('q');

// input handling
var inputMan = new InputManager();
var argv = inputMan.getArguments();

var logger = require('logger-generator')(inputMan.retrieveLoggerConfig(argv))
var downloader =  new Downloader(logger, [1000, 5000]);

function getFullUrl(pathOrElement) {
  return "http://www.parliament.bg" + (typeof pathOrElement === "string" ? pathOrElement : $(el).attr('href'));
}

function scrapeReport(url, html) {
  var text = $("#leftcontent1 .markcontent", html).text(),
      date = $('#leftcontent1 .dateclass', html).text().trim(),
      committee = $('#leftcontent1 .marktitle', html).contents()[0].data;
      console.log(date);

  return {
    url: url,
    text: text,
    date: date,
    committee: committee
  }
}

function scrapeBills(url, html) {
  var $content = $('table.bills', html),
      gov_id = url.match(/\d+$/g)[0],
      name = $("tr:contains('Име на законопроекта') td strong", $content).eq(0).text(),
      signature = $("tr:contains('Сигнатура') td", $content).eq(1).text(),
      session = $("tr:contains('Сесия') td", $content).eq(1).text(),
      date = $("tr:contains('Дата на постъпване') td", $content).eq(1).text(),
      rtf = $("tr:contains('Текст на законопроекта') td a:contains('RTF')", $content).attr('href'),
      importers = [],
      committees = [],
      reports = [],
      history = [];

  $("tr:contains('Вносители') a", $content).each(function(idx, el) {
    importers.push({
      link: $(el).attr("href"),
      name: $(el).text()
    });
  });

  $("tr:contains('Разпределение по комисии') li", $content).each(function(idx, el) {
    committees.push({
      link: $("a", el).attr("href"),
      name: $("a", el).text(),
      leading: $(el).text().match(/\((.*)\)$/)[1]
    });
  });

  $("tr:contains('Доклади от комисии') a", $content).each(function(idx, el) {
    reports.push($(el).attr("href"));
  });

  $("tr:contains('Хронология') li", $content).each(function(idx, el) {
    var text = $(el).text();
    var status = text.match(/\d{2}\/\d{2}\/\d{4} - (.*)/)[1];
    var date = text.match(/\d{2}\/\d{2}\/\d{4}/)[0].replace(/(\d{2})\/(\d{2})\/(\d{4})/, function(match, d, m, y) { return [y, m, d].join("-") });

    history.push({
      date: date,
      status: status
    });
  });

  promisedReports = reports.map(function(report_path) {
    var report_url = getFullUrl(report_path);
    return downloader.get(report_url).then(function(html) { return scrapeReport(report_url, html) },
                                           function(error) { logger.error(error) });
  });

  // when reports are fetched - assemble the final results and print to STDOUT.
  Q.all(promisedReports).done(function(results) {

    console.log(JSON.stringify({
      gov_id: gov_id,
      name: name,
      date: date,
      signature: signature,
      session: session,
      importers: importers,
      committees: committees,
      reports: results,
      history: history,
      rtf: rtf
    }));

  }, function(error) { logger.error(error) });

}

function scrapeBillURLS(html) {
  $("#monthview ul.frontList li a", html).each(function(idx, el) {
    var url = getFullUrl(el)
    downloader.get(url, function(html) {
      scrapeBills(url, html);
    });
  });
}

function scrapeMonths(html) {
  $(".calendar_columns a", html).each(function(idx, el) {
    var url = getFullUrl(el)
    downloader.get(url, scrapeBillURLS);
  });
}

function init() {
  downloader.get(argv.url, scrapeMonths);
  //downloader.get("http://www.parliament.bg/bg/bills/ID/14141", function(html) { scrapeBills("http://www.parliament.bg/bg/bills/ID/14141", html) } )
}

init();

<commit_msg>Made cosmetic changes to fetcher for bills.<commit_after>var Downloader = require('downloader');
var InputManager = require('./input');
var $ = require('cheerio');
var Q = require('q');

// input handling
var inputMan = new InputManager();
var argv = inputMan.getArguments();

var logger = require('logger-generator')(inputMan.retrieveLoggerConfig(argv))
var downloader =  new Downloader(logger, [1000, 5000]);

function getFullUrl(pathOrElement) {
  return "http://www.parliament.bg" + (typeof pathOrElement === "string" ? pathOrElement : $(el).attr('href'));
}

function scrapeReport(url, html) {
  var text = $("#leftcontent1 .markcontent", html).text(),
      date = $('#leftcontent1 .dateclass', html).text().trim(),
      committee = $('#leftcontent1 .marktitle', html).contents()[0].data;

  return {
    url: url,
    text: text,
    date: date,
    committee: committee
  }
}

function scrapeBills(url, html) {
  var $content = $('table.bills', html),
      gov_id = url.match(/\d+$/g)[0],
      name = $("tr:contains('Име на законопроекта') td strong", $content).eq(0).text(),
      signature = $("tr:contains('Сигнатура') td", $content).eq(1).text(),
      session = $("tr:contains('Сесия') td", $content).eq(1).text(),
      date = $("tr:contains('Дата на постъпване') td", $content).eq(1).text(),
      rtf = $("tr:contains('Текст на законопроекта') td a:contains('RTF')", $content).attr('href'),
      importers = [],
      committees = [],
      reports = [],
      history = [];

  $("tr:contains('Вносители') a", $content).each(function(idx, el) {
    importers.push({
      link: $(el).attr("href"),
      name: $(el).text()
    });
  });

  $("tr:contains('Разпределение по комисии') li", $content).each(function(idx, el) {
    committees.push({
      link: $("a", el).attr("href"),
      name: $("a", el).text(),
      leading: $(el).text().match(/\((.*)\)$/)[1]
    });
  });

  $("tr:contains('Доклади от комисии') a", $content).each(function(idx, el) {
    reports.push($(el).attr("href"));
  });

  $("tr:contains('Хронология') li", $content).each(function(idx, el) {
    var text = $(el).text();
    var status = text.match(/\d{2}\/\d{2}\/\d{4} - (.*)/)[1];
    var date = text.match(/\d{2}\/\d{2}\/\d{4}/)[0]
      .replace(/(\d{2})\/(\d{2})\/(\d{4})/, function(match, d, m, y) { return [y, m, d].join("-") });

    history.push({
      date: date,
      status: status
    });
  });

  promisedReports = reports.map(function(report_path) {
    var report_url = getFullUrl(report_path);
    return downloader.get(report_url).then(function(html) { return scrapeReport(report_url, html) },
                                           logger.error);
  });

  // when reports are fetched - assemble the final results and print to STDOUT.
  Q.all(promisedReports).done(function(results) {

    console.log(JSON.stringify({
      gov_id: gov_id,
      name: name,
      date: date,
      signature: signature,
      session: session,
      importers: importers,
      committees: committees,
      reports: results,
      history: history,
      rtf: rtf
    }));

  }, logger.error);

}

function scrapeBillURLS(html) {
  $("#monthview ul.frontList li a", html).each(function(idx, el) {
    var url = getFullUrl(el);
    downloader.get(url, function(html) {
      scrapeBills(url, html);
    });
  });
}

function scrapeMonths(html) {
  $(".calendar_columns a", html).each(function(idx, el) {
    var url = getFullUrl(el);
    downloader.get(url, scrapeBillURLS);
  });
}

function init() {
  downloader.get(argv.url, scrapeMonths);
  //downloader.get("http://www.parliament.bg/bg/bills/ID/14141", function(html) { scrapeBills("http://www.parliament.bg/bg/bills/ID/14141", html) } )
}

init();

<|endoftext|>


<commit_before>/*

    Easy widget to serve as a tabbed container.

    var $tabs =  new kbaseTabs($('#tabs'), {
            tabPosition : 'bottom', //or left or right or top. Defaults to 'top'
            canDelete : true,       //whether or not the tab can be removed. Defaults to false.
            tabs : [
                {
                    tab : 'T1',                                     //name of the tab
                    content : $('

').html("I am a tab"),  //jquery object to stuff into the content
                    canDelete : false,                              //override the canDelete param on a per tab basis
                },
                {
                    tab : 'T2',
                    content : $('

').html("I am a tab 2"),
                },
                {
                    tab : 'T3',
                    content : $('

').html("I am a tab 3"),
                    show : true,                                    //boolean. This tab gets shown by default. If not specified, the first tab is shown
                    showContentCallback: function // if you don't want to show the content right away, add a callback method that returns the content...
                },
            ],
        }
    );

    useful methods would be:

    $('#tabs').kbaseTabs('showTab', 'T1');
    $('#tabs').kbaseTabs('addTab', tabObject);  //the tabObject defined up above

*/

define(['kbwidget', 'bootstrap', 'jquery', 'kbaseDeletePrompt'], (
    KBWidget,
    bootstrap,
    $,
    kbaseDeletePrompt
) => {
    'use strict';
    return KBWidget({
        name: 'kbaseTabs',

        version: '1.0.0',

        _accessors: ['tabsHeight'],

        options: {
            tabPosition: 'top',
            canDelete: false,
            borderColor: 'lightgray',
        },

        init: function (options) {
            this._super(options);

            this.data('tabs', {});
            this.data('nav', {});

            this.appendUI($(this.$elem));

            return this;
        },

        appendUI: function ($elem, tabs) {
            if (tabs == undefined) {
                tabs = this.options.tabs;
            }

            const $block = $('

').addClass('tabbable');
            const $tabs = $('

')
                .addClass('tab-content')
                .attr('id', 'tabs-content')
                .css('height', this.tabsHeight());
            const $nav = $('

')
                .addClass('nav nav-tabs')
                .attr('id', 'tabs-nav');
            $block.append($nav).append($tabs);
            /*if (this.options.tabPosition == 'top') {
                $block.addClass('tabs-above');
                $block.append($nav).append($tabs);
            }
            else if (this.options.tabPosition == 'bottom') {
                $block.addClass('tabs-below');
                $block.append($tabs).append($nav);
            }
            else if (this.options.tabPosition == 'left') {
                $block.addClass('tabs-left');
                $block.append($nav).append($tabs);
            }
            else if (this.options.tabPosition == 'right') {
                $block.addClass('tabs-right');
                $block.append($tabs).append($nav);
            }*/

            this._rewireIds($block, this);

            $elem.append($block);

            if (tabs) {
                $.each(
                    tabs,
                    $.proxy(function (idx, tab) {
                        this.addTab(tab);
                    }, this)
                );
            }
        },

        addTab: function (tab) {
            if (tab.canDelete == undefined) {
                tab.canDelete = this.options.canDelete;
            }

            const $tab = $('

').addClass('tab-pane fade');

            $tab.hasContent = false;
            if (tab.content) {
                $tab.append(tab.content);
                $tab.hasContent = true;
            }

            if (this.options.border) {
                $tab.css('border', 'solid ' + this.options.borderColor);
                $tab.css('border-width', '0px 1px 0px 1px');
                $tab.css('padding', '3px');
            }

            const $that = this; //thanks bootstrap! You suck!

            const $nav = $('

')
                .css('white-space', 'nowrap')
                .append(
                    $('')
                        .attr('href', '#')
                        .text(tab.tab)
                        .attr('data-tab', tab.tab)
                        .bind('click', function (e) {
                            e.preventDefault();
                            e.stopPropagation();

                            const previous = $that.data('tabs-nav').find('.active:last a')[0];

                            //we can't just call 'show' directly, since it requires an href or data-target attribute
                            //on the link which MUST be an idref to something else in the dom. We don't have those,
                            //so we just do what show does and call activate directly.
                            //
                            //oh, but we can't just say $(this).tab('activate',...) because bootstrap is specifically
                            //wired up not to pass along any arguments to methods invoked in this manner.
                            //
                            //Because bootstrap -sucks-.
                            $.fn.tab.Constructor.prototype.activate.call(
                                $(this),
                                $(this).parent('li'),
                                $that.data('tabs-nav')
                            );

                            $.fn.tab.Constructor.prototype.activate.call(
                                $(this),
                                $tab,
                                $tab.parent(),
                                function () {
                                    $(this).trigger({
                                        type: 'shown',
                                        relatedTarget: previous,
                                    });
                                }
                            );
                            if (!$tab.hasContent) {
                                if (tab.showContentCallback) {
                                    $tab.append(tab.showContentCallback($tab));
                                    if (!tab.dynamicContent) {
                                        $tab.hasContent = true;
                                    }
                                }
                            }
                        })
                        .append(
                            $('<button></button>')
                                .addClass('btn btn-default btn-xs')
                                //.append($('').addClass(this.closeIcon()))
                                .append($('').append('✖'))
                                .css('padding', '0px')
                                .css('width', '12px')
                                .css('height', '12px')
                                .css('margin-left', '10px')
                                .css('font-size', '10px')
                                .css('margin-bottom', '3px')
                                .css('border', '0')
                                //.attr('title', this.deleteTabToolTip(tab.tab))
                                .tooltip()
                                .bind(
                                    'click',
                                    $.proxy(function (e) {
                                        e.preventDefault();
                                        e.stopPropagation();

                                        if (tab.deleteCallback != undefined) {
                                            tab.deleteCallback(tab.tab);
                                        } else {
                                            this.deletePrompt(tab.tab);
                                        }
                                    }, this)
                                )
                        )
                );
            if (!tab.canDelete) {
                $nav.find('button').remove();
            }

            this.data('tabs')[tab.tab] = $tab;
            this.data('nav')[tab.tab] = $nav;

            this.data('tabs-content').append($tab);
            this.data('tabs-nav').append($nav);

            const tabCount = this.data('tabs').length;

            if (tab.show || tabCount == 1) {
                this.showTab(tab.tab);
            }
        },

        closeIcon: function () {
            return 'icon-remove';
        },

        deleteTabToolTip: function (tabName) {
            return 'Remove ' + tabName;
        },

        hasTab: function (tabName) {
            return this.data('tabs')[tabName];
        },

        showTab: function (tab) {
            this.data('nav')[tab].find('a').trigger('click');
        },

        removeTab: function (tabName) {
            const $tab = this.data('tabs')[tabName];
            const $nav = this.data('nav')[tabName];

            if ($nav.hasClass('active')) {
                if ($nav.next('li').length) {
                    $nav.next().find('a').trigger('click');
                } else {
                    $nav.prev('li').find('a').trigger('click');
                }
            }

            $tab.remove();
            $nav.remove();

            this.data('tabs')[tabName] = undefined;
            this.data('nav')[tabName] = undefined;
        },

        deletePrompt: function (tabName) {
            const $deleteModal = new kbaseDeletePrompt($('

'), {
                name: tabName,
                callback: this.deleteTabCallback(tabName),
            });
            $deleteModal.openPrompt();
        },

        deleteTabCallback: function (tabName) {
            return $.proxy(function (e, $prompt) {
                if ($prompt != undefined) {
                    $prompt.closePrompt();
                }

                this.removeTab(tabName);
            }, this);
        },

        activeTab: function () {
            const activeNav = this.data('tabs-nav').find('.active:last a')[0];
            return $(activeNav).attr('data-tab');
        },
    });
});
<commit_msg>Add "onShown" tab method to allow for updating a tab upon activation<commit_after>/*

    Easy widget to serve as a tabbed container.

    var $tabs =  new kbaseTabs($('#tabs'), {
            tabPosition : 'bottom', //or left or right or top. Defaults to 'top'
            canDelete : true,       //whether or not the tab can be removed. Defaults to false.
            tabs : [
                {
                    tab : 'T1',                                     //name of the tab
                    content : $('

').html("I am a tab"),  //jquery object to stuff into the content
                    canDelete : false,                              //override the canDelete param on a per tab basis
                },
                {
                    tab : 'T2',
                    content : $('

').html("I am a tab 2"),
                },
                {
                    tab : 'T3',
                    content : $('

').html("I am a tab 3"),
                    show : true,                                    //boolean. This tab gets shown by default. If not specified, the first tab is shown
                    showContentCallback: function // if you don't want to show the content right away, add a callback method that returns the content...
                },
            ],
        }
    );

    useful methods would be:

    $('#tabs').kbaseTabs('showTab', 'T1');
    $('#tabs').kbaseTabs('addTab', tabObject);  //the tabObject defined up above

*/

define(['kbwidget', 'bootstrap', 'jquery', 'kbaseDeletePrompt'], (
    KBWidget,
    bootstrap,
    $,
    kbaseDeletePrompt
) => {
    'use strict';
    return KBWidget({
        name: 'kbaseTabs',

        version: '1.0.0',

        _accessors: ['tabsHeight'],

        options: {
            tabPosition: 'top',
            canDelete: false,
            borderColor: 'lightgray',
        },

        init: function (options) {
            this._super(options);

            this.data('tabs', {});
            this.data('nav', {});

            this.appendUI($(this.$elem));

            return this;
        },

        appendUI: function ($elem, tabs) {
            if (tabs == undefined) {
                tabs = this.options.tabs;
            }

            const $block = $('

').addClass('tabbable');
            const $tabs = $('

')
                .addClass('tab-content')
                .attr('id', 'tabs-content')
                .css('height', this.tabsHeight());
            const $nav = $('

')
                .addClass('nav nav-tabs')
                .attr('id', 'tabs-nav');
            $block.append($nav).append($tabs);
            /*if (this.options.tabPosition == 'top') {
                $block.addClass('tabs-above');
                $block.append($nav).append($tabs);
            }
            else if (this.options.tabPosition == 'bottom') {
                $block.addClass('tabs-below');
                $block.append($tabs).append($nav);
            }
            else if (this.options.tabPosition == 'left') {
                $block.addClass('tabs-left');
                $block.append($nav).append($tabs);
            }
            else if (this.options.tabPosition == 'right') {
                $block.addClass('tabs-right');
                $block.append($tabs).append($nav);
            }*/

            this._rewireIds($block, this);

            $elem.append($block);

            if (tabs) {
                $.each(
                    tabs,
                    $.proxy(function (idx, tab) {
                        this.addTab(tab);
                    }, this)
                );
            }
        },

        addTab: function (tab) {
            if (tab.canDelete == undefined) {
                tab.canDelete = this.options.canDelete;
            }

            const $tab = $('

').addClass('tab-pane fade');

            $tab.hasContent = false;
            if (tab.content) {
                $tab.append(tab.content);
                $tab.hasContent = true;
            }

            if (this.options.border) {
                $tab.css('border', 'solid ' + this.options.borderColor);
                $tab.css('border-width', '0px 1px 0px 1px');
                $tab.css('padding', '3px');
            }

            const $that = this; //thanks bootstrap! You suck!

            const $nav = $('

')
                .css('white-space', 'nowrap')
                .append(
                    $('')
                        .attr('href', '#')
                        .text(tab.tab)
                        .attr('data-tab', tab.tab)
                        .bind('click', function (e) {
                            e.preventDefault();
                            e.stopPropagation();

                            const previous = $that.data('tabs-nav').find('.active:last a')[0];

                            //we can't just call 'show' directly, since it requires an href or data-target attribute
                            //on the link which MUST be an idref to something else in the dom. We don't have those,
                            //so we just do what show does and call activate directly.
                            //
                            //oh, but we can't just say $(this).tab('activate',...) because bootstrap is specifically
                            //wired up not to pass along any arguments to methods invoked in this manner.
                            //
                            //Because bootstrap -sucks-.
                            $.fn.tab.Constructor.prototype.activate.call(
                                $(this),
                                $(this).parent('li'),
                                $that.data('tabs-nav')
                            );

                            $.fn.tab.Constructor.prototype.activate.call(
                                $(this),
                                $tab,
                                $tab.parent(),
                                function () {
                                    $(this).trigger({
                                        type: 'shown',
                                        relatedTarget: previous,
                                    });
                                }
                            );
                            if (!$tab.hasContent) {
                                if (tab.showContentCallback) {
                                    $tab.append(tab.showContentCallback($tab));
                                    if (!tab.dynamicContent) {
                                        $tab.hasContent = true;
                                    }
                                }
                            }
                            if (tab.onShown) {
                                try {
                                    tab.onShown();
                                } catch (ex) {
                                    console.error('Error in "onShown"', ex);
                                }
                            }
                        })
                        .append(
                            $('<button></button>')
                                .addClass('btn btn-default btn-xs')
                                //.append($('').addClass(this.closeIcon()))
                                .append($('').append('✖'))
                                .css('padding', '0px')
                                .css('width', '12px')
                                .css('height', '12px')
                                .css('margin-left', '10px')
                                .css('font-size', '10px')
                                .css('margin-bottom', '3px')
                                .css('border', '0')
                                //.attr('title', this.deleteTabToolTip(tab.tab))
                                .tooltip()
                                .bind(
                                    'click',
                                    $.proxy(function (e) {
                                        e.preventDefault();
                                        e.stopPropagation();

                                        if (tab.deleteCallback != undefined) {
                                            tab.deleteCallback(tab.tab);
                                        } else {
                                            this.deletePrompt(tab.tab);
                                        }
                                    }, this)
                                )
                        )
                );
            if (!tab.canDelete) {
                $nav.find('button').remove();
            }

            this.data('tabs')[tab.tab] = $tab;
            this.data('nav')[tab.tab] = $nav;

            this.data('tabs-content').append($tab);
            this.data('tabs-nav').append($nav);

            const tabCount = this.data('tabs').length;

            if (tab.show || tabCount == 1) {
                this.showTab(tab.tab);
            }
        },

        closeIcon: function () {
            return 'icon-remove';
        },

        deleteTabToolTip: function (tabName) {
            return 'Remove ' + tabName;
        },

        hasTab: function (tabName) {
            return this.data('tabs')[tabName];
        },

        showTab: function (tab) {
            this.data('nav')[tab].find('a').trigger('click');
        },

        removeTab: function (tabName) {
            const $tab = this.data('tabs')[tabName];
            const $nav = this.data('nav')[tabName];

            if ($nav.hasClass('active')) {
                if ($nav.next('li').length) {
                    $nav.next().find('a').trigger('click');
                } else {
                    $nav.prev('li').find('a').trigger('click');
                }
            }

            $tab.remove();
            $nav.remove();

            this.data('tabs')[tabName] = undefined;
            this.data('nav')[tabName] = undefined;
        },

        deletePrompt: function (tabName) {
            const $deleteModal = new kbaseDeletePrompt($('

'), {
                name: tabName,
                callback: this.deleteTabCallback(tabName),
            });
            $deleteModal.openPrompt();
        },

        deleteTabCallback: function (tabName) {
            return $.proxy(function (e, $prompt) {
                if ($prompt != undefined) {
                    $prompt.closePrompt();
                }

                this.removeTab(tabName);
            }, this);
        },

        activeTab: function () {
            const activeNav = this.data('tabs-nav').find('.active:last a')[0];
            return $(activeNav).attr('data-tab');
        },
    });
});
<|endoftext|>


<commit_before>const readline = require('readline');
const fs = require('fs');
const path = require("path");

const MAX_LINE_LENGTH = 80;
let tabString;

// Go through all lines in a file, formatting them if needed and appending
// them to a temporary file. When done, replaces the original file with the
// temporary one
function formatFile(file, tabSize = 4) {
  tabString = Array(parseInt(tabSize) + 1).join(" ");
  const rl = readline.createInterface({
    input: fs.createReadStream(file)
  });
  const folder = file.replace(path.basename(file), "");
  let neededFormatting = false;

  rl.on('line', (line) => {
    const lines = trimLine(line, []);
    if(lines.length > 1) {
      neededFormatting = true;
    }
    lines.forEach((newLine, i) => {
      fs.appendFileSync(folder + 'output.tmp', newLine + "\n");
    })
  });

  rl.on('close', () => {
    console.log(`${neededFormatting ? "Formatted" : "No formatting needed for"} ${file}`);
    fs.renameSync(folder + 'output.tmp', file);
  });
}

// Recursive function taking one line as an input and returning and array of lines
// broken down below a MAX_LINE_LENGTH and according to Java, C# and PHP standards
function trimLine(line, lines) {
  const relativeMaxLineLength = getLineMaxLength(lines, line);
  if(line.length > relativeMaxLineLength) {
    let lastBreakingChar = relativeMaxLineLength;
    let previousLastBreakingChar;
    let trimmedLine = line;
    do {
      trimmedLine = trimmedLine.substring(0, lastBreakingChar);
      previousLastBreakingChar = lastBreakingChar;
      lastBreakingChar = getLastBreakingChar(trimmedLine);
    } while(lastBreakingChar > 0 &&
      lastBreakingChar != previousLastBreakingChar &&
      !isBreakingCharOutsideQuotes(trimmedLine, lastBreakingChar));

    if(!isBreakingCharOutsideQuotes(line, lastBreakingChar) || lastBreakingChar <= 0) {
      storeCurrentLine(lines, line);
      return lines;
    }

    if(line.charAt(lastBreakingChar) == ',') {
      lastBreakingChar++;
    }

    storeCurrentLine(lines, line.substring(0, lastBreakingChar));

    return trimLine(line.substring(lastBreakingChar, line.length).trim(), lines);
  } else {
    storeCurrentLine(lines, line);
    return lines;
  }
}

function storeCurrentLine(lines, line) {
  lines.push(tabLineIfNeeded(lines, line));
}

// Normalize max line length for different line indentations
function getLineMaxLength(lines, line) {
  return lines.length == 0 ? MAX_LINE_LENGTH : MAX_LINE_LENGTH - getTab(lines[0]).length;
}

function isBreakingCharOutsideQuotes(line, lastIndexOf) {
  const trimmmedLine = line.substring(0, lastIndexOf)
  const evenDoubleQuotes = isEvenNumberOfCharacters(trimmmedLine, "'");
  const evenSingleQuotes = isEvenNumberOfCharacters(trimmmedLine, '"');
  return evenSingleQuotes && evenDoubleQuotes;
}

function isEvenNumberOfCharacters(line, char) {
  const regex = new RegExp(char, 'gi');
  return (line.match(regex) || []).length % 2 == 0;
}

function getLastBreakingChar(line) {
  return Math.max(line.lastIndexOf("."), line.lastIndexOf(","));
}

function tabLineIfNeeded(lines, line) {
  return lines.length > 0 ? getTab(lines[0]) + line : line;
}

// Get tab spaces considering already formatted lines
function getTab(line) {
  const baseTab = line.match(/^ */);
  return line.trim().charAt(0) == "." ? baseTab : baseTab + tabString;
}

module.exports.formatFile = formatFile;
module.exports.trimLine = trimLine;
<commit_msg>Fix code-sample-formatter default tab size parameter<commit_after>const readline = require('readline');
const fs = require('fs');
const path = require("path");

const MAX_LINE_LENGTH = 80;
const DEFAULT_TAB_SIZE = 4;
let tabString;

// Go through all lines in a file, formatting them if needed and appending
// them to a temporary file. When done, replaces the original file with the
// temporary one
function formatFile(file, tabSize) {
  tabString = Array(parseInt(tabSize || DEFAULT_TAB_SIZE) + 1).join(" ");
  const rl = readline.createInterface({
    input: fs.createReadStream(file)
  });
  const folder = file.replace(path.basename(file), "");
  let neededFormatting = false;

  rl.on('line', (line) => {
    const lines = trimLine(line, []);
    if(lines.length > 1) {
      neededFormatting = true;
    }
    lines.forEach((newLine, i) => {
      fs.appendFileSync(folder + 'output.tmp', newLine + "\n");
    })
  });

  rl.on('close', () => {
    console.log(`${neededFormatting ? "Formatted" : "No formatting needed for"} ${file}`);
    fs.renameSync(folder + 'output.tmp', file);
  });
}

// Recursive function taking one line as an input and returning and array of lines
// broken down below a MAX_LINE_LENGTH and according to Java, C# and PHP standards
function trimLine(line, lines) {
  const relativeMaxLineLength = getLineMaxLength(lines, line);
  if(line.length > relativeMaxLineLength) {
    let lastBreakingChar = relativeMaxLineLength;
    let previousLastBreakingChar;
    let trimmedLine = line;
    do {
      trimmedLine = trimmedLine.substring(0, lastBreakingChar);
      previousLastBreakingChar = lastBreakingChar;
      lastBreakingChar = getLastBreakingChar(trimmedLine);
    } while(lastBreakingChar > 0 &&
      lastBreakingChar != previousLastBreakingChar &&
      !isBreakingCharOutsideQuotes(trimmedLine, lastBreakingChar));

    if(!isBreakingCharOutsideQuotes(line, lastBreakingChar) || lastBreakingChar <= 0) {
      storeCurrentLine(lines, line);
      return lines;
    }

    if(line.charAt(lastBreakingChar) == ',') {
      lastBreakingChar++;
    }

    storeCurrentLine(lines, line.substring(0, lastBreakingChar));

    return trimLine(line.substring(lastBreakingChar, line.length).trim(), lines);
  } else {
    storeCurrentLine(lines, line);
    return lines;
  }
}

function storeCurrentLine(lines, line) {
  lines.push(tabLineIfNeeded(lines, line));
}

// Normalize max line length for different line indentations
function getLineMaxLength(lines, line) {
  return lines.length == 0 ? MAX_LINE_LENGTH : MAX_LINE_LENGTH - getTab(lines[0]).length;
}

function isBreakingCharOutsideQuotes(line, lastIndexOf) {
  const trimmmedLine = line.substring(0, lastIndexOf)
  const evenDoubleQuotes = isEvenNumberOfCharacters(trimmmedLine, "'");
  const evenSingleQuotes = isEvenNumberOfCharacters(trimmmedLine, '"');
  return evenSingleQuotes && evenDoubleQuotes;
}

function isEvenNumberOfCharacters(line, char) {
  const regex = new RegExp(char, 'gi');
  return (line.match(regex) || []).length % 2 == 0;
}

function getLastBreakingChar(line) {
  return Math.max(line.lastIndexOf("."), line.lastIndexOf(","));
}

function tabLineIfNeeded(lines, line) {
  return lines.length > 0 ? getTab(lines[0]) + line : line;
}

// Get tab spaces considering already formatted lines
function getTab(line) {
  const baseTab = line.match(/^ */);
  return line.trim().charAt(0) == "." ? baseTab : baseTab + tabString;
}

module.exports.formatFile = formatFile;
module.exports.trimLine = trimLine;
<|endoftext|>