<commit_before>import React, { Component } from 'react';
import {
  Linking,
  Button,
  Clipboard,
  Text,
  View,
  TouchableOpacity,
} from 'react-native';

import Device from 'react-native-device-info';

import { FluidCard, FluidHeader, FluidButton } from '../components';
import { SetItem, GetItem, RemoveItem } from '../persistence/db-helper';
import Icon from '../resources/icon';

import { width, height } from '../global';

class NewTrip extends Component {
  state = { id: '' };

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    this.fetchID();
    GetItem('ID').then(id => this.setState({ id: id }));
  }

  copyID = id => {
    Clipboard.setString(id);
  };

  fetchID = () => {
    fetch('http://localhost:8000/v1/keygen')
      .then(response => response.json())
      .then(responseJSON => {
        SetItem('ID', responseJSON.id);
      })
      .catch(error => {
        RemoveItem('ID');
        console.log('Failed to fetch response.', error);
      });
  };

  openMailApp = () => {
    const url = 'message://';
    Linking.canOpenURL(url)
      .then(supported => {
        if (!supported) {
          console.log("Can't handle url: " + url);
        } else {
          return Linking.openURL(url);
        }
      })
      .catch(err => console.log('An error occured', err));
  };

  devicePlus = () =>
    <View style={{ borderWidth: 0, flex: 0, justifyContent: 'flex-start' }}>
      <View style={{ borderWidth: 0 }}>
        <FluidButton
          style={styles.idStyle}
          alignSelf="center"
          onPress={() => this.copyID(this.state.id)}>
          {this.state.id}
        </FluidButton>

        <Icon
          name="Up-Chevron"
          size={14}
          color="#2B2B2B"
          style={[
            {
              textAlign: 'center',
              marginTop: height * 0.7 * 0.02,
              marginBottom: height * 0.7 * 0.02,
            },
          ]}
        />

        <Text style={[styles.textStyle]}>Tap that to copy.</Text>
      </View>

      <FluidButton
        alignSelf="center"
        style={{
          marginTop: height * 0.7 * 0.04,
          marginBottom: height * 0.7 * 0.04,
        }}
        onPress={this.openMailApp}>
        Go to your mailbox
      </FluidButton>

      <View style={{ borderWidth: 0 }}>
        <Text style={[styles.textStyle]}>Forward your ticket to</Text>
        <Text style={[styles.textStyle, styles.emailStyle]}>
          reservations@skyborne.co
        </Text>
        <Text style={styles.textStyle}>
          {'with the copied ID as\nthe subject line.'}
        </Text>
      </View>

      <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.05 }]}>
        {'Come back and hit next\nwhen ready'}.
      </Text>
    </View>;

  deviceStd = () =>
    <View style={{ borderWidth: 0, flex: 1 }}>
      <FluidButton
        style={styles.idStyle}
        alignSelf="center"
        onPress={() => this.copyID(this.state.id)}>
        {this.state.id}
      </FluidButton>

      <Icon
        name="Up-Chevron"
        size={15}
        color="#2B2B2B"
        style={[{ textAlign: 'center' }, { marginTop: height * 0.7 * 0.03 }]}
      />

      <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.02 }]}>
        Tap that to copy.
      </Text>

      <FluidButton
        alignSelf="center"
        style={[{ marginTop: height * 0.7 * 0.05 }]}
        onPress={this.openMailApp}>
        Go to your mailbox
      </FluidButton>

      <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.05 }]}>
        Forward your ticket to
      </Text>
      <Text style={[styles.textStyle, styles.emailStyle]}>
        reservations@skyborne.co
      </Text>
      <Text style={styles.textStyle}>
        {'with the copied ID as\nthe subject line.'}
      </Text>

      <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.03 }]}>
        {'Come back and hit next\nwhen ready'}.
      </Text>
    </View>;

  deviceSmall = () =>
    <View>
      <FluidButton
        style={styles.idStyle}
        textStyle={{ fontSize: 12 }}
        alignSelf="center"
        onPress={() => this.copyID(this.state.id)}>
        {this.state.id}
      </FluidButton>

      <Icon
        name="Up-Chevron"
        size={15}
        color="#2B2B2B"
        style={[{ textAlign: 'center' }, { marginTop: height * 0.7 * 0.03 }]}
      />

      <Text style={[styles.smallTextStyle, { marginTop: height * 0.7 * 0.02 }]}>
        Tap that to copy.
      </Text>

      <FluidButton
        alignSelf="center"
        style={[{ marginTop: height * 0.7 * 0.04 }]}
        onPress={this.openMailApp}>
        Go to your mailbox
      </FluidButton>

      <Text style={[styles.smallTextStyle, { marginTop: height * 0.7 * 0.04 }]}>
        Forward your ticket to
      </Text>
      <Text style={[styles.smallTextStyle, styles.emailStyle]}>
        reservations@skyborne.co
      </Text>
      <Text style={styles.smallTextStyle}>
        {'with the copied ID as\nthe subject line.'}
      </Text>

      <Text style={[styles.smallTextStyle, { marginTop: height * 0.7 * 0.03 }]}>
        {'Come back and hit next\nwhen ready'}.
      </Text>
    </View>;

  isPlus = () => {
    const PLUS = Device.getModel().indexOf('Plus') >= 0;

    return PLUS ? true : false;
  };

  isStd = () => {
    const SIX = Device.getModel().indexOf('6') >= 0;
    const SEVEN = Device.getModel().indexOf('7') >= 0;

    return SIX || SEVEN ? true : false;
  };

  isSmall = () => {
    const SE = Device.getModel().indexOf('SE') >= 0;
    const FIVE = Device.getModel().indexOf('5') >= 0;

    return SE || FIVE ? true : false;
  };

  adjustFontSize() {
    if (() => this.isSmall()) return 16;
    else return 18;
  }

  render() {
    let size = this.adjustFontSize();
    const styles = {
      textStyle: {
        fontFamily: 'Rubik',
        fontSize: size,
        fontWeight: '300',
        textAlign: 'center',
      },

      smallTextStyle: {
        fontFamily: 'Rubik',
        fontSize: 16,
        fontWeight: '300',
        textAlign: 'center',
      },

      idStyle: {
        shadowOpacity: 0,
        backgroundColor: '#E8E8E8',
        borderRadius: 20,
      },

      emailStyle: {
        color: '#8C8C8C',
      },
    };

    return (
      <View style={{ borderWidth: 0, flex: 0, justifyContent: 'flex-start' }}>
        <View style={{ borderWidth: 0 }}>
          <FluidButton
            style={styles.idStyle}
            alignSelf="center"
            onPress={() => this.copyID(this.state.id)}>
            {this.state.id}
          </FluidButton>

          <Icon
            name="Up-Chevron"
            size={14}
            color="#2B2B2B"
            style={[
              {
                textAlign: 'center',
                marginTop: height * 0.7 * 0.02,
                marginBottom: height * 0.7 * 0.02,
              },
            ]}
          />

          <Text style={[styles.textStyle]}>Tap that to copy.</Text>
        </View>

        <FluidButton
          alignSelf="center"
          style={{
            marginTop: height * 0.7 * 0.04,
            marginBottom: height * 0.7 * 0.04,
          }}
          onPress={this.openMailApp}>
          Go to your mailbox
        </FluidButton>

        <View style={{ borderWidth: 0 }}>
          <Text style={[styles.textStyle]}>Forward your ticket to</Text>
          <Text style={[styles.textStyle, styles.emailStyle]}>
            reservations@skyborne.co
          </Text>
          <Text style={styles.textStyle}>
            {'with the copied ID as\nthe subject line.'}
          </Text>
        </View>

        <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.03 }]}>
          {'Come back and hit next\nwhen ready'}.
        </Text>
      </View>
    );
  }
}

export default NewTrip;
<commit_msg>remove 'RemoveItem' from func<commit_after>import React, { Component } from 'react';
import {
  Linking,
  Button,
  Clipboard,
  Text,
  View,
  TouchableOpacity,
} from 'react-native';

import Device from 'react-native-device-info';

import { FluidCard, FluidHeader, FluidButton } from '../components';
import { SetItem, GetItem } from '../persistence/db-helper';
import Icon from '../resources/icon';

import { width, height } from '../global';

class NewTrip extends Component {
  state = { id: '' };

  constructor(props) {
    super(props);
  }

  componentDidMount() {
    this.fetchID();
    GetItem('ID').then(id => this.setState({ id: id }));
  }

  copyID = id => {
    Clipboard.setString(id);
  };

  fetchID = () => {
    fetch('http://localhost:8000/v1/keygen')
      .then(response => response.json())
      .then(responseJSON => {
        SetItem('ID', responseJSON.id);
      })
      .catch(error => {
        console.log('Failed to fetch response.', error);
      });
  };

  openMailApp = () => {
    const url = 'message://';
    Linking.canOpenURL(url)
      .then(supported => {
        if (!supported) {
          console.log("Can't handle url: " + url);
        } else {
          return Linking.openURL(url);
        }
      })
      .catch(err => console.log('An error occured', err));
  };

  devicePlus = () =>
    <View style={{ borderWidth: 0, flex: 0, justifyContent: 'flex-start' }}>
      <View style={{ borderWidth: 0 }}>
        <FluidButton
          style={styles.idStyle}
          alignSelf="center"
          onPress={() => this.copyID(this.state.id)}>
          {this.state.id}
        </FluidButton>

        <Icon
          name="Up-Chevron"
          size={14}
          color="#2B2B2B"
          style={[
            {
              textAlign: 'center',
              marginTop: height * 0.7 * 0.02,
              marginBottom: height * 0.7 * 0.02,
            },
          ]}
        />

        <Text style={[styles.textStyle]}>Tap that to copy.</Text>
      </View>

      <FluidButton
        alignSelf="center"
        style={{
          marginTop: height * 0.7 * 0.04,
          marginBottom: height * 0.7 * 0.04,
        }}
        onPress={this.openMailApp}>
        Go to your mailbox
      </FluidButton>

      <View style={{ borderWidth: 0 }}>
        <Text style={[styles.textStyle]}>Forward your ticket to</Text>
        <Text style={[styles.textStyle, styles.emailStyle]}>
          reservations@skyborne.co
        </Text>
        <Text style={styles.textStyle}>
          {'with the copied ID as\nthe subject line.'}
        </Text>
      </View>

      <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.05 }]}>
        {'Come back and hit next\nwhen ready'}.
      </Text>
    </View>;

  deviceStd = () =>
    <View style={{ borderWidth: 0, flex: 1 }}>
      <FluidButton
        style={styles.idStyle}
        alignSelf="center"
        onPress={() => this.copyID(this.state.id)}>
        {this.state.id}
      </FluidButton>

      <Icon
        name="Up-Chevron"
        size={15}
        color="#2B2B2B"
        style={[{ textAlign: 'center' }, { marginTop: height * 0.7 * 0.03 }]}
      />

      <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.02 }]}>
        Tap that to copy.
      </Text>

      <FluidButton
        alignSelf="center"
        style={[{ marginTop: height * 0.7 * 0.05 }]}
        onPress={this.openMailApp}>
        Go to your mailbox
      </FluidButton>

      <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.05 }]}>
        Forward your ticket to
      </Text>
      <Text style={[styles.textStyle, styles.emailStyle]}>
        reservations@skyborne.co
      </Text>
      <Text style={styles.textStyle}>
        {'with the copied ID as\nthe subject line.'}
      </Text>

      <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.03 }]}>
        {'Come back and hit next\nwhen ready'}.
      </Text>
    </View>;

  deviceSmall = () =>
    <View>
      <FluidButton
        style={styles.idStyle}
        textStyle={{ fontSize: 12 }}
        alignSelf="center"
        onPress={() => this.copyID(this.state.id)}>
        {this.state.id}
      </FluidButton>

      <Icon
        name="Up-Chevron"
        size={15}
        color="#2B2B2B"
        style={[{ textAlign: 'center' }, { marginTop: height * 0.7 * 0.03 }]}
      />

      <Text style={[styles.smallTextStyle, { marginTop: height * 0.7 * 0.02 }]}>
        Tap that to copy.
      </Text>

      <FluidButton
        alignSelf="center"
        style={[{ marginTop: height * 0.7 * 0.04 }]}
        onPress={this.openMailApp}>
        Go to your mailbox
      </FluidButton>

      <Text style={[styles.smallTextStyle, { marginTop: height * 0.7 * 0.04 }]}>
        Forward your ticket to
      </Text>
      <Text style={[styles.smallTextStyle, styles.emailStyle]}>
        reservations@skyborne.co
      </Text>
      <Text style={styles.smallTextStyle}>
        {'with the copied ID as\nthe subject line.'}
      </Text>

      <Text style={[styles.smallTextStyle, { marginTop: height * 0.7 * 0.03 }]}>
        {'Come back and hit next\nwhen ready'}.
      </Text>
    </View>;

  isPlus = () => {
    const PLUS = Device.getModel().indexOf('Plus') >= 0;

    return PLUS ? true : false;
  };

  isStd = () => {
    const SIX = Device.getModel().indexOf('6') >= 0;
    const SEVEN = Device.getModel().indexOf('7') >= 0;

    return SIX || SEVEN ? true : false;
  };

  isSmall = () => {
    const SE = Device.getModel().indexOf('SE') >= 0;
    const FIVE = Device.getModel().indexOf('5') >= 0;

    return SE || FIVE ? true : false;
  };

  adjustFontSize() {
    if (() => this.isSmall()) return 16;
    else return 18;
  }

  render() {
    let size = this.adjustFontSize();
    const styles = {
      textStyle: {
        fontFamily: 'Rubik',
        fontSize: size,
        fontWeight: '300',
        textAlign: 'center',
      },

      smallTextStyle: {
        fontFamily: 'Rubik',
        fontSize: 16,
        fontWeight: '300',
        textAlign: 'center',
      },

      idStyle: {
        shadowOpacity: 0,
        backgroundColor: '#E8E8E8',
        borderRadius: 20,
      },

      emailStyle: {
        color: '#8C8C8C',
      },
    };

    return (
      <View style={{ borderWidth: 0, flex: 0, justifyContent: 'flex-start' }}>
        <View style={{ borderWidth: 0 }}>
          <FluidButton
            style={styles.idStyle}
            alignSelf="center"
            onPress={() => this.copyID(this.state.id)}>
            {this.state.id}
          </FluidButton>

          <Icon
            name="Up-Chevron"
            size={14}
            color="#2B2B2B"
            style={[
              {
                textAlign: 'center',
                marginTop: height * 0.7 * 0.02,
                marginBottom: height * 0.7 * 0.02,
              },
            ]}
          />

          <Text style={[styles.textStyle]}>Tap that to copy.</Text>
        </View>

        <FluidButton
          alignSelf="center"
          style={{
            marginTop: height * 0.7 * 0.04,
            marginBottom: height * 0.7 * 0.04,
          }}
          onPress={this.openMailApp}>
          Go to your mailbox
        </FluidButton>

        <View style={{ borderWidth: 0 }}>
          <Text style={[styles.textStyle]}>Forward your ticket to</Text>
          <Text style={[styles.textStyle, styles.emailStyle]}>
            reservations@skyborne.co
          </Text>
          <Text style={styles.textStyle}>
            {'with the copied ID as\nthe subject line.'}
          </Text>
        </View>

        <Text style={[styles.textStyle, { marginTop: height * 0.7 * 0.03 }]}>
          {'Come back and hit next\nwhen ready'}.
        </Text>
      </View>
    );
  }
}

export default NewTrip;
<|endoftext|>


<commit_before>(function () {
  'use strict';

  define(function (require) {
    var languages = require('cs!configs/languages');

    return {
      // Directory from which webview is served
      root: '/',

      // Hostname and port for the cnx-archive server
      cnxarchive: {
        host: 'archive-cte-cnx-dev.cnx.org',
        port: 80
      },

      // Hostname and port for the cnx-authoring server
      cnxauthoring: {
        host: location.hostname,
        port: 8080
      },

      // Prefix to prepend to page titles
      titleSuffix: ' - OpenStax CNX',

      // Google Analytics tracking ID
      analyticsID: 'UA-7903479-1',

      // Supported languages
      languages: languages,

      // Legacy URL
      // URLs are concatenated using the following logic: location.protocol + '//' + legacy + '/' + view.url
      //   Example: 'http:' + '//' + 'cnx.org' + '/' + 'contents'
      // Do not include the protocol or a trailing slash
      legacy: 'legacy.cnx.org',

      // Webmaster E-mail address
      webmaster: 'support@openstax.org',

      // Content shortcodes
      shortcodes: {
        'college-physics': '031da8d3-b525-429c-80cf-6c8ed997733a@8.1',
        'college-introduction-to-sociology': 'afe4332a-c97f-4fc4-be27-4e4d384a32d8@7.15',
        'biology': '185cbf87-c72e-48f5-b51e-f14f21b5eabd@9.44',
        'concepts-of-biology': 'b3c1e1d2-839c-42b0-a314-e119a8aafbdd@8.39',
        'anatomy-and-physiology': '14fb4ad7-39a1-4eee-ab6e-3ef2482e3e22@6.19',
        'introductory-statistics': '30189442-6998-4686-ac05-ed152b91b9de@17.20'
      },

      accountProfile: 'https://accounts.cnx.org/profile',

      cnxSupport: 'http://openstax.force.com/support?l=en_US&c=Products%3ACNX',

      terpUrl: function (itemCode) {
        return 'https://openstaxtutor.org/terp/' + itemCode + '/quiz_start';
      },

      exerciseUrl: function (itemCode) {
        // // stub.  Comment out to use local exercises stub.
        // // Copied from https://exercises-dev1.openstax.org/api/exercises?q=tag:k12phys-ch04-s01-lo01
        // return 'http://localhost:8000/data/exercises.json';
        return 'https://exercises-dev1.openstax.org/api/exercises?q=tag:' + itemCode;
      },

      defaultLicense: {
        code: 'by'
      },

      conceptCoach: {
        uuids: {
          'f10533ca-f803-490d-b935-88899941197f': ['art-exercise', 'free-response', 'multiple-choice'],
          '8QUzyvgD': ['art-exercise', 'free-response', 'multiple-choice'] // only long-codes are currently supported
        },
        url: 'https://tutor-qa.openstax.org'
      }

    };

  });

})();
<commit_msg>use production archive by default<commit_after>(function () {
  'use strict';

  define(function (require) {
    var languages = require('cs!configs/languages');

    return {
      // Directory from which webview is served
      root: '/',

      // Hostname and port for the cnx-archive server
      cnxarchive: {
        host: 'archive.cnx.org',
        port: 80
      },

      // Hostname and port for the cnx-authoring server
      cnxauthoring: {
        host: location.hostname,
        port: 8080
      },

      // Prefix to prepend to page titles
      titleSuffix: ' - OpenStax CNX',

      // Google Analytics tracking ID
      analyticsID: 'UA-7903479-1',

      // Supported languages
      languages: languages,

      // Legacy URL
      // URLs are concatenated using the following logic: location.protocol + '//' + legacy + '/' + view.url
      //   Example: 'http:' + '//' + 'cnx.org' + '/' + 'contents'
      // Do not include the protocol or a trailing slash
      legacy: 'legacy.cnx.org',

      // Webmaster E-mail address
      webmaster: 'support@openstax.org',

      // Content shortcodes
      shortcodes: {
        'college-physics': '031da8d3-b525-429c-80cf-6c8ed997733a@8.1',
        'college-introduction-to-sociology': 'afe4332a-c97f-4fc4-be27-4e4d384a32d8@7.15',
        'biology': '185cbf87-c72e-48f5-b51e-f14f21b5eabd@9.44',
        'concepts-of-biology': 'b3c1e1d2-839c-42b0-a314-e119a8aafbdd@8.39',
        'anatomy-and-physiology': '14fb4ad7-39a1-4eee-ab6e-3ef2482e3e22@6.19',
        'introductory-statistics': '30189442-6998-4686-ac05-ed152b91b9de@17.20'
      },

      accountProfile: 'https://accounts.cnx.org/profile',

      cnxSupport: 'http://openstax.force.com/support?l=en_US&c=Products%3ACNX',

      terpUrl: function (itemCode) {
        return 'https://openstaxtutor.org/terp/' + itemCode + '/quiz_start';
      },

      exerciseUrl: function (itemCode) {
        // // stub.  Comment out to use local exercises stub.
        // // Copied from https://exercises-dev1.openstax.org/api/exercises?q=tag:k12phys-ch04-s01-lo01
        // return 'http://localhost:8000/data/exercises.json';
        return 'https://exercises-dev1.openstax.org/api/exercises?q=tag:' + itemCode;
      },

      defaultLicense: {
        code: 'by'
      },

      conceptCoach: {
        uuids: {
          'f10533ca-f803-490d-b935-88899941197f': ['art-exercise', 'free-response', 'multiple-choice'],
          '8QUzyvgD': ['art-exercise', 'free-response', 'multiple-choice'] // only long-codes are currently supported
        },
        url: 'https://tutor-qa.openstax.org'
      }

    };

  });

})();
<|endoftext|>


<commit_before>/**
 * == Scripty 2 ==
 *
 * Version: <%=VERSION%>
 **/

/** section: Scripty 2
 * class zebedee
 *
 * creates an accordion widget
 **/

/** section: Scripty 2
 * new zebedee(container [, options])
 *
 * - container (String | HTMLDOMElement | Prototype Object): The containing div for the accordion
 * - options (Object): Optional options object for overriding default configuration
 *
 * Creates a new zebedee accordion
 **/
var zebedee = (function(container,options){
	/** section: Scripty 2
     * zebedee._scope -> zebedee
     *
     * Internal variable to enable event handlers to be executed in the main object's scope
     **/
	var _scope = this;

	if(!$(container))
		throw new Exception('Element ' + container + " does not exist!");

    /** section:  Scripty 2
     * zebedee._container -> Scriptaculous Object
     *
     * Internal reference to container element
     **/
	this._container = $(container);

	//utility methods
    /** section: Scripty 2
     * zebedee._getObjVars(obj) -> Array
     *
     * - obj (Object) - The object to get the properties of
     *
     *  Utility method to get array of object properties
     **/
	this._getObjVars = function(obj){
		if (typeof obj !== 'Object')
			return [];

		return Object.values(obj);
	}

    /** section: Scripty 2
     * zebedee._merge(destination,source) -> Object
     *
     * - destination (Object): The object to have it's properties overridden
     * - source (Object): The object with the new properties
     *
     * Merges two objects together, overwriting the properties of the first with the new values from the second.
     **/
	this._merge = function(destination,source){
		var property,prop;
		for (property in source){
			if(this._getObjVars(destination[property]).length>0 && this._getObjVars(source[property]).length>0){
				for(prop in source[property])
 			 		destination[property][prop] = source[property][prop];
 			 } else {
 			 	destination[property] = source[property];
 			 }
 		}
		return destination;
	};

    /** section: Scripty 2
     * zebedee._getTransition() -> Object
     *
     *  Returns the correct transition object based on the configured option.  Will
     *  use CSS transitions where available and supported.
     */
	this._getTransition = function(){

		if(!S2.Extensions.CSSTransitions)
			return S2.FX.Transitions[this.options.transition];
		else
			return S2.FX.Operators.CssTransition[this.options.transition]

	}

	//public members
    /** section: Scripty 2
     * zebedee.toggle(e) -> null
     *
     * - e (Event): The trigger event for the opening/closing of the accordion
     *
     * Event handler for opening/closing the accordion.  Checks for any opened blocks, and if they're not the one clicked, closes them.  Then
     * either opens or closes the clicked block as necessary.
     **/
	this.toggle = function(e){
		var open = $$('#' + _scope._container.identify() + ">." + _scope.options.classNames.handleActive)[0];
		if(!Object.isUndefined(open) && open!=e.findElement())
			_scope.close($$('#' + _scope._container.identify() + ">." + _scope.options.classNames.handleActive)[0]);
		if($(e.findElement()).hasClassName(_scope.options.classNames.handleActive))
			_scope.close(e.findElement());
		else
			_scope.open(e.findElement());
	};

    /** section: Scripty 2
     * zebedee.open(el) -> null
     *
     * - el (HTMLDOMElement): The handle clicked
     *
     * Opens the content block for the handle selected
     **/
	this.open = function(el){
		$(el).addClassName(this.options.classNames.handleActive);

		var css = (this.options.direction == 'vertical') ? 'height:' + $(el).next().readAttribute('data-zebheight') + "px;" : 'width:' + $(el).next().readAttribute('data-zebwidth') + "px;";
		$(el).next().morph(css,{duration:this.options.duration,transition:this._getTransition()});
	};

	/** section: Scripty 2
     * zebedee.close(el) -> null
     *
     * - el (HTMLDOMElement): The handle clicked
     *
     * Closes the content block for the handle selected
     **/
	this.close = function(el){
	 	$(el).removeClassName(this.options.classNames.handleActive);

	 	$(el).next().morph(((this.options.direction=='vertical') ? 'height' : 'width') + ":0px" ,{duration:this.options.duration,transition:this._getTransition()});

	};

	//initialize options
    /** section: Scripty 2
     * zebedee.options
     *
     * Contains the configuration details for the current accordion
     **/
	this.options = this._merge({
        /** section: Scripty 2
         * zebedee.options.classNames -> Object
         *
         * Defines the default css classes for the accordion components.  Has 3 properties
         * - handle: Defines the css class for the clickable section headers
         * - handleActive: Defines the css class for the currently selected handle
         * - content: Defines the css class for the content blocks.
         **/
		classNames: {
			handle: 'zebedee-handle',
			handleActive: 'zebedee-active',
			content: 'zebedee-content'
		},
		linkElement:null,
		direction: 'vertical',
        /** section: Scripty 2
         * zebedee.options.duration -> Number
         *
         * Defines the duration of the opening/closing animation.
         **/
		duration:1,
		/** section: Scripty 2
         * zebedee.options.transition -> String
         *
         * Defines the animation effect used in the opening/closing animation.
         **/
		transition:'sinusoidal',
		/** section: Scripty 2
         * zebedee.options.trigger -> String
         *
         * Defines the event that triggers the opening/closing of blocks.
         **/
		trigger:'touchend'
	},options);

	//initialize accordion

	//set up handles

	$$('#' + this._container.identify()+ '>.' + this.options.classNames.handle).each(function(el,i){
		$(el).observe(this.options.trigger,this.toggle);

	},this);

	$$('.' + this.options.classNames.content).each(function(el,i){
		switch(this.options.direction){
		  case 'vertical' : $(el).writeAttribute('data-zebheight',$(el).getHeight()).setStyle({'overflow':'hidden'}); break;
		  case 'horizontal' : $(el).writeAttribute('data-zebwidth',$(el).getWidth()).setStyle({'overflow':'hidden'}); break;
		}

	},this);
	$$('.' + this.options.classNames.handle).each(function(el,i){this.close(el)},this);

	return this
});<commit_msg>Fixed bug with child elements of handle triggering click event<commit_after>/**
 * == Scripty 2 ==
 *
 * Version: <%=VERSION%>
 **/

/** section: Scripty 2
 * class zebedee
 *
 * creates an accordion widget
 **/

/** section: Scripty 2
 * new zebedee(container [, options])
 *
 * - container (String | HTMLDOMElement | Prototype Object): The containing div for the accordion
 * - options (Object): Optional options object for overriding default configuration
 *
 * Creates a new zebedee accordion
 **/
var zebedee = (function(container,options){
	/** section: Scripty 2
     * zebedee._scope -> zebedee
     *
     * Internal variable to enable event handlers to be executed in the main object's scope
     **/
	var _scope = this;

	if(!$(container))
		throw new Exception('Element ' + container + " does not exist!");

    /** section:  Scripty 2
     * zebedee._container -> Scriptaculous Object
     *
     * Internal reference to container element
     **/
	this._container = $(container);

	//utility methods
    /** section: Scripty 2
     * zebedee._getObjVars(obj) -> Array
     *
     * - obj (Object) - The object to get the properties of
     *
     *  Utility method to get array of object properties
     **/
	this._getObjVars = function(obj){
		if (typeof obj !== 'Object')
			return [];

		return Object.values(obj);
	}

    /** section: Scripty 2
     * zebedee._merge(destination,source) -> Object
     *
     * - destination (Object): The object to have it's properties overridden
     * - source (Object): The object with the new properties
     *
     * Merges two objects together, overwriting the properties of the first with the new values from the second.
     **/
	this._merge = function(destination,source){
		var property,prop;
		for (property in source){
			if(this._getObjVars(destination[property]).length>0 && this._getObjVars(source[property]).length>0){
				for(prop in source[property])
 			 		destination[property][prop] = source[property][prop];
 			 } else {
 			 	destination[property] = source[property];
 			 }
 		}
		return destination;
	};

    /** section: Scripty 2
     * zebedee._getTransition() -> Object
     *
     *  Returns the correct transition object based on the configured option.  Will
     *  use CSS transitions where available and supported.
     */
	this._getTransition = function(){

		if(!S2.Extensions.CSSTransitions)
			return S2.FX.Transitions[this.options.transition];
		else
			return S2.FX.Operators.CssTransition[this.options.transition]

	}

	//public members
    /** section: Scripty 2
     * zebedee.toggle(e) -> null
     *
     * - e (Event): The trigger event for the opening/closing of the accordion
     *
     * Event handler for opening/closing the accordion.  Checks for any opened blocks, and if they're not the one clicked, closes them.  Then
     * either opens or closes the clicked block as necessary.
     **/
	this.toggle = function(e){
		var open = $$('#' + _scope._container.identify() + ">." + _scope.options.classNames.handleActive)[0];
		if(!Object.isUndefined(open) && open!=e.findElement())
			_scope.close($$('#' + _scope._container.identify() + ">." + _scope.options.classNames.handleActive)[0]);
		if($(e.findElement('.'+_scope.options.classNames.handle)).hasClassName(_scope.options.classNames.handleActive))
			_scope.close(e.findElement('.'+_scope.options.classNames.handle));
		else
			_scope.open(e.findElement('.'+_scope.options.classNames.handle));
	};

    /** section: Scripty 2
     * zebedee.open(el) -> null
     *
     * - el (HTMLDOMElement): The handle clicked
     *
     * Opens the content block for the handle selected
     **/
	this.open = function(el){
		$(el).addClassName(this.options.classNames.handleActive);

		var css = (this.options.direction == 'vertical') ? 'height:' + $(el).next().readAttribute('data-zebheight') + "px;" : 'width:' + $(el).next().readAttribute('data-zebwidth') + "px;";
		$(el).next().morph(css,{duration:this.options.duration,transition:this._getTransition()});
	};

	/** section: Scripty 2
     * zebedee.close(el) -> null
     *
     * - el (HTMLDOMElement): The handle clicked
     *
     * Closes the content block for the handle selected
     **/
	this.close = function(el){
	 	$(el).removeClassName(this.options.classNames.handleActive);

	 	$(el).next().morph(((this.options.direction=='vertical') ? 'height' : 'width') + ":0px" ,{duration:this.options.duration,transition:this._getTransition()});

	};

	//initialize options
    /** section: Scripty 2
     * zebedee.options
     *
     * Contains the configuration details for the current accordion
     **/
	this.options = this._merge({
        /** section: Scripty 2
         * zebedee.options.classNames -> Object
         *
         * Defines the default css classes for the accordion components.  Has 3 properties
         * - handle: Defines the css class for the clickable section headers
         * - handleActive: Defines the css class for the currently selected handle
         * - content: Defines the css class for the content blocks.
         **/
		classNames: {
			handle: 'zebedee-handle',
			handleActive: 'zebedee-active',
			content: 'zebedee-content'
		},
		linkElement:null,
		direction: 'vertical',
        /** section: Scripty 2
         * zebedee.options.duration -> Number
         *
         * Defines the duration of the opening/closing animation.
         **/
		duration:1,
		/** section: Scripty 2
         * zebedee.options.transition -> String
         *
         * Defines the animation effect used in the opening/closing animation.
         **/
		transition:'sinusoidal',
		/** section: Scripty 2
         * zebedee.options.trigger -> String
         *
         * Defines the event that triggers the opening/closing of blocks.
         **/
		trigger:'click',
		start:false
	},options);

	//initialize accordion

	//set up handles

	$$('#' + this._container.identify()+ '>.' + this.options.classNames.handle).each(function(el,i){
		$(el).observe(this.options.trigger,this.toggle);

	},this);

	$$('.' + this.options.classNames.content).each(function(el,i){
		switch(this.options.direction){
		  case 'vertical' : $(el).writeAttribute('data-zebheight',$(el).getHeight()).setStyle({'overflow':'hidden'}); break;
		  case 'horizontal' : $(el).writeAttribute('data-zebwidth',$(el).getWidth()).setStyle({'overflow':'hidden'}); break;
		}

	},this);
	$$('.' + this.options.classNames.handle).each(function(el,i){
		if(this.options.start && i==(this.options.start-1))
			$(el).addClassName(this.options.classNames.handleActive);
		else
			this.close(el);
		},this);

	return this
});<|endoftext|>


<commit_before>import url from 'url';
import request from 'request-promise';
import cheerio from 'cheerio';
import _ from 'lodash';

const USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36';

export default class PornhubService {

	static async init() {
		PornhubService.categories = await PornhubService.getCategories();
	}

	async getRandom() {
		// await request('')
	}

	static async getVideosByCategory(category) {
		if (!_.isObject(category)) {
			throw new Error('Category should be object.');
		}

		const html = await request.get({
			url: url.resolve('http://www.pornhub.com/', category.href),
			headers: {
				'User-Agent': USER_AGENT,
			},
		});
		console.log(html);

		const $ = cheerio.load(html, {
			ignoreWhitespace: true,
		});

		const results = [];

		$('.wrap').each((i, el) => {
			results.push({
				// name: $('h5 strong', el).text(),
				// href: $('h5 a', el).attr('href'),
				img: $('.img > a img', el).attr('src'),
			});
		});

		return results;
	}

	static async getCategories() {
		if (PornhubService.categories) {
			return PornhubService.categories;
		}
		const html = await request.get({
			url: 'http://www.pornhub.com/categories',
			headers: {
				'User-Agent': USER_AGENT,
			},
		});

		const $ = cheerio.load(html, {
			ignoreWhitespace: true,
		});

		const results = [];

		$('.category-wrapper').each((i, el) => {
			results.push({
				name: $('h5 strong', el).text(),
				href: $('h5 a', el).attr('href'),
				img: $('a img', el).attr('src'),
			});
		});

		return results;
	}
}
<commit_msg>Parse category video<commit_after>import url from 'url';
import request from 'request-promise';
import cheerio from 'cheerio';
import _ from 'lodash';

const USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36';

const IFRAME_PATTERN = '<iframe src="http://www.pornhub.com/embed/{{vkey}}" frameborder="0" width="560" height="340" scrolling="no"></iframe>';

export default class PornhubService {

	static async init() {
		PornhubService.categories = await PornhubService.getCategories();
	}

	async getRandom() {
		// await request('')
	}

	// static async getVideo(video) {
	// 	if (!_.isObject(video)) {
	// 		throw new Error('Category should be object.');
	// 	}
	//
	// 	const html = await request.get({
	// 		url: url.resolve('http://www.pornhub.com/', video.href),
	// 		headers: {
	// 			'User-Agent': USER_AGENT,
	// 		},
	// 	});
	//
	// 	const $ = cheerio.load(html, {
	// 		ignoreWhitespace: true,
	// 	});
	//
	// 	console.log($('.embed').html());
	//
	// 	return $('.embed textarea').text();
	//
	// 	// await request(video.url)
	// }

	static async getVideosByCategory(category) {
		if (!_.isObject(category)) {
			throw new Error('Category should be object.');
		}

		const html = await request.get({
			url: url.resolve('http://www.pornhub.com/', category.href),
			headers: {
				'User-Agent': USER_AGENT,
			},
		});

		const $ = cheerio.load(html, {
			ignoreWhitespace: true,
		});

		const results = [];

		$('.videoblock').each((i, el) => {
			const vkey = $(el).attr('_vkey');
			results.push({
				name: $('a', el).attr('title'),
				href: $('a', el).attr('href'),
				img: $('img', el).attr('data-path'),
				thumbsCount: $('img', el).attr('data-thumbs'),
				vkey,
				iframe: IFRAME_PATTERN.replace('{{vkey}}', vkey),
			});
		});

		return results;
	}

	static async getCategories() {
		if (PornhubService.categories) {
			return PornhubService.categories;
		}
		const html = await request.get({
			url: 'http://www.pornhub.com/categories',
			headers: {
				'User-Agent': USER_AGENT,
			},
		});

		const $ = cheerio.load(html, {
			ignoreWhitespace: true,
		});

		const results = [];

		$('.category-wrapper').each((i, el) => {
			results.push({
				name: $('h5 strong', el).text(),
				href: $('h5 a', el).attr('href'),
				img: $('a img', el).attr('src'),
			});
		});

		return results;
	}
}
<|endoftext|>


<commit_before>angular
.module('wheelie')
.factory('wheelie', [function () {
    var isDefined = angular.isDefined;
    var isFunction = angular.isFunction;

    return {
        bind: function (element, callbacks) {
            callbacks = callbacks || {};

            if (isDefined(callbacks.up) && !isFunction(callbacks.up)) {
                throw new Error('The \'up\' callback must be a function');
            }
            if (isDefined(callbacks.down) && !isFunction(callbacks.down)) {
                throw new Error('The \'down\' callback must be a function');
            }
            if (isDefined(callbacks.left) && !isFunction(callbacks.left)) {
                throw new Error('The \'left\' callback must be a function');
            }
            if (isDefined(callbacks.right) && !isFunction(callbacks.right)) {
                throw new Error('The \'right\' callback must be a function');
            }
            if (!isDefined(callbacks.up) &&
                !isDefined(callbacks.down) &&
                !isDefined(callbacks.left) &&
                !isDefined(callbacks.right)) {
                throw new Error(
                    'At least one callback (\'up\', \'down\', \'left\' or \'right\') must be provided'
                );
            }

            function bindWheel(e) {
                if (e.originalEvent) {
                    e = e.originalEvent;
                }

                var deltaX = Math.max(-1, Math.min(1, -e.deltaX));
                var deltaY = Math.max(-1, Math.min(1, -e.deltaY));

                if (deltaX) {
                    deltaX > 0 && callbacks.left && callbacks.left(e);
                    deltaX < 0 && callbacks.right && callbacks.right(e);
                }

                if (deltaY) {
                    deltaY > 0 && callbacks.up && callbacks.up(e);
                    deltaY < 0 && callbacks.down && callbacks.down(e);
                }
            }

            element.data('___wheelie_bindWheel___', bindWheel);
            element.on('wheel', bindWheel);
        },

        unbind: function (element) {
            var bindWheel = element.data('___wheelie_bindWheel___');
            if (isFunction(bindWheel)) {
                element.data('___wheelie_bindWheel___', null);
                element.off('wheel', bindWheel);
            }
        }
    };
}]);
<commit_msg>Stop normalising wheel delta<commit_after>angular
.module('wheelie')
.factory('wheelie', [function () {
    var isDefined = angular.isDefined;
    var isFunction = angular.isFunction;

    return {
        bind: function (element, callbacks) {
            callbacks = callbacks || {};

            if (isDefined(callbacks.up) && !isFunction(callbacks.up)) {
                throw new Error('The \'up\' callback must be a function');
            }
            if (isDefined(callbacks.down) && !isFunction(callbacks.down)) {
                throw new Error('The \'down\' callback must be a function');
            }
            if (isDefined(callbacks.left) && !isFunction(callbacks.left)) {
                throw new Error('The \'left\' callback must be a function');
            }
            if (isDefined(callbacks.right) && !isFunction(callbacks.right)) {
                throw new Error('The \'right\' callback must be a function');
            }
            if (!isDefined(callbacks.up) &&
                !isDefined(callbacks.down) &&
                !isDefined(callbacks.left) &&
                !isDefined(callbacks.right)) {
                throw new Error(
                    'At least one callback (\'up\', \'down\', \'left\' or \'right\') must be provided'
                );
            }

            function bindWheel(e) {
                if (e.originalEvent) {
                    e = e.originalEvent;
                }

                if (e.deltaX) {
                    e.deltaX < 0 && callbacks.left && callbacks.left(e);
                    e.deltaX > 0 && callbacks.right && callbacks.right(e);
                }

                if (e.deltaY) {
                    e.deltaY < 0 && callbacks.up && callbacks.up(e);
                    e.deltaY > 0 && callbacks.down && callbacks.down(e);
                }
            }

            element.data('___wheelie_bindWheel___', bindWheel);
            element.on('wheel', bindWheel);
        },

        unbind: function (element) {
            var bindWheel = element.data('___wheelie_bindWheel___');
            if (isFunction(bindWheel)) {
                element.data('___wheelie_bindWheel___', null);
                element.off('wheel', bindWheel);
            }
        }
    };
}]);
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Hippo B.V. (http://www.onehippo.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
(function () {
    "use strict";

    angular.module('hippo.theme')

        /*
        * jstree directive
        * via http://plnkr.co/edit/xHIc4J?p=preview
        */
        .directive('hippo.theme.tree', [function() {
            return {
                restrict: 'A',
                scope: {
                    data: "="
                },
                template: '
Filter did not load.
',
                controller: function($scope) {
                    this.setSelectedItem = function(itemId) {
                        //$scope.$parent.setSelectedItemId(itemId);
                    };
                },
                link: function (scope, element, attrs, treeCtrl) {
                    function selectFirstElement(list) {
                        console.log(list);
                        var item = list[0] || {};
                        item.state = item.state || {};
                        item.state.selected = item.state.selected || true;
                    }

                    scope.$watch('data', function() {
                        // select first item by default
                        selectFirstElement(scope.data);

                        element.jstree('destroy');
                        element.jstree({
                            plugins : [ "themes" ],
                            core: {
                                data: scope.data
                            },
                            themes: {
                                theme: 'hippo'
                            }
                        }).bind('select_node.jstree', function(event, item) {
                            treeCtrl.setSelectedItem(item.node.id);
                        }).jstree('set_theme', 'hippo');
                    }, true);
                }
            };
        }]);
})();<commit_msg>CMS7-7679 Consistent use of single quotes<commit_after>/*
 * Copyright 2014 Hippo B.V. (http://www.onehippo.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
(function () {
    'use strict';

    angular.module('hippo.theme')

        /*
        * jstree directive
        * via http://plnkr.co/edit/xHIc4J?p=preview
        */
        .directive('hippo.theme.tree', [function() {
            return {
                restrict: 'A',
                scope: {
                    data: '='
                },
                template: '
Filter did not load.
',
                controller: function($scope) {
                    this.setSelectedItem = function(itemId) {
                        //$scope.$parent.setSelectedItemId(itemId);
                    };
                },
                link: function (scope, element, attrs, treeCtrl) {
                    function selectFirstElement(list) {
                        console.log(list);
                        var item = list[0] || {};
                        item.state = item.state || {};
                        item.state.selected = item.state.selected || true;
                    }

                    scope.$watch('data', function() {
                        // select first item by default
                        selectFirstElement(scope.data);

                        element.jstree('destroy');
                        element.jstree({
                            plugins : [ 'themes' ],
                            core: {
                                data: scope.data
                            },
                            themes: {
                                theme: 'hippo'
                            }
                        }).bind('select_node.jstree', function(event, item) {
                            treeCtrl.setSelectedItem(item.node.id);
                        }).jstree('set_theme', 'hippo');
                    }, true);
                }
            };
        }]);
})();<|endoftext|>


<commit_before>/*
 * Copyright 2014 Hippo B.V. (http://www.onehippo.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
(function () {
    'use strict';

    angular.module('hippo.theme')

        /*
        * jstree directive
        * via http://plnkr.co/edit/xHIc4J?p=preview
        */
        .directive('hippo.theme.tree', [function() {
            return {
                restrict: 'A',
                scope: {
                    data: '=',
                    onSelect: '&onSelect'
                },
                template: '
Filter did not load.
',
                link: function (scope, element, attrs, treeCtrl) {

                    // render js tree every time the provided data structure changes
                    scope.$watch('data', function() {
                        selectFirstElement(scope.data);
                        addLevelInfo(scope.data);
                        createJsTree(scope.data, element);
                    }, true);

                    function selectFirstElement(list) {
                        var item = list[0] || {};
                        item.state = item.state || {};
                        item.state.selected = item.state.selected || true;
                    }

                    function addLevelInfo(list, level) {
                        level = level || 1;
                        _.each(list, function (item) {
                            item.li_attr = {
                                'data-level': level
                            };

                            if (item.children) {
                                addLevelInfo(item.children, (level + 1));
                            }
                        });
                    }

                    function createJsTree(data, element) {
                        element.jstree('destroy');
                        element.jstree({
                            plugins : [ 'themes' ],
                            core: {
                                data: data,
                                check_callback: true
                            },
                            themes: {
                                theme: 'hippo'
                            }
                        }).bind('activate_node.jstree', function(event, node) {
                            // remove active classes
                            node.instance.element.find('.jstree-node').removeClass('active');

                            // set active class
                            $('#' + node.node.id, element).addClass('active');

                            // trigger on select function
                            scope.onSelect({itemId: node.node.id});
                        }).bind("move_node.jstree", function (event, data) {
                            // TODO: set indenting levels
                            // TODO: always expand dom, otherwise this won't work! (or does it with the indenting levels?)

                            // get JSON
                            var result = $.jstree.reference(element).get_json(element, {});
                            var jsonString = JSON.stringify(result);
                        }).jstree('set_theme', 'hippo');
                    }
                }
            };
        }]);
})();<commit_msg>CMS7-7679 Trigger select on initial load<commit_after>/*
 * Copyright 2014 Hippo B.V. (http://www.onehippo.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
(function () {
    'use strict';

    angular.module('hippo.theme')

        /*
        * jstree directive
        * via http://plnkr.co/edit/xHIc4J?p=preview
        */
        .directive('hippo.theme.tree', [function() {
            return {
                restrict: 'A',
                scope: {
                    data: '=',
                    onSelect: '&onSelect'
                },
                template: '
Filter did not load.
',
                link: function (scope, element, attrs, treeCtrl) {

                    // render js tree every time the provided data structure changes
                    scope.$watch('data', function() {
                        selectFirstElement(scope.data);
                        addLevelInfo(scope.data);
                        createJsTree(scope.data, element);
                    }, true);

                    function selectFirstElement(list) {
                        var item = list[0] || {};
                        item.state = item.state || {};
                        item.state.selected = item.state.selected || true;
                    }

                    function addLevelInfo(list, level) {
                        level = level || 1;
                        _.each(list, function (item) {
                            item.li_attr = {
                                'data-level': level
                            };

                            if (item.children) {
                                addLevelInfo(item.children, (level + 1));
                            }
                        });
                    }

                    function createJsTree(data, element) {
                        element.jstree('destroy');
                        element.jstree({
                            plugins : [ 'themes' ],
                            core: {
                                data: data,
                                check_callback: true
                            },
                            themes: {
                                theme: 'hippo'
                            }
                        }).bind('select_node.jstree', function(event, item) {
                            // trigger on select function
                            scope.onSelect({itemId: item.node.id});
                        }).bind('activate_node.jstree', function(event, node) {
                            // remove active classes
                            node.instance.element.find('.jstree-node').removeClass('active');

                            // set active class
                            $('#' + node.node.id, element).addClass('active');

                            // trigger on select function
                            scope.onSelect({itemId: node.node.id});
                        }).bind("move_node.jstree", function (event, data) {
                            // TODO: set indenting levels
                            // TODO: always expand dom, otherwise this won't work! (or does it with the indenting levels?)

                            // get JSON
                            var result = $.jstree.reference(element).get_json(element, {});
                            var jsonString = JSON.stringify(result);
                        }).jstree('set_theme', 'hippo');
                    }
                }
            };
        }]);
})();<|endoftext|>


<commit_before>import Vue from 'vue';
import axios from 'axios';

const cookieparser = process.server ? require('cookieparser') : null;

export const state = () => ({
	loggedIn: false,
	user: null,
	token: null,
	config: null
});

/* eslint-disable no-shadow */
export const mutations = {
	loggedIn(state, payload) {
		state.loggedIn = payload;
	},
	user(state, payload) {
		if (!payload) {
			state.user = null;
			return;
		}
		state.user = payload;
	},
	token(state, payload) {
		if (!payload) {
			state.token = null;
			return;
		}
		setAuthorizationHeader(payload);
		state.token = payload;
	},
	config(state, payload) {
		state.config = payload;
	}
};

export const actions = {
	async nuxtServerInit({ commit }, { req }) {
		commit('config', {
			version: process.env.npm_package_version,
			URL: process.env.DOMAIN,
			baseURL: `${process.env.DOMAIN}${process.env.ROUTE_PREFIX}`,
			serviceName: process.env.SERVICE_NAME,
			maxFileSize: parseInt(process.env.MAX_SIZE, 10),
			chunkSize: parseInt(process.env.CHUNK_SIZE, 10),
			maxLinksPerAlbum: parseInt(process.env.MAX_LINKS_PER_ALBUM, 10),
			publicMode: process.env.PUBLIC_MODE == 'true' ? true : false,
			enableAccounts: process.env.USER_ACCOUNTS == 'true' ? true : false
		});

		let token = null;
		if (req.headers.cookie) {
			try {
				token = cookieparser.parse(req.headers.cookie).token;
				commit('loggedIn', true);
				commit('token', token);

				const res = await axios.get(`${this.config.baseURL}/verify`);
				if (!res || !res.data.user);
				commit('user', res.data.user);
			} catch (error) {
				// TODO: Deactivate this on production
				console.error(error);
			}
		}
		commit('token', token);
		if (!token) {
			commit('user', null);
			commit('loggedIn', false);
		}
	}
};

const setAuthorizationHeader = payload => {
	Vue.axios.defaults.headers.common.Authorization = payload ? `Bearer ${payload}` : '';
};
<commit_msg>proper baseurl<commit_after>import Vue from 'vue';
import axios from 'axios';

const cookieparser = process.server ? require('cookieparser') : null;

export const state = () => ({
	loggedIn: false,
	user: null,
	token: null,
	config: null
});

/* eslint-disable no-shadow */
export const mutations = {
	loggedIn(state, payload) {
		state.loggedIn = payload;
	},
	user(state, payload) {
		if (!payload) {
			state.user = null;
			return;
		}
		state.user = payload;
	},
	token(state, payload) {
		if (!payload) {
			state.token = null;
			return;
		}
		setAuthorizationHeader(payload);
		state.token = payload;
	},
	config(state, payload) {
		state.config = payload;
	}
};

export const actions = {
	async nuxtServerInit({ commit }, { req }) {
		commit('config', {
			version: process.env.npm_package_version,
			URL: process.env.DOMAIN,
			baseURL: `${process.env.DOMAIN}${process.env.ROUTE_PREFIX}`,
			serviceName: process.env.SERVICE_NAME,
			maxFileSize: parseInt(process.env.MAX_SIZE, 10),
			chunkSize: parseInt(process.env.CHUNK_SIZE, 10),
			maxLinksPerAlbum: parseInt(process.env.MAX_LINKS_PER_ALBUM, 10),
			publicMode: process.env.PUBLIC_MODE == 'true' ? true : false,
			enableAccounts: process.env.USER_ACCOUNTS == 'true' ? true : false
		});

		let token = null;
		if (req.headers.cookie) {
			try {
				token = cookieparser.parse(req.headers.cookie).token;
				commit('loggedIn', true);
				commit('token', token);

				const res = await axios.get(`${process.env.DOMAIN}${process.env.ROUTE_PREFIX}/verify`);
				if (!res || !res.data.user);
				commit('user', res.data.user);
			} catch (error) {
				// TODO: Deactivate this on production
				console.error(error);
			}
		}
		commit('token', token);
		if (!token) {
			commit('user', null);
			commit('loggedIn', false);
		}
	}
};

const setAuthorizationHeader = payload => {
	Vue.axios.defaults.headers.common.Authorization = payload ? `Bearer ${payload}` : '';
};
<|endoftext|>


<commit_before>import has from 'lodash/has';
import Utils from '@/snappy/Utils';

const genotypeRegex = new RegExp('^([AGCTI-]);?([AGCTI-])$');

export default class Genotypes {
  static async get(snp, genotype) {
    const GenotypeData = await import(/* webpackChunkName: "genotypes" */ '#/genotypes.json');

    if (!has(GenotypeData, snp) && snp.startsWith('i')) {
      // 23andMe SNP; might have a corresponding rsID
      const resolved = Utils.resolveIidAlias(snp);
      if (resolved) {
        /* eslint-disable no-param-reassign */
        snp = resolved;
        if (Utils.isSnpOrientationMinus(snp)) {
          genotype = Utils.flipAlleles(genotype);
        }
        /* eslint-enable no-param-reassign */
      }
    }

    if (has(GenotypeData, snp)) {
      if (typeof genotype === 'undefined') {
        // get(snp)
        return GenotypeData[snp];
      }

      const g = genotypeRegex.exec(genotype);
      if (!g) {
        throw Error(`Invalid genotype ${snp} ${genotype}`);
      }

      let result = null;

      const match = this.getMatch(
        Object.keys(GenotypeData[snp]),
        g[1],
        g[2],
      );
      if (match) {
        // Exact match
        result = GenotypeData[snp][match];

        if (typeof result === 'string') {
          // Redirect
          const target = result;
          result = GenotypeData[snp][target];
          result.o = target;
        } else {
          result.o = match;
        }
      } else if (genotype.indexOf('I') !== -1) {
        // Insertion
        const candidates = [];
        for (const candidate of Object.keys(GenotypeData[snp])) {
          const normalized = candidate.replace(new RegExp('[AGCT]+', 'g'), 'I');
          if (this.matches(normalized, g[1], g[2])) {
            candidates.push(candidate);
          }
        }
        if (candidates.length !== 1) {
          // Ambiguous insertion
          throw Error(`${snp} ${genotype} is ambiguous or has no info; Possible genotypes include ${candidates}`);
        }
        result = GenotypeData[snp][candidates[0]];
        result.o = candidates[0];
      } else {
        throw Error(`Cannot interpret ${snp} ${genotype}: ${genotype} has no info`);
      }

      // Success
      result.name = snp;
      result.g = genotype;
      return result;
    }

    throw Error(`Cannot interpret ${snp} ${genotype}: ${snp} not supported`);
  }

  static async getSupportedSnps() {
    const GenotypeData = await import(/* webpackChunkName: "genotypes" */ '#/genotypes.json');
    return Object.keys(GenotypeData);
  }

  static getMatch(genotypes, allele1, allele2) {
    const s1 = `${allele1};${allele2}`;
    const s2 = `${allele2};${allele1}`;

    if (genotypes.includes(s2)) {
      return s2;
    }
    if (genotypes.includes(s1)) {
      return s1;
    }
    return false;
  }

  static matches(genotype, allele1, allele2) {
    return genotype === `${allele1};${allele2}` ||
           genotype === `${allele2};${allele1}`;
  }
}
<commit_msg>Genotypes.js: Include 23andMe SNP aliases in supported SNPs<commit_after>import has from 'lodash/has';
import Utils from '@/snappy/Utils';
import IidAliases from '#/iidaliases.json';

const genotypeRegex = new RegExp('^([AGCTI-]);?([AGCTI-])$');

export default class Genotypes {
  static async get(snp, genotype) {
    const GenotypeData = await import(/* webpackChunkName: "genotypes" */ '#/genotypes.json');

    if (!has(GenotypeData, snp) && snp.startsWith('i')) {
      // 23andMe SNP; might have a corresponding rsID
      const resolved = Utils.resolveIidAlias(snp);
      if (resolved) {
        /* eslint-disable no-param-reassign */
        snp = resolved;
        if (Utils.isSnpOrientationMinus(snp)) {
          genotype = Utils.flipAlleles(genotype);
        }
        /* eslint-enable no-param-reassign */
      }
    }

    if (has(GenotypeData, snp)) {
      if (typeof genotype === 'undefined') {
        // get(snp)
        return GenotypeData[snp];
      }

      const g = genotypeRegex.exec(genotype);
      if (!g) {
        throw Error(`Invalid genotype ${snp} ${genotype}`);
      }

      let result = null;

      const match = this.getMatch(
        Object.keys(GenotypeData[snp]),
        g[1],
        g[2],
      );
      if (match) {
        // Exact match
        result = GenotypeData[snp][match];

        if (typeof result === 'string') {
          // Redirect
          const target = result;
          result = GenotypeData[snp][target];
          result.o = target;
        } else {
          result.o = match;
        }
      } else if (genotype.indexOf('I') !== -1) {
        // Insertion
        const candidates = [];
        for (const candidate of Object.keys(GenotypeData[snp])) {
          const normalized = candidate.replace(new RegExp('[AGCT]+', 'g'), 'I');
          if (this.matches(normalized, g[1], g[2])) {
            candidates.push(candidate);
          }
        }
        if (candidates.length !== 1) {
          // Ambiguous insertion
          throw Error(`${snp} ${genotype} is ambiguous or has no info; Possible genotypes include ${candidates}`);
        }
        result = GenotypeData[snp][candidates[0]];
        result.o = candidates[0];
      } else {
        throw Error(`Cannot interpret ${snp} ${genotype}: ${genotype} has no info`);
      }

      // Success
      result.name = snp;
      result.g = genotype;
      return result;
    }

    throw Error(`Cannot interpret ${snp} ${genotype}: ${snp} not supported`);
  }

  static async getSupportedSnps() {
    const GenotypeData = await import(/* webpackChunkName: "genotypes" */ '#/genotypes.json');
    const result = Object.keys(GenotypeData).concat(Object.keys(IidAliases));
    Genotypes.getSupportedSnps = async () => result;
    return result;
  }

  static getMatch(genotypes, allele1, allele2) {
    const s1 = `${allele1};${allele2}`;
    const s2 = `${allele2};${allele1}`;

    if (genotypes.includes(s2)) {
      return s2;
    }
    if (genotypes.includes(s1)) {
      return s1;
    }
    return false;
  }

  static matches(genotype, allele1, allele2) {
    return genotype === `${allele1};${allele2}` ||
           genotype === `${allele2};${allele1}`;
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

var NetworkError = require('../NetworkError');
var WorkPool = require('../collections/WorkPool');
var chain = require('../util/chain');
var delay = require('../util/delay');
var clock = require('../util/clock');

// Map template properties to their corresponding tile properties.
var templateProperties = {
  x: 'x',
  y: 'y',
  z: 'z',
  f: 'face'
};

// Default face order for cube maps.
var defaultCubeMapFaceOrder = 'bdflru';

// Default maximum number of concurrent requests.
var defaultConcurrency = 4;

// Default milliseconds to wait before retrying failed requests.
var defaultRetryDelay = 10000;

/**
 * @class
 * @classdesc Source that loads images given a URL and a crop rectangle.
 * @implements Source
 * @param {Function} sourceFromTile Function that receives a tile and returns
 * an object `{ url, rect }`.
 * @param {Object} opts
 * @param {number} [opts.concurrency=4] Maximum number of tiles to load at the
 * same time.
 * @param {number} [opts.retryDelay=10000] Milliseconds to wait before
 * retrying failed requests.
*/
function ImageUrlSource(sourceFromTile, opts) {

  opts = opts ? opts : {};

  this._loadPool = new WorkPool({
    concurrency: opts.concurrency || defaultConcurrency
  });

  this._retryDelay = opts.retryDelay || defaultRetryDelay;
  this._retryMap = {};

  this._sourceFromTile = sourceFromTile;
}

ImageUrlSource.prototype.loadAsset = function(stage, tile, done) {

  var self = this;

  var retryDelay = this._retryDelay;
  var retryMap = this._retryMap;

  var tileSource = this._sourceFromTile(tile);
  var url = tileSource.url;
  var rect = tileSource.rect;

  var loadImage = stage.loadImage.bind(stage, url, rect);

  var loadFn = function(done) {
    return self._loadPool.push(loadImage, function(err, asset) {
      if (err) {
        if (err instanceof NetworkError) {
          // If a network error occurred, wait before retrying.
          retryMap[url] = clock();
        }
        done(err, tile);
      } else {
        // On a successful fetch, forget the previous timeout.
        delete retryMap[url];
        done(null, tile, asset);
      }
    });
  };

  // Check whether we are retrying a failed request.
  var delayAmount;
  var lastTime = retryMap[url];
  if (lastTime != null) {
    var now = clock();
    var elapsed = now - lastTime;
    if (elapsed < retryDelay) {
      // Wait before retrying.
      delayAmount = retryDelay - elapsed;
    } else {
      // Retry timeout expired; perform the request at once.
      delayAmount = 0;
      delete retryMap[url];
    }
  }

  var delayFn = delay.bind(null, delayAmount);

  return chain(delayFn, loadFn)(done);
};

/**
 * Create an ImageUrlSource from a string template
 * @param {String} url Template for the tile URLs. The following placeholders may be used:
   - {z} : tile level index (0 is the smallest level)
   - {f} : tile face (b, d, f, l, r, u)
   - {x} : tile x index
   - {y} : tile y index
 * @param {Object} opts
 * @param {String} opts.cubeMapPreviewUrl Use the cube map preview at this URL as the first level
 * @param {String} [opts.cubeMapPreviewFaceOrder='bdflru'] Ordering of the faces on the cube map preview
*/
ImageUrlSource.fromString = function(url, opts) {
  opts = opts || {};

  var faceOrder = opts && opts.cubeMapPreviewFaceOrder || defaultCubeMapFaceOrder;

  var urlFn = opts.cubeMapPreviewUrl ? withPreview : withoutPreview;

  return new ImageUrlSource(urlFn);

  function withoutPreview(tile) {
    var tileUrl = url;

    for (var property in templateProperties) {
      var regExp = propertyRegExp(property);
      var valueFromTile = tile[templateProperties[property]] || '';
      tileUrl = tileUrl.replace(regExp, valueFromTile);
    }

    return { url: tileUrl };
  }

  function withPreview(tile) {
    if (tile.z === 0) {
      return cubeMapUrl(tile);
    }
    else {
      return withoutPreview(tile);
    }
  }

  function cubeMapUrl(tile) {
    var y = faceOrder.indexOf(tile.face) / 6;
    return {
      url: opts.cubeMapPreviewUrl,
      rect: { x: 0, y: y, width: 1, height: 1/6 }
    };
  }
};

ImageUrlSource.stringHasFace = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'f');
};

ImageUrlSource.stringHasX = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'x');
};

ImageUrlSource.stringHasY = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'y');
};

ImageUrlSource.stringHasLevel = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'z');
};

ImageUrlSource.stringHasProperty = function(url, property) {
  return !!url.match(propertyRegExp(property));
};

function propertyRegExp(property) {
  var regExpStr = '\\{(' + property + ')\\}';
  return new RegExp(regExpStr, 'g');
}

module.exports = ImageUrlSource;
<commit_msg>Fix ImageUrl not interpolating a template property with value 0 correctly.<commit_after>/*
 * Copyright 2016 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

var NetworkError = require('../NetworkError');
var WorkPool = require('../collections/WorkPool');
var chain = require('../util/chain');
var delay = require('../util/delay');
var clock = require('../util/clock');

// Map template properties to their corresponding tile properties.
var templateProperties = {
  x: 'x',
  y: 'y',
  z: 'z',
  f: 'face'
};

// Default face order for cube maps.
var defaultCubeMapFaceOrder = 'bdflru';

// Default maximum number of concurrent requests.
var defaultConcurrency = 4;

// Default milliseconds to wait before retrying failed requests.
var defaultRetryDelay = 10000;

/**
 * @class
 * @classdesc Source that loads images given a URL and a crop rectangle.
 * @implements Source
 * @param {Function} sourceFromTile Function that receives a tile and returns
 * an object `{ url, rect }`.
 * @param {Object} opts
 * @param {number} [opts.concurrency=4] Maximum number of tiles to load at the
 * same time.
 * @param {number} [opts.retryDelay=10000] Milliseconds to wait before
 * retrying failed requests.
*/
function ImageUrlSource(sourceFromTile, opts) {

  opts = opts ? opts : {};

  this._loadPool = new WorkPool({
    concurrency: opts.concurrency || defaultConcurrency
  });

  this._retryDelay = opts.retryDelay || defaultRetryDelay;
  this._retryMap = {};

  this._sourceFromTile = sourceFromTile;
}

ImageUrlSource.prototype.loadAsset = function(stage, tile, done) {

  var self = this;

  var retryDelay = this._retryDelay;
  var retryMap = this._retryMap;

  var tileSource = this._sourceFromTile(tile);
  var url = tileSource.url;
  var rect = tileSource.rect;

  var loadImage = stage.loadImage.bind(stage, url, rect);

  var loadFn = function(done) {
    return self._loadPool.push(loadImage, function(err, asset) {
      if (err) {
        if (err instanceof NetworkError) {
          // If a network error occurred, wait before retrying.
          retryMap[url] = clock();
        }
        done(err, tile);
      } else {
        // On a successful fetch, forget the previous timeout.
        delete retryMap[url];
        done(null, tile, asset);
      }
    });
  };

  // Check whether we are retrying a failed request.
  var delayAmount;
  var lastTime = retryMap[url];
  if (lastTime != null) {
    var now = clock();
    var elapsed = now - lastTime;
    if (elapsed < retryDelay) {
      // Wait before retrying.
      delayAmount = retryDelay - elapsed;
    } else {
      // Retry timeout expired; perform the request at once.
      delayAmount = 0;
      delete retryMap[url];
    }
  }

  var delayFn = delay.bind(null, delayAmount);

  return chain(delayFn, loadFn)(done);
};

/**
 * Create an ImageUrlSource from a string template
 * @param {String} url Template for the tile URLs. The following placeholders may be used:
   - {z} : tile level index (0 is the smallest level)
   - {f} : tile face (b, d, f, l, r, u)
   - {x} : tile x index
   - {y} : tile y index
 * @param {Object} opts
 * @param {String} opts.cubeMapPreviewUrl Use the cube map preview at this URL as the first level
 * @param {String} [opts.cubeMapPreviewFaceOrder='bdflru'] Ordering of the faces on the cube map preview
*/
ImageUrlSource.fromString = function(url, opts) {
  opts = opts || {};

  var faceOrder = opts && opts.cubeMapPreviewFaceOrder || defaultCubeMapFaceOrder;

  var urlFn = opts.cubeMapPreviewUrl ? withPreview : withoutPreview;

  return new ImageUrlSource(urlFn);

  function withoutPreview(tile) {
    var tileUrl = url;

    for (var property in templateProperties) {
      var templateProperty = templateProperties[property];
      var regExp = propertyRegExp(property);
      var valueFromTile = tile.hasOwnProperty(templateProperty) ? tile[templateProperty] : '';
      tileUrl = tileUrl.replace(regExp, valueFromTile);
    }

    return { url: tileUrl };
  }

  function withPreview(tile) {
    if (tile.z === 0) {
      return cubeMapUrl(tile);
    }
    else {
      return withoutPreview(tile);
    }
  }

  function cubeMapUrl(tile) {
    var y = faceOrder.indexOf(tile.face) / 6;
    return {
      url: opts.cubeMapPreviewUrl,
      rect: { x: 0, y: y, width: 1, height: 1/6 }
    };
  }
};

ImageUrlSource.stringHasFace = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'f');
};

ImageUrlSource.stringHasX = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'x');
};

ImageUrlSource.stringHasY = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'y');
};

ImageUrlSource.stringHasLevel = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'z');
};

ImageUrlSource.stringHasProperty = function(url, property) {
  return !!url.match(propertyRegExp(property));
};

function propertyRegExp(property) {
  var regExpStr = '\\{(' + property + ')\\}';
  return new RegExp(regExpStr, 'g');
}

module.exports = ImageUrlSource;
<|endoftext|>


<commit_before>require('cupoftea');
var assert = require('assert');
var _ = require('underscore');
var util = require('util');

spec('parser', function () {
	var MemoTable = function () {
		var memos = [];
		var addMemo = function(memo) {
			memos.push(memo);
		};

		this.clear = function () {
            for (var i in memos) {
                var memo = memos[i];
                memo.table = {};
            }
		};

	    this.memoise = function (parser) {
	        var memo = {table: {}};
	        addMemo(memo);
	        return function (source, index, continuation) {
	            var parseResult = memo.table[index];
	            if (parseResult) {
	                continuation(parseResult);
	            } else {
  	            parser(source, index, function (parseResult) {
  	              memo.table[index] = parseResult;
  	              continuation(parseResult);
  	            });
	            }
	        };
	    };
	};

	var memotable = new MemoTable();

	var ignoreLeadingWhitespace = function (parser) {
    return function (source, index, continuation) {
      whitespace(source, index, function (parsedWhitespace) {
        parser(source, parsedWhitespace.index, continuation);
      });
    };
  };

	var createParser = function (name, originalRe, createTerm, ignoreWhitespace) {
		var ignoreCaseFlag = originalRe.ignoreCase? 'i': '';

		var re = new RegExp(originalRe.source, 'g' + ignoreCaseFlag);
		var parser = memotable.memoise(function (source, index, continuation) {
			re.lastIndex = index;
			var match = re.exec(source);
			if (match && match.index == index) {
				var term = createTerm(match[0]);
				term.index = re.lastIndex;
				continuation(term);
			} else {
				continuation(null);
			}
		});

		var nameParser = function (parser) {
		  parser.parserName = name;
		  return parser;
		};

		if (ignoreWhitespace) {
		  return nameParser(ignoreLeadingWhitespace(parser));
		} else {
  		return nameParser(parser);
  	}
	};

  var sequence = function () {
    var termType = arguments[0];
    var namedParsers = [];
    for (var n = 1; n < arguments.length; n++) {
      namedParsers.push(arguments[n]);
    }

    return function (source, index, continuation) {
      var result = {};

      if (namedParsers.length > 0) {
        for (var i = 0; i < namedParsers.length; i++) {
          var namedParser = namedParsers[i];

          var name;
          var parser;
          if (_.isArray(namedParser)) {
            name = namedParser[0];
            parser = namedParser[1];
          } else {
            parser = namedParser;
            name = null;
          }

          var parseResult = parser(source, index);
          if (parseResult) {
            index = parseResult.index;
            if (name) {
              result[name] = parseResult;
            }
          } else {
            continuation(null);
            return;
          }
        }
      } else {
        continuation(null);
        return;
      }

      result.termType = termType;
      result.index = index;
      continuation(result);
    };
  };

	var integer = createParser(
	  'integer',
		/\d+/,
		function (match) {
			return {integer: parseInt(match)};
		},
		true
	);

	var float = createParser(
	  'float',
		/\d+\.\d+/,
		function (match) {
			return {float: parseFloat(match)};
		},
		true
	);

	var whitespace = createParser(
	  'whitespace',
	  /[ \t]*/,
	  function (match) {
	    return {};
	  }
	);

	var identifier = createParser(
	  'identifier',
		/[a-z]+/i,
		function (match) {
			return {identifier: match};
		},
		true
	);

	var keyword = function (kw) {
	  return createParser(
	    'keyword ' + kw,
  	  new RegExp(kw),
  	  function (match) {
  	    return {keyword: match};
      },
      true
    );
	};

	assert.containsFields = function (term, fields) {
		for (var name in fields) {
			assert.deepEqual(fields[name], term[name], 'term ' + util.inspect(term) + ' should contain field: ' + name + ' == ' + util.inspect(fields[name]));
		}
	};

	assert.doesntParse = function (obj) {
		assert.strictEqual(obj, null);
	};

	spec('integer', function () {
  	spec('parses integer', function () {
  	  integer('8', 0, function (result) {
    		assert.containsFields(result, {integer: 8, index: 1});
  	  });
  	});

  	spec('parses big integer', function () {
  	  integer('888', 0, function (result) {
    		assert.containsFields(result, {integer: 888, index: 3});
  	  });
  	});

  	spec("doesn't parse identifier", function () {
  	  integer('id', 0, function (result) {
    		assert.doesntParse(result);
  		});
  	});

  	spec("parses integer followed by id", function () {
  	  integer('8 id', 0, function (result) {
    		assert.containsFields(result, {integer: 8, index: 1});
  	  });
  	});

  	spec("parses integer within source", function () {
  	  integer('id 8', 3, function (result) {
  		  assert.containsFields(result, {integer: 8, index: 4});
		  });
  	});

  	spec("doesn't parse integer within source", function () {
  	  integer('id id 8', 3, shouldCall(function (result) {
    		assert.doesntParse(result);
  	  }));
  	});

  	spec("parses one integer from many in source", function () {
  	  integer('id 1 2 3 4 5 6 7 8', 9, function (result) {
    		assert.containsFields(result, {integer: 4, index: 10});
  	  });
  	});
	});

	spec('identifier parses identifier', function () {
    spec('parses identifier', function () {
      identifier('one two tHrEe four five', 8, shouldCall(function (result) {
        assert.containsFields(result, {identifier: 'tHrEe', index: 13});
      }));
    });

    spec('parses identifier with leading spaces', function () {
      identifier(' one', 0, shouldCall(function (result) {
        assert.containsFields(result, {identifier: 'one', index: 4});
      }));
    });
	});

	spec('whitespace', function () {
    spec('parses some whitespace', function () {
      whitespace('   one', 0, shouldCall(function (result) {
        assert.containsFields(result, {index: 3});
      }));
    });

    spec('even parses no whitespace', function () {
      whitespace('one', 0, shouldCall(function (result) {
        assert.containsFields(result, {index: 0});
      }));
    });
	});

	spec('float', function () {
  	spec('parses floats', function () {
  	  float('5.6', 0, shouldCall(function (result) {
    		assert.containsFields(result, {float: 5.6, index: 3});
  		}));
  	});

  	spec("doesn't parse floats", function () {
  		spec("that terminate immediately after the point", function() {
  			float('5.', 0, shouldCall(function (result) {
  			  assert.doesntParse(result);
  			}));
  		});

  		spec("that don't have digits after the point", function() {
  			float('5.d', 0, shouldCall(function (result) {
  			  assert.doesntParse(result);
  			}));
  		});

  		spec("that are just integers", function() {
  			float('5', 0, shouldCall(function (result) {
  			  assert.doesntParse(result);
  			}));
  		});
  	});
	});

	spec('keyword', function () {
	  spec('parses keywords', function () {
	    keyword('object')('object', 0, function (result) {
  	    assert.containsFields(result, {keyword: 'object', index: 6});
      });
	  });
	});

	spec('sequence', function () {
    var seq = sequence(
        'type',
        ['name', identifier],
        keyword('to'),
        ['id', integer]);

  	spec('parses correct sequences', function () {
      seq('tank to 8', 0, shouldCall(function (result) {
        assert.containsFields(result, {index: 9, termType: 'type', name: {identifier: 'tank', index: 4}, id: {integer: 8, index: 9}});
      }));
    });

    spec('should not parse sequence', function () {
        seq('9 to tank', 0, shouldCall(function (result) {
          assert.doesntParse(result);
        }));
    });
	});
});
<commit_msg>asyched sequence<commit_after>require('cupoftea');
var assert = require('assert');
var _ = require('underscore');
var util = require('util');

spec('parser', function () {
	var MemoTable = function () {
		var memos = [];
		var addMemo = function(memo) {
			memos.push(memo);
		};

		this.clear = function () {
            for (var i in memos) {
                var memo = memos[i];
                memo.table = {};
            }
		};

	    this.memoise = function (parser) {
	        var memo = {table: {}};
	        addMemo(memo);
	        return function (source, index, continuation) {
	            var parseResult = memo.table[index];
	            if (parseResult) {
	                continuation(parseResult);
	            } else {
  	            parser(source, index, function (parseResult) {
  	              memo.table[index] = parseResult;
  	              continuation(parseResult);
  	            });
	            }
	        };
	    };
	};

	var memotable = new MemoTable();

	var ignoreLeadingWhitespace = function (parser) {
    return function (source, index, continuation) {
      whitespace(source, index, function (parsedWhitespace) {
        parser(source, parsedWhitespace.index, continuation);
      });
    };
  };

	var createParser = function (name, originalRe, createTerm, ignoreWhitespace) {
		var ignoreCaseFlag = originalRe.ignoreCase? 'i': '';

		var re = new RegExp(originalRe.source, 'g' + ignoreCaseFlag);
		var parser = memotable.memoise(function (source, index, continuation) {
			re.lastIndex = index;
			var match = re.exec(source);
			if (match && match.index == index) {
				var term = createTerm(match[0]);
				term.index = re.lastIndex;
				continuation(term);
			} else {
				continuation(null);
			}
		});

		var nameParser = function (parser) {
		  parser.parserName = name;
		  return parser;
		};

		if (ignoreWhitespace) {
		  return nameParser(ignoreLeadingWhitespace(parser));
		} else {
  		return nameParser(parser);
  	}
	};

  var sequence = (function () {
    var NamedSubTerm = function (name, parser) {
      this.name = name;
      this.parser = parser;
      this.addToTerm = function (term, result) {
        term[this.name] = result;
      };
    };

    var UnnamedSubTerm = function (parser) {
      this.parser = parser;
      this.addToTerm = function (term) {
        // unnamed sub terms are not added to the term
      };
    };

    var readSubTerm = function (subterm) {
      if (_.isArray(subterm)) {
        return new NamedSubTerm(subterm[0], subterm[1]);
      } else {
        return new UnnamedSubTerm(subterm);
      }
    };

    return function () {
      var termName = arguments[0];

      var subterms = _.map(_.rest(arguments), function (subtermArgument) {
        return readSubTerm(subtermArgument);
      });

      return function (source, startIndex, continuation) {
        var term = {termName: termName, index: startIndex};

        var parseSubTerm = function (subtermIndex, index, nextSubtermIndex) {
          var subterm = subterms[subtermIndex];
          if (subterm) {
            subterm.parser(source, index, nextSubTermParser(subterm, subtermIndex + 1));
          } else {
            term.index = index;
            continuation(term);
          }
        };

        var nextSubTermParser = function (previousSubterm, subtermIndex) {
          return function (result) {
            if (result) {
              previousSubterm.addToTerm(term, result);
              parseSubTerm(subtermIndex, result.index);
            } else {
              continuation(null);
            }
          };
        };

        parseSubTerm(0, startIndex);
      };
    };
  }());

	var integer = createParser(
	  'integer',
		/\d+/,
		function (match) {
			return {integer: parseInt(match)};
		},
		true
	);

	var float = createParser(
	  'float',
		/\d+\.\d+/,
		function (match) {
			return {float: parseFloat(match)};
		},
		true
	);

	var whitespace = createParser(
	  'whitespace',
	  /[ \t]*/,
	  function (match) {
	    return {};
	  }
	);

	var identifier = createParser(
	  'identifier',
		/[a-z]+/i,
		function (match) {
			return {identifier: match};
		},
		true
	);

	var keyword = function (kw) {
	  return createParser(
	    'keyword "' + kw + '"',
  	  new RegExp(kw),
  	  function (match) {
  	    return {keyword: match};
      },
      true
    );
	};

	assert.containsFields = function (term, fields) {
		for (var name in fields) {
			assert.deepEqual(fields[name], term[name], 'term ' + util.inspect(term) + ' should contain field: ' + name + ' == ' + util.inspect(fields[name]));
		}
	};

	assert.doesntParse = function (obj) {
		assert.strictEqual(obj, null);
	};

	spec('integer', function () {
  	spec('parses integer', function () {
  	  integer('8', 0, function (result) {
    		assert.containsFields(result, {integer: 8, index: 1});
  	  });
  	});

  	spec('parses big integer', function () {
  	  integer('888', 0, function (result) {
    		assert.containsFields(result, {integer: 888, index: 3});
  	  });
  	});

  	spec("doesn't parse identifier", function () {
  	  integer('id', 0, function (result) {
    		assert.doesntParse(result);
  		});
  	});

  	spec("parses integer followed by id", function () {
  	  integer('8 id', 0, function (result) {
    		assert.containsFields(result, {integer: 8, index: 1});
  	  });
  	});

  	spec("parses integer within source", function () {
  	  integer('id 8', 3, function (result) {
  		  assert.containsFields(result, {integer: 8, index: 4});
		  });
  	});

  	spec("doesn't parse integer within source", function () {
  	  integer('id id 8', 3, shouldCall(function (result) {
    		assert.doesntParse(result);
  	  }));
  	});

  	spec("parses one integer from many in source", function () {
  	  integer('id 1 2 3 4 5 6 7 8', 9, function (result) {
    		assert.containsFields(result, {integer: 4, index: 10});
  	  });
  	});
	});

	spec('identifier parses identifier', function () {
    spec('parses identifier', function () {
      identifier('one two tHrEe four five', 8, shouldCall(function (result) {
        assert.containsFields(result, {identifier: 'tHrEe', index: 13});
      }));
    });

    spec('parses identifier with leading spaces', function () {
      identifier(' one', 0, shouldCall(function (result) {
        assert.containsFields(result, {identifier: 'one', index: 4});
      }));
    });
	});

	spec('whitespace', function () {
    spec('parses some whitespace', function () {
      whitespace('   one', 0, shouldCall(function (result) {
        assert.containsFields(result, {index: 3});
      }));
    });

    spec('even parses no whitespace', function () {
      whitespace('one', 0, shouldCall(function (result) {
        assert.containsFields(result, {index: 0});
      }));
    });
	});

	spec('float', function () {
  	spec('parses floats', function () {
  	  float('5.6', 0, shouldCall(function (result) {
    		assert.containsFields(result, {float: 5.6, index: 3});
  		}));
  	});

  	spec("doesn't parse floats", function () {
  		spec("that terminate immediately after the point", function() {
  			float('5.', 0, shouldCall(function (result) {
  			  assert.doesntParse(result);
  			}));
  		});

  		spec("that don't have digits after the point", function() {
  			float('5.d', 0, shouldCall(function (result) {
  			  assert.doesntParse(result);
  			}));
  		});

  		spec("that are just integers", function() {
  			float('5', 0, shouldCall(function (result) {
  			  assert.doesntParse(result);
  			}));
  		});
  	});
	});

	spec('keyword', function () {
	  spec('parses keywords', function () {
	    keyword('object')('object', 0, function (result) {
  	    assert.containsFields(result, {keyword: 'object', index: 6});
      });
	  });
	});

	spec('sequence', function () {
    var seq = sequence(
        'type',
        ['name', identifier],
        keyword('to'),
        ['id', integer]);

  	spec('parses correct sequences', function () {
      seq('tank to 8', 0, shouldCall(function (result) {
        assert.containsFields(result, {index: 9, termName: 'type', name: {identifier: 'tank', index: 4}, id: {integer: 8, index: 9}});
      }));
    });

    spec('should not parse sequence', function () {
        seq('9 to tank', 0, shouldCall(function (result) {
          assert.doesntParse(result);
        }));
    });
	});
});
<|endoftext|>