<commit_before>var events = require('events');
var url = require('url');
var _ = require('lodash');
var RangeURL = require('./RangeURL.js');
var UserAgents = require('./UserAgents.json');
var ResourcePool = require('./ResourcePool.js');
var PhantomClient = require('./client/PhantomClient.js');
var CheerioClient = require('./client/CheerioClient.js');

// Brain is all of the settings you may want to override

var DefaultBrain = {
	// {int} Max number of connections
	legs: 10,
	// {int} Max number of requests to process on one connection
	//   before retiring the connection, and opening a fresh one
	legAge: 10,
	// {bool} If we should remove '#' Anchors from links
	trimHashes: true,
	// {bool} If we should skip Duplicate URI's we see
	skipDupes: true,
	// {bool} If we should stay on the base uri path
	stayInRange: false,
	// {string} User agent to use in requests
	//          We have several presets in UserAgents.json
	userAgent: 'Google Chrome 32 (Mac)',
	// {object} any other headers you might need
	headers: {},
	// {string} You may specify 'PhantomJS' or 'Cheerio'
	leg: 'Cheerio',
	// {string} If you need a proxy, specify domain and port "127.0.0.1:8080"
	// Sorry, no auth support yet
	proxy: '',
	// TODO: http proxy-auth supported by Request and PhatonmJS
	// TODO: PhatomJS ignore-ssl-errors + Request strictSSL
};
function Tarantula (brain) {
	this.brain = _.merge({}, DefaultBrain, brain);
	this.uris = [];
	this.visited = 0;
	this.range = new RangeURL();
	this.legs = new ResourcePool({
		max: this.brain.legs,
		maxUses: this.brain.legAge,
		create: this.growLeg.bind(this),
		destroy: this.shedLeg.bind(this),
		task: this.follow.bind(this),
		complete: this.complete.bind(this),
	});
	if (this.brain.shouldVisit) {
		this.shouldVisit = this.brain.shouldVisit;
	}
	if (this.brain.userAgent) {
		this.brain.headers['User-Agent'] = UserAgents[this.brain.userAgent] || this.brain.userAgent;
	}

	this.on('data', function (task, uris, pageUri) {
		this.push(uris, pageUri, task);
	});
}
Tarantula.prototype = new events.EventEmitter();

// Public Functions
// You probably Do want to override as an implementor

/**
 * How to Scrape the given page
 *
 * In Parse mode
 *   You may not access "client-side" vars (document, window)
 *   You may access server globals.
 *
 * In PhantomJS mode
 *   You may access "client-side" vars (document, window)
 *   You may not access server globals.
 *
 * @param {jQuery} $ - A jQuery-like interface scoped to page
 * @param {string} pageUri - The URL we are visiting
 * @return {string[]} - A list of urls to crawl
 */
Tarantula.prototype.visit = function ($, pageUri) {
	var elements = $('a').toArray();
	var uris = new Array(elements.length);
	// return elements.length;
	var element;
	var uri;
	// return 'i hate you';
	for (var i = 0; i < elements.length; i++) {
		element = elements[i];
		uri = $(element).attr('href');
		uris[i] = uri;
	}
	return uris;
};
/**
 * Determine if Tarantula should visit this uri
 *
 * @public
 * @param {string} pageUri
 *   The URL we are considering visiting
 * @return {bool}
 *   True, if Tarantula should visit the URI
 */
Tarantula.prototype.shouldVisit = function (pageUri) {
	if (pageUri) {
		return true;
	}
};

// Private Functions
// You probably don't want to override as an implementor

/**
 * Tell the Tarantula to crawl the site
 *
 * @param {string[]} frontier
 *   Optional list of URI's to enqueue
 */
Tarantula.prototype.start = function (frontier) {
	this.push(frontier);
};
/**
 * Add more URIs for our crawler to visit
 *
 * @param {string|string[]} uris
 *   A list of URI's we intend to visit later
 * @param {string} pageUri
 *   The URI where these were found
 * @param {object} parentTask
 *   (optional) If not passed, we assume a root task
 * @param {string} parentTask.uri
 *   (optional) The URI where these were found
 * @param {string} parentTask.parent
 *   (optional) The URI where parentTask.uri was found
 * @emits "uris": with {string[]} uris, {string} parent
 *   If new URI's are enqueued
 * @return {string[]}
 *   New URI's that were pushed to the stack
 */
Tarantula.prototype.push = function (uris, pageUri, parentTask) {
	// 1. Clean-up partial urls
	if (pageUri) {
		uris = uris.map(function (uri) {
			if (uri) {
				return url.resolve(pageUri, uri);
			}
		});
	}
	// 2. Filter any empty URL's (happens)
	uris = _.filter(uris);
	// 3. Clean-up mailto: and javascript:
	uris = _.filter(uris, isValidUrl);
	// 4. Optionally, remove any anchor tags
	//    ex: http://example.com/#anchor
	if (this.brain.trimHashes) {
		uris = uris.map(trimHash);
	}
	// 5. Optionally, skip duplicate URL's
	//    otherwise, we'll likely get stuck in a loop
	if (this.brain.skipDupes) {
		uris = _.difference(_.unique(uris), this.uris);
	}
	// 6. Optionally, stay in range of the base (range) URL's
	//    Don't go to google.com, if we're on example.com
	if (this.brain.stayInRange && parentTask) {
		uris = uris.filter(this.range.test);
	}
	// 7. Optional, custom logic from the implementor
	if (this.shouldVisit) {
		uris = uris.filter(this.shouldVisit);
	}
	// Finally, If we've got new URI's, then queue them up
	if (uris.length) {
		var tasks = uris.map(function (uri) {
			return {
				uri: uri,
				parent: pageUri
			};
		});
		this.uris.push.apply(this.uris, uris);
		this.legs.enqueue(tasks);
		if (!parentTask) {
			this.range.push.apply(this.range, uris);
		}
		else {
			this.emit('uris', parentTask, uris.length);
		}
	}
	return uris;
};
/**
 * Tells the Tarantula to how to make a new leg
 */
Tarantula.prototype.growLeg = function () {
	if (this.brain.leg === 'PhantomJS') {
		return new PhantomClient();
	}
	else {
		return new CheerioClient();
	}
};
/**
 * Tells the Tarantula to how to make a new leg
 */
Tarantula.prototype.shedLeg = function (leg) {
	leg.destroy();
};
/**
/**
 * Logic to process a given task
 *
 * @param {object} task
 * @param {string} task.uri
 *   The URI to process
 * @param {Client} leg
 *   Instance of a leg (aka "Client")
 * @param {function} release
 *   Will release the leg back to the pool
 */
Tarantula.prototype.follow = function (task, leg) {
	this.emit('follow', task.uri);
	leg
		.setHeaders(this.brain.headers)
		.setProxy(this.brain.proxy)
		.setListener(this.legEvent.bind(this, leg, task))
		.get(task.uri, this.visit);
};
Tarantula.prototype.legEvent = function (leg, task, eventName) {
	var args = Array.prototype.slice.call(arguments, 3);
	args.unshift(eventName, task);
	this.emit.apply(this, args);
	if (eventName === 'data' || eventName === 'error') {
		this.legs.release(leg);
		this.visited++;
	}
};
Tarantula.prototype.complete = function () {
	this.emit('done');
};
/**
 * Remove Hash part of a URL
 *
 * @param {string} uri
 *   The URL to remove hash from
 * @param {string}
 *   The URL without hash
 */
function trimHash (uri) {
	var indexOfHash = uri.indexOf('#');
	return (indexOfHash != -1) ? uri.substring(0, indexOfHash) : uri;
}
function isValidUrl (uri) {
	return (uri.indexOf('http:') === 0 || uri.indexOf('https:') === 0);
}

module.exports = Tarantula;
<commit_msg>Keep backwards compat, allow brain functions to transfer<commit_after>var events = require('events');
var url = require('url');
var _ = require('lodash');
var RangeURL = require('./RangeURL.js');
var UserAgents = require('./UserAgents.json');
var ResourcePool = require('./ResourcePool.js');
var PhantomClient = require('./client/PhantomClient.js');
var CheerioClient = require('./client/CheerioClient.js');

// Brain is all of the settings you may want to override

var DefaultBrain = {
	// {int} Max number of connections
	legs: 10,
	// {int} Max number of requests to process on one connection
	//   before retiring the connection, and opening a fresh one
	legAge: 10,
	// {bool} If we should remove '#' Anchors from links
	trimHashes: true,
	// {bool} If we should skip Duplicate URI's we see
	skipDupes: true,
	// {bool} If we should stay on the base uri path
	stayInRange: false,
	// {string} User agent to use in requests
	//          We have several presets in UserAgents.json
	userAgent: 'Google Chrome 32 (Mac)',
	// {object} any other headers you might need
	headers: {},
	// {string} You may specify 'PhantomJS' or 'Cheerio'
	leg: 'Cheerio',
	// {string} If you need a proxy, specify domain and port "127.0.0.1:8080"
	// Sorry, no auth support yet
	proxy: '',
	// TODO: http proxy-auth supported by Request and PhatonmJS
	// TODO: PhatomJS ignore-ssl-errors + Request strictSSL
};
function Tarantula (brain) {
	this.brain = _.merge({}, DefaultBrain, brain);
	this.uris = [];
	this.visited = 0;
	this.range = new RangeURL();
	this.legs = new ResourcePool({
		max: this.brain.legs,
		maxUses: this.brain.legAge,
		create: this.growLeg.bind(this),
		destroy: this.shedLeg.bind(this),
		task: this.follow.bind(this),
		complete: this.complete.bind(this),
	});
	if (this.brain.shouldVisit) {
		this.shouldVisit = this.brain.shouldVisit;
	}
	if (this.brain.visit) {
		this.visit = this.brain.visit;
	}
	if (this.brain.userAgent) {
		this.brain.headers['User-Agent'] = UserAgents[this.brain.userAgent] || this.brain.userAgent;
	}

	this.on('data', function (task, uris, pageUri) {
		this.push(uris, pageUri, task);
	});
}
Tarantula.prototype = new events.EventEmitter();

// Public Functions
// You probably Do want to override as an implementor

/**
 * How to Scrape the given page
 *
 * In Parse mode
 *   You may not access "client-side" vars (document, window)
 *   You may access server globals.
 *
 * In PhantomJS mode
 *   You may access "client-side" vars (document, window)
 *   You may not access server globals.
 *
 * @param {jQuery} $ - A jQuery-like interface scoped to page
 * @param {string} pageUri - The URL we are visiting
 * @return {string[]} - A list of urls to crawl
 */
Tarantula.prototype.visit = function ($, pageUri) {
	var elements = $('a').toArray();
	var uris = new Array(elements.length);
	// return elements.length;
	var element;
	var uri;
	// return 'i hate you';
	for (var i = 0; i < elements.length; i++) {
		element = elements[i];
		uri = $(element).attr('href');
		uris[i] = uri;
	}
	return uris;
};
/**
 * Determine if Tarantula should visit this uri
 *
 * @public
 * @param {string} pageUri
 *   The URL we are considering visiting
 * @return {bool}
 *   True, if Tarantula should visit the URI
 */
Tarantula.prototype.shouldVisit = function (pageUri) {
	if (pageUri) {
		return true;
	}
};

// Private Functions
// You probably don't want to override as an implementor

/**
 * Tell the Tarantula to crawl the site
 *
 * @param {string[]} frontier
 *   Optional list of URI's to enqueue
 */
Tarantula.prototype.start = function (frontier) {
	this.push(frontier);
};
/**
 * Add more URIs for our crawler to visit
 *
 * @param {string|string[]} uris
 *   A list of URI's we intend to visit later
 * @param {string} pageUri
 *   The URI where these were found
 * @param {object} parentTask
 *   (optional) If not passed, we assume a root task
 * @param {string} parentTask.uri
 *   (optional) The URI where these were found
 * @param {string} parentTask.parent
 *   (optional) The URI where parentTask.uri was found
 * @emits "uris": with {string[]} uris, {string} parent
 *   If new URI's are enqueued
 * @return {string[]}
 *   New URI's that were pushed to the stack
 */
Tarantula.prototype.push = function (uris, pageUri, parentTask) {
	// 1. Clean-up partial urls
	if (pageUri) {
		uris = uris.map(function (uri) {
			if (uri) {
				return url.resolve(pageUri, uri);
			}
		});
	}
	// 2. Filter any empty URL's (happens)
	uris = _.filter(uris);
	// 3. Clean-up mailto: and javascript:
	uris = _.filter(uris, isValidUrl);
	// 4. Optionally, remove any anchor tags
	//    ex: http://example.com/#anchor
	if (this.brain.trimHashes) {
		uris = uris.map(trimHash);
	}
	// 5. Optionally, skip duplicate URL's
	//    otherwise, we'll likely get stuck in a loop
	if (this.brain.skipDupes) {
		uris = _.difference(_.unique(uris), this.uris);
	}
	// 6. Optionally, stay in range of the base (range) URL's
	//    Don't go to google.com, if we're on example.com
	if (this.brain.stayInRange && parentTask) {
		uris = uris.filter(this.range.test);
	}
	// 7. Optional, custom logic from the implementor
	if (this.shouldVisit) {
		uris = uris.filter(this.shouldVisit);
	}
	// Finally, If we've got new URI's, then queue them up
	if (uris.length) {
		var tasks = uris.map(function (uri) {
			return {
				uri: uri,
				parent: pageUri
			};
		});
		this.uris.push.apply(this.uris, uris);
		this.legs.enqueue(tasks);
		if (!parentTask) {
			this.range.push.apply(this.range, uris);
		}
		else {
			this.emit('uris', parentTask, uris.length);
		}
	}
	return uris;
};
/**
 * Tells the Tarantula to how to make a new leg
 */
Tarantula.prototype.growLeg = function () {
	if (this.brain.leg === 'PhantomJS') {
		return new PhantomClient();
	}
	else {
		return new CheerioClient();
	}
};
/**
 * Tells the Tarantula to how to make a new leg
 */
Tarantula.prototype.shedLeg = function (leg) {
	leg.destroy();
};
/**
/**
 * Logic to process a given task
 *
 * @param {object} task
 * @param {string} task.uri
 *   The URI to process
 * @param {Client} leg
 *   Instance of a leg (aka "Client")
 * @param {function} release
 *   Will release the leg back to the pool
 */
Tarantula.prototype.follow = function (task, leg) {
	this.emit('follow', task.uri);
	leg
		.setHeaders(this.brain.headers)
		.setProxy(this.brain.proxy)
		.setListener(this.legEvent.bind(this, leg, task))
		.get(task.uri, this.visit);
};
Tarantula.prototype.legEvent = function (leg, task, eventName) {
	var args = Array.prototype.slice.call(arguments, 3);
	args.unshift(eventName, task);
	this.emit.apply(this, args);
	if (eventName === 'data' || eventName === 'error') {
		this.legs.release(leg);
		this.visited++;
	}
};
Tarantula.prototype.complete = function () {
	this.emit('done');
};
/**
 * Remove Hash part of a URL
 *
 * @param {string} uri
 *   The URL to remove hash from
 * @param {string}
 *   The URL without hash
 */
function trimHash (uri) {
	var indexOfHash = uri.indexOf('#');
	return (indexOfHash != -1) ? uri.substring(0, indexOfHash) : uri;
}
function isValidUrl (uri) {
	return (uri.indexOf('http:') === 0 || uri.indexOf('https:') === 0);
}

module.exports = Tarantula;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import {
  Dimensions,
  ScrollView,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

import Day from './Day';

import moment from 'moment';
import styles from './styles';

const DEVICE_WIDTH = Dimensions.get('window').width;
const VIEW_INDEX = 2;

function getNumberOfWeeks(month) {
  const firstWeek = moment(month).startOf('month').week();
  const lastWeek = moment(month).endOf('month').week();
  return lastWeek - firstWeek + 1;
}

export default class Calendar extends Component {

  state = {
    currentMonthMoment: moment(this.props.startDate),
    selectedMoment: moment(this.props.selectedDate),
    rowHeight: null,
  };

  static propTypes = {
    customStyle: PropTypes.object,
    dayHeadings: PropTypes.array,
    eventDates: PropTypes.array,
    monthNames: PropTypes.array,
    nextButtonText: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.object
    ]),
    onDateSelect: PropTypes.func,
    onSwipeNext: PropTypes.func,
    onSwipePrev: PropTypes.func,
    onTouchNext: PropTypes.func,
    onTouchPrev: PropTypes.func,
    prevButtonText: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.object
    ]),
    scrollEnabled: PropTypes.bool,
    selectedDate: PropTypes.any,
    showControls: PropTypes.bool,
    showEventIndicators: PropTypes.bool,
    startDate: PropTypes.any,
    titleFormat: PropTypes.string,
    today: PropTypes.any,
    weekStart: PropTypes.number,
  };

  static defaultProps = {
    customStyle: {},
    width: DEVICE_WIDTH,
    dayHeadings: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    eventDates: [],
    monthNames: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
      'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    nextButtonText: 'Next',
    prevButtonText: 'Prev',
    scrollEnabled: false,
    showControls: false,
    showEventIndicators: false,
    startDate: moment().format('YYYY-MM-DD'),
    titleFormat: 'MMMM YYYY',
    today: moment(),
    weekStart: 1,
  };

  componentDidMount() {
    // fixes initial scrolling bug on Android
    setTimeout(() => this.scrollToItem(VIEW_INDEX), 0)
  }

  componentDidUpdate() {
    this.scrollToItem(VIEW_INDEX);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.selectedDate && this.props.selectedDate !== nextProps.selectedDate) {
      this.setState({selectedMoment: nextProps.selectedDate});
    }
  }

  getMonthStack(currentMonth) {
    if (this.props.scrollEnabled) {
      const res = [];
      for (let i = -VIEW_INDEX; i <= VIEW_INDEX; i++) {
        res.push(moment(currentMonth).add(i, 'month'));
      }
      return res;
    }
    return [moment(currentMonth)];
  }

 prepareEventDates(eventDates, events) {
    const parsedDates = {};

    // Dates without any custom properties
    eventDates.forEach(event => {
      const date = moment(event);
      const month = moment(date).startOf('month').format();
      parsedDates[month] = parsedDates[month] || {};
      parsedDates[month][date.date() - 1] = {};
    });

    // Dates with custom properties
    if (events) {
      events.forEach(event => {
        if (event.date) {
            const date = moment(event.date);
            const month = moment(date).startOf('month').format();
            parsedDates[month] = parsedDates[month] || {};
            parsedDates[month][date.date() - 1] = event;
        }
      });
    }
    return parsedDates;
  }

  selectDate(date) {
    if (this.props.selectedDate === undefined) {
      this.setState({ selectedMoment: date });
    }
    this.props.onDateSelect && this.props.onDateSelect(date ? date.format(): null );
  }

  onPrev = () => {
    const newMoment = moment(this.state.currentMonthMoment).subtract(1, 'month');
    this.setState({ currentMonthMoment: newMoment });
    this.props.onTouchPrev && this.props.onTouchPrev(newMoment);
  }

  onNext = () => {
    const newMoment = moment(this.state.currentMonthMoment).add(1, 'month');
    this.setState({ currentMonthMoment: newMoment });
    this.props.onTouchNext && this.props.onTouchNext(newMoment);
  }

  scrollToItem(itemIndex) {
    const scrollToX = itemIndex * this.props.width;
    if (this.props.scrollEnabled) {
      this._calendar.scrollTo({ y: 0, x: scrollToX, animated: false });
    }
  }

  scrollEnded(event) {
    const position = event.nativeEvent.contentOffset.x;
    const currentPage = position / this.props.width;
    const newMoment = moment(this.state.currentMonthMoment).add(currentPage - VIEW_INDEX, 'month');
    this.setState({ currentMonthMoment: newMoment });

    if (currentPage < VIEW_INDEX) {
      this.props.onSwipePrev && this.props.onSwipePrev(newMoment);
    } else if (currentPage > VIEW_INDEX) {
      this.props.onSwipeNext && this.props.onSwipeNext(newMoment);
    }
  }

  onWeekRowLayout = (event) => {
    if (this.state.rowHeight !== event.nativeEvent.layout.height) {
      this.setState({ rowHeight: event.nativeEvent.layout.height });
    }
  }

  renderMonthView(argMoment, eventsMap) {

    let
      renderIndex = 0,
      weekRows = [],
      days = [],
      startOfArgMonthMoment = argMoment.startOf('month');

    const
      selectedMoment = moment(this.state.selectedMoment),
      weekStart = this.props.weekStart,
      todayMoment = moment(this.props.today),
      todayIndex = todayMoment.date() - 1,
      argMonthDaysCount = argMoment.daysInMonth(),
      offset = (startOfArgMonthMoment.isoWeekday() - weekStart + 7) % 7,
      argMonthIsToday = argMoment.isSame(todayMoment, 'month'),
      selectedIndex = moment(selectedMoment).date() - 1,
      selectedMonthIsArg = selectedMoment.isSame(argMoment, 'month');

    const events = (eventsMap !== null)
      ? eventsMap[argMoment.startOf('month').format()]
      : null;

    do {
      const dayIndex = renderIndex - offset;
      const isoWeekday = (renderIndex + weekStart) % 7;

      if (dayIndex >= 0 && dayIndex < argMonthDaysCount) {
        days.push((
          <Day
            startOfMonth={startOfArgMonthMoment}
            isWeekend={isoWeekday === 0 || isoWeekday === 6}
            key={`${renderIndex}`}
            onPress={() => {
              this.selectDate(moment(startOfArgMonthMoment).set('date', dayIndex + 1));
            }}
            caption={`${dayIndex + 1}`}
            isToday={argMonthIsToday && (dayIndex === todayIndex)}
            isSelected={selectedMonthIsArg && (dayIndex === selectedIndex)}
            event={events && events[dayIndex]}
            showEventIndicators={this.props.showEventIndicators}
            customStyle={this.props.customStyle}
          />
        ));
      } else {
        days.push(<Day key={`${renderIndex}`} filler customStyle={this.props.customStyle} />);
      }
      if (renderIndex % 7 === 6) {
        weekRows.push(
          <View
            key={weekRows.length}
            onLayout={weekRows.length ? undefined : this.onWeekRowLayout}
            style={[styles.weekRow, this.props.customStyle.weekRow]}
          >
            {days}
          </View>);
        days = [];
        if (dayIndex + 1 >= argMonthDaysCount) {
          break;
        }
      }
      renderIndex += 1;
    } while (true)
    const containerStyle = [styles.monthContainer, this.props.customStyle.monthContainer];
    return <View key={argMoment.month()} style={containerStyle}>{weekRows}</View>;
  }

  renderHeading() {
    const headings = [];
    for (let i = 0; i < 7; i++) {
      const j = (i + this.props.weekStart) % 7;
      headings.push(
        <Text
          key={i}
          style={j === 0 || j === 6 ?
            [styles.weekendHeading, this.props.customStyle.weekendHeading] :
            [styles.dayHeading, this.props.customStyle.dayHeading]}
        >
          {this.props.dayHeadings[j]}
        </Text>
      );
    }

    return (
      <View style={[styles.calendarHeading, this.props.customStyle.calendarHeading]}>
        {headings}
      </View>
    );
  }

  renderTopBar() {
    let localizedMonth = this.props.monthNames[this.state.currentMonthMoment.month()];
    return this.props.showControls
    ? (
        <View style={[styles.calendarControls, this.props.customStyle.calendarControls]}>
          <TouchableOpacity
            style={[styles.controlButton, this.props.customStyle.controlButton]}
            onPress={this.onPrev}
          >
            <Text style={[styles.controlButtonText, this.props.customStyle.controlButtonText]}>
              {this.props.prevButtonText}
            </Text>
          </TouchableOpacity>
          <Text style={[styles.title, this.props.customStyle.title]}>
            {this.state.currentMonthMoment.format(this.props.titleFormat)}
          </Text>
          <TouchableOpacity
            style={[styles.controlButton, this.props.customStyle.controlButton]}
            onPress={this.onNext}
          >
            <Text style={[styles.controlButtonText, this.props.customStyle.controlButtonText]}>
              {this.props.nextButtonText}
            </Text>
          </TouchableOpacity>
        </View>
      )
    : (
      <View style={[styles.calendarControls, this.props.customStyle.calendarControls]}>
        <Text style={[styles.title, this.props.customStyle.title]}>
          {this.state.currentMonthMoment.format(this.props.titleFormat)}
        </Text>
      </View>
    );
  }

  render() {
    const calendarDates = this.getMonthStack(this.state.currentMonthMoment);
    const eventDatesMap = this.prepareEventDates(this.props.eventDates, this.props.events);
    const numOfWeeks = getNumberOfWeeks(this.state.currentMonthMoment);

    return (
      <View style={[styles.calendarContainer, this.props.customStyle.calendarContainer]}>
        {this.renderTopBar()}
        {this.renderHeading(this.props.titleFormat)}
        {this.props.scrollEnabled ?
          <ScrollView
            ref={calendar => this._calendar = calendar}
            horizontal
            scrollEnabled
            pagingEnabled
            removeClippedSubviews
            scrollEventThrottle={1000}
            showsHorizontalScrollIndicator={false}
            automaticallyAdjustContentInsets
            onMomentumScrollEnd={(event) => this.scrollEnded(event)}
            style={{
              height: this.state.rowHeight ? this.state.rowHeight * numOfWeeks : null,
            }}
          >
            {calendarDates.map((date) => this.renderMonthView(moment(date), eventDatesMap))}
          </ScrollView>
          :
          <View ref={calendar => this._calendar = calendar}>
            {calendarDates.map((date) => this.renderMonthView(moment(date), eventDatesMap))}
          </View>
        }
      </View>
    );
  }
}
<commit_msg>change getNumberofWeeks method (#103)<commit_after>import React, { Component, PropTypes } from 'react';
import {
  Dimensions,
  ScrollView,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

import Day from './Day';

import moment from 'moment';
import styles from './styles';

const DEVICE_WIDTH = Dimensions.get('window').width;
const VIEW_INDEX = 2;

function getNumberOfWeeks(month, weekStart) {
  const firstDay = moment(month).startOf('month').day();
  const offset = (firstDay - weekStart + 7) % 7;
  const days = moment(month).daysInMonth();
  return Math.ceil((offset + days) / 7);
}

export default class Calendar extends Component {

  state = {
    currentMonthMoment: moment(this.props.startDate),
    selectedMoment: moment(this.props.selectedDate),
    rowHeight: null,
  };

  static propTypes = {
    customStyle: PropTypes.object,
    dayHeadings: PropTypes.array,
    eventDates: PropTypes.array,
    monthNames: PropTypes.array,
    nextButtonText: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.object
    ]),
    onDateSelect: PropTypes.func,
    onSwipeNext: PropTypes.func,
    onSwipePrev: PropTypes.func,
    onTouchNext: PropTypes.func,
    onTouchPrev: PropTypes.func,
    prevButtonText: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.object
    ]),
    scrollEnabled: PropTypes.bool,
    selectedDate: PropTypes.any,
    showControls: PropTypes.bool,
    showEventIndicators: PropTypes.bool,
    startDate: PropTypes.any,
    titleFormat: PropTypes.string,
    today: PropTypes.any,
    weekStart: PropTypes.number,
  };

  static defaultProps = {
    customStyle: {},
    width: DEVICE_WIDTH,
    dayHeadings: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
    eventDates: [],
    monthNames: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
      'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    nextButtonText: 'Next',
    prevButtonText: 'Prev',
    scrollEnabled: false,
    showControls: false,
    showEventIndicators: false,
    startDate: moment().format('YYYY-MM-DD'),
    titleFormat: 'MMMM YYYY',
    today: moment(),
    weekStart: 1,
  };

  componentDidMount() {
    // fixes initial scrolling bug on Android
    setTimeout(() => this.scrollToItem(VIEW_INDEX), 0)
  }

  componentDidUpdate() {
    this.scrollToItem(VIEW_INDEX);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.selectedDate && this.props.selectedDate !== nextProps.selectedDate) {
      this.setState({selectedMoment: nextProps.selectedDate});
    }
  }

  getMonthStack(currentMonth) {
    if (this.props.scrollEnabled) {
      const res = [];
      for (let i = -VIEW_INDEX; i <= VIEW_INDEX; i++) {
        res.push(moment(currentMonth).add(i, 'month'));
      }
      return res;
    }
    return [moment(currentMonth)];
  }

 prepareEventDates(eventDates, events) {
    const parsedDates = {};

    // Dates without any custom properties
    eventDates.forEach(event => {
      const date = moment(event);
      const month = moment(date).startOf('month').format();
      parsedDates[month] = parsedDates[month] || {};
      parsedDates[month][date.date() - 1] = {};
    });

    // Dates with custom properties
    if (events) {
      events.forEach(event => {
        if (event.date) {
            const date = moment(event.date);
            const month = moment(date).startOf('month').format();
            parsedDates[month] = parsedDates[month] || {};
            parsedDates[month][date.date() - 1] = event;
        }
      });
    }
    return parsedDates;
  }

  selectDate(date) {
    if (this.props.selectedDate === undefined) {
      this.setState({ selectedMoment: date });
    }
    this.props.onDateSelect && this.props.onDateSelect(date ? date.format(): null );
  }

  onPrev = () => {
    const newMoment = moment(this.state.currentMonthMoment).subtract(1, 'month');
    this.setState({ currentMonthMoment: newMoment });
    this.props.onTouchPrev && this.props.onTouchPrev(newMoment);
  }

  onNext = () => {
    const newMoment = moment(this.state.currentMonthMoment).add(1, 'month');
    this.setState({ currentMonthMoment: newMoment });
    this.props.onTouchNext && this.props.onTouchNext(newMoment);
  }

  scrollToItem(itemIndex) {
    const scrollToX = itemIndex * this.props.width;
    if (this.props.scrollEnabled) {
      this._calendar.scrollTo({ y: 0, x: scrollToX, animated: false });
    }
  }

  scrollEnded(event) {
    const position = event.nativeEvent.contentOffset.x;
    const currentPage = position / this.props.width;
    const newMoment = moment(this.state.currentMonthMoment).add(currentPage - VIEW_INDEX, 'month');
    this.setState({ currentMonthMoment: newMoment });

    if (currentPage < VIEW_INDEX) {
      this.props.onSwipePrev && this.props.onSwipePrev(newMoment);
    } else if (currentPage > VIEW_INDEX) {
      this.props.onSwipeNext && this.props.onSwipeNext(newMoment);
    }
  }

  onWeekRowLayout = (event) => {
    if (this.state.rowHeight !== event.nativeEvent.layout.height) {
      this.setState({ rowHeight: event.nativeEvent.layout.height });
    }
  }

  renderMonthView(argMoment, eventsMap) {

    let
      renderIndex = 0,
      weekRows = [],
      days = [],
      startOfArgMonthMoment = argMoment.startOf('month');

    const
      selectedMoment = moment(this.state.selectedMoment),
      weekStart = this.props.weekStart,
      todayMoment = moment(this.props.today),
      todayIndex = todayMoment.date() - 1,
      argMonthDaysCount = argMoment.daysInMonth(),
      offset = (startOfArgMonthMoment.isoWeekday() - weekStart + 7) % 7,
      argMonthIsToday = argMoment.isSame(todayMoment, 'month'),
      selectedIndex = moment(selectedMoment).date() - 1,
      selectedMonthIsArg = selectedMoment.isSame(argMoment, 'month');

    const events = (eventsMap !== null)
      ? eventsMap[argMoment.startOf('month').format()]
      : null;

    do {
      const dayIndex = renderIndex - offset;
      const isoWeekday = (renderIndex + weekStart) % 7;

      if (dayIndex >= 0 && dayIndex < argMonthDaysCount) {
        days.push((
          <Day
            startOfMonth={startOfArgMonthMoment}
            isWeekend={isoWeekday === 0 || isoWeekday === 6}
            key={`${renderIndex}`}
            onPress={() => {
              this.selectDate(moment(startOfArgMonthMoment).set('date', dayIndex + 1));
            }}
            caption={`${dayIndex + 1}`}
            isToday={argMonthIsToday && (dayIndex === todayIndex)}
            isSelected={selectedMonthIsArg && (dayIndex === selectedIndex)}
            event={events && events[dayIndex]}
            showEventIndicators={this.props.showEventIndicators}
            customStyle={this.props.customStyle}
          />
        ));
      } else {
        days.push(<Day key={`${renderIndex}`} filler customStyle={this.props.customStyle} />);
      }
      if (renderIndex % 7 === 6) {
        weekRows.push(
          <View
            key={weekRows.length}
            onLayout={weekRows.length ? undefined : this.onWeekRowLayout}
            style={[styles.weekRow, this.props.customStyle.weekRow]}
          >
            {days}
          </View>);
        days = [];
        if (dayIndex + 1 >= argMonthDaysCount) {
          break;
        }
      }
      renderIndex += 1;
    } while (true)
    const containerStyle = [styles.monthContainer, this.props.customStyle.monthContainer];
    return <View key={argMoment.month()} style={containerStyle}>{weekRows}</View>;
  }

  renderHeading() {
    const headings = [];
    for (let i = 0; i < 7; i++) {
      const j = (i + this.props.weekStart) % 7;
      headings.push(
        <Text
          key={i}
          style={j === 0 || j === 6 ?
            [styles.weekendHeading, this.props.customStyle.weekendHeading] :
            [styles.dayHeading, this.props.customStyle.dayHeading]}
        >
          {this.props.dayHeadings[j]}
        </Text>
      );
    }

    return (
      <View style={[styles.calendarHeading, this.props.customStyle.calendarHeading]}>
        {headings}
      </View>
    );
  }

  renderTopBar() {
    let localizedMonth = this.props.monthNames[this.state.currentMonthMoment.month()];
    return this.props.showControls
    ? (
        <View style={[styles.calendarControls, this.props.customStyle.calendarControls]}>
          <TouchableOpacity
            style={[styles.controlButton, this.props.customStyle.controlButton]}
            onPress={this.onPrev}
          >
            <Text style={[styles.controlButtonText, this.props.customStyle.controlButtonText]}>
              {this.props.prevButtonText}
            </Text>
          </TouchableOpacity>
          <Text style={[styles.title, this.props.customStyle.title]}>
            {this.state.currentMonthMoment.format(this.props.titleFormat)}
          </Text>
          <TouchableOpacity
            style={[styles.controlButton, this.props.customStyle.controlButton]}
            onPress={this.onNext}
          >
            <Text style={[styles.controlButtonText, this.props.customStyle.controlButtonText]}>
              {this.props.nextButtonText}
            </Text>
          </TouchableOpacity>
        </View>
      )
    : (
      <View style={[styles.calendarControls, this.props.customStyle.calendarControls]}>
        <Text style={[styles.title, this.props.customStyle.title]}>
          {this.state.currentMonthMoment.format(this.props.titleFormat)}
        </Text>
      </View>
    );
  }

  render() {
    const calendarDates = this.getMonthStack(this.state.currentMonthMoment);
    const eventDatesMap = this.prepareEventDates(this.props.eventDates, this.props.events);
    const numOfWeeks = getNumberOfWeeks(this.state.currentMonthMoment, this.props.weekStart);

    return (
      <View style={[styles.calendarContainer, this.props.customStyle.calendarContainer]}>
        {this.renderTopBar()}
        {this.renderHeading(this.props.titleFormat)}
        {this.props.scrollEnabled ?
          <ScrollView
            ref={calendar => this._calendar = calendar}
            horizontal
            scrollEnabled
            pagingEnabled
            removeClippedSubviews
            scrollEventThrottle={1000}
            showsHorizontalScrollIndicator={false}
            automaticallyAdjustContentInsets
            onMomentumScrollEnd={(event) => this.scrollEnded(event)}
            style={{
              height: this.state.rowHeight ? this.state.rowHeight * numOfWeeks : null,
            }}
          >
            {calendarDates.map((date) => this.renderMonthView(moment(date), eventDatesMap))}
          </ScrollView>
          :
          <View ref={calendar => this._calendar = calendar}>
            {calendarDates.map((date) => this.renderMonthView(moment(date), eventDatesMap))}
          </View>
        }
      </View>
    );
  }
}
<|endoftext|>


<commit_before>let util = require('util'); // Native Node util module
let EventEmitter = require('events').EventEmitter;
let Task;
let TaskBase;
let utils = require('../utils');
let async = require('async');
let chalk = require('chalk');
// 'rule' module is lazy-required at the bottom

let UNDEFINED_VALUE;

/**
  @name jake
  @namespace jake
*/
/**
  @name jake.Task
  @constructor
  @augments EventEmitter
  @description A Jake Task

  @param {String} name The name of the Task
  @param {Array} [prereqs] Prerequisites to be run before this task
  @param {Function} [action] The action to perform for this task
  @param {Object} [opts]
    @param {Array} [opts.asyc=false] Perform this task asynchronously.
    If you flag a task with this option, you must call the global
    `complete` method inside the task's action, for execution to proceed
    to the next task.
 */
Task = function () {
  // Do constructor-work only on actual instances, not when used
  // for inheritance
  if (arguments.length) {
    this.init.apply(this, arguments);
  }
};

util.inherits(Task, EventEmitter);

TaskBase = new (function () {

  // Parse any positional args attached to the task-name
  let parsePrereqName = function (name) {
        let taskArr = name.split('[');
        let taskName = taskArr[0];
        let taskArgs = [];
        if (taskArr[1]) {
          taskArgs = taskArr[1].replace(/\]$/, '');
          taskArgs = taskArgs.split(',');
        }
        return {
          name: taskName,
         args: taskArgs
        };
      };

  /**
    @name jake.Task#event:complete
    @event
   */

  this.init = function (name, prereqs, action, options) {

    if (name.indexOf(':') > -1) {
      throw new Error('Task name cannot include a colon. It is used internally as namespace delimiter.');
    }

    let opts = options || {};

    this._currentPrereqIndex = 0;
    this._internal = false;
    this._skipped = false;

    this.name = name;
    this.prereqs = prereqs;
    this.action = action;
    this.async = false;
    this.taskStatus = Task.runStatuses.UNSTARTED;
    this.fullName = null;
    this.description = null;
    this.args = [];
    this.value = UNDEFINED_VALUE;
    this.namespace = null;
    this.parallelLimit = 1;
    this.startTime = null;
    this.endTime = null;

    // Support legacy async-flag -- if not explicitly passed or falsy, will
    // be set to empty-object
    if (typeof opts == 'boolean' && opts === true) {
      this.async = true;
    }
    else {
      if (opts.async) {
        this.async = true;
      }
      if (opts.parallelLimit) {
        this.parallelLimit = opts.parallelLimit;
      }
    }

    //Do a test on self dependencies for this task
    if(Array.isArray(this.prereqs) && this.prereqs.indexOf(this.name) !== -1) {
      throw new Error("Cannot use prereq " + this.name + " as a dependency of itself");
    }

  };

  /**
    @name jake.Task#invoke
    @function
    @description Runs prerequisites, then this task. If the task has already
    been run, will not run the task again.
   */
  this.invoke = function () {
    jake._invocationChain.push(this);
    this.args = Array.prototype.slice.call(arguments);
    this.runPrereqs();
  };

  /**
    @name jake.Task#execute
    @function
    @description Runs prerequisites, then this task. If the task has already
    been run, *will* run the task again.
   */
  this.execute = function () {
    jake._invocationChain.push(this);
    this.args = Array.prototype.slice.call(arguments);
    this.reenable();
    this.run();
  };

  this.runPrereqs = function () {
    if (this.prereqs && this.prereqs.length) {
      if (this.parallelLimit > 1) {
        let self = this;
        async.eachLimit(self.prereqs, self.parallelLimit,

        function (name, cb) {
          let parsed = parsePrereqName(name);

          let prereq = self.namespace.resolveTask(parsed.name) ||
          jake.attemptRule(name, self.namespace, 0) ||
          jake.createPlaceholderFileTask(name, self.namespace);

          if (!prereq) {
            throw new Error('Unknown task "' + name + '"');
          }

          //Test for circular invocation
          if(prereq === self) {
            setImmediate(function () {
              cb(new Error("Cannot use prereq " + prereq.name + " as a dependency of itself"));
            });
          }

          if (prereq.taskStatus === Task.runStatuses.DONE) {
            //prereq already done, return
            setImmediate(cb);
          }
          else {
            //wait for complete before calling cb
            prereq.once('complete', function () {
              setImmediate(cb);
            });
            //start te prereq if we are the first to encounter it
            if(prereq.taskStatus === Task.runStatuses.UNSTARTED) {
              prereq.taskStatus = Task.runStatuses.STARTED;
              prereq.invoke.apply(prereq, parsed.args);
            }
          }
        },

        function (err) {
          //async callback is called after all prereqs have run.
          if(err) {
            throw err;
          }
          else {
            setImmediate(self.run.bind(self));
          }
        }
        );
      }
      else {
        setImmediate(this.nextPrereq.bind(this));
      }
    }
    else {
      setImmediate(this.run.bind(this));
    }
  };

  this.nextPrereq = function () {
    let self = this;
    let index = this._currentPrereqIndex;
    let name = this.prereqs[index];
    let prereq;
    let parsed;

    if (name) {

      parsed = parsePrereqName(name);

      prereq = this.namespace.resolveTask(parsed.name) ||
          jake.attemptRule(name, this.namespace, 0) ||
          jake.createPlaceholderFileTask(name, this.namespace);

      if (!prereq) {
        throw new Error('Unknown task "' + name + '"');
      }

      // Do when done
      if (prereq.taskStatus === Task.runStatuses.DONE) {
        self.handlePrereqComplete(prereq);
      }
 else {
        prereq.once('complete', function () {
          self.handlePrereqComplete(prereq);
        });
        if(prereq.taskStatus === Task.runStatuses.UNSTARTED) {
           prereq.taskStatus = Task.runStatuses.STARTED;
           prereq.invoke.apply(prereq, parsed.args);
        }
      }
    }
  };

  /**
    @name jake.Task#reenable
    @function
    @description Reenables a task so that it can be run again.
   */
  this.reenable = function (deep) {
    let prereqs;
    let prereq;
    this._skipped = false;
    this.taskStatus = Task.runStatuses.UNSTARTED;
    this.value = UNDEFINED_VALUE;
    if (deep && this.prereqs) {
      prereqs = this.prereqs;
      for (let i = 0, ii = prereqs.length; i < ii; i++) {
        prereq = jake.Task[prereqs[i]];
        if (prereq) {
          prereq.reenable(deep);
        }
      }
    }
  };

  this.handlePrereqComplete = function (prereq) {
    this._currentPrereqIndex++;
    if (this._currentPrereqIndex < this.prereqs.length) {
      setImmediate(this.nextPrereq.bind(this));
    }
    else {
      setImmediate(this.run.bind(this));
    }
  };

  this.run = function () {
    let val;
    let hasAction = typeof this.action == 'function';

    if (this.taskStatus == Task.runStatuses.DONE) {
      this.emit('skip');
    }
    else {
      if (!(this._internal || jake.program.opts.quiet)) {
        console.log("Starting '" + chalk.green(this.name) + "'...");
      }

      this.startTime = (new Date()).getTime();
      this.emit('start');

      if (hasAction) {
        try {
          val = this.action.apply(this, this.args);

          if (typeof val == 'object' && typeof val.then == 'function') {
            this.async = true;

            val.then(
              (result) => {
                setImmediate(() => {
                  this.complete(result);
                });
              },
              (err) => {
                setImmediate(() => {
                  fail(err);
                });
              });
          }
        }
        catch (e) {
          this.emit('error', e);
          return; // Bail out, not complete
        }
      }

      if (!(hasAction && this.async)) {
        setImmediate(() => {
          this.complete(val);
        });
      }
    }

  };

  this.complete = function (val) {
    jake._invocationChain.splice(jake._invocationChain.indexOf(this), 1);

    this._currentPrereqIndex = 0;

    // If 'complete' getting called because task has been
    // run already, value will not be passed -- leave in place
    if (!this._skippped) {
      this.taskStatus = Task.runStatuses.DONE;
      this.value = val;

      this.emit('complete', this.value);

      this.endTime = (new Date()).getTime();
      let taskTime = this.endTime - this.startTime;

      if (!(this._internal || jake.program.opts.quiet)) {
        console.log("Finished '" + chalk.green(this.name) + "' after " + chalk.magenta(taskTime + ' ms'));
      }

    }

  };

})();
utils.mixin(Task.prototype, TaskBase);

Task.getBaseNamespacePath = function (fullName) {
  return fullName.split(':').slice(0, -1).join(':');
};

Task.getBaseTaskName = function (fullName) {
  return fullName.split(':').pop();
};

//The task is in one of three states
Task.runStatuses = {UNSTARTED: 'unstarted', DONE: 'done', STARTED: 'started'};

exports.Task = Task;

// Lazy-require the 'rule' module
require('../rule');

<commit_msg>Ensure starting/completion order<commit_after>let util = require('util'); // Native Node util module
let EventEmitter = require('events').EventEmitter;
let Task;
let TaskBase;
let utils = require('../utils');
let async = require('async');
let chalk = require('chalk');
// 'rule' module is lazy-required at the bottom

let UNDEFINED_VALUE;
const POLLING_INTERVAL = 100;

/**
  @name jake
  @namespace jake
*/
/**
  @name jake.Task
  @constructor
  @augments EventEmitter
  @description A Jake Task

  @param {String} name The name of the Task
  @param {Array} [prereqs] Prerequisites to be run before this task
  @param {Function} [action] The action to perform for this task
  @param {Object} [opts]
    @param {Array} [opts.asyc=false] Perform this task asynchronously.
    If you flag a task with this option, you must call the global
    `complete` method inside the task's action, for execution to proceed
    to the next task.
 */
Task = function () {
  // Do constructor-work only on actual instances, not when used
  // for inheritance
  if (arguments.length) {
    this.init.apply(this, arguments);
  }
};

util.inherits(Task, EventEmitter);

TaskBase = new (function () {

  // Parse any positional args attached to the task-name
  let parsePrereqName = function (name) {
        let taskArr = name.split('[');
        let taskName = taskArr[0];
        let taskArgs = [];
        if (taskArr[1]) {
          taskArgs = taskArr[1].replace(/\]$/, '');
          taskArgs = taskArgs.split(',');
        }
        return {
          name: taskName,
         args: taskArgs
        };
      };

  /**
    @name jake.Task#event:complete
    @event
   */

  this.init = function (name, prereqs, action, options) {

    if (name.indexOf(':') > -1) {
      throw new Error('Task name cannot include a colon. It is used internally as namespace delimiter.');
    }

    let opts = options || {};

    this._currentPrereqIndex = 0;
    this._internal = false;
    this._skipped = false;

    this.name = name;
    this.prereqs = prereqs;
    this.action = action;
    this.async = false;
    this.taskStatus = Task.runStatuses.UNSTARTED;
    this.fullName = null;
    this.description = null;
    this.args = [];
    this.value = UNDEFINED_VALUE;
    this.namespace = null;
    this.parallelLimit = 1;
    this.startTime = null;
    this.endTime = null;

    // Support legacy async-flag -- if not explicitly passed or falsy, will
    // be set to empty-object
    if (typeof opts == 'boolean' && opts === true) {
      this.async = true;
    }
    else {
      if (opts.async) {
        this.async = true;
      }
      if (opts.parallelLimit) {
        this.parallelLimit = opts.parallelLimit;
      }
    }

    //Do a test on self dependencies for this task
    if(Array.isArray(this.prereqs) && this.prereqs.indexOf(this.name) !== -1) {
      throw new Error("Cannot use prereq " + this.name + " as a dependency of itself");
    }

  };

  this._initInvocationChain = function () {
    // Legacy global invocation chain
    jake._invocationChain.push(this);

    // New root chain
    if (!this._invocationChain) {
      this._invocationChainRoot = true;
      this._invocationChain = [];
      if (jake.currentRunningTask) {
        jake.currentRunningTask._waitForChains = jake.currentRunningTask._waitForChains || [];
        jake.currentRunningTask._waitForChains.push(this._invocationChain);
      }
    }
  };

  /**
    @name jake.Task#invoke
    @function
    @description Runs prerequisites, then this task. If the task has already
    been run, will not run the task again.
   */
  this.invoke = function () {
    this._initInvocationChain();

    this.args = Array.prototype.slice.call(arguments);
    this.runPrereqs();
  };

  /**
    @name jake.Task#execute
    @function
    @description Runs prerequisites, then this task. If the task has already
    been run, *will* run the task again.
   */
  this.execute = function () {
    this._initInvocationChain();

    this.args = Array.prototype.slice.call(arguments);
    this.reenable();
    this.run();
  };

  this.runPrereqs = function () {
    //console.log('running prereqs for', this.name, 'prereqs', this.prereqs);

    if (this.prereqs && this.prereqs.length) {

      if (this.parallelLimit > 1) {
        let self = this;
        async.eachLimit(self.prereqs, self.parallelLimit,

        function (name, cb) {
          let parsed = parsePrereqName(name);

          let prereq = self.namespace.resolveTask(parsed.name) ||
          jake.attemptRule(name, self.namespace, 0) ||
          jake.createPlaceholderFileTask(name, self.namespace);

          if (!prereq) {
            throw new Error('Unknown task "' + name + '"');
          }

          //Test for circular invocation
          if(prereq === self) {
            setImmediate(function () {
              cb(new Error("Cannot use prereq " + prereq.name + " as a dependency of itself"));
            });
          }

          if (prereq.taskStatus === Task.runStatuses.DONE) {
            //prereq already done, return
            setImmediate(cb);
          }
          else {
            //wait for complete before calling cb
            prereq.once('complete', function () {
              setImmediate(cb);
            });
            // Start the prereq if we are the first to encounter it
            if (prereq.taskStatus === Task.runStatuses.UNSTARTED) {
              prereq.taskStatus = Task.runStatuses.STARTED;
              prereq.invoke.apply(prereq, parsed.args);
            }
          }
        },

        function (err) {
          //async callback is called after all prereqs have run.
          if(err) {
            throw err;
          }
          else {
            setImmediate(self.run.bind(self));
          }
        }
        );
      }
      else {
        setImmediate(this.nextPrereq.bind(this));
      }
    }
    else {
      setImmediate(this.run.bind(this));
    }
  };

  this.nextPrereq = function () {
    let self = this;
    let index = this._currentPrereqIndex;
    let name = this.prereqs[index];
    let prereq;
    let parsed;

    if (name) {

      parsed = parsePrereqName(name);

      prereq = this.namespace.resolveTask(parsed.name) ||
          jake.attemptRule(name, this.namespace, 0) ||
          jake.createPlaceholderFileTask(name, this.namespace);

      if (!prereq) {
        throw new Error('Unknown task "' + name + '"');
      }

      //console.log('running prereq', name);
      ////console.log('prereq taskStatus', prereq.taskStatus);

      // Do when done
      if (prereq.taskStatus == Task.runStatuses.DONE) {
        self.handlePrereqComplete(prereq);
      }
      else {
        prereq.once('complete', function () {
          self.handlePrereqComplete(prereq);
        });
        if (prereq.taskStatus == Task.runStatuses.UNSTARTED) {
          prereq.taskStatus = Task.runStatuses.STARTED;
          prereq._invocationChain = this._invocationChain;
          prereq.invoke.apply(prereq, parsed.args);
        }
      }
    }
  };

  /**
    @name jake.Task#reenable
    @function
    @description Reenables a task so that it can be run again.
   */
  this.reenable = function (deep) {
    let prereqs;
    let prereq;
    this._skipped = false;
    this.taskStatus = Task.runStatuses.UNSTARTED;
    this.value = UNDEFINED_VALUE;
    if (deep && this.prereqs) {
      prereqs = this.prereqs;
      for (let i = 0, ii = prereqs.length; i < ii; i++) {
        prereq = jake.Task[prereqs[i]];
        if (prereq) {
          prereq.reenable(deep);
        }
      }
    }
  };

  this.handlePrereqComplete = function (prereq) {
    this._currentPrereqIndex++;
    if (this._currentPrereqIndex < this.prereqs.length) {
      setImmediate(this.nextPrereq.bind(this));
    }
    else {
      setImmediate(this.run.bind(this));
    }
  };

  this.run = function () {
    let val;
    let hasAction = typeof this.action == 'function';

    if (this.taskStatus == Task.runStatuses.DONE) {
      this.emit('skip');
    }
    else {

      if (this._invocationChain.length) {
        let previous = this._invocationChain[this._invocationChain.length - 1];
        if (previous.taskStatus != Task.runStatuses.DONE) {
          let now = (new Date()).getTime();
          if (now - this.startTime > jake._taskTimeout) {
            return jake.fail();
          }
          setTimeout(this.run.bind(this), POLLING_INTERVAL);
          return;
        }
      }

      //console.log('pushing to _invocationChain', this.name);
      this._invocationChain.push(this);

      if (!(this._internal || jake.program.opts.quiet)) {
        console.log("Starting '" + chalk.green(this.name) + "'...");
      }

      this.startTime = (new Date()).getTime();
      this.emit('start');

      jake.currentRunningTask = this;

      if (hasAction) {
        try {
          val = this.action.apply(this, this.args);

          if (typeof val == 'object' && typeof val.then == 'function') {
            this.async = true;

            val.then(
              (result) => {
                setImmediate(() => {
                  this.complete(result);
                });
              },
              (err) => {
                setImmediate(() => {
                  fail(err);
                });
              });
          }
        }
        catch (e) {
          this.taskStatus = Task.runStatuses.ERROR;
          this._invocationChain.chainStatus = Task.runStatuses.ERROR;
          this.emit('error', e);
          return; // Bail out, not complete
        }
      }

      if (!(hasAction && this.async)) {
        setImmediate(() => {
          this.complete(val);
        });
      }
    }

  };

  this.complete = function (val) {

    if (Array.isArray(this._waitForChains)) {
      let stillWaiting = this._waitForChains.some((chain) => {
        return !(chain.chainStatus == Task.runStatuses.DONE ||
              chain.chainStatus == Task.runStatuses.ERROR);
      });
      if (stillWaiting) {
        let now = (new Date()).getTime();
        if (now - this.startTime > jake._taskTimeout) {
          return jake.fail();
        }
        setTimeout(() => {
          this.complete(val);
        }, POLLING_INTERVAL);
        return;
      }
    }

    jake._invocationChain.splice(jake._invocationChain.indexOf(this), 1);

    if (this._invocationChainRoot) {
      this._invocationChain.chainStatus = Task.runStatuses.DONE;
    }

    this._currentPrereqIndex = 0;

    // If 'complete' getting called because task has been
    // run already, value will not be passed -- leave in place
    if (!this._skippped) {
      this.taskStatus = Task.runStatuses.DONE;
      this.value = val;

      this.emit('complete', this.value);

      this.endTime = (new Date()).getTime();
      let taskTime = this.endTime - this.startTime;

      if (!(this._internal || jake.program.opts.quiet)) {
        console.log("Finished '" + chalk.green(this.name) + "' after " + chalk.magenta(taskTime + ' ms'));
      }

    }

  };

})();
utils.mixin(Task.prototype, TaskBase);

Task.getBaseNamespacePath = function (fullName) {
  return fullName.split(':').slice(0, -1).join(':');
};

Task.getBaseTaskName = function (fullName) {
  return fullName.split(':').pop();
};

Task.runStatuses = {
  UNSTARTED: 'unstarted',
  DONE: 'done',
  STARTED: 'started',
  ERROR: 'error'
};

exports.Task = Task;

// Lazy-require the 'rule' module
require('../rule');

<|endoftext|>


<commit_before>'use strict';
/*global require, module, self*/

(function(module) {
	var templates = {
			cache: {},
			globals: {}
		},
		helpers = {},
		loader,
		worker;

	var regexes = {
		nestedConditionals: /(?!^)<!-- IF([\s\S]*?)ENDIF[ a-zA-Z0-9\._:]*-->(?!$)/gi,
		conditionalBlock: /[\r\n?\n]*?<!-- ELSE -->[\r\n?\n]*?/,
		conditionalHelper: /IF function.([\s\S]*?)-->/i,
		removeTabspace: /^\t*?|^\r\n?\t*?|\t?$|\r\n?\t*?$/g,
		removeWhitespace: /(^[\r\n?|\n]*)|([\r\n\t]*$)/g,
		cleanupEmptyLoops: /\s*<!-- BEGIN([\s\S]*?)END ([\s\S]*?)-->/gi,
		cleanupMissingKeys: /[\r\n]*?[\/]?\{[a-zA-Z0-9\.]+[\r\n]*?\}/g,
		getUndefinedKeys: /<!-- IF[\s!]*([\s\S]*?)[\s]*-->/gi,
		backReferenceFix: /\$+/g
	};

	if (typeof self !== 'undefined' && self.addEventListener) {
		self.addEventListener('message', function(ev) {
			var data = ev.data;

			self.postMessage({
				result: !data.block ? templates.parse(data.template, data.object) : templates.parse(data.template, data.block, data.object),
				signature: data.signature
			});
		}, false);
	}

	var callbacks = {},
		signature = 0,
		MAX_SAFE_INT = Math.pow(2, 53) - 1;

	templates.setupWebWorker = function(pathToScript) {
		try {
			worker = new Worker(pathToScript);

			worker.addEventListener('message', function(e) {
				if (callbacks[e.data.signature]) {
					callbacks[e.data.signature](e.data.result);
				}
			}, false);
		} catch (err) {}
	};

	function launchWorker(template, obj, block, callback) {
		signature++;
		if (signature > MAX_SAFE_INT) {
			signature = 0;
		}

		obj = sanitise(obj);

		worker.postMessage({
			template: template,
			object: obj,
			block: block,
			signature: signature
		});

		callbacks[signature] = function(result) {
			callback(result);
			delete callbacks[signature];
		};
	}

	function sanitise(obj) {
		for(var prop in obj) {
			if (!obj.hasOwnProperty(prop) || typeof obj[prop] === 'function') {
				delete obj[prop];
			}
		}

		return obj;
	}

	templates.parse = function(template, block, obj, callback) {
		if (typeof block !== 'string') {
			callback = obj;
			obj = block;
			block = false;
		}

		if (!template) {
			return callback ? callback('') : '';
		}

		obj = registerGlobals(obj || {});

		if (loader && callback) {
			if (!templates.cache[template]) {
				loader(template, function(loaded) {
					templates.cache[template] = loaded;

					launchCallback(loaded, obj, block, callback);
				});
			} else {
				launchCallback(templates.cache[template], obj, block, callback);
			}
		} else if (callback) {
			launchCallback(template, obj, block, callback);
		} else {
			return parseTemplate(block, template, obj);
		}
	};

	function launchCallback(template, obj, block, callback) {
		if (worker) {
			launchWorker(template, obj, block, callback);
		} else {
			callback(parseTemplate(block, template, obj));
		}
	}

	function parseTemplate(block, template, obj) {
		block = !block ? template : templates.getBlock(template, block);
		template = parse(block, obj);

		return parseFunctions(template, template, obj);
	}

	templates.registerHelper = function(name, func) {
		helpers[name] = func;
	};

	templates.registerLoader = function(func) {
		loader = func;
	};

	templates.setGlobal = function(key, value) {
		templates.globals[key] = value;
	};

	templates.getBlock = function(template, block) {
		return template.replace(new RegExp('[\\s\\S]*(<!--[\\s]*BEGIN ' + block + '[\\s]*-->[\\s\\S]*?<!--[\\s]*END ' + block + '[\\s]*-->)[\\s\\S]*', 'g'), '$1');
	};

	templates.flush = function() {
		templates.cache = {};
	};

	function express(filename, options, fn) {
		var fs = require('fs'),
			tpl = filename.replace(options.settings.views + '/', '');

		options._locals = null;

		if (!templates.cache[tpl]) {
			fs.readFile(filename, function(err, html) {
				templates.cache[tpl] = (html || '').toString();
				return fn(err, templates.parse(templates.cache[tpl], options));
			});
		} else {
			return fn(null, templates.parse(templates.cache[tpl], options));
		}
	}

	function replace(string, regex, value) {
		return string.replace(regex, value.toString().replace(regexes.backReferenceFix, '$$$'));
	}

	function replaceValue(template, namespacedKey, value) {
		var string = generateSubspacedRegex(namespacedKey);
		return replace(template, new RegExp('{' + string + '}', 'g'), value);
	}

	function generateSubspacedRegex(namespacedKey) {
		var prefix = '';

		if (namespacedKey.substring(0,1) === '!') {
			prefix = '!';
			namespacedKey = namespacedKey.substring(1);
		}

		var subspace = namespacedKey.split('.'),
			key = subspace.pop(),
			string = [];

		while(subspace.length) {
			string.push(subspace.join('.') + '.');
			subspace.shift();
		}

		string = string ? ('(' + string.join('|') + ')?' + key) : key;
		return prefix + string;
	}

	function makeRegex(block, namespace) {
		namespace = '(' + namespace + ')?';
		return new RegExp('[\\t ]*<!--[\\s]*BEGIN ' + namespace + block + '[\\s]*-->[\\s\\S]*?<!--[\\s]*END ' + namespace + block + '[\\s]*-->');
	}

	function makeBlockRegex(block, namespace) {
		namespace = '(' + namespace + ')?';
		return new RegExp('([\\t ]*<!--[\\s]*BEGIN ' + namespace + block + '[\\s]*-->[\\r\\n?|\\n]?)|(<!--[\\s]*END ' + namespace + block + '[\\s]*-->)', 'g');
	}

	function makeConditionalRegex(namespacedKey) {
		var block = generateSubspacedRegex(namespacedKey);
		return new RegExp('<!--[\\s]*IF ' + block + '[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF ' + block.split(',')[0] + '[\\s]*-->', 'g');
	}

	function makeStatementRegex(namespacedKey) {
		var key = generateSubspacedRegex(namespacedKey);
		return new RegExp('(<!--[\\s]*IF ' + key + '[\\s]*-->)|(<!--[\\s]*ENDIF ' + key.split(',')[0] + '[\\s]*-->)', 'g');
	}

	function registerGlobals(obj) {
		for (var g in templates.globals) {
			if (templates.globals.hasOwnProperty(g)) {
				obj[g] = obj[g] || templates.globals[g];
			}
		}

		return obj;
	}

	function checkConditionals(template, key, value, dev) {
		return checkConditional(checkConditional(template, '!' + key, !value, dev), key, value, dev);
	}

	function checkConditional(template, key, value, dev) {
		var matches = template.match(makeConditionalRegex(key));

		if (matches !== null) {
			for (var i = 0, ii = matches.length; i < ii; i++) {
				var statement = makeStatementRegex(key),
					nestedConditionals = matches[i].match(regexes.nestedConditionals),
					match = replace(matches[i].replace(statement, ''), regexes.nestedConditionals, '<!-- NESTED -->'),
					conditionalBlock = match.split(regexes.conditionalBlock);

				if (conditionalBlock[1]) {
					// there is an else statement
					template = replace(template, matches[i], replace(conditionalBlock[value ? 0 : 1], regexes.removeWhitespace, ''));
				} else {
					// regular if statement
					template = replace(template, matches[i], value ? replace(match, regexes.removeWhitespace, '') : '');
				}

				if (nestedConditionals) {
					for (var x = 0, xx = nestedConditionals.length; x < xx; x++) {
						template = replace(template, '<!-- NESTED -->', nestedConditionals[x]);
					}
				}
			}
		}

		return template;
	}

	function checkConditionalHelpers(template, obj) {
		var string,
			func;

		while ((string = template.match(regexes.conditionalHelper)) !== null) {
			var fn = string[1].trim(),
				args = fn.split(/[ ]*,[ ]*/);

			func = args[0];

			if (helpers[func]) {
				args.shift();
				args.unshift(obj);
				template = checkConditionals(template, 'function.' + fn, helpers[func].apply(null, args), 'dev');
			} else {
				template = template.replace(makeConditionalRegex('function.' + fn));
			}
		}

		return template;
	}

	function callMethod(method, parameters) {
		return method.apply(templates, parameters);
	}

	function parseFunctions(block, result, obj) {
		var functions = block.match(/{function.*?}/gi, '');
		if (!functions) {
			return result;
		}

		for (var i=0, ii=functions.length; i<ii; ++i) {
			var search = functions[i],
				fn = functions[i].replace('{function.', '').split('}').shift().split(/[ ]*,[ ]*/),
				method = fn.shift(),
				parameters = [];

			if (fn.length) {
				for (var j = 0, jj = fn.length; j < jj; j++) {
					parameters.push(obj[fn[j]]);
				}
			} else {
				parameters = [obj];
			}

			if (helpers[method]) {
				result = replace(result, new RegExp(search, 'gi'), callMethod(helpers[method], parameters));
			}
		}

		return result;
	}

	function parseObject(template, array, key, namespace) {
		return parseArray(template, array, key, namespace, true);
	}

	function parseArray(template, array, key, namespace, isObject) {
		template = checkConditionals(template, namespace  + key + '.length', array[key].length);

		var regex = makeRegex(key, namespace), block, result;

		if (!array[key].length && !isObject) {
			return template.replace(regex, '');
		}

		while ((block = template.match(regex)) !== null) {
			block = block[0].replace(makeBlockRegex(key, namespace), '');
			result = parseArrayBlock(template, block, array[key], namespace + key + '.', isObject);
			template = replace(template, regex, result.replace(regexes.removeWhitespace, ''));
		}

		return template;
	}

	function parseArrayBlock(template, block, array, namespace, isObject) {
		var numblocks = array.length - 1,
			result = '';

		for (var iterator in array) {
			if (array.hasOwnProperty(iterator)) {
				if (!isObject) {
					iterator = parseInt(iterator, 10);
				}

				result += parse(block, array[iterator], namespace);
				if (!isObject) {
					result = checkConditionals(result, '@first', iterator === 0);
					result = checkConditionals(result, '@last', iterator === numblocks);
				}

				result = result
					.replace(regexes.removeTabspace, '');

				if (!isObject) {
					result = result.replace('@index', iterator);
				} else {
					result = result
						.replace('@key', iterator)
						.replace('@value', array[iterator]);
				}

				result = parseFunctions(block, result, array[iterator]);
			}
		}

		return result;
	}

	function parseValue(template, key, value) {
		template = checkConditionals(template, key, value);
		return replaceValue(template, key, value);
	}

	function parse(template, obj, namespace) {
		namespace = namespace || '';

		for (var key in obj) {
			if (obj.hasOwnProperty(key)) {
				if (typeof obj[key] === 'undefined' || typeof obj[key] === 'function') {
					continue;
				} else if (obj[key] === null) {
					template = replaceValue(template, namespace + key, '');
				} else if (obj[key].constructor === Array) {
					template = replaceValue(template, namespace + key + '.length', obj[key].length);
					template = parseArray(template, obj, key, namespace);
				} else if (obj[key] instanceof Object) {
					//template = parseObject(template, obj, key, namespace);
					template = checkConditionals(template, key, obj[key]);
					template = parse(template, obj[key], namespace + key + '.');
				} else {
					template = parseValue(template, namespace + key, obj[key]);
				}
			}
		}

		if (namespace) {
			template = template.replace(new RegExp('{' + namespace + '\\.[\\s\\S]*?}', 'g'), '');
			namespace = '';
		} else {
			template = checkConditionalHelpers(template, obj);
			template = cleanup(template);
		}

		return template;
	}

	function cleanup(template) {
		var missingKey,
			missingKeys = [];

		template = template
			.replace(regexes.cleanupEmptyLoops, '');

		var temp = template;
		while ((missingKey = regexes.cleanupMissingKeys.exec(template)) !== null) {
			if (missingKey[0].substr(0, 1) !== '/') {
				temp = temp.replace(missingKey[0], '');
			} else {
				temp = temp.replace(missingKey[0], missingKey[0].slice(1))
			}
		}

		template = temp;

		var undefinedKey,
			undefinedKeys = {},
			parseUndefined;

		while ((undefinedKey = regexes.getUndefinedKeys.exec(template)) !== null) {
			undefinedKeys[undefinedKey[1]] = false;
			parseUndefined = true;
		}

		return parseUndefined ? parse(template, undefinedKeys, '') : template;
	}

	module.exports = templates;
	module.exports.__express = express;

	if ('undefined' !== typeof window) {
		window.templates = module.exports;
	}

})('undefined' === typeof module ? {
	module: {
		exports: {}
	}
} : module);
<commit_msg>how did this one not get caught sooner<commit_after>'use strict';
/*global require, module, self*/

(function(module) {
	var templates = {
			cache: {},
			globals: {}
		},
		helpers = {},
		loader,
		worker;

	var regexes = {
		nestedConditionals: /(?!^)<!-- IF([\s\S]*?)ENDIF[ a-zA-Z0-9\._:]*-->(?!$)/gi,
		conditionalBlock: /[\r\n?\n]*?<!-- ELSE -->[\r\n?\n]*?/,
		conditionalHelper: /<!-- IF function.([\s\S]*?)-->/i,
		removeTabspace: /^\t*?|^\r\n?\t*?|\t?$|\r\n?\t*?$/g,
		removeWhitespace: /(^[\r\n?|\n]*)|([\r\n\t]*$)/g,
		cleanupEmptyLoops: /\s*<!-- BEGIN([\s\S]*?)END ([\s\S]*?)-->/gi,
		cleanupMissingKeys: /[\r\n]*?[\/]?\{[a-zA-Z0-9\.]+[\r\n]*?\}/g,
		getUndefinedKeys: /<!-- IF[\s!]*([\s\S]*?)[\s]*-->/gi,
		backReferenceFix: /\$+/g
	};

	if (typeof self !== 'undefined' && self.addEventListener) {
		self.addEventListener('message', function(ev) {
			var data = ev.data;

			self.postMessage({
				result: !data.block ? templates.parse(data.template, data.object) : templates.parse(data.template, data.block, data.object),
				signature: data.signature
			});
		}, false);
	}

	var callbacks = {},
		signature = 0,
		MAX_SAFE_INT = Math.pow(2, 53) - 1;

	templates.setupWebWorker = function(pathToScript) {
		try {
			worker = new Worker(pathToScript);

			worker.addEventListener('message', function(e) {
				if (callbacks[e.data.signature]) {
					callbacks[e.data.signature](e.data.result);
				}
			}, false);
		} catch (err) {}
	};

	function launchWorker(template, obj, block, callback) {
		signature++;
		if (signature > MAX_SAFE_INT) {
			signature = 0;
		}

		obj = sanitise(obj);

		worker.postMessage({
			template: template,
			object: obj,
			block: block,
			signature: signature
		});

		callbacks[signature] = function(result) {
			callback(result);
			delete callbacks[signature];
		};
	}

	function sanitise(obj) {
		for(var prop in obj) {
			if (!obj.hasOwnProperty(prop) || typeof obj[prop] === 'function') {
				delete obj[prop];
			}
		}

		return obj;
	}

	templates.parse = function(template, block, obj, callback) {
		if (typeof block !== 'string') {
			callback = obj;
			obj = block;
			block = false;
		}

		if (!template) {
			return callback ? callback('') : '';
		}

		obj = registerGlobals(obj || {});

		if (loader && callback) {
			if (!templates.cache[template]) {
				loader(template, function(loaded) {
					templates.cache[template] = loaded;

					launchCallback(loaded, obj, block, callback);
				});
			} else {
				launchCallback(templates.cache[template], obj, block, callback);
			}
		} else if (callback) {
			launchCallback(template, obj, block, callback);
		} else {
			return parseTemplate(block, template, obj);
		}
	};

	function launchCallback(template, obj, block, callback) {
		if (worker) {
			launchWorker(template, obj, block, callback);
		} else {
			callback(parseTemplate(block, template, obj));
		}
	}

	function parseTemplate(block, template, obj) {
		block = !block ? template : templates.getBlock(template, block);
		template = parse(block, obj);

		return parseFunctions(template, template, obj);
	}

	templates.registerHelper = function(name, func) {
		helpers[name] = func;
	};

	templates.registerLoader = function(func) {
		loader = func;
	};

	templates.setGlobal = function(key, value) {
		templates.globals[key] = value;
	};

	templates.getBlock = function(template, block) {
		return template.replace(new RegExp('[\\s\\S]*(<!--[\\s]*BEGIN ' + block + '[\\s]*-->[\\s\\S]*?<!--[\\s]*END ' + block + '[\\s]*-->)[\\s\\S]*', 'g'), '$1');
	};

	templates.flush = function() {
		templates.cache = {};
	};

	function express(filename, options, fn) {
		var fs = require('fs'),
			tpl = filename.replace(options.settings.views + '/', '');

		options._locals = null;

		if (!templates.cache[tpl]) {
			fs.readFile(filename, function(err, html) {
				templates.cache[tpl] = (html || '').toString();
				return fn(err, templates.parse(templates.cache[tpl], options));
			});
		} else {
			return fn(null, templates.parse(templates.cache[tpl], options));
		}
	}

	function replace(string, regex, value) {
		return string.replace(regex, value.toString().replace(regexes.backReferenceFix, '$$$'));
	}

	function replaceValue(template, namespacedKey, value) {
		var string = generateSubspacedRegex(namespacedKey);
		return replace(template, new RegExp('{' + string + '}', 'g'), value);
	}

	function generateSubspacedRegex(namespacedKey) {
		var prefix = '';

		if (namespacedKey.substring(0,1) === '!') {
			prefix = '!';
			namespacedKey = namespacedKey.substring(1);
		}

		var subspace = namespacedKey.split('.'),
			key = subspace.pop(),
			string = [];

		while(subspace.length) {
			string.push(subspace.join('.') + '.');
			subspace.shift();
		}

		string = string ? ('(' + string.join('|') + ')?' + key) : key;
		return prefix + string;
	}

	function makeRegex(block, namespace) {
		namespace = '(' + namespace + ')?';
		return new RegExp('[\\t ]*<!--[\\s]*BEGIN ' + namespace + block + '[\\s]*-->[\\s\\S]*?<!--[\\s]*END ' + namespace + block + '[\\s]*-->');
	}

	function makeBlockRegex(block, namespace) {
		namespace = '(' + namespace + ')?';
		return new RegExp('([\\t ]*<!--[\\s]*BEGIN ' + namespace + block + '[\\s]*-->[\\r\\n?|\\n]?)|(<!--[\\s]*END ' + namespace + block + '[\\s]*-->)', 'g');
	}

	function makeConditionalRegex(namespacedKey) {
		var block = generateSubspacedRegex(namespacedKey);
		return new RegExp('<!--[\\s]*IF ' + block + '[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF ' + block.split(',')[0] + '[\\s]*-->', 'g');
	}

	function makeStatementRegex(namespacedKey) {
		var key = generateSubspacedRegex(namespacedKey);
		return new RegExp('(<!--[\\s]*IF ' + key + '[\\s]*-->)|(<!--[\\s]*ENDIF ' + key.split(',')[0] + '[\\s]*-->)', 'g');
	}

	function registerGlobals(obj) {
		for (var g in templates.globals) {
			if (templates.globals.hasOwnProperty(g)) {
				obj[g] = obj[g] || templates.globals[g];
			}
		}

		return obj;
	}

	function checkConditionals(template, key, value, dev) {
		return checkConditional(checkConditional(template, '!' + key, !value, dev), key, value, dev);
	}

	function checkConditional(template, key, value, dev) {
		var matches = template.match(makeConditionalRegex(key));

		if (matches !== null) {
			for (var i = 0, ii = matches.length; i < ii; i++) {
				var statement = makeStatementRegex(key),
					nestedConditionals = matches[i].match(regexes.nestedConditionals),
					match = replace(matches[i].replace(statement, ''), regexes.nestedConditionals, '<!-- NESTED -->'),
					conditionalBlock = match.split(regexes.conditionalBlock);

				if (conditionalBlock[1]) {
					// there is an else statement
					template = replace(template, matches[i], replace(conditionalBlock[value ? 0 : 1], regexes.removeWhitespace, ''));
				} else {
					// regular if statement
					template = replace(template, matches[i], value ? replace(match, regexes.removeWhitespace, '') : '');
				}

				if (nestedConditionals) {
					for (var x = 0, xx = nestedConditionals.length; x < xx; x++) {
						template = replace(template, '<!-- NESTED -->', nestedConditionals[x]);
					}
				}
			}
		}

		return template;
	}

	function checkConditionalHelpers(template, obj) {
		var string,
			func;

		while ((string = template.match(regexes.conditionalHelper)) !== null) {
			var fn = string[1].trim(),
				args = fn.split(/[ ]*,[ ]*/);

			func = args[0];

			if (helpers[func]) {
				args.shift();
				args.unshift(obj);
				template = checkConditionals(template, 'function.' + fn, helpers[func].apply(null, args), 'dev');
			} else {
				template = template.replace(makeConditionalRegex('function.' + fn));
			}
		}

		return template;
	}

	function callMethod(method, parameters) {
		return method.apply(templates, parameters);
	}

	function parseFunctions(block, result, obj) {
		var functions = block.match(/{function.*?}/gi, '');
		if (!functions) {
			return result;
		}

		for (var i=0, ii=functions.length; i<ii; ++i) {
			var search = functions[i],
				fn = functions[i].replace('{function.', '').split('}').shift().split(/[ ]*,[ ]*/),
				method = fn.shift(),
				parameters = [];

			if (fn.length) {
				for (var j = 0, jj = fn.length; j < jj; j++) {
					parameters.push(obj[fn[j]]);
				}
			} else {
				parameters = [obj];
			}

			if (helpers[method]) {
				result = replace(result, new RegExp(search, 'gi'), callMethod(helpers[method], parameters));
			}
		}

		return result;
	}

	function parseObject(template, array, key, namespace) {
		return parseArray(template, array, key, namespace, true);
	}

	function parseArray(template, array, key, namespace, isObject) {
		template = checkConditionals(template, namespace  + key + '.length', array[key].length);

		var regex = makeRegex(key, namespace), block, result;

		if (!array[key].length && !isObject) {
			return template.replace(regex, '');
		}

		while ((block = template.match(regex)) !== null) {
			block = block[0].replace(makeBlockRegex(key, namespace), '');
			result = parseArrayBlock(template, block, array[key], namespace + key + '.', isObject);
			template = replace(template, regex, result.replace(regexes.removeWhitespace, ''));
		}

		return template;
	}

	function parseArrayBlock(template, block, array, namespace, isObject) {
		var numblocks = array.length - 1,
			result = '';

		for (var iterator in array) {
			if (array.hasOwnProperty(iterator)) {
				if (!isObject) {
					iterator = parseInt(iterator, 10);
				}

				result += parse(block, array[iterator], namespace);
				if (!isObject) {
					result = checkConditionals(result, '@first', iterator === 0);
					result = checkConditionals(result, '@last', iterator === numblocks);
				}

				result = result
					.replace(regexes.removeTabspace, '');

				if (!isObject) {
					result = result.replace('@index', iterator);
				} else {
					result = result
						.replace('@key', iterator)
						.replace('@value', array[iterator]);
				}

				result = parseFunctions(block, result, array[iterator]);
			}
		}

		return result;
	}

	function parseValue(template, key, value) {
		template = checkConditionals(template, key, value);
		return replaceValue(template, key, value);
	}

	function parse(template, obj, namespace) {
		namespace = namespace || '';

		for (var key in obj) {
			if (obj.hasOwnProperty(key)) {
				if (typeof obj[key] === 'undefined' || typeof obj[key] === 'function') {
					continue;
				} else if (obj[key] === null) {
					template = replaceValue(template, namespace + key, '');
				} else if (obj[key].constructor === Array) {
					template = replaceValue(template, namespace + key + '.length', obj[key].length);
					template = parseArray(template, obj, key, namespace);
				} else if (obj[key] instanceof Object) {
					//template = parseObject(template, obj, key, namespace);
					template = checkConditionals(template, key, obj[key]);
					template = parse(template, obj[key], namespace + key + '.');
				} else {
					template = parseValue(template, namespace + key, obj[key]);
				}
			}
		}

		if (namespace) {
			template = template.replace(new RegExp('{' + namespace + '\\.[\\s\\S]*?}', 'g'), '');
			namespace = '';
		} else {
			template = checkConditionalHelpers(template, obj);
			template = cleanup(template);
		}

		return template;
	}

	function cleanup(template) {
		var missingKey,
			missingKeys = [];

		template = template
			.replace(regexes.cleanupEmptyLoops, '');

		var temp = template;
		while ((missingKey = regexes.cleanupMissingKeys.exec(template)) !== null) {
			if (missingKey[0].substr(0, 1) !== '/') {
				temp = temp.replace(missingKey[0], '');
			} else {
				temp = temp.replace(missingKey[0], missingKey[0].slice(1))
			}
		}

		template = temp;

		var undefinedKey,
			undefinedKeys = {},
			parseUndefined;

		while ((undefinedKey = regexes.getUndefinedKeys.exec(template)) !== null) {
			undefinedKeys[undefinedKey[1]] = false;
			parseUndefined = true;
		}

		return parseUndefined ? parse(template, undefinedKeys, '') : template;
	}

	module.exports = templates;
	module.exports.__express = express;

	if ('undefined' !== typeof window) {
		window.templates = module.exports;
	}

})('undefined' === typeof module ? {
	module: {
		exports: {}
	}
} : module);
<|endoftext|>


<commit_before>/**
 * @fileOverview Importer module for restartless addons
 * @author       SHIMODA "Piro" Hiroshi
 * @version      1
 *
 * @license
 *   The MIT License, Copyright (c) 2010 SHIMODA "Piro" Hiroshi.
 *   http://www.cozmixng.org/repos/piro/restartless-addon/trunk/license.txt
 * @url http://www.cozmixng.org/repos/piro/restartless-addon/trunk/restartless/
 */

/** You can customize shared properties for imported scripts. */
var _namespacePrototype = {
		Cc : Components.classes,
		Ci : Components.interfaces,
		Cu : Components.utils,
		Cr : Components.results,
		Application : Components.classes['@mozilla.org/fuel/application;1']
						.getService(Components.interfaces.fuelIApplication)
	};
var _namespaces;

/**
 * This functiom imports specified script into a unique namespace for the URL.
 * Namespaces for imported scripts have a wrapped version of this function.
 * Both this and wrapped work like as Components.utils.import().
 *
 * @param {String} aScriptURL
 *   URL of a script. Wrapped version of import() can handle related path.
 *   Related path will be resolved based on the location of the caller script.
 * @param {Object=} aExportTarget
 *   EXPORTED_SYMBOLS in the imported script will be exported to the object.
 *   If no object is specified, symbols will be exported to the global object
 *   of the caller.
 *
 * @returns {Object}
 *   The global object for the imported script.
 */
function import(aURISpec, aExportTarget)
{
	if (!_namespaces)
		_namespaces = {};
	var ns;
	if (aURISpec in _namespaces) {
		ns = _namespaces[aURISpec];
		_exportSymbols(ns, aExportTarget);
		return ns;
	}
	ns = _createNamespace(aURISpec);
	Components.classes['@mozilla.org/moz/jssubscript-loader;1']
		.getService(Components.interfaces.mozIJSSubScriptLoader)
		.loadSubScript(aURISpec, ns);
	_exportSymbols(ns, aExportTarget);
	return _namespaces[aURISpec] = ns;
}

function _exportSymbols(aSource, aTarget)
{
	if (
		!aTarget ||
		!('EXPORTED_SYMBOLS' in aSource) ||
		!aSource.EXPORTED_SYMBOLS ||
		!aSource.EXPORTED_SYMBOLS.map ||
		typeof aSource.EXPORTED_SYMBOLS.map != 'function'
		)
		return;
	aSource.EXPORTED_SYMBOLS.map(function(aSymbol) {
		aTarget[aSymbol] = aSource[aSymbol];
	});
}

function _createNamespace(aURISpec)
{
	const IOService = Components.classes['@mozilla.org/network/io-service;1']
						.getService(Components.interfaces.nsIIOService);
	var baseFile = IOService.getProtocolHandler('file')
					.QueryInterface(Components.interfaces.nsIFileProtocolHandler)
					.getFileFromURLSpec(aURISpec);
	var baseURI = IOService.newFileURI(baseFile);
	var ns = {
			location : {
				href : aURISpec,
				toString : function() {
					return this.href;
				}
			},
			__proto__ : _namespacePrototype
		};
	ns.import = function(aURISpec, aExportTarget) {
		var resolved = baseURI.resolve(aURISpec);
		if (resolved == aURISpec)
			throw new Error('Recursive import!');
		return import(resolved, aExportTarget || ns);
	};
	return ns;
}

function _callHandler(aHandler, aReason)
{
	for (var i in _namespaces)
	{
		try {
			if (_namespaces[i][aHandler] &&
				typeof _namespaces[i][aHandler] == 'function')
				_namespaces[i][aHandler](aReason);
		}
		catch(e) {
			dump(i+'('+aHandler+', '+aReason+')\n'+e+'\n');
		}
	}
}

/** Handler for "install" of the bootstrap.js */
function install(aReason)
{
	_callHandler('iustall', aReason);
}

/** Handler for "uninstall" of the bootstrap.js */
function uninstall(aReason)
{
	_callHandler('uninstall', aReason);
}

/** Handler for "shutdown" of the bootstrap.js */
function shutdown(aReason)
{
	_callHandler('shutdown', aReason);
	_namespaces = void(0);

	import = void(0);
	_exportSymbols = void(0);
	_createNamespace = void(0);
	_callHandler = void(0);
	install = void(0);
	uninstall = void(0);
	shutdown = void(0);
}
<commit_msg>support CommonJS style require() and exports ( http://www.commonjs.org/specs/ )<commit_after>/**
 * @fileOverview Importer module for restartless addons
 * @author       SHIMODA "Piro" Hiroshi
 * @version      1
 *
 * @license
 *   The MIT License, Copyright (c) 2010 SHIMODA "Piro" Hiroshi.
 *   http://www.cozmixng.org/repos/piro/restartless-addon/trunk/license.txt
 * @url http://www.cozmixng.org/repos/piro/restartless-addon/trunk/restartless/
 */

/** You can customize shared properties for imported scripts. */
var _namespacePrototype = {
		Cc : Components.classes,
		Ci : Components.interfaces,
		Cu : Components.utils,
		Cr : Components.results,
		Application : Components.classes['@mozilla.org/fuel/application;1']
						.getService(Components.interfaces.fuelIApplication)
	};
var _namespaces;

/**
 * This functiom imports specified script into a unique namespace for the URL.
 * Namespaces for imported scripts have a wrapped version of this function.
 * Both this and wrapped work like as Components.utils.import().
 *
 * @param {String} aScriptURL
 *   URL of a script. Wrapped version of import() can handle related path.
 *   Related path will be resolved based on the location of the caller script.
 * @param {Object=} aExportTarget
 *   EXPORTED_SYMBOLS in the imported script will be exported to the object.
 *   If no object is specified, symbols will be exported to the global object
 *   of the caller.
 *
 * @returns {Object}
 *   The global object for the imported script.
 */
function import(aURISpec, aExportTarget)
{
	if (!_namespaces)
		_namespaces = {};
	var ns;
	if (aURISpec in _namespaces) {
		ns = _namespaces[aURISpec];
		_exportSymbols(ns, aExportTarget);
		return ns;
	}
	ns = _createNamespace(aURISpec);
	Components.classes['@mozilla.org/moz/jssubscript-loader;1']
		.getService(Components.interfaces.mozIJSSubScriptLoader)
		.loadSubScript(aURISpec, ns);
	_exportSymbols(ns, aExportTarget);
	return _namespaces[aURISpec] = ns;
}

function _exportSymbols(aSource, aTarget)
{
	if (!aTarget)
		return;

	// JavaScript code module style
	if (
		('EXPORTED_SYMBOLS' in aSource) &&
		aSource.EXPORTED_SYMBOLS &&
		aSource.EXPORTED_SYMBOLS.map &&
		typeof aSource.EXPORTED_SYMBOLS.map == 'function') {
		aSource.EXPORTED_SYMBOLS.map(function(aSymbol) {
			aTarget[aSymbol] = aSource[aSymbol];
		});
	}

	// CommonJS style
	if (
		('exports' in aSource) &&
		aSource.exports &&
		typeof aSource.exports == 'object') {
		for (let symbol in aSource.exports)
		{
			aTarget[symbol] = aSource[symbol];
		}
	}
}

function _createNamespace(aURISpec)
{
	const IOService = Components.classes['@mozilla.org/network/io-service;1']
						.getService(Components.interfaces.nsIIOService);
	var baseFile = IOService.getProtocolHandler('file')
					.QueryInterface(Components.interfaces.nsIFileProtocolHandler)
					.getFileFromURLSpec(aURISpec);
	var baseURI = IOService.newFileURI(baseFile);
	var ns = {
			__proto__ : _namespacePrototype,
			location : {
				href : aURISpec,
				toString : function() {
					return this.href;
				}
			},
			/** JavaScript code module style */
			import : function(aURISpec, aExportTarget) {
				var resolved = baseURI.resolve(aURISpec);
				if (resolved == aURISpec)
					throw new Error('Recursive import!');
				return import(resolved, aExportTarget || ns);
			},
			/**
			 * CommonJS style
			 * @url http://www.commonjs.org/specs/
			 */
			require : function(aURISpec) {
				aURISpec += '.js';
				var resolved = baseURI.resolve(aURISpec);
				if (resolved == aURISpec)
					throw new Error('Recursive import!');
				var exported = {};
				import(resolved, exported);
				return exported;
			},
			exports : {}
		};
	return ns;
}

function _callHandler(aHandler, aReason)
{
	for (var i in _namespaces)
	{
		try {
			if (_namespaces[i][aHandler] &&
				typeof _namespaces[i][aHandler] == 'function')
				_namespaces[i][aHandler](aReason);
		}
		catch(e) {
			dump(i+'('+aHandler+', '+aReason+')\n'+e+'\n');
		}
	}
}

/** Handler for "install" of the bootstrap.js */
function install(aReason)
{
	_callHandler('iustall', aReason);
}

/** Handler for "uninstall" of the bootstrap.js */
function uninstall(aReason)
{
	_callHandler('uninstall', aReason);
}

/** Handler for "shutdown" of the bootstrap.js */
function shutdown(aReason)
{
	_callHandler('shutdown', aReason);
	_namespaces = void(0);

	import = void(0);
	_exportSymbols = void(0);
	_createNamespace = void(0);
	_callHandler = void(0);
	install = void(0);
	uninstall = void(0);
	shutdown = void(0);
}
<|endoftext|>


<commit_before>//stay fresh, don't repeat rhymes.
var player1Rhymes = [];
var player2Rhymes = [];

//would be so dope to rap about what's happened in the session.
//could read session summary?
//worry about this LATER, and definitely not in this stand alone page.
//for now, rap about your own interests.
window.onload = function() {
		rap(1);
		rap(2);
		rap(1);
}

function rap(playerNum){
	Math.seed =  getRandomSeed();
	var interest =  getRandomElementFromArray(["Pranks","Swimming"]); //TODO make work for all interest categories.
	var chosenRapTemplate = getRandomElementFromArray(rapTemplates);
	var firstWord = chosenRapTemplate.findWordBasedOnPart1AndInterest(interest)
	var secondWord = chosenRapTemplate.findWordBasedOnPart2AndInterestAndPart1Word(interest, firstWord)

	if(firstWord && secondWord && firstWord != secondWord){
		var str = chosenRapTemplate.part1 + firstWord + chosenRapTemplate.part2 + secondWord
		if(playerNum==1){
			if(player1Rhymes.indexOf(firstWord) == -1 && player1Rhymes.indexOf(secondWord) == -1) {
				player1Rhymes.push(firstWord);
				player1Rhymes.push(secondWord);
				rapper1Line(str);
				rap(1); //keep going till you can't
				return;
			}else{
				rapper1Line(str + " Uh. Wait. Um...")
				return;
			}
		}else{
			if(player2Rhymes.indexOf(firstWord) == -1 && player2Rhymes.indexOf(secondWord) == -1) {
				player2Rhymes.push(firstWord);
				player2Rhymes.push(secondWord);
				rapper2Line(str);
				rap(2); //keep going till you can't.
				return;
			}else{
				rapper2Line(str + " Uh. Wait. Um...");
				return;
			}
		}

	}else{
		if(playerNum==1){
				rapper1Line("Um. Shit.");
		}else{
			rapper2Line("Um. Shit.");
		}
	}

	//TODO, KEEP GOING UNTIL HIT MAX LINES OR NO RHYME (HEY, THAT RHYMED)
	//give up if either word is null or if they match.
}

//red text
function rapper1Line(line){
	$("#rap").append(""+line+"
")
}

//blue text
function rapper2Line(line){
	$("#rap").append(""+line+"
")
}
<commit_msg>shitty rap is 'working' just gotta flesh out<commit_after>//stay fresh, don't repeat rhymes.
var player1Rhymes = [];
var player2Rhymes = [];

//would be so dope to rap about what's happened in the session.
//could read session summary?
//worry about this LATER, and definitely not in this stand alone page.
//for now, rap about your own interests.
window.onload = function() {
		rap(1);
		rap(2);
		rap(1);
}

function rap(playerNum){
	Math.seed =  getRandomSeed();
	var interest =  getRandomElementFromArray(["Pranks","Swimming"]); //TODO make work for all interest categories.
	var chosenRapTemplate = getRandomElementFromArray(rapTemplates);
	var firstWord = chosenRapTemplate.findWordBasedOnPart1AndInterest(interest)
	var secondWord = chosenRapTemplate.findWordBasedOnPart2AndInterestAndPart1Word(interest, firstWord)

	if(firstWord && secondWord && firstWord != secondWord){
		var str = chosenRapTemplate.part1 + firstWord + chosenRapTemplate.part2 + secondWord +"."
		if(playerNum==1){
			if(player1Rhymes.indexOf(firstWord) == -1 && player1Rhymes.indexOf(secondWord) == -1) {
				player1Rhymes.push(firstWord);
				player1Rhymes.push(secondWord);
				rapper1Line(str);
				rap(1); //keep going till you can't
				return;
			}else{
				rapper1Line(str + " Uh. Wait. Um...")
				return;
			}
		}else{
			if(player2Rhymes.indexOf(firstWord) == -1 && player2Rhymes.indexOf(secondWord) == -1) {
				player2Rhymes.push(firstWord);
				player2Rhymes.push(secondWord);
				rapper2Line(str);
				rap(2); //keep going till you can't.
				return;
			}else{
				rapper2Line(str + " Uh. Wait. Um...");
				return;
			}
		}

	}else{
		if(playerNum==1){
				rapper1Line("Um. Shit.");
		}else{
			rapper2Line("Um. Shit.");
		}
	}

	//TODO, KEEP GOING UNTIL HIT MAX LINES OR NO RHYME (HEY, THAT RHYMED)
	//give up if either word is null or if they match.
}

//red text
function rapper1Line(line){
	$("#rap").append(""+line+"
")
}

//blue text
function rapper2Line(line){
	$("#rap").append(""+line+"
")
}
<|endoftext|>


<commit_before>/**
 * Wheel, copyright (c) 2017 - present by Arno van der Vegt
 * Distributed under an MIT license: https://arnovandervegt.github.io/wheel/license.txt
**/
const sensorModuleConstants = require('../../../../shared/vm/modules/sensorModuleConstants');
const VMModule              = require('./../VMModule').VMModule;

exports.SensorModule = class extends VMModule {
    run(commandId) {
        let vm     = this._vm;
        let vmData = this._vmData;
        let device = this._device();
        let sensor;
        switch (commandId) {
            case sensorModuleConstants.SENSOR_SET_TYPE:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id', 'type']);
                this.emit('Sensor.SetType', sensor);
                device.module(sensorModuleConstants.MODULE_SENSOR, sensorModuleConstants.SENSOR_SET_TYPE, sensor);
                break;
            case sensorModuleConstants.SENSOR_GET_TYPE:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id']);
                sensor.callback = function(value) { vmData.setNumberAtRet(value); };
                this.emit('Sensor.GetType', sensor);
                break;
            case sensorModuleConstants.SENSOR_SET_MODE:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id', 'mode']);
                this.emit('Sensor.SetMode', sensor);
                device.module(sensorModuleConstants.MODULE_SENSOR, sensorModuleConstants.SENSOR_SET_MODE, sensor);
                break;
            case sensorModuleConstants.SENSOR_RESET:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id']);
                device.module(sensorModuleConstants.MODULE_SENSOR, sensorModuleConstants.SENSOR_RESET, sensor);
                break;
            case sensorModuleConstants.SENSOR_READ:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id']);
                device.module(sensorModuleConstants.MODULE_SENSOR, sensorModuleConstants.SENSOR_READ, sensor);
                let state = device.getLayerState(sensor.layer);
                if (state) {
                    let sensors = state.getSensors() || [];
                    let value   = sensors[sensor.id];
                    vmData.setNumberAtRet(value);
                }
                break;
        }
    }
};
<commit_msg>Check if sensors are assigned.<commit_after>/**
 * Wheel, copyright (c) 2017 - present by Arno van der Vegt
 * Distributed under an MIT license: https://arnovandervegt.github.io/wheel/license.txt
**/
const sensorModuleConstants = require('../../../../shared/vm/modules/sensorModuleConstants');
const VMModule              = require('./../VMModule').VMModule;

exports.SensorModule = class extends VMModule {
    run(commandId) {
        let vm     = this._vm;
        let vmData = this._vmData;
        let device = this._device();
        let sensor;
        switch (commandId) {
            case sensorModuleConstants.SENSOR_SET_TYPE:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id', 'type']);
                this.emit('Sensor.SetType', sensor);
                device.module(sensorModuleConstants.MODULE_SENSOR, sensorModuleConstants.SENSOR_SET_TYPE, sensor);
                break;
            case sensorModuleConstants.SENSOR_GET_TYPE:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id']);
                sensor.callback = function(value) { vmData.setNumberAtRet(value); };
                this.emit('Sensor.GetType', sensor);
                break;
            case sensorModuleConstants.SENSOR_SET_MODE:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id', 'mode']);
                this.emit('Sensor.SetMode', sensor);
                device.module(sensorModuleConstants.MODULE_SENSOR, sensorModuleConstants.SENSOR_SET_MODE, sensor);
                break;
            case sensorModuleConstants.SENSOR_RESET:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id']);
                device.module(sensorModuleConstants.MODULE_SENSOR, sensorModuleConstants.SENSOR_RESET, sensor);
                break;
            case sensorModuleConstants.SENSOR_READ:
                sensor = vmData.getRecordFromSrcOffset(['layer', 'id']);
                device.module(sensorModuleConstants.MODULE_SENSOR, sensorModuleConstants.SENSOR_READ, sensor);
                let state = device.getLayerState(sensor.layer);
                if (state && state.getSensors) {
                    let sensors = state.getSensors() || [];
                    let value   = sensors[sensor.id];
                    vmData.setNumberAtRet(value);
                }
                break;
        }
    }
};
<|endoftext|>