<commit_before>import pc from '../lib/prevent-collision';

import Rect from './rect';

/**
 * Represents the region being measured.
 */
export default class Ruler {
  /**
   * Constructor
   * @param {Overlay} overlay
   * @returns {Ruler}
   */
  constructor(overlay) {
    this.el = document.getElementById(pc(`ruler-rect`));
    if (!this.el) {
      this.el = document.createElement(`div`);
      this.el.id = pc(`ruler-rect`);
      this.el.classList.add(pc(`ruler-rect`));

      this.label = document.createElement(`span`);
      this.label.id = pc(`ruler-label`);
      this.label.classList.add(pc(`ruler-label`));

      this.el.appendChild(this.label);

      overlay.appendChild(this.el);
    }

    this.rect = new Rect();
  }

  /**
   * Draws the Ruler
   * @param {Point} origin
   * @param {Point} terminus
   * @returns {undefined}
   */
  draw(origin, terminus) {
    if (origin) {
      this.terminus.origin = origin;
    }

    if (terminus) {
      this.rect.terminus = terminus;
    }

    if (!this.rect.terminus.x || !this.rect.terminus.y) {
      this.rect.terminus = this.rect.origin;
    }

    console.debug(`draw`, this.rect.origin, this.rect.terminus);

    this.el.style.top = `${this.rect.top}px`;
    this.el.style.right = `${this.rect.right}px`;
    this.el.style.bottom = `${this.rect.bottom}px`;
    this.el.style.left = `${this.rect.left}px`;

    this.el.style.width = `${this.rect.width}px`;
    this.el.style.height = `${this.rect.height}px`;

    this.label.innerHTML = `W:${this.rect.width}, H:${this.rect.height}`;

    console.debug(this.rect.top, this.rect.right, this.rect.bottom, this.rect.left);
  }

  /**
   * Indicates if the ruler being drawn or complete
   * @returns {boolean}
   */
  isDrawing() {
    return this._isDrawing;
  }

  /**
   * Sets the start point for ruler box
   * @param {Point} point
   * @returns {undefined}
   */
  setOrigin(point) {
    console.debug(`setOrigin`, point);

    this._isDrawing = true;

    this.rect = new Rect(point);
    this.draw();
  }

  /**
   * Sets the end point for ruler box
   * @param {Point} point
   * @returns {undefined}
   */
  setTerminus(point) {
    console.debug(`setTerminus`, point);

    this.rect.terminus = point;
    this.draw();

    this._isDrawing = false;
  }
}
<commit_msg>Add diagonal/distance indicator<commit_after>import pc from '../lib/prevent-collision';

import Rect from './rect';

/**
 * Represents the region being measured.
 */
export default class Ruler {
  /**
   * Constructor
   * @param {Overlay} overlay
   * @returns {Ruler}
   */
  constructor(overlay) {
    this.el = document.getElementById(pc(`ruler-rect`));
    if (!this.el) {
      this.el = document.createElement(`div`);
      this.el.id = pc(`ruler-rect`);
      this.el.classList.add(pc(`ruler-rect`));

      this.label = document.createElement(`span`);
      this.label.id = pc(`ruler-label`);
      this.label.classList.add(pc(`ruler-label`));

      this.el.appendChild(this.label);

      overlay.appendChild(this.el);
    }

    this.rect = new Rect();
  }

  /**
   * Draws the Ruler
   * @param {Point} origin
   * @param {Point} terminus
   * @returns {undefined}
   */
  draw(origin, terminus) {
    if (origin) {
      this.terminus.origin = origin;
    }

    if (terminus) {
      this.rect.terminus = terminus;
    }

    if (!this.rect.terminus.x || !this.rect.terminus.y) {
      this.rect.terminus = this.rect.origin;
    }

    console.debug(`draw`, this.rect.origin, this.rect.terminus);

    this.el.style.top = `${this.rect.top}px`;
    this.el.style.right = `${this.rect.right}px`;
    this.el.style.bottom = `${this.rect.bottom}px`;
    this.el.style.left = `${this.rect.left}px`;

    this.el.style.width = `${this.rect.width}px`;
    this.el.style.height = `${this.rect.height}px`;

    var diagonal = Math.sqrt(Math.pow(this.rect.width, 2) + Math.pow(this.rect.height, 2));
    this.label.innerHTML = `W:${this.rect.width}, H:${this.rect.height}  D:${diagonal.toFixed(1)}`;

    console.debug(this.rect.top, this.rect.right, this.rect.bottom, this.rect.left);
  }

  /**
   * Indicates if the ruler being drawn or complete
   * @returns {boolean}
   */
  isDrawing() {
    return this._isDrawing;
  }

  /**
   * Sets the start point for ruler box
   * @param {Point} point
   * @returns {undefined}
   */
  setOrigin(point) {
    console.debug(`setOrigin`, point);

    this._isDrawing = true;

    this.rect = new Rect(point);
    this.draw();
  }

  /**
   * Sets the end point for ruler box
   * @param {Point} point
   * @returns {undefined}
   */
  setTerminus(point) {
    console.debug(`setTerminus`, point);

    this.rect.terminus = point;
    this.draw();

    this._isDrawing = false;
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import {StyleSheet, View, ScrollView, Image, Text, TouchableOpacity, Animated, Dimensions, Platform} from 'react-native';
import {SharedElement} from 'navigation-react-native';
import ZoomShared from './ZoomShared';

export default class Detail extends React.Component {
  constructor(props, context) {
    super(props, context);
    const {url} = props.stateNavigator.stateContext;
    this.url = url;
    this.state = {
      contentAnimation: new Animated.Value(0),
      navbarAnimation: new Animated.Value(0),
    };
  }
  shouldComponentUpdate(props) {
    return this.url === props.stateNavigator.stateContext.url;
  }
  componentDidMount() {
    Animated.timing(this.state.contentAnimation, {
      toValue: 1,
      duration: 500,
      useNativeDriver: true,
    }).start();
    setTimeout(() => {
      Animated.timing(this.state.navbarAnimation, {
        toValue: 1,
        duration: 300,
        useNativeDriver: true,
      }).start();
    }, 500);
  }
  _goBack = () => {
    const {stateNavigator} = this.props;
    if (this.url === stateNavigator.stateContext.url) {
      stateNavigator.navigateBack(1);
      Animated.timing(this.state.contentAnimation, {
        toValue: 0,
        duration: 500,
        useNativeDriver: true,
      }).start();
      Animated.timing(this.state.navbarAnimation, {
        toValue: 0,
        duration: 50,
        useNativeDriver: true,
      }).start();
    }
  };
  render() {
    const {place, rowId, stateNavigator} = this.props;
    const contentTranslateY = this.state.contentAnimation.interpolate({
      inputRange: [0, 1],
      outputRange: [200, 0],
    });
      return (
      <View style={{ flex: 1 }}>
        <ZoomShared stateNavigator={stateNavigator} />
        <ScrollView style={{ flex: 1 }}>
          <View style={styles.imageContainer}>
            <SharedElement
              name={`image${rowId}`}
              data={{image: place.image}}
              stateNavigator={stateNavigator}>
              <Image
                source={place.image}
                style={{ flex: 1 }}
              />
            </SharedElement>
            <SharedElement
              name={`label${rowId}`}
              data={{text: place.price}}
              stateNavigator={stateNavigator}>
              <View style={styles.priceLabel}>
                <Text style={styles.priceLabelText}>
                  ${place.price}
                </Text>
              </View>
            </SharedElement>
            <Animated.View
              style={[styles.navbar, { opacity: this.state.navbarAnimation }]}>
              <TouchableOpacity
                onPress={this._goBack}
                hitSlop={{ top: 10, right: 10, bottom: 10, left: 10 }}>
                <Image
                  style={styles.backButton}
                  source={{
                    uri: 'https://www.android.com/static/img/map/back-arrow.png',
                  }}
                />
              </TouchableOpacity>
              <Text style={styles.title} numberOfLines={1}>{place.title}</Text>
              <View style={styles.placeholder} />
            </Animated.View>
            <Animated.View
              style={{ transform: [{ translateY: contentTranslateY }] }}>
              <Text style={styles.descriptionText}>{place.description}</Text>
            </Animated.View>
          </View>
        </ScrollView>
      </View>
    );
  }
};

const styles = StyleSheet.create({
  imageContainer: {},
  image: {
    width: Dimensions.get('window').width,
    height: 220,
  },
  priceLabel: {
    alignSelf: 'flex-start',
    backgroundColor: 'rgba(0, 0, 0, .75)',
    marginVertical: 16,
    marginLeft: 10,
    paddingVertical: 7.5,
    paddingHorizontal: 10,
  },
  priceLabelText: {
    fontSize: 19,
    color: 'white',
  },
  navbar: {
    position: 'absolute',
    top: Platform.select({
      android: 0,
      ios: 20,
    }),
    left: 0,
    right: 0,
    height: 56,
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: 'transparent',
  },
  backButton: {
    tintColor: 'white',
    width: 20,
    height: 20,
    marginLeft: 16,
  },
  title: {
    color: 'white',
    fontSize: 16,
    flex: 1,
    paddingHorizontal: 10,
    fontWeight: 'bold',
  },
  placeholder: {
    width: 20,
    height: 20,
    marginRight: 16,
  },
  descriptionText: {
    paddingHorizontal: 10,
    marginBottom: 200,
    fontWeight: 'bold',
  },
});
<commit_msg>Refactored into single animate function<commit_after>import React from 'react';
import {StyleSheet, View, ScrollView, Image, Text, TouchableOpacity, Animated, Dimensions, Platform} from 'react-native';
import {SharedElement} from 'navigation-react-native';
import ZoomShared from './ZoomShared';

export default class Detail extends React.Component {
  constructor(props, context) {
    super(props, context);
    const {url} = props.stateNavigator.stateContext;
    this.url = url;
    this.state = {
      contentAnimation: new Animated.Value(0),
      navbarAnimation: new Animated.Value(0),
    };
  }
  shouldComponentUpdate(props) {
    return this.url === props.stateNavigator.stateContext.url;
  }
  componentDidMount() {
    this._animate(false);
  }
  _animate(inverse) {
    Animated.timing(this.state.contentAnimation, {
      toValue: inverse ? 0 : 1,
      duration: 500,
      useNativeDriver: true,
    }).start();
    setTimeout(() => {
      Animated.timing(this.state.navbarAnimation, {
        toValue: inverse ? 0 : 1,
        duration: inverse ? 50 : 300,
        useNativeDriver: true,
      }).start();
    }, inverse ? 0 : 500);

  }
  _goBack = () => {
    const {stateNavigator} = this.props;
    if (this.url === stateNavigator.stateContext.url) {
      stateNavigator.navigateBack(1);
      this._animate(true);
    }
  };
  render() {
    const {place, rowId, stateNavigator} = this.props;
    const contentTranslateY = this.state.contentAnimation.interpolate({
      inputRange: [0, 1],
      outputRange: [200, 0],
    });
      return (
      <View style={{ flex: 1 }}>
        <ZoomShared stateNavigator={stateNavigator} />
        <ScrollView style={{ flex: 1 }}>
          <View style={styles.imageContainer}>
            <SharedElement
              name={`image${rowId}`}
              data={{image: place.image}}
              stateNavigator={stateNavigator}>
              <Image
                source={place.image}
                style={{ flex: 1 }}
              />
            </SharedElement>
            <SharedElement
              name={`label${rowId}`}
              data={{text: place.price}}
              stateNavigator={stateNavigator}>
              <View style={styles.priceLabel}>
                <Text style={styles.priceLabelText}>
                  ${place.price}
                </Text>
              </View>
            </SharedElement>
            <Animated.View
              style={[styles.navbar, { opacity: this.state.navbarAnimation }]}>
              <TouchableOpacity
                onPress={this._goBack}
                hitSlop={{ top: 10, right: 10, bottom: 10, left: 10 }}>
                <Image
                  style={styles.backButton}
                  source={{
                    uri: 'https://www.android.com/static/img/map/back-arrow.png',
                  }}
                />
              </TouchableOpacity>
              <Text style={styles.title} numberOfLines={1}>{place.title}</Text>
              <View style={styles.placeholder} />
            </Animated.View>
            <Animated.View
              style={{ transform: [{ translateY: contentTranslateY }] }}>
              <Text style={styles.descriptionText}>{place.description}</Text>
            </Animated.View>
          </View>
        </ScrollView>
      </View>
    );
  }
};

const styles = StyleSheet.create({
  imageContainer: {},
  image: {
    width: Dimensions.get('window').width,
    height: 220,
  },
  priceLabel: {
    alignSelf: 'flex-start',
    backgroundColor: 'rgba(0, 0, 0, .75)',
    marginVertical: 16,
    marginLeft: 10,
    paddingVertical: 7.5,
    paddingHorizontal: 10,
  },
  priceLabelText: {
    fontSize: 19,
    color: 'white',
  },
  navbar: {
    position: 'absolute',
    top: Platform.select({
      android: 0,
      ios: 20,
    }),
    left: 0,
    right: 0,
    height: 56,
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: 'transparent',
  },
  backButton: {
    tintColor: 'white',
    width: 20,
    height: 20,
    marginLeft: 16,
  },
  title: {
    color: 'white',
    fontSize: 16,
    flex: 1,
    paddingHorizontal: 10,
    fontWeight: 'bold',
  },
  placeholder: {
    width: 20,
    height: 20,
    marginRight: 16,
  },
  descriptionText: {
    paddingHorizontal: 10,
    marginBottom: 200,
    fontWeight: 'bold',
  },
});
<|endoftext|>


<commit_before>/* @flow */
import React from 'react';
import { connect } from 'react-redux';
import { View, StyleSheet, KeyboardAvoidingView } from 'react-native';

import type { NavigationState } from '../types';
import ModalSearchNavBar from '../nav/ModalSearchNavBar';

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'stretch',
  },
  screenWrapper: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'stretch',
  },
});

class SearchScreen extends React.Component {

  props: {
    keyboardAvoiding: boolean,
    title: string,
    isSearchBarApplied: boolean,
    searchBar: boolean,
    searchBarOnChange: () => {},
    nav: NavigationState,
    children: any[],
  };

  state: {
    filter: string,
  };

  render() {
    const { keyboardAvoiding, title, searchBar } = this.props;
    const { searchBarOnChange, nav, children } = this.props;
    const WrapperView = keyboardAvoiding ? KeyboardAvoidingView : View;

    return (
      <View style={styles.screen}>
        <ModalSearchNavBar
          title={title}
          nav={nav}
          searchBar={searchBar}
          searchBarOnChange={searchBarOnChange}
        />
        <WrapperView style={styles.screenWrapper} behavior="padding">
          {children}
        </WrapperView>
      </View>
    );
  }
}

export default connect((state) => ({
  orientation: state.app.orientation,
}))(SearchScreen);
<commit_msg>Fix: messageList not displayed when keyboard pops up in SearchMessageScreen.<commit_after>/* @flow */
import React from 'react';
import { connect } from 'react-redux';
import { View, StyleSheet, KeyboardAvoidingView, Platform } from 'react-native';

import type { NavigationState } from '../types';
import ModalSearchNavBar from '../nav/ModalSearchNavBar';

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'stretch',
  },
  screenWrapper: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center',
    alignItems: 'stretch',
  },
});

class SearchScreen extends React.Component {

  props: {
    keyboardAvoiding: boolean,
    title: string,
    isSearchBarApplied: boolean,
    searchBar: boolean,
    searchBarOnChange: () => {},
    nav: NavigationState,
    children: any[],
  };

  state: {
    filter: string,
  };

  render() {
    const { keyboardAvoiding, title, searchBar } = this.props;
    const { searchBarOnChange, nav, children } = this.props;
    const WrapperView = keyboardAvoiding && Platform.OS === 'ios' ? KeyboardAvoidingView : View;

    return (
      <View style={styles.screen}>
        <ModalSearchNavBar
          title={title}
          nav={nav}
          searchBar={searchBar}
          searchBarOnChange={searchBarOnChange}
        />
        <WrapperView style={styles.screenWrapper} behavior="padding">
          {children}
        </WrapperView>
      </View>
    );
  }
}

export default connect((state) => ({
  orientation: state.app.orientation,
}))(SearchScreen);
<|endoftext|>


<commit_before>/**
 * Generic search/higlight component. Used for instance in http logger and
 * console logger to highlight words. Searches through an actual DOM for
 * the target text.
 * @see VirtualTextSearch
 * @see ListTextSearch
 * @constructor
 */
var TextSearch = function(min_length)
{
  this._init(min_length);
}

TextSearch.prototype = new function()
{
  const
  DEFAULT_SCROLL_MARGIN = 50,
  SEARCH_DELAY = 50, // in ms
  MIN_TERM_LENGTH = 2, // search term must be this long or longer
  NO_MATCH = 1,
  EMPTY = 2;
  window.cls.MessageMixin.apply(this); // mix in message handler behaviour.

  window.addEventListener('load', function()
  {
    var style_sheets = document.styleSheets;
    this._match_style_default =
      style_sheets.getDeclaration ('.search-highlight').cssText;
    this._match_style_highlight =
      style_sheets.getDeclaration ('.search-highlight-selected').cssText;
  }.bind(this), false);

  this._init = function(min_length)
  {
    this._search_term = '';
    this._input_search_term = '';
    // collection of span elements. This is so because a hit may cross an
    // element border, so multiple elements needed for highlight.
    this._hits = [];
    this._match_cursor = -1;
    this._container = null;
    this._input = null;
    this._min_term_length = min_length || MIN_TERM_LENGTH;
    this._search_target = "";
    this._search_term_length = 0;
    this._current_match_index = 0;
    this._last_match_index = 0;
    this._consumed_total_length = 0;
    this._to_consume_hit_length = 0;
    this._curent_search_result = null;
    this._timeouts = new Timeouts();
    this._search_bound = this.search.bind(this);

  }

  this._set_default_style = function(span)
  {
    span.style.cssText = this._match_style_default;
  };

  this._set_highlight_style = function(span)
  {
    span.style.cssText = this._match_style_highlight;
  };

  this._update_info = function(type)
  {
    if(this._info_ele)
    {
      var info = "";
      switch (type)
      {
        case EMPTY:
        {
          break;
        }
        case NO_MATCH:
        {
          info = ui_strings.S_TEXT_STATUS_SEARCH_NO_MATCH.
                 replace("%(SEARCH_TERM)s", this._search_term);
          break;
        }
        default:
        {
          info = ui_strings.S_TEXT_STATUS_SEARCH.
                 replace("%(SEARCH_TERM)s", this._search_term).
                 replace("%(SEARCH_COUNT_TOTAL)s", this._get_match_counts()).
                 replace("%(SEARCH_COUNT_INDEX)s", this._get_search_cursor());
        }
      }
      this._info_ele.textContent = info;
    }
  };

  this._get_match_counts = function()
  {
    return this._hits.length;
  }

  this._get_search_cursor = function()
  {
    return this._match_cursor + 1;
  }

  this._consume_node = function(node)
  {
    if (node && (this._current_match_index != -1 || this._to_consume_hit_length))
    {
      switch (node.nodeType)
      {
        case 1:
        {
          this._consume_node(node.firstChild);
          break;
        }
        case 3:
        {
          if (this._to_consume_hit_length)
          {
            if (node.nodeValue.length >= this._to_consume_hit_length)
            {
              node.splitText(this._to_consume_hit_length);
            }
            // if the search token does not fit in the current node value and
            // the current node is not part of some simple text formatting
            // sequence disregard that this._current_match_index
            else if (!(node.nextSibling || node.parentNode.nextSibling))
            {
              this._to_consume_hit_length = 0;
              this._consumed_total_length += node.nodeValue.length;
              this._hits.pop();
              return;
            }
            this._to_consume_hit_length -= node.nodeValue.length;
            var span = document.createElement('em');
            this._curent_search_result.push(span);
            node.parentNode.replaceChild(span, node);
            span.appendChild(node);
            span.style.cssText = this._match_style_default;
            this._consumed_total_length += node.nodeValue.length;
            node = span;
          }
          else
          {
            if (this._current_match_index - this._consumed_total_length < node.nodeValue.length)
            {
              node.splitText(this._current_match_index - this._consumed_total_length);
              this._current_match_index = this._search_target.indexOf(this._search_term,
                                                                      this._last_match_index)
              if (this._current_match_index != -1)
              {
                this._last_match_index = this._current_match_index + this._search_term_length;
              }
              this._to_consume_hit_length = this._search_term_length;
              this._curent_search_result = this._hits[this._hits.length] = [];
            }
            this._consumed_total_length += node.nodeValue.length;
          }
        }
      }
      this._consume_node(node.nextSibling);
    }
  };

  this._search_node = function(node)
  {
    this._search_target = this._container.textContent.toLowerCase();
    this._search_term_length = this._search_term.length;
    this._current_match_index = this._search_target.indexOf(this._search_term);
    this._last_match_index = this._current_match_index != -1 ?
                             this._current_match_index + this._search_term_length :
                             0;
    this._consumed_total_length = 0;
    this._to_consume_hit_length = 0;
    this._curent_search_result = null;
    this._consume_node(this._container);
  };

  this._clear_search_results = function()
  {
    var cur = null, i = 0, parent = null, search_hit = null, j = 0;
    for (; cur = this._hits[i]; i++)
    {
      for (j = 0; search_hit = cur[j]; j++)
      {
        if (parent = search_hit.parentNode)
        {
          parent.replaceChild(search_hit.firstChild, search_hit);
          parent.normalize();
        }
      }
    }
  };

  this.search = function(new_search_term, old_cursor, force_search)
  {
    if (new_search_term != this._search_term)
    {
      this._search_term = new_search_term;
      this._search_forced = Boolean(force_search);
      if(this._hits)
      {
        this._clear_search_results();
      }
      this._hits = [];
      this._match_cursor = -1;
      this.post_message("onbeforesearch",
                        {search_term: this._search_term.length >= this._min_term_length ?
                                      this._search_term : ""});
      if (this._search_term.length >= this._min_term_length || force_search)
      {
        if(this._container)
        {
          this._search_node(this._container);
          if( old_cursor && this._hits[old_cursor] )
          {
            this._match_cursor = old_cursor;
            this._hits[this._match_cursor].style.cssText = this._match_style_highlight;
            this._update_info();
          }
          else
          {
            this._update_info(NO_MATCH);
            this.highlight(true);
          }
        }
      }
      else
      {
        this._update_info(EMPTY);
      }
    }
  };

  this.search_delayed =
  this.searchDelayed = function(new_search_term)
  {
    this._input_search_term = new_search_term;
    this._timeouts.set(this._search_bound, SEARCH_DELAY,
                       new_search_term.toLowerCase());
  }

  this.update = function()
  {
    var new_search_term = this._search_term;
    this._search_term = '';
    this.search(new_search_term);
  }

  /**
   * Highlight a search result. The result to highlight is kept in the
   * "this._match_cursor" instance variable. If check_position is true the highlight will
   * only be applied to what is visible withing the viewport.
   */
  this.highlight = function(check_position, direction)
  {
    if (this._search_term &&
        this._search_term.length < this._min_term_length &&
        !this._search_forced)
    {
      var new_search_term = this._search_term;
      this._search_term = '';
      this.search(new_search_term, null, true);
      this._search_forced = true;
      return;
    }
    if (this._hits.length)
    {
      if (this._match_cursor >= 0) // if we have a currently highlighted hit..
      {
        // then reset its style to the default
        this._hits[this._match_cursor].forEach(this._set_default_style, this);
      }

      if (check_position)
      {
        this._match_cursor = 0;
        while (this._hits[this._match_cursor] &&
               this.getRealOffsetTop(this._hits[this._match_cursor][0]) < 0)
        {
          this._match_cursor++;
        }
      }
      else
      {
        this._match_cursor += direction;
      }
      if (this._match_cursor > this._hits.length - 1)
      {
        this._match_cursor = 0;
      }
      else if (this._match_cursor < 0)
      {
        this._match_cursor = this._hits.length - 1;
      }
      this._hits[this._match_cursor].forEach(this._set_highlight_style, this);
      var target = this._hits[this._match_cursor][0];
      this._scroll_into_margined_view(this._container.offsetHeight,
                                      target.offsetHeight,
                                      this.getRealOffsetTop(target),
                                      DEFAULT_SCROLL_MARGIN,
                                      direction,
                                      'scrollTop');
      this._scroll_into_margined_view(this._container.offsetWidth,
                                      target.offsetWidth,
                                      this.getRealOffsetLeft(target),
                                      DEFAULT_SCROLL_MARGIN,
                                      direction,
                                      'scrollLeft');
      this._update_info();
    }
    else if (this._search_term)
    {
      this._update_info(NO_MATCH);
    }
    else
    {
      this._update_info(EMPTY);
    }
  };

  this._scroll_into_margined_view = function(container_dim,
                                             target_dim,
                                             offset_dim,
                                             scroll_margin,
                                             direction,
                                             scroll_dim)
  {
    if (container_dim < 2 * scroll_margin + target_dim)
      scroll_margin = (container_dim - target_dim) / 2;
    if (offset_dim < scroll_margin ||
        container_dim - scroll_margin - target_dim < offset_dim)
    {
      if (direction == 1)
        this._container[scroll_dim] += offset_dim - scroll_margin;
      else
        this._container[scroll_dim] += offset_dim - (container_dim -
                                               scroll_margin -
                                               target_dim);
    }
  };

  this.highlight_next = function()
  {
    this.highlight(null, 1);
  };

  this.highlight_previous = function()
  {
    this.highlight(null, -1);
  };

  /**
   * Returns the top coordinate of ele in releation to this._container
   */
  this.getRealOffsetTop = function(ele)
  {
    return ele.getBoundingClientRect().top - this._container.getBoundingClientRect().top;
  };

  this.getRealOffsetLeft = function(ele)
  {
    return ele.getBoundingClientRect().left - this._container.getBoundingClientRect().left;
  };

  this.revalidateSearch = function()
  {
    if( this._container && this._search_term )
    {
      var new_search_term = this._search_term;
      this._search_term = '';
      this.search(new_search_term, this._match_cursor);
    }
  };

  this.set_container =
  this.setContainer = function(cont)
  {
    if( this._container != cont )
    {
      this._container = cont;
    }
  };

  this.set_info_element = function(info_ele)
  {
    this._info_ele = info_ele;
  };

  this.set_form_input =
  this.setFormInput = function(input)
  {
    this._input = input;
    if(this._search_term)
    {
      var new_search_term = this._search_term;
      this._input.value = this._input_search_term;
      this._input.parentNode.firstChild.textContent = '';
      this._search_term = '';
      this.searchDelayed(new_search_term);
    }
  };

  /**
   * Cleanup to be performed when searching has ended
   */
  this.cleanup = function()
  {
    this._clear_search_results();
    this._hits = [];
    this._match_cursor = -1;
    this._input = this._container = this._info_ele = null;
  };

  this.set_search_term = function(search_term)
  {
    this._search_term = search_term;
  }

};
<commit_msg>Added support for ignore case, reg exp and limiting the search to a list of elements, defined with a css selector.<commit_after>/**
 * Generic search/higlight component. Used for instance in http logger and
 * console logger to highlight words. Searches through an actual DOM for
 * the target text.
 * @see VirtualTextSearch
 * @see ListTextSearch
 * @constructor
 */
var TextSearch = function(min_length)
{
  this._init(min_length);
};

TextSearch.PLAIN_TEXT = 1;
TextSearch.REGEXP = 2;

TextSearch.prototype = new function()
{
  const
  DEFAULT_SCROLL_MARGIN = 50,
  SEARCH_DELAY = 50, // in ms
  MIN_TERM_LENGTH = 2, // search term must be this long or longer
  NO_MATCH = 1,
  EMPTY = 2;
  window.cls.MessageMixin.apply(this); // mix in message handler behaviour.

  window.addEventListener('load', function()
  {
    var style_sheets = document.styleSheets;
    this._match_style_default =
      style_sheets.getDeclaration ('.search-highlight').cssText;
    this._match_style_highlight =
      style_sheets.getDeclaration ('.search-highlight-selected').cssText;
  }.bind(this), false);

  this._init = function(min_length)
  {
    this._search_term = '';
    this._input_search_term = '';
    // collection of span elements. This is so because a hit may cross an
    // element border, so multiple elements needed for highlight.
    this._hits = [];
    this._match_cursor = -1;
    this._container = null;
    this._input = null;
    this._min_term_length = min_length || MIN_TERM_LENGTH;
    this._search_target = "";
    this._search_term_length = 0;
    this._current_match_index = 0;
    this._last_match_index = 0;
    this._consumed_total_length = 0;
    this._to_consume_hit_length = 0;
    this._curent_search_result = null;
    this._timeouts = new Timeouts();
    this._search_bound = this.search.bind(this);
    this.ignore_case = 1;
    this.search_type = TextSearch.PLAIN_TEXT;
  }

  this._set_default_style = function(span)
  {
    span.style.cssText = this._match_style_default;
  };

  this._set_highlight_style = function(span)
  {
    span.style.cssText = this._match_style_highlight;
  };

  this._update_info = function(type)
  {
    if(this._info_ele)
    {
      var info = "";
      switch (type)
      {
        case EMPTY:
        {
          break;
        }
        case NO_MATCH:
        {
          info = ui_strings.S_TEXT_STATUS_SEARCH_NO_MATCH.
                 replace("%(SEARCH_TERM)s", this._search_term);
          break;
        }
        default:
        {
          info = ui_strings.S_TEXT_STATUS_SEARCH.
                 replace("%(SEARCH_TERM)s", this._search_term).
                 replace("%(SEARCH_COUNT_TOTAL)s", this._get_match_counts()).
                 replace("%(SEARCH_COUNT_INDEX)s", this._get_search_cursor());
        }
      }
      this._info_ele.textContent = info;
    }
  };

  this._get_match_counts = function()
  {
    return this._hits.length;
  }

  this._get_search_cursor = function()
  {
    return this._match_cursor + 1;
  }

  this._consume_node = function(node)
  {
    if (node && (this._current_match_index != -1 || this._to_consume_hit_length))
    {
      switch (node.nodeType)
      {
        case 1:
        {
          this._consume_node(node.firstChild);
          break;
        }
        case 3:
        {
          if (this._to_consume_hit_length)
          {
            if (node.nodeValue.length >= this._to_consume_hit_length)
            {
              node.splitText(this._to_consume_hit_length);
            }
            // if the search token does not fit in the current node value and
            // the current node is not part of some simple text formatting
            // sequence disregard that this._current_match_index
            else if (!(node.nextSibling || node.parentNode.nextSibling))
            {
              this._to_consume_hit_length = 0;
              this._consumed_total_length += node.nodeValue.length;
              this._hits.pop();
              return;
            }
            this._to_consume_hit_length -= node.nodeValue.length;
            var span = document.createElement('em');
            this._curent_search_result.push(span);
            node.parentNode.replaceChild(span, node);
            span.appendChild(node);
            span.style.cssText = this._match_style_default;
            this._consumed_total_length += node.nodeValue.length;
            node = span;
          }
          else
          {
            if (this._current_match_index - this._consumed_total_length < node.nodeValue.length)
            {
              node.splitText(this._current_match_index - this._consumed_total_length);
              this._to_consume_hit_length = this._search_term_length;
              if (this._reg_exp)
              {
                this._reg_exp_match = this._reg_exp.exec(this._search_target);
                this._current_match_index = this._reg_exp_match ?
                                            this._reg_exp_match.index : -1;
                this._search_term_length = this._reg_exp_match ?
                                           this._reg_exp_match[0].length : 0;
              }
              else
              {
                this._current_match_index = this._search_target.indexOf(this._search_term,
                                                                        this._last_match_index)
              }
              if (this._current_match_index != -1)
              {
                this._last_match_index = this._current_match_index + this._search_term_length;
              }
              this._curent_search_result = this._hits[this._hits.length] = [];
            }
            this._consumed_total_length += node.nodeValue.length;
          }
        }
      }
      this._consume_node(node.nextSibling);
    }
  };

  this._search_node = function(node)
  {
    this._search_target = this.ignore_case &&
                          this.search_type == TextSearch.PLAIN_TEXT ?
                          node.textContent.toLowerCase() :
                          node.textContent;
    if (this._reg_exp)
    {
      this._reg_exp.lastIndex = 0;
      this._reg_exp_match = this._reg_exp.exec(this._search_target);
      this._current_match_index = this._reg_exp_match ?
                                  this._reg_exp_match.index : -1;
      this._search_term_length = this._reg_exp_match ?
                                 this._reg_exp_match[0].length : 0;
    }
    else
    {
      this._search_term_length = this._search_term.length;
      this._current_match_index = this._search_target.indexOf(this._search_term);
    }
    this._last_match_index = this._current_match_index != -1 ?
                             this._current_match_index + this._search_term_length :
                             0;
    this._consumed_total_length = 0;
    this._to_consume_hit_length = 0;
    this._curent_search_result = null;
    this._consume_node(node);
  };

  this._clear_search_results = function()
  {
    var cur = null, i = 0, parent = null, search_hit = null, j = 0;
    for (; cur = this._hits[i]; i++)
    {
      for (j = 0; search_hit = cur[j]; j++)
      {
        if (parent = search_hit.parentNode)
        {
          parent.replaceChild(search_hit.firstChild, search_hit);
          parent.normalize();
        }
      }
    }
  };

  this.search = function(new_search_term, old_cursor, force_search)
  {
    if (new_search_term != this._search_term)
    {
      this._search_term = new_search_term;
      this._search_forced = Boolean(force_search);
      if(this._hits)
      {
        this._clear_search_results();
      }
      this._hits = [];
      this._match_cursor = -1;
      this.post_message("onbeforesearch",
                        {search_term: this._search_term.length >= this._min_term_length ?
                                      this._search_term : ""});
      if (this._search_term.length >= this._min_term_length || force_search)
      {
        this._reg_exp = this.search_type == TextSearch.REGEXP ?
                        new RegExp(this._search_term,
                                   this.ignore_case ? 'gi' : 'g') :
                        null;
        if(this._container)
        {
          if (this._query_selector)
          {
            var nodes = this._container.querySelectorAll(this._query_selector);
            Array.prototype.forEach.call(nodes, this._search_node, this);
          }
          else
          {
            this._search_node(this._container);
          }
          if( old_cursor && this._hits[old_cursor] )
          {
            this._match_cursor = old_cursor;
            this._hits[this._match_cursor].style.cssText = this._match_style_highlight;
            this._update_info();
          }
          else
          {
            this._update_info(NO_MATCH);
            this._container.scrollTop = 0;
            this.highlight(true);
          }
        }
      }
      else
      {
        this._update_info(EMPTY);
      }
    }
  };

  this.search_delayed =
  this.searchDelayed = function(new_search_term)
  {
    this._input_search_term = new_search_term;
    this._timeouts.set(this._search_bound, SEARCH_DELAY,
                       new_search_term.toLowerCase());
  }

  this.update = function()
  {
    var new_search_term = this._search_term;
    this._search_term = '';
    this.search(new_search_term);
  }

  /**
   * Highlight a search result. The result to highlight is kept in the
   * "this._match_cursor" instance variable. If check_position is true the highlight will
   * only be applied to what is visible withing the viewport.
   */
  this.highlight = function(check_position, direction)
  {
    if (this._search_term &&
        this._search_term.length < this._min_term_length &&
        !this._search_forced)
    {
      var new_search_term = this._search_term;
      this._search_term = '';
      this.search(new_search_term, null, true);
      this._search_forced = true;
      return;
    }
    if (this._hits.length)
    {
      if (this._match_cursor >= 0) // if we have a currently highlighted hit..
      {
        // then reset its style to the default
        this._hits[this._match_cursor].forEach(this._set_default_style, this);
      }

      if (check_position)
      {
        this._match_cursor = 0;
        while (this._hits[this._match_cursor] &&
               this.getRealOffsetTop(this._hits[this._match_cursor][0]) < 0)
        {
          this._match_cursor++;
        }
      }
      else
      {
        this._match_cursor += direction;
      }
      if (this._match_cursor > this._hits.length - 1)
      {
        this._match_cursor = 0;
      }
      else if (this._match_cursor < 0)
      {
        this._match_cursor = this._hits.length - 1;
      }
      this._hits[this._match_cursor].forEach(this._set_highlight_style, this);
      var target = this._hits[this._match_cursor][0];
      this._scroll_into_margined_view(this._container.offsetHeight,
                                      target.offsetHeight,
                                      this.getRealOffsetTop(target),
                                      DEFAULT_SCROLL_MARGIN,
                                      direction,
                                      'scrollTop');
      this._scroll_into_margined_view(this._container.offsetWidth,
                                      target.offsetWidth,
                                      this.getRealOffsetLeft(target),
                                      DEFAULT_SCROLL_MARGIN,
                                      direction,
                                      'scrollLeft');
      this._update_info();
    }
    else if (this._search_term)
    {
      this._update_info(NO_MATCH);
    }
    else
    {
      this._update_info(EMPTY);
    }
  };

  this._scroll_into_margined_view = function(container_dim,
                                             target_dim,
                                             offset_dim,
                                             scroll_margin,
                                             direction,
                                             scroll_dim)
  {
    if (container_dim < 2 * scroll_margin + target_dim)
      scroll_margin = (container_dim - target_dim) / 2;
    if (offset_dim < scroll_margin ||
        container_dim - scroll_margin - target_dim < offset_dim)
    {
      if (direction == 1)
        this._container[scroll_dim] += offset_dim - scroll_margin;
      else
        this._container[scroll_dim] += offset_dim - (container_dim -
                                               scroll_margin -
                                               target_dim);
    }
  };

  this.highlight_next = function()
  {
    this.highlight(null, 1);
  };

  this.highlight_previous = function()
  {
    this.highlight(null, -1);
  };

  /**
   * Returns the top coordinate of ele in releation to this._container
   */
  this.getRealOffsetTop = function(ele)
  {
    return ele.getBoundingClientRect().top - this._container.getBoundingClientRect().top;
  };

  this.getRealOffsetLeft = function(ele)
  {
    return ele.getBoundingClientRect().left - this._container.getBoundingClientRect().left;
  };

  this.revalidateSearch = function()
  {
    if( this._container && this._search_term )
    {
      var new_search_term = this._search_term;
      this._search_term = '';
      this.search(new_search_term, this._match_cursor);
    }
  };

  this.set_container =
  this.setContainer = function(cont)
  {
    if( this._container != cont )
    {
      this._container = cont;
    }
  };

  this.set_info_element = function(info_ele)
  {
    this._info_ele = info_ele;
  };

  this.set_form_input =
  this.setFormInput = function(input)
  {
    this._input = input;
    if(this._search_term)
    {
      var new_search_term = this._search_term;
      this._input.value = this._input_search_term;
      this._input.parentNode.firstChild.textContent = '';
      this._search_term = '';
      this.searchDelayed(new_search_term);
    }
  };

  /**
   * Cleanup to be performed when searching has ended
   */
  this.cleanup = function()
  {
    this._clear_search_results();
    this._hits = [];
    this._match_cursor = -1;
    this._input = this._container = this._info_ele = null;
  };

  this.set_search_term = function(search_term)
  {
    this._search_term = search_term;
  }

};
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc filter
 * @name orderBy
 * @kind function
 *
 * @description
 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
 * correctly, make sure they are actually being saved as numbers and not strings.
 *
 * @param {Array} array The array to sort.
 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
 *    used by the comparator to determine the order of elements.
 *
 *    Can be one of:
 *
 *    - `function`: Getter function. The result of this function will be sorted using the
 *      `<`, `=`, `>` operator.
 *    - `string`: An Angular expression. The result of this expression is used to compare elements
 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
 *      3 first characters of a property called `name`). The result of a constant expression
 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
 *      to sort object by the value of their `special name` property). An expression can be
 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
 *      element itself is used to compare where sorting.
 *    - `Array`: An array of function or string predicates. The first predicate in the array
 *      is used for sorting, but when two items are equivalent, the next predicate is used.
 *
 *    If the predicate is missing or empty then it defaults to `'+'`.
 *
 * @param {boolean=} reverse Reverse the order of the array.
 * @returns {Array} Sorted copy of the source array.
 *
 * @example
   <example module="orderByExample">
     <file name="index.html">

Sorting predicate = {{predicate}}; reverse = {{reverse}}

         [ unsorted ]

             Name
                 (^)
             Phone Number
             Age

             {{friend.name}}
             {{friend.phone}}
             {{friend.age}}

     </file>
   </example>
 *
 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
 * desired parameters.
 *
 * Example:
 *
 * @example
  <example module="orderByExample">
    <file name="index.html">

            Name
              (^)
            Phone Number
            Age

            {{friend.name}}
            {{friend.phone}}
            {{friend.age}}

    </file>

    <file name="script.js">
      angular.module('orderByExample', [])
        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
          var orderBy = $filter('orderBy');
          $scope.friends = [
            { name: 'John',    phone: '555-1212',    age: 10 },
            { name: 'Mary',    phone: '555-9876',    age: 19 },
            { name: 'Mike',    phone: '555-4321',    age: 21 },
            { name: 'Adam',    phone: '555-5678',    age: 35 },
            { name: 'Julie',   phone: '555-8765',    age: 29 }
          ];
          $scope.order = function(predicate, reverse) {
            $scope.friends = orderBy($scope.friends, predicate, reverse);
          };
          $scope.order('-age',false);
        }]);
    </file>
</example>
 */
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse) {
  return function(array, sortPredicate, reverseOrder) {
    if (!(isArrayLike(array))) return array;
    sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
    sortPredicate = sortPredicate.map(function(predicate) {
      var descending = false, get = predicate || identity;
      if (isString(predicate)) {
        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
          descending = predicate.charAt(0) == '-';
          predicate = predicate.substring(1);
        }
        if (predicate === '') {
          // Effectively no predicate was passed so we compare identity
          return reverseComparator(compare, descending);
        }
        get = $parse(predicate);
        if (get.constant) {
          var key = get();
          return reverseComparator(function(a, b) {
            return compare(a[key], b[key]);
          }, descending);
        }
      }
      return reverseComparator(function(a, b) {
        return compare(get(a),get(b));
      }, descending);
    });
    return slice.call(array).sort(reverseComparator(comparator, reverseOrder));

    function comparator(o1, o2) {
      for (var i = 0; i < sortPredicate.length; i++) {
        var comp = sortPredicate[i](o1, o2);
        if (comp !== 0) return comp;
      }
      return 0;
    }
    function reverseComparator(comp, descending) {
      return descending
          ? function(a, b) {return comp(b,a);}
          : comp;
    }

    function isPrimitive(value) {
      switch (typeof value) {
        case 'number': /* falls through */
        case 'boolean': /* falls through */
        case 'string':
          return true;
        default:
          return false;
      }
    }

    function objectToString(value) {
      if (value === null) return 'null';
      if (typeof value.valueOf === 'function') {
        value = value.valueOf();
        if (isPrimitive(value)) return value;
      }
      if (typeof value.toString === 'function') {
        value = value.toString();
        if (isPrimitive(value)) return value;
      }
      return '';
    }

    function compare(v1, v2) {
      var t1 = typeof v1;
      var t2 = typeof v2;
      if (t1 === t2 && t1 === "object") {
        v1 = objectToString(v1);
        v2 = objectToString(v2);
      }
      if (t1 === t2) {
        if (t1 === "string") {
           v1 = v1.toLowerCase();
           v2 = v2.toLowerCase();
        }
        if (v1 === v2) return 0;
        return v1 < v2 ? -1 : 1;
      } else {
        return t1 < t2 ? -1 : 1;
      }
    }
  };
}
<commit_msg>docs(orderBy): Start with a simpler example.<commit_after>'use strict';

/**
 * @ngdoc filter
 * @name orderBy
 * @kind function
 *
 * @description
 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
 * correctly, make sure they are actually being saved as numbers and not strings.
 *
 * @param {Array} array The array to sort.
 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
 *    used by the comparator to determine the order of elements.
 *
 *    Can be one of:
 *
 *    - `function`: Getter function. The result of this function will be sorted using the
 *      `<`, `=`, `>` operator.
 *    - `string`: An Angular expression. The result of this expression is used to compare elements
 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
 *      3 first characters of a property called `name`). The result of a constant expression
 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
 *      to sort object by the value of their `special name` property). An expression can be
 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
 *      element itself is used to compare where sorting.
 *    - `Array`: An array of function or string predicates. The first predicate in the array
 *      is used for sorting, but when two items are equivalent, the next predicate is used.
 *
 *    If the predicate is missing or empty then it defaults to `'+'`.
 *
 * @param {boolean=} reverse Reverse the order of the array.
 * @returns {Array} Sorted copy of the source array.
 *
 *
 * @example
 * The example below demonstrates a simple ngRepeat, where the data is sorted
 * by age in descending order (predicate is set to `'-age'`).
 * `reverse` is not set, which means it defaults to `false`.
   <example module="orderByExample">
     <file name="index.html">

             Name
             Phone Number
             Age

             {{friend.name}}
             {{friend.phone}}
             {{friend.age}}

     </file>
   </example>
 *
 * The predicate and reverse parameters can be controlled dynamically through scope properties,
 * as shown in the next example.
 * @example
   <example module="orderByExample">
     <file name="index.html">

Sorting predicate = {{predicate}}; reverse = {{reverse}}

         [ unsorted ]

             Name
                 (^)
             Phone Number
             Age

             {{friend.name}}
             {{friend.phone}}
             {{friend.age}}

     </file>
   </example>
 *
 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
 * desired parameters.
 *
 * Example:
 *
 * @example
  <example module="orderByExample">
    <file name="index.html">

            Name
              (^)
            Phone Number
            Age

            {{friend.name}}
            {{friend.phone}}
            {{friend.age}}

    </file>

    <file name="script.js">
      angular.module('orderByExample', [])
        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
          var orderBy = $filter('orderBy');
          $scope.friends = [
            { name: 'John',    phone: '555-1212',    age: 10 },
            { name: 'Mary',    phone: '555-9876',    age: 19 },
            { name: 'Mike',    phone: '555-4321',    age: 21 },
            { name: 'Adam',    phone: '555-5678',    age: 35 },
            { name: 'Julie',   phone: '555-8765',    age: 29 }
          ];
          $scope.order = function(predicate, reverse) {
            $scope.friends = orderBy($scope.friends, predicate, reverse);
          };
          $scope.order('-age',false);
        }]);
    </file>
</example>
 */
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse) {
  return function(array, sortPredicate, reverseOrder) {
    if (!(isArrayLike(array))) return array;
    sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
    sortPredicate = sortPredicate.map(function(predicate) {
      var descending = false, get = predicate || identity;
      if (isString(predicate)) {
        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
          descending = predicate.charAt(0) == '-';
          predicate = predicate.substring(1);
        }
        if (predicate === '') {
          // Effectively no predicate was passed so we compare identity
          return reverseComparator(compare, descending);
        }
        get = $parse(predicate);
        if (get.constant) {
          var key = get();
          return reverseComparator(function(a, b) {
            return compare(a[key], b[key]);
          }, descending);
        }
      }
      return reverseComparator(function(a, b) {
        return compare(get(a),get(b));
      }, descending);
    });
    return slice.call(array).sort(reverseComparator(comparator, reverseOrder));

    function comparator(o1, o2) {
      for (var i = 0; i < sortPredicate.length; i++) {
        var comp = sortPredicate[i](o1, o2);
        if (comp !== 0) return comp;
      }
      return 0;
    }
    function reverseComparator(comp, descending) {
      return descending
          ? function(a, b) {return comp(b,a);}
          : comp;
    }

    function isPrimitive(value) {
      switch (typeof value) {
        case 'number': /* falls through */
        case 'boolean': /* falls through */
        case 'string':
          return true;
        default:
          return false;
      }
    }

    function objectToString(value) {
      if (value === null) return 'null';
      if (typeof value.valueOf === 'function') {
        value = value.valueOf();
        if (isPrimitive(value)) return value;
      }
      if (typeof value.toString === 'function') {
        value = value.toString();
        if (isPrimitive(value)) return value;
      }
      return '';
    }

    function compare(v1, v2) {
      var t1 = typeof v1;
      var t2 = typeof v2;
      if (t1 === t2 && t1 === "object") {
        v1 = objectToString(v1);
        v2 = objectToString(v2);
      }
      if (t1 === t2) {
        if (t1 === "string") {
           v1 = v1.toLowerCase();
           v2 = v2.toLowerCase();
        }
        if (v1 === v2) return 0;
        return v1 < v2 ? -1 : 1;
      } else {
        return t1 < t2 ? -1 : 1;
      }
    }
  };
}
<|endoftext|>


<commit_before>import { compileLogger, runLogger, runExecutablesAsCompedExecUser } from "../cli.controller";
const MockExpressRequest = require("mock-express-request");
const MockExpressResponse = require("mock-express-response");
import mongoose from "mongoose";
import config from "../../../config/environment";
import { seedTestData, testLoggerMsgToBePrinted } from "../../../config/seed";
import Logger from "../../logger/logger.model";

beforeAll(() => {
  // Connect to database
  mongoose.Promise = global.Promise;
  mongoose.connect(config.mongo.uri, config.mongo.options);

  // Populate DB with sample data
  if (config.seedDB) {
    return seedTestData();
  }
});

const goodCodeCompileResponseJSON = {
  error: false,
  stdout: "",
  stderr: ""
};

const badCodeCompileResponseJSON = {
  error: true,
  stdout: "",
  stderr: expect.stringMatching(/users\/\w+\/\w+\/BadCode\.c:\d+:\d+: error: /)
};

const infiniteLoopCodeCompileResponseJSON = goodCodeCompileResponseJSON;

const goodCodeRunResponseJSON = {
  error: false,
  stdout: testLoggerMsgToBePrinted,
  stderr: ""
};

const badCodeRunResponseJSON = {
  error: true,
  stdout: "",
  stderr: expect.stringMatching(runExecutablesAsCompedExecUser ?
    (/sudo: users\/\w+\/\w+\/BadCode: command not found/) :
    (/users\/\w+\/\w+\/BadCode: No such file or directory/)
  )
};

const infiniteLoopCodeRunResponseJSON = {
  error: true,
  stdout: "",
  stderr: expect.stringMatching(/TimeoutError/)
};

function getMockExpressRequestForLogger(logger) {
  return new MockExpressRequest({
    user: {
      id: "testUserId"
    },
    params: {
      id: logger._id
    }
  });
}

test("Compile Logger with Good Code", async () => {
  let goodCodeLogger = await Logger.findOne({
    className: "GoodCode"
  });

  let req = getMockExpressRequestForLogger(goodCodeLogger);
  let res = new MockExpressResponse();

  let response = await compileLogger(req, res);
  let responseJson = response._getJSON();
  expect(responseJson).toMatchObject(goodCodeCompileResponseJSON);
});

test("Compile Logger with Bad Code", async () => {
  let badCodeLogger = await Logger.findOne({
    className: "BadCode"
  });

  let req = getMockExpressRequestForLogger(badCodeLogger);
  let res = new MockExpressResponse();

  let response = await compileLogger(req, res);
  let responseJson = response._getJSON();
  expect(responseJson).toMatchObject(badCodeCompileResponseJSON);
});

test("Compile Logger with Infinite Loop Code", async () => {
  let infiniteLoopCodeLogger = await Logger.findOne({
    className: "InfiniteLoopCode"
  });

  let req = getMockExpressRequestForLogger(infiniteLoopCodeLogger);
  let res = new MockExpressResponse();

  let response = await compileLogger(req, res);
  let responseJson = response._getJSON();
  expect(responseJson).toMatchObject({
    error: false,
    stdout: "",
    stderr: ""
  });
});

test("Compile Logger Load Test", async () => {
  // Note: On average this test requires:
  // - 15 seconds to run with 2 simultaneous calls to `compileLogger`
  // - 30 seconds to run with 5 simultaneous calls to `compileLogger`
  // - 40 seconds to run with 10 simultaneous calls to `compileLogger`
  // - 100 seconds to run with 30 simultaneous calls to `compileLogger`
  // - 300 seconds to run with 100 simultaneous calls to `compileLogger`
  // Change `numberOfRequests` and `testRunTimeInSeconds` as desired.
  const numberOfRequests = 100;
  const testRunTimeInSeconds = 600;
  jest.setTimeout(testRunTimeInSeconds * 1000);

  let goodCodeLogger = await Logger.findOne({
    className: "GoodCode"
  });
  let badCodeLogger = await Logger.findOne({
    className: "BadCode"
  });
  let infiniteLoopCodeLogger = await Logger.findOne({
    className: "InfiniteLoopCode"
  });

  const allLoggers = [goodCodeLogger, badCodeLogger, infiniteLoopCodeLogger];
  const allCompileResponseJSONs = [goodCodeCompileResponseJSON, badCodeCompileResponseJSON, infiniteLoopCodeCompileResponseJSON];

  const outputResponsePromises = [];

  expect.assertions(numberOfRequests);

  // execute a bunch of calls to compileLogger
  for (let i = 0; i < numberOfRequests; i++) {
    let randomIndex = Math.floor(Math.random() * 3);  // random integer between 0 and 2 inclusive
    let req = getMockExpressRequestForLogger(allLoggers[randomIndex]);
    let res = new MockExpressResponse();
    outputResponsePromises[i] = compileLogger(req, res).then(response => {
      let responseJson = response._getJSON();
      expect(responseJson).toMatchObject(allCompileResponseJSONs[randomIndex]);
    }).catch(err => {
      throw err;
    });
  }

  return Promise.all(outputResponsePromises);
});

test("Run Logger with Good Code", async () => {
  let goodCodeLogger = await Logger.findOne({
    className: "GoodCode"
  });

  let req = getMockExpressRequestForLogger(goodCodeLogger);
  let res = new MockExpressResponse();

  let compileResponse = await compileLogger(req, res);
  res = new MockExpressResponse();
  let response = await runLogger(req, res);
  let responseJson = response._getJSON();
  expect(responseJson).toMatchObject(goodCodeRunResponseJSON);
});

test("Run Logger with Bad Code", async () => {
  let badCodeLogger = await Logger.findOne({
    className: "BadCode"
  });

  let req = getMockExpressRequestForLogger(badCodeLogger);
  let res = new MockExpressResponse();

  let compileResponse = await compileLogger(req, res);
  res = new MockExpressResponse();
  let response = await runLogger(req, res);
  let responseJson = response._getJSON();
  expect(responseJson).toMatchObject(badCodeRunResponseJSON);
});

test("Run Logger with Infinite Loop Code", async () => {
  jest.setTimeout(25000);
  let infiniteLoopCodeLogger = await Logger.findOne({
    className: "InfiniteLoopCode"
  });

  let req = getMockExpressRequestForLogger(infiniteLoopCodeLogger);
  let res = new MockExpressResponse();

  let compileResponse = await compileLogger(req, res);
  res = new MockExpressResponse();
  let response = await runLogger(req, res);
  let responseJson = response._getJSON();
  expect(responseJson).toMatchObject(infiniteLoopCodeRunResponseJSON);
});

test("Run Logger Load Test", async () => {
  // Note: On average this test requires:
  // - 20 seconds to run with 2 simultaneous calls to `runLogger`
  // - 40 seconds to run with 5 simultaneous calls to `runLogger`
  // - 60 seconds to run with 10 simultaneous calls to `runLogger`
  // - 200 seconds to run with 30 simultaneous calls to `runLogger`
  // - 600 seconds to run with 100 simultaneous calls to `runLogger`
  // Change `numberOfRequests` and `testRunTimeInSeconds` as desired.
  const numberOfRequests = 100;
  const testRunTimeInSeconds = 600;
  jest.setTimeout(testRunTimeInSeconds * 1000);

  let goodCodeLogger = await Logger.findOne({
    className: "GoodCode"
  });
  let badCodeLogger = await Logger.findOne({
    className: "BadCode"
  });
  let infiniteLoopCodeLogger = await Logger.findOne({
    className: "InfiniteLoopCode"
  });

  const allLoggers = [goodCodeLogger, badCodeLogger, infiniteLoopCodeLogger];
  const allRunResponseJSONs = [goodCodeRunResponseJSON, badCodeRunResponseJSON, infiniteLoopCodeRunResponseJSON];

  const outputResponsePromises = [];

  expect.assertions(numberOfRequests);

  // execute compileLogger for each logger in advance of calling runLogger
  for (let i = 0; i < 3; i++) {
    let req = getMockExpressRequestForLogger(allLoggers[i]);
    let res = new MockExpressResponse();
    await compileLogger(req, res);
  }

  // execute a bunch of calls to runLogger
  for (let i = 0; i < numberOfRequests; i++) {
    let randomIndex = Math.floor(Math.random() * 3);  // random integer between 0 and 2 inclusive
    let req = getMockExpressRequestForLogger(allLoggers[randomIndex]);
    let res = new MockExpressResponse();
    outputResponsePromises[i] = runLogger(req, res).then(response => {
      let responseJson = response._getJSON();
      expect(responseJson).toMatchObject(allRunResponseJSONs[randomIndex]);
    }).catch(err => {
      throw err;
    });
  }

  return Promise.all(outputResponsePromises);
});

test("Compile And Run Logger Load Test", async () => {
  // Note: On average this test requires:
  // - 20 seconds to run with 2 simultaneous calls to `runLogger`
  // - 40 seconds to run with 5 simultaneous calls to `runLogger`
  // - 60 seconds to run with 10 simultaneous calls to `runLogger`
  // - 200 seconds to run with 30 simultaneous calls to `runLogger`
  // - 600 seconds to run with 100 simultaneous calls to `runLogger`
  // Change `numberOfRequests` and `testRunTimeInSeconds` as desired.
  const numberOfRequests = 100;
  const testRunTimeInSeconds = 600;
  jest.setTimeout(testRunTimeInSeconds * 1000);

  let goodCodeLogger = await Logger.findOne({
    className: "GoodCode"
  });
  let badCodeLogger = await Logger.findOne({
    className: "BadCode"
  });
  let infiniteLoopCodeLogger = await Logger.findOne({
    className: "InfiniteLoopCode"
  });

  const allLoggers = [goodCodeLogger, badCodeLogger, infiniteLoopCodeLogger, goodCodeLogger, badCodeLogger, infiniteLoopCodeLogger];
  const allResponseJSONs = [
    goodCodeCompileResponseJSON, badCodeCompileResponseJSON, infiniteLoopCodeCompileResponseJSON,
    goodCodeRunResponseJSON, badCodeRunResponseJSON, infiniteLoopCodeRunResponseJSON
  ];
  const runOrCompileFunctions = [compileLogger, runLogger];

  const outputResponsePromises = [];

  expect.assertions(numberOfRequests);

  // execute compileLogger for each logger in advance of calling runLogger
  for (let i = 0; i < 3; i++) {
    let req = getMockExpressRequestForLogger(allLoggers[i]);
    let res = new MockExpressResponse();
    await compileLogger(req, res);
  }

  let startTime = Date.now();

  // execute a bunch of calls to runLogger
  for (let i = 0; i < numberOfRequests; i++) {
    let randomIndex = Math.floor(Math.random() * 6);  // random integer between 0 and 5 inclusive
    let runOrCompileIndex = Math.floor(randomIndex / 3);  // random choice of 0 or 1
    let runOrCompile = runOrCompileFunctions[runOrCompileIndex];  // random choice of `compileLogger` or `runLogger`
    let req = getMockExpressRequestForLogger(allLoggers[randomIndex]);
    let res = new MockExpressResponse();
    outputResponsePromises[i] = runOrCompile(req, res).then(response => {
      let responseJson = response._getJSON();
      expect(responseJson).toMatchObject(allResponseJSONs[randomIndex]);
    }).catch(err => {
      throw err;
    });
  }

  return Promise.all(outputResponsePromises).then(_ => {
    let endTime = Date.now();
    let timeElapsed = (endTime - startTime) / 1000;
    console.log(`We're done! It took ${timeElapsed} seconds to complete ${numberOfRequests} calls.`);
  });
});
<commit_msg>Comment out all tests except run load test<commit_after>import { compileLogger, runLogger, runExecutablesAsCompedExecUser } from "../cli.controller";
const MockExpressRequest = require("mock-express-request");
const MockExpressResponse = require("mock-express-response");
import mongoose from "mongoose";
import config from "../../../config/environment";
import { seedTestData, testLoggerMsgToBePrinted } from "../../../config/seed";
import Logger from "../../logger/logger.model";

beforeAll(() => {
  // Connect to database
  mongoose.Promise = global.Promise;
  mongoose.connect(config.mongo.uri, config.mongo.options);

  // Populate DB with sample data
  if (config.seedDB) {
    return seedTestData();
  }
});

const goodCodeCompileResponseJSON = {
  error: false,
  stdout: "",
  stderr: ""
};

const badCodeCompileResponseJSON = {
  error: true,
  stdout: "",
  stderr: expect.stringMatching(/users\/\w+\/\w+\/BadCode\.c:\d+:\d+: error: /)
};

const infiniteLoopCodeCompileResponseJSON = goodCodeCompileResponseJSON;

const goodCodeRunResponseJSON = {
  error: false,
  stdout: testLoggerMsgToBePrinted,
  stderr: ""
};

const badCodeRunResponseJSON = {
  error: true,
  stdout: "",
  stderr: expect.stringMatching(runExecutablesAsCompedExecUser ?
    (/sudo: users\/\w+\/\w+\/BadCode: command not found/) :
    (/users\/\w+\/\w+\/BadCode: No such file or directory/)
  )
};

const infiniteLoopCodeRunResponseJSON = {
  error: true,
  stdout: "",
  stderr: expect.stringMatching(/TimeoutError/)
};

function getMockExpressRequestForLogger(logger) {
  return new MockExpressRequest({
    user: {
      id: "testUserId"
    },
    params: {
      id: logger._id
    }
  });
}

// test("Compile Logger with Good Code", async () => {
//   let goodCodeLogger = await Logger.findOne({
//     className: "GoodCode"
//   });
//
//   let req = getMockExpressRequestForLogger(goodCodeLogger);
//   let res = new MockExpressResponse();
//
//   let response = await compileLogger(req, res);
//   let responseJson = response._getJSON();
//   expect(responseJson).toMatchObject(goodCodeCompileResponseJSON);
// });
//
// test("Compile Logger with Bad Code", async () => {
//   let badCodeLogger = await Logger.findOne({
//     className: "BadCode"
//   });
//
//   let req = getMockExpressRequestForLogger(badCodeLogger);
//   let res = new MockExpressResponse();
//
//   let response = await compileLogger(req, res);
//   let responseJson = response._getJSON();
//   expect(responseJson).toMatchObject(badCodeCompileResponseJSON);
// });
//
// test("Compile Logger with Infinite Loop Code", async () => {
//   let infiniteLoopCodeLogger = await Logger.findOne({
//     className: "InfiniteLoopCode"
//   });
//
//   let req = getMockExpressRequestForLogger(infiniteLoopCodeLogger);
//   let res = new MockExpressResponse();
//
//   let response = await compileLogger(req, res);
//   let responseJson = response._getJSON();
//   expect(responseJson).toMatchObject({
//     error: false,
//     stdout: "",
//     stderr: ""
//   });
// });
//
// test("Compile Logger Load Test", async () => {
//   // Note: On average this test requires:
//   // - 15 seconds to run with 2 simultaneous calls to `compileLogger`
//   // - 30 seconds to run with 5 simultaneous calls to `compileLogger`
//   // - 40 seconds to run with 10 simultaneous calls to `compileLogger`
//   // - 100 seconds to run with 30 simultaneous calls to `compileLogger`
//   // - 300 seconds to run with 100 simultaneous calls to `compileLogger`
//   // Change `numberOfRequests` and `testRunTimeInSeconds` as desired.
//   const numberOfRequests = 100;
//   const testRunTimeInSeconds = 600;
//   jest.setTimeout(testRunTimeInSeconds * 1000);
//
//   let goodCodeLogger = await Logger.findOne({
//     className: "GoodCode"
//   });
//   let badCodeLogger = await Logger.findOne({
//     className: "BadCode"
//   });
//   let infiniteLoopCodeLogger = await Logger.findOne({
//     className: "InfiniteLoopCode"
//   });
//
//   const allLoggers = [goodCodeLogger, badCodeLogger, infiniteLoopCodeLogger];
//   const allCompileResponseJSONs = [goodCodeCompileResponseJSON, badCodeCompileResponseJSON, infiniteLoopCodeCompileResponseJSON];
//
//   const outputResponsePromises = [];
//
//   expect.assertions(numberOfRequests);
//
//   // execute a bunch of calls to compileLogger
//   for (let i = 0; i < numberOfRequests; i++) {
//     let randomIndex = Math.floor(Math.random() * 3);  // random integer between 0 and 2 inclusive
//     let req = getMockExpressRequestForLogger(allLoggers[randomIndex]);
//     let res = new MockExpressResponse();
//     outputResponsePromises[i] = compileLogger(req, res).then(response => {
//       let responseJson = response._getJSON();
//       expect(responseJson).toMatchObject(allCompileResponseJSONs[randomIndex]);
//     }).catch(err => {
//       throw err;
//     });
//   }
//
//   return Promise.all(outputResponsePromises);
// });
//
// test("Run Logger with Good Code", async () => {
//   let goodCodeLogger = await Logger.findOne({
//     className: "GoodCode"
//   });
//
//   let req = getMockExpressRequestForLogger(goodCodeLogger);
//   let res = new MockExpressResponse();
//
//   let compileResponse = await compileLogger(req, res);
//   res = new MockExpressResponse();
//   let response = await runLogger(req, res);
//   let responseJson = response._getJSON();
//   expect(responseJson).toMatchObject(goodCodeRunResponseJSON);
// });
//
// test("Run Logger with Bad Code", async () => {
//   let badCodeLogger = await Logger.findOne({
//     className: "BadCode"
//   });
//
//   let req = getMockExpressRequestForLogger(badCodeLogger);
//   let res = new MockExpressResponse();
//
//   let compileResponse = await compileLogger(req, res);
//   res = new MockExpressResponse();
//   let response = await runLogger(req, res);
//   let responseJson = response._getJSON();
//   expect(responseJson).toMatchObject(badCodeRunResponseJSON);
// });
//
// test("Run Logger with Infinite Loop Code", async () => {
//   jest.setTimeout(25000);
//   let infiniteLoopCodeLogger = await Logger.findOne({
//     className: "InfiniteLoopCode"
//   });
//
//   let req = getMockExpressRequestForLogger(infiniteLoopCodeLogger);
//   let res = new MockExpressResponse();
//
//   let compileResponse = await compileLogger(req, res);
//   res = new MockExpressResponse();
//   let response = await runLogger(req, res);
//   let responseJson = response._getJSON();
//   expect(responseJson).toMatchObject(infiniteLoopCodeRunResponseJSON);
// });

test("Run Logger Load Test", async () => {
  // Note: On average this test requires:
  // - 20 seconds to run with 2 simultaneous calls to `runLogger`
  // - 40 seconds to run with 5 simultaneous calls to `runLogger`
  // - 60 seconds to run with 10 simultaneous calls to `runLogger`
  // - 200 seconds to run with 30 simultaneous calls to `runLogger`
  // - 600 seconds to run with 100 simultaneous calls to `runLogger`
  // Change `numberOfRequests` and `testRunTimeInSeconds` as desired.
  const numberOfRequests = 100;
  const testRunTimeInSeconds = 600;
  jest.setTimeout(testRunTimeInSeconds * 1000);

  let goodCodeLogger = await Logger.findOne({
    className: "GoodCode"
  });
  let badCodeLogger = await Logger.findOne({
    className: "BadCode"
  });
  let infiniteLoopCodeLogger = await Logger.findOne({
    className: "InfiniteLoopCode"
  });

  const allLoggers = [goodCodeLogger, badCodeLogger, infiniteLoopCodeLogger];
  const allRunResponseJSONs = [goodCodeRunResponseJSON, badCodeRunResponseJSON, infiniteLoopCodeRunResponseJSON];

  const outputResponsePromises = [];

  expect.assertions(numberOfRequests);

  // execute compileLogger for each logger in advance of calling runLogger
  for (let i = 0; i < 3; i++) {
    let req = getMockExpressRequestForLogger(allLoggers[i]);
    let res = new MockExpressResponse();
    await compileLogger(req, res);
  }

  // execute a bunch of calls to runLogger
  for (let i = 0; i < numberOfRequests; i++) {
    let randomIndex = Math.floor(Math.random() * 3);  // random integer between 0 and 2 inclusive
    let req = getMockExpressRequestForLogger(allLoggers[randomIndex]);
    let res = new MockExpressResponse();
    outputResponsePromises[i] = runLogger(req, res).then(response => {
      let responseJson = response._getJSON();
      expect(responseJson).toMatchObject(allRunResponseJSONs[randomIndex]);
    }).catch(err => {
      throw err;
    });
  }

  return Promise.all(outputResponsePromises);
});

// test("Compile And Run Logger Load Test", async () => {
//   // Note: On average this test requires:
//   // - 20 seconds to run with 2 simultaneous calls to `runLogger`
//   // - 40 seconds to run with 5 simultaneous calls to `runLogger`
//   // - 60 seconds to run with 10 simultaneous calls to `runLogger`
//   // - 200 seconds to run with 30 simultaneous calls to `runLogger`
//   // - 600 seconds to run with 100 simultaneous calls to `runLogger`
//   // Change `numberOfRequests` and `testRunTimeInSeconds` as desired.
//   const numberOfRequests = 100;
//   const testRunTimeInSeconds = 600;
//   jest.setTimeout(testRunTimeInSeconds * 1000);
//
//   let goodCodeLogger = await Logger.findOne({
//     className: "GoodCode"
//   });
//   let badCodeLogger = await Logger.findOne({
//     className: "BadCode"
//   });
//   let infiniteLoopCodeLogger = await Logger.findOne({
//     className: "InfiniteLoopCode"
//   });
//
//   const allLoggers = [goodCodeLogger, badCodeLogger, infiniteLoopCodeLogger, goodCodeLogger, badCodeLogger, infiniteLoopCodeLogger];
//   const allResponseJSONs = [
//     goodCodeCompileResponseJSON, badCodeCompileResponseJSON, infiniteLoopCodeCompileResponseJSON,
//     goodCodeRunResponseJSON, badCodeRunResponseJSON, infiniteLoopCodeRunResponseJSON
//   ];
//   const runOrCompileFunctions = [compileLogger, runLogger];
//
//   const outputResponsePromises = [];
//
//   expect.assertions(numberOfRequests);
//
//   // execute compileLogger for each logger in advance of calling runLogger
//   for (let i = 0; i < 3; i++) {
//     let req = getMockExpressRequestForLogger(allLoggers[i]);
//     let res = new MockExpressResponse();
//     await compileLogger(req, res);
//   }
//
//   let startTime = Date.now();
//
//   // execute a bunch of calls to runLogger
//   for (let i = 0; i < numberOfRequests; i++) {
//     let randomIndex = Math.floor(Math.random() * 6);  // random integer between 0 and 5 inclusive
//     let runOrCompileIndex = Math.floor(randomIndex / 3);  // random choice of 0 or 1
//     let runOrCompile = runOrCompileFunctions[runOrCompileIndex];  // random choice of `compileLogger` or `runLogger`
//     let req = getMockExpressRequestForLogger(allLoggers[randomIndex]);
//     let res = new MockExpressResponse();
//     outputResponsePromises[i] = runOrCompile(req, res).then(response => {
//       let responseJson = response._getJSON();
//       expect(responseJson).toMatchObject(allResponseJSONs[randomIndex]);
//     }).catch(err => {
//       throw err;
//     });
//   }
//
//   return Promise.all(outputResponsePromises).then(_ => {
//     let endTime = Date.now();
//     let timeElapsed = (endTime - startTime) / 1000;
//     console.log(`We're done! It took ${timeElapsed} seconds to complete ${numberOfRequests} calls.`);
//   });
// });
<|endoftext|>


<commit_before>// Copyright (c) 2012 Titanium I.T. LLC. All rights reserved. See LICENSE.txt for details.
(function() {
	"use strict";

	var server = require("./server.js");
	var http = require("http");
	var fs = require("fs");
	var assert = require("assert");

	var CONTENT_DIR = "generated/test";

	var HOME_PAGE = "index.html";
	var NOT_FOUND_PAGE = "test404.html";

	var HOME_PAGE_DATA = "This is home page file";
	var NOT_FOUND_DATA = "This is 404 page file";

	var PORT = 5020;
	var BASE_URL = "http://localhost:" + PORT;

	exports.setUp = function(done) {
		fs.writeFileSync(CONTENT_DIR + "/" + HOME_PAGE, HOME_PAGE_DATA);
		fs.writeFileSync(CONTENT_DIR + "/" + NOT_FOUND_PAGE, NOT_FOUND_DATA);

		done();
	};

	exports.tearDown = function(done) {
		cleanUpFile(CONTENT_DIR + "/" + HOME_PAGE);
		cleanUpFile(CONTENT_DIR + "/" + NOT_FOUND_PAGE);
		done();
	};

	exports.test_servesFilesFromDirectory = function(test) {
		httpGet(BASE_URL + "/index.html", function(response, responseData) {
			test.equals(200, response.statusCode, "status code");
			test.equals(HOME_PAGE_DATA, responseData, "response text");
			test.done();
		});
	};

	exports.test_supportsMultipleFiles = function(test) {
		//TODO
		test.done();
	};

	exports.test_servesIndexDotHtmlWhenAskedForHomePage = function(test) {
		httpGet(BASE_URL, function(response, responseData) {
			test.equals(200, response.statusCode, "status code");
			test.equals(HOME_PAGE_DATA, responseData, "response text");
			test.done();
		});
	};

	exports.test_returns404WhenFileDoesNotExist = function(test) {
		httpGet(BASE_URL + "/bargle", function(response, responseData) {
			test.equals(404, response.statusCode, "status code");
			test.equals(NOT_FOUND_DATA, responseData, "404 text");
			test.done();
		});
	};

	exports.test_requiresHomePageParameter = function(test) {
		test.throws(function() {
			server.start();
		});
		test.done();
	};

	exports.test_requires404PageParameter = function(test) {
		test.throws(function() {
			server.start(CONTENT_DIR);
		});
		test.done();
	};

	exports.test_requiresPortParameter = function(test) {
		test.throws(function() {
			server.start(CONTENT_DIR, NOT_FOUND_PAGE);
		});
		test.done();
	};

	exports.test_runsCallbackWhenStopCompletes = function(test) {
		server.start(CONTENT_DIR, NOT_FOUND_PAGE, PORT);
		server.stop(function() {
			test.done();
		});
	};

	exports.test_stopThrowsExceptionWhenNotRunning = function(test) {
		test.throws(function() {
			server.stop();
		});
		test.done();
	};

	function httpGet(url, callback) {
		server.start(CONTENT_DIR, NOT_FOUND_PAGE, PORT, function() {
			http.get(url, function(response) {
				var receivedData = "";
				response.setEncoding("utf8");

				response.on("data", function(chunk) {
					receivedData += chunk;
				});
				response.on("error", function(err) {
					console.log("ERROR", err);
				});
				response.on("end", function() {
					server.stop(function() {
						callback(response, receivedData);
					});
				});
			});
		});
	}

	function cleanUpFile(file) {
		if (fs.existsSync(file)) {
			fs.unlinkSync(file);
			assert.ok(!fs.existsSync(file), "could not delete test file: [" + file + "]");
		}
	}

}());<commit_msg>Added test for multi-page support<commit_after>// Copyright (c) 2012 Titanium I.T. LLC. All rights reserved. See LICENSE.txt for details.
(function() {
	"use strict";

	var server = require("./server.js");
	var http = require("http");
	var fs = require("fs");
	var assert = require("assert");

	var CONTENT_DIR = "generated/test";

	var INDEX_PAGE = "index.html";
	var OTHER_PAGE = "other.html";
	var NOT_FOUND_PAGE = "test404.html";

	var INDEX_PAGE_DATA = "This is index page file";
	var OTHER_PAGE_DATA = "This is another page";
	var NOT_FOUND_DATA = "This is 404 page file";

	var PORT = 5020;
	var BASE_URL = "http://localhost:" + PORT;

	exports.setUp = function(done) {
		fs.writeFileSync(CONTENT_DIR + "/" + INDEX_PAGE, INDEX_PAGE_DATA);
		fs.writeFileSync(CONTENT_DIR + "/" + OTHER_PAGE, OTHER_PAGE_DATA);
		fs.writeFileSync(CONTENT_DIR + "/" + NOT_FOUND_PAGE, NOT_FOUND_DATA);

		done();
	};

	exports.tearDown = function(done) {
		cleanUpFile(CONTENT_DIR + "/" + INDEX_PAGE);
		cleanUpFile(CONTENT_DIR + "/" + OTHER_PAGE);
		cleanUpFile(CONTENT_DIR + "/" + NOT_FOUND_PAGE);
		done();
	};

	exports.test_servesFilesFromDirectory = function(test) {
		httpGet(BASE_URL + "/" + INDEX_PAGE, function(response, responseData) {
			test.equals(200, response.statusCode, "status code");
			test.equals(INDEX_PAGE_DATA, responseData, "response text");
			test.done();
		});
	};

	exports.test_supportsMultipleFiles = function(test) {
		httpGet(BASE_URL + "/" + OTHER_PAGE, function(response, responseData) {
			test.equals(200, response.statusCode, "status code");
			test.equals(OTHER_PAGE_DATA, responseData, "response text");
			test.done();
		});
	};

	exports.test_servesIndexDotHtmlWhenAskedForHomePage = function(test) {
		httpGet(BASE_URL, function(response, responseData) {
			test.equals(200, response.statusCode, "status code");
			test.equals(INDEX_PAGE_DATA, responseData, "response text");
			test.done();
		});
	};

	exports.test_returns404WhenFileDoesNotExist = function(test) {
		httpGet(BASE_URL + "/bargle", function(response, responseData) {
			test.equals(404, response.statusCode, "status code");
			test.equals(NOT_FOUND_DATA, responseData, "404 text");
			test.done();
		});
	};

	exports.test_requiresHomePageParameter = function(test) {
		test.throws(function() {
			server.start();
		});
		test.done();
	};

	exports.test_requires404PageParameter = function(test) {
		test.throws(function() {
			server.start(CONTENT_DIR);
		});
		test.done();
	};

	exports.test_requiresPortParameter = function(test) {
		test.throws(function() {
			server.start(CONTENT_DIR, NOT_FOUND_PAGE);
		});
		test.done();
	};

	exports.test_runsCallbackWhenStopCompletes = function(test) {
		server.start(CONTENT_DIR, NOT_FOUND_PAGE, PORT);
		server.stop(function() {
			test.done();
		});
	};

	exports.test_stopThrowsExceptionWhenNotRunning = function(test) {
		test.throws(function() {
			server.stop();
		});
		test.done();
	};

	function httpGet(url, callback) {
		server.start(CONTENT_DIR, NOT_FOUND_PAGE, PORT, function() {
			http.get(url, function(response) {
				var receivedData = "";
				response.setEncoding("utf8");

				response.on("data", function(chunk) {
					receivedData += chunk;
				});
				response.on("error", function(err) {
					console.log("ERROR", err);
				});
				response.on("end", function() {
					server.stop(function() {
						callback(response, receivedData);
					});
				});
			});
		});
	}

	function cleanUpFile(file) {
		if (fs.existsSync(file)) {
			fs.unlinkSync(file);
			assert.ok(!fs.existsSync(file), "could not delete test file: [" + file + "]");
		}
	}

}());<|endoftext|>


<commit_before>export default {

  create () {
    this.stage.backgroundColor = 0x000000; // black

    var aiLogo = this.add.image(0, 0, 'logo-adventure-islands');
    var rbLogo = this.add.image(0, 0, 'logo-rb');

    var [ first, second ] = this._makeLogoFadeEffect(aiLogo);
    var [      , last   ] = this._makeLogoFadeEffect(rbLogo, second);

    first.start();
    last.onComplete.addOnce(this._goToNextState, this);
  },

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

  _makeTween (object, chainWith = null) {
    var tween = this.add.tween(object);

    if (chainWith !== null)
      chainWith.chain(tween);

    return tween;
  },

  _makeLogoFadeEffect (logo, chainWith = null) {
    var easingFunction = Phaser.Easing.Sinusoidal.Out;

    var reveal = this._makeTween(logo, chainWith);
    var fade   = this._makeTween(logo, reveal);

    reveal.from({ alpha: 0 }, 1000, easingFunction, false, 1500);
    fade.to({ alpha: 0 }, 1000, easingFunction, false, 1500);

    return [ reveal, fade ];
  },

  _goToNextState () {
    this.state.start(this._nextState, true, false, 'iris');
  },

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

  get _nextState () {
    return 'Title';
  }

};
<commit_msg>Workaround: Tween#from on chained tweens not working properly<commit_after>export default {

  create () {
    this.stage.backgroundColor = 0x000000; // black

    var aiLogo = this.add.image(0, 0, 'logo-adventure-islands');
    var rbLogo = this.add.image(0, 0, 'logo-rb');
    rbLogo.alpha = 0; // Phaser BUG

    var [ first, second ] = this._makeLogoFadeEffect(aiLogo);
    var [      , last   ] = this._makeLogoFadeEffect(rbLogo, second);

    first.start();
    second.onComplete.addOnce(() => rbLogo.alpha = 1, this); // Phaser BUG
    last.onComplete.addOnce(this._goToNextState, this);
  },

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

  _makeTween (object, chainWith = null) {
    var tween = this.add.tween(object);

    if (chainWith !== null)
      chainWith.chain(tween);

    return tween;
  },

  _makeLogoFadeEffect (logo, chainWith = null) {
    var easingFunction = Phaser.Easing.Sinusoidal.Out;

    var reveal = this._makeTween(logo, chainWith);
    var fade   = this._makeTween(logo, reveal);

    reveal.from({ alpha: 0 }, 1000, easingFunction, false, 1500);
    fade.to({ alpha: 0 }, 1000, easingFunction, false, 1500);

    return [ reveal, fade ];
  },

  _goToNextState () {
    this.state.start(this._nextState, true, false, 'iris');
  },

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

  get _nextState () {
    return 'Title';
  }

};
<|endoftext|>