<commit_before>'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Status = include('util/status.js');

var ChannelSchema = new Schema({
  'users': [{
    'type': mongoose.Schema.Types.ObjectId,
    'ref': 'User'
  }],
  'messages': [{
    'text': String,
    'postedTime': Date,
    'postedBy': {
      'type': mongoose.Schema.Types.ObjectId,
      'ref': 'User'
    }
  }],
  'name': String
});

module.exports.mongooseModel = mongoose.model('Channel', ChannelSchema);
var ChannelModel = mongoose.exports.mongooseModel;

function getChannelsForUser(userid, cb) {
  // let ChannelModel = mongoose.model('Channel');
  ChannelModel.find({
    'users': userid
  }, (err, obj) => {
    if (err != null || obj == null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      cb(new Status(200, 'alls well', null), obj);
    }
  });
}

function getAllChannels(cb) {
  // let ChannelModel = mongoose.model('Channel');
  ChannelModel.find({})
    .select('-messages')
    .exec((err, obj) => {
      if (err != null || obj == null) {
        cb(new Status(404, 'Error getting or saving object', err), null);
      } else {
        cb(new Status(200, 'alls well', null), obj);
      }
    });
}

function createChannel(obj, cb) {
  let ChannelModel = mongoose.model('Channel');
  let channel = new ChannelModel({
    users: obj.users,
    messages: [],
    name: obj.name
  });
  channel.save((err, obj) => {
    if (err !== null || obj === null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      cb(new Status(201, 'Channel ' + channel.name + ' created'), {
        name: channel.name,
        _id: channel.id,
        users: channel.users
      });
    }
  });
}

function deleteChannel(channelId, cb) {
  let ChannelModel = mongoose.model('Channel');
  ChannelModel.findByIdAndRemove(channelId, (err, channel) => {
    if (err != null || channel == null) {
      cb(new Status(404, 'Error getting or deleting object', err), null);
    } else {
      cb(new Status(201, 'channel removed', null), channel);
    }
  });
}

function addUserToChannel(channelId, userId, cb) {
  let ChannelModel = mongoose.model('Channel');
  ChannelModel.findOne({
    _id: channelId
  }, (err, channel) => {
    if (err != null || channel == null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      channel.users.push(userId);
      channel.save((err, savedChannel) => {
        cb(new Status(err != null ? 500 : 201,
          err != null ? 'Error getting or saving object' : null,
          err), savedChannel);
      });
    }
  });
}

function removeUserFromChannel(channelId, userId, cb) {
  let ChannelModel = mongoose.model('Channel');
  ChannelModel.findOne({
    _id: channelId
  }, (err, channel) => {
    if (err != null || channel == null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      let index = channel.users.indexOf(userId);
      if (index >= -1) {
        channel.users.splice(index, 1);
      }
      channel.save((err, savedChannel) => {
        cb(new Status(err != null ? 500 : 201,
          err != null ? 'Error getting or saving object' : null,
          err), savedChannel);
      });
    }
  });
}

function addMessageToChannel(channelId, postedBy, message, cb) {
  let ChannelModel = mongoose.model('Channel');
  ChannelModel.findOne({
    _id: channelId
  }, (err, channel) => {
    if (err != null || channel == null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      let msg = {
        text: message,
        postedBy: postedBy,
        postedTime: new Date()
      };
      channel.messages.push(msg);
      channel.save((err) => {
        if (err != null) {
          cb(new Status(500, 'Error getting or saving object', err), null);
        } else {
          msg.channelId = channelId;
          cb(new Status(201, 'alls well', null), msg);
        }
      });
    }
  });
}

function getSimpleChannel(channelId, cb) {
  let ChannelModel = mongoose.model('Channel');
  return ChannelModel.findById(channelId)
    .select('-messages')
    .exec((err, channel) => {
      if (err != null || channel == null) {
        return cb(new Status(404, 'bad channel', err), null);
      } else {
        cb(new Status(200, 'found channel', null), channel);
      }
    });
}

function getFullChannel(channelId, cb) {
  let ChannelModel = mongoose.model('Channel');
  return ChannelModel.findById(channelId)
    .exec((err, channel) => {
      if (err != null || channel == null) {
        return cb(new Status(404, 'bad channel', err), null);
      } else {
        cb(new Status(200, 'found channel', null), channel);
      }
    });
}

module.exports = {
  mongooseModel: mm,

  // obj{name, users }, cb(status, obj (or list of objs))
  createChannel: createChannel,

  // channelId
  deleteChannel: deleteChannel,

  // channelId, cb
  getSimpleChannel: getSimpleChannel,
  getFullChannel: getFullChannel,

  // cb
  getAllChannels: getAllChannels,

  // userid, cb(status, user)
  getChannelsForUser: getChannelsForUser,
  // channelId, userId, cb(status, channel)
  addUserToChannel: addUserToChannel,

  // channelId, userId, cb(status, channel)
  removeUserFromChannel: removeUserFromChannel,

  // channelId, posedBy are both Object Ids (channel, user)
  // cb(Status, {_id: <channelId>})
  addMessageToChannel: addMessageToChannel
};
<commit_msg>more stuff<commit_after>'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Status = include('util/status.js');

var ChannelSchema = new Schema({
  'users': [{
    'type': mongoose.Schema.Types.ObjectId,
    'ref': 'User'
  }],
  'messages': [{
    'text': String,
    'postedTime': Date,
    'postedBy': {
      'type': mongoose.Schema.Types.ObjectId,
      'ref': 'User'
    }
  }],
  'name': String
});

module.exports.mongooseModel = mongoose.model('Channel', ChannelSchema);
var ChannelModel = module.exports.mongooseModel;

module.exports.getChannelsForUser = function (userid, cb) {
  ChannelModel.find({
    'users': userid
  }, (err, obj) => {
    if (err != null || obj == null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      cb(new Status(200, 'alls well', null), obj);
    }
  });
};

module.exports.getAllChannels = function (cb) {
  ChannelModel.find({})
    .select('-messages')
    .exec((err, obj) => {
      if (err != null || obj == null) {
        cb(new Status(404, 'Error getting or saving object', err), null);
      } else {
        cb(new Status(200, 'alls well', null), obj);
      }
    });
};

module.exports.createChannel = function (obj, cb) {
  let channel = new ChannelModel({
    users: obj.users,
    messages: [],
    name: obj.name
  });
  channel.save((err, obj) => {
    if (err !== null || obj === null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      cb(new Status(201, 'Channel ' + channel.name + ' created'), {
        name: channel.name,
        _id: channel.id,
        users: channel.users
      });
    }
  });
};

module.exports.deleteChannel = function (channelId, cb) {
  ChannelModel.findByIdAndRemove(channelId, (err, channel) => {
    if (err != null || channel == null) {
      cb(new Status(404, 'Error getting or deleting object', err), null);
    } else {
      cb(new Status(201, 'channel removed', null), channel);
    }
  });
};

module.exports.addUserToChannel = function (channelId, userId, cb) {
  ChannelModel.findOne({
    _id: channelId
  }, (err, channel) => {
    if (err != null || channel == null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      channel.users.push(userId);
      channel.save((err, savedChannel) => {
        cb(new Status(err != null ? 500 : 201,
          err != null ? 'Error getting or saving object' : null,
          err), savedChannel);
      });
    }
  });
};

module.exports.removeUserFromChannel = function (channelId, userId, cb) {
  ChannelModel.findOne({
    _id: channelId
  }, (err, channel) => {
    if (err != null || channel == null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      let index = channel.users.indexOf(userId);
      if (index >= -1) {
        channel.users.splice(index, 1);
      }
      channel.save((err, savedChannel) => {
        cb(new Status(err != null ? 500 : 201,
          err != null ? 'Error getting or saving object' : null,
          err), savedChannel);
      });
    }
  });
};

module.exports.addMessageToChannel = function (channelId,
  postedBy, message, cb) {
  ChannelModel.findOne({
    _id: channelId
  }, (err, channel) => {
    if (err != null || channel == null) {
      cb(new Status(404, 'Error getting or saving object', err), null);
    } else {
      let msg = {
        text: message,
        postedBy: postedBy,
        postedTime: new Date()
      };
      channel.messages.push(msg);
      channel.save((err) => {
        if (err != null) {
          cb(new Status(500, 'Error getting or saving object', err), null);
        } else {
          msg.channelId = channelId;
          cb(new Status(201, 'alls well', null), msg);
        }
      });
    }
  });
};

module.exports.getSimpleChannel = function (channelId, cb) {
  return ChannelModel.findById(channelId)
    .select('-messages')
    .exec((err, channel) => {
      if (err != null || channel == null) {
        return cb(new Status(404, 'bad channel', err), null);
      } else {
        cb(new Status(200, 'found channel', null), channel);
      }
    });
};

module.exports.getFullChannel = function (channelId, cb) {
  return ChannelModel.findById(channelId)
    .exec((err, channel) => {
      if (err != null || channel == null) {
        return cb(new Status(404, 'bad channel', err), null);
      } else {
        cb(new Status(200, 'found channel', null), channel);
      }
    });
};
<|endoftext|>


<commit_before>/**
 * Copyright 2015-present 650 Industries. All rights reserved.
 *
 * @providesModule ExponentApp
 * @flow
 */
'use strict';

import React, { PropTypes } from 'react';
import ReactNative, {
  BackAndroid,
  Dimensions,
  Image,
  Linking,
  NativeModules,
  ScrollView,
  StyleSheet,
  Keyboard,
  TouchableOpacity,
  Text,
  TextInput,
  View,
} from 'react-native';
import { connect } from 'react-redux';
import UrlHandler from 'react-native-url-handler';
import ResponsiveImage from '@exponent/react-native-responsive-image';
import TouchableNativeFeedbackSafe from '@exponent/react-native-touchable-native-feedback-safe';

import autobind from 'autobind-decorator';
import { bindActionCreators } from 'redux';

import BrowserActions from 'BrowserActions';
import ExColors from 'ExColors';
import ExLayout from 'ExLayout';
import ExUrls from 'ExUrls';
import ExperienceCollection from 'ExperienceCollection';
import FeaturedExperiences from 'FeaturedExperiences';
import NuxFirstExperienceOverlay from 'NuxFirstExperienceOverlay';

import { Components, Permissions } from 'exponent';

const {
  ExponentConstants,
  ExponentKernel,
} = NativeModules;

let deviceHeight = Dimensions.get('window').height;
let deviceMinusHeaderHeight = deviceHeight - ExLayout.headerHeight;
const AutoFillUrl = '';

class InputAccessoryButton extends React.Component {

  render() {
    return (
      <TouchableOpacity
        style={styles.inputAccessoryButton}
        onPress={this.props.onPress}>
        <Text style={styles.inputAccessoryButtonText}>
          {this.props.title}
        </Text>
      </TouchableOpacity>
    );
  }

}

class ExponentApp extends React.Component {
  static NUX_HAS_FINISHED_FIRST_RUN = 'nuxHasFinishedFirstRun';

  static propTypes = {
    exp: PropTypes.object,
  };

  static getDataProps(data) {
    return {
      history: data.browser.history,
    };
  }

  static getDispatchActions(dispatch) {
    return {
      browserActions: bindActionCreators(BrowserActions, dispatch),
    };
  }

  constructor(props, context) {
    super(props, context);

    BackAndroid.addEventListener('hardwareBackPress', this._closeCamera);

    FeaturedExperiences.setReferrer(this.props.exp.referrer);

    this.state = {
      keyboardHeight: 0,
      urlBarIsFocused: false,
      settingsUrl: '',
      urlSubmitButtonStyle: styles.urlBarSubmitButton,
      isShowingNux: false,
      nuxTooltipFrame: null,
      viewFinderActive: false,
      hasCameraPermission: null,
    };
  }

  async componentDidMount() {
    this._keyboardDidShowSubscription = Keyboard.addListener('keyboardDidShow', (e) => {
      this.setState({keyboardHeight: e.endCoordinates.height});
    });

    this._keyboardDidHideSubscription = Keyboard.addListener('keyboardDidHide', () => {
      this.setState({keyboardHeight: 0});
      TextInput.State.blurTextInput(TextInput.State.currentlyFocusedField());
    });

    let isFirstRunFinished = this.props.exp.nuxHasFinishedFirstRun;
    if (!isFirstRunFinished) {
      requestAnimationFrame(() => {
        this.setState({
          isShowingNux: true,
        }, () => {
          requestAnimationFrame(this._scrollPastUrlBar);
        });
      });
    }

    requestAnimationFrame(this._scrollPastUrlBar);
  }

  _scrollPastUrlBar = () => {
    this._scrollView.scrollTo({ y: ExLayout.navigationBarHeight + 4, x: 0, animated: false });
  }

  componentWillUnmount() {
    this._keyboardDidShowSubscription.remove();
    this._keyboardDidHideSubscription.remove();
  }

  render() {
    let camera;

    // TODO figure out the correct way to position the X
    if (this.state.viewFinderActive) {
      camera = (
        <View style={StyleSheet.absoluteFill}>
          <Components.BarCodeScanner
            style={StyleSheet.absoluteFill}
            onBarCodeRead={this._handleBarCodeRead}
          />
          <TouchableOpacity
            onPress={this._closeCamera}
            style={{position: 'absolute', top: 85, left: 15}}>
            <Text style={{fontSize: 40, color: '#fff'}}>X</Text>
          </TouchableOpacity>
        </View>
      );
    }

    return (
      <View style={styles.container} ref={component => this._rootView = component}>
        {this._renderTitleBar()}
        <ScrollView
          style={styles.scrollView}
          contentContainerStyle={styles.scrollViewContentContainer}
          showsVerticalScrollIndicator={false}
          ref={view => { this._scrollView = view; }}
          keyboardShouldPersistTaps>
          {this._renderNuxSpacer()}
          {this._renderRecentSection()}
          {this._renderFeaturedSection()}
          {this._renderOverlay()}
          {this._renderURLBar()}
        </ScrollView>
        {this._renderNux()}
        {this._renderInputAccessory()}
        {camera}
      </View>
    );
  }

  _renderInputAccessory = () => {
    if (this.state.keyboardHeight === 0 || !this.state.urlBarIsFocused) {
      return;
    }

    return (
      <View style={[styles.inputAccessory, {bottom: this.state.keyboardHeight}]}>
        <InputAccessoryButton
          title="exp.host/@"
          onPress={this._addExpHostToTitle}
        />

        <InputAccessoryButton
          title=".exp.direct:80"
          onPress={this._addExpDirectToTitle}
        />
      </View>
    );
  }

  _addExpHostToTitle = () => {
    const text = 'exp.host/@';

    if (!this.state.settingsUrl) {
      this.setState({settingsUrl: text});
    } else {
      this.setState({settingsUrl: text + this.state.settingsUrl});
    }
  }

  _addExpDirectToTitle = () => {
    const text = '.exp.direct:80';
    if (!this.state.settingsUrl) {
      this.setState({settingsUrl: text});
    } else {
      this.setState({settingsUrl: this.state.settingsUrl + text});
    }
  }

  _renderOverlay() {
    if (this.state.urlBarIsFocused) {
      return (
        <View style={[StyleSheet.absoluteFill, {backgroundColor: 'rgba(0,0,0,0.5)'}]} />
      );
    }
  }

  _renderTitleBar() {
    return (
      <View style={styles.titleBar}>
        <ResponsiveImage
          sources={{
            2: { uri: 'https://s3.amazonaws.com/exp-us-standard/ios-home-header-logo@2x.png' },
            3: { uri: 'https://s3.amazonaws.com/exp-us-standard/ios-home-header-logo@3x.png' },
          }}
          style={styles.titleBarLogo}
        />
        <View>
          <ResponsiveImage
            sources={{
              2: { uri: 'https://s3.amazonaws.com/exp-us-standard/ios-home-header-wordmark@2x.png' },
              3: { uri: 'https://s3.amazonaws.com/exp-us-standard/ios-home-header-wordmark@3x.png' },
            }}
            style={styles.titleBarWordmark}
          />
        </View>
      </View>
    );
  }

  _handleFocus = () => {
    if (!this.state.settingsUrl) {
      this.setState({settingsUrl: AutoFillUrl, urlBarIsFocused: true});
      requestAnimationFrame(() => {
        this._scrollView.scrollTo({ y: 0, x: 0, animated: true });
      });
    } else {
      this.setState({urlBarIsFocused: true});
    }
  }

  _handleBlur = () => {
    if (this.state.settingsUrl === AutoFillUrl) {
      this.setState({settingsUrl: '', urlBarIsFocused: false});
    } else {
      this.setState({urlBarIsFocused: false});
    }
  }

  _renderURLBar() {
    let clearButton;
    let qrButton;
    let { urlBarIsFocused, settingsUrl } = this.state;

    if (urlBarIsFocused) {
      let isEmpty = settingsUrl === AutoFillUrl;
      clearButton = (
        <TouchableNativeFeedbackSafe
          testID="clear_button"
          hitSlop={{left: 10, top: 10, right: 10, bottom: 10}}
          style={[styles.urlBarClearButtonContainer, isEmpty ? {opacity: 0.5} : {}]}
          onPress={this._onClearUrlBar}>
          <Text style={styles.urlBarClearButtonText}>
            X
          </Text>
        </TouchableNativeFeedbackSafe>
      );
    } else {
      // TODO size this correctly
      // TODO upload a proper image to s3
      qrButton = (
        <TouchableNativeFeedbackSafe
          onPress={this._onQrPress}>
          <Image
            style={{width: 50, height: 50}}
            source={{uri: 'https://i.imgur.com/ccaJ2WN.png'}}
          />
        </TouchableNativeFeedbackSafe>
      );
    }

    return (
      <View style={[styles.urlBarContainer, urlBarIsFocused ? {opacity: 1} : {opacity: 0.5}]}>
        {qrButton}
        <TextInput
          testID="url_bar"
          value={settingsUrl}
          onChangeText={text => this._updateUrlBarState(text)}
          onSubmitEditing={this._onSubmitUrlBar}
          keyboardType="url"
          autoCapitalize="none"
          autoCorrect={false}
          enablesReturnKeyAutomatically
          returnKeyType="go"
          onFocus={this._handleFocus}
          onBlur={this._handleBlur}
          placeholder={`Enter an experience URL`}
          placeholderTextColor="#888"
          underlineColorAndroid="transparent"
          style={styles.urlBarTextInput}
        />
        {clearButton}
      </View>
    );
  }

  _openCamera = () => {
    this.setState({viewFinderActive: true});
  }

  _closeCamera = () => {
    let alreadyOpen = this.state.viewFinderActive;
    this.setState({viewFinderActive: false});
    return alreadyOpen; // the event handler will still exit if the camera was not already open
  }

  _onQrPress = async () => {
    if (!this.state.hasCameraPermission) {
      const { status } = await Permissions.askAsync('camera');
      this.setState({hasCameraPermission: status === 'granted'});
    }

    // toggle the viewfinder so people can turn it off
    this._openCamera();
  }

  _handleBarCodeRead = ({ data: url }) => {
    this.setState({viewFinderActive: false});
    Linking.openURL(url);
  }

  _onClearUrlBar = () => {
    this.setState({settingsUrl: AutoFillUrl});
  }

  _renderNuxSpacer() {
    // if we're showing the nux, render some space
    // underneath the nux featured experiences row.
    if (!this.state.isShowingNux) {
      return;
    }
    return (
      <View ref={this._measureNux} style={styles.nuxSpacer} />
    );
  }

  _renderRecentSection() {
    if (this.props.history.size === 0) {
      return null;
    }

    let actionLabel = 'Clear';
    let experiences = (this.props.history) ? this.props.history.toArray() : [];

    return (
      <ExperienceCollection
        experiences={experiences}
        headingLabel="Recent"
        headingStyle={{marginTop: 0}}
        actionLabel={actionLabel}
        onPressItem={(experience) => this._onPressLink(experience.url)}
        onPressHeading={this._onPressRecentHeading}
      />
    );
  }

  _renderFeaturedSection() {
    return (
      <ExperienceCollection
        experiences={this._getFeaturedExperiences()}
        headingLabel="Featured"
        headingStyle={{marginBottom: 0}}
        onPressItem={(experience) => this._onPressLink(experience.url)}
      />
    );
  }

  _renderNux() {
    if (!this.state.isShowingNux) {
      return null;
    }
    return (
      <NuxFirstExperienceOverlay
        referrer={this.props.exp.referrer}
        firstExperienceFrame={this.state.nuxTooltipFrame}
        onPressExperience={this._onPressNuxIcon}
      />
    );
  }

  @autobind
  async _onPressNuxIcon(experience) {
    this.setState(() => ({
        isShowingNux: false,
    }));

    this._onPressLink(experience.url);
  }

  _measureNux = (view) => {
    // needed to correctly position the nux tooltip.
    if (view) {
      requestAnimationFrame(() => {
        view.measureLayout(
          ReactNative.findNodeHandle(this._rootView), (x, y, width, height) => {
            // account for initial scroll position with URL bar.
            y -= ExLayout.navigationBarHeight;
            this.setState((state) => ({
              nuxTooltipFrame: { x, y, width, height },
            }));
        });
      });
    }
  }

  _onPressLink = (url) => {
    Linking.openURL(url);
  }

  _onPressRecentHeading = () => {
    this.props.browserActions.clearHistoryAsync();
  }

  async _onPressCreateShortcut(url, manifest, bundleUrl) {
    await ExponentKernel.createShortcutAsync(url, manifest, bundleUrl);
  }

  _onSubmitUrlBar = () => {
    let { settingsUrl } = this.state;
    if (settingsUrl.toLowerCase() === 'dev menu') {
      ExponentKernel.addDevMenu();
    } else if (settingsUrl.length > 0) {
      let expUrl = ExUrls.normalizeUrl(settingsUrl);
      Linking.openURL(expUrl);
    }
  }

  _updateUrlBarState = (text) => {
    this.setState({
      settingsUrl: text,
    });
  }

  _getFeaturedExperiences() {
    return FeaturedExperiences.getFeatured();
  }
}

export default connect(
  data => ExponentApp.getDataProps(data),
  dispatch => ExponentApp.getDispatchActions(dispatch),
)(ExponentApp);

let styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  titleBar: {
    elevation: 3,
    paddingTop: ExponentConstants.statusBarHeight + 15,
    paddingBottom: 15,
    paddingLeft: 15,
    flexDirection: 'row',
    backgroundColor: ExColors.exponentBlue,
  },
  titleBarLogo: {
    width: 19.5,
    height: 17,
    marginTop: 4,
    marginRight: 12,
  },
  titleBarWordmark: {
    width: 92,
    height: 20,
    marginTop: 4,
  },
  scrollView: {
    flex: 1,
  },
  scrollViewContentContainer: {
    backgroundColor: '#fff',
    paddingTop: 60,
  },
  urlBarContainer: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    height: 60,
    elevation: 2,
    justifyContent: 'center',
    alignItems: 'center',
    flexDirection: 'row',
    backgroundColor: '#fff',
  },
  urlBarTextInput: {
    paddingLeft: 15,
    paddingRight: 55,
    fontSize: 16,
    flex: 1,
    height: 58,
    backgroundColor: '#fff',
  },
  urlBarClearButtonContainer: {
    position: 'absolute',
    right: 15,
    top: 20,
    height: 20,
    width: 20,
    backgroundColor: '#504747',
    borderRadius: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
  urlBarClearButtonText: {
    color: '#fff',
    fontSize: 12,
  },
  urlBarSubmitButton: {
    justifyContent: 'center',
    alignItems: 'center',
    marginLeft: 10,
    height: 40,
    flex: 1,
    overflow: 'hidden',
    borderRadius: 20,
    backgroundColor: '#bbbbbb',
  },
  urlBarSubmitButtonEnabled: {
    backgroundColor: ExColors.navy,
  },
  urlClearSubmitButtonText: {
    color: 'white',
  },
  nuxSpacer: {
    height: 140,
  },
  inputAccessory: {
    flexDirection: 'row',
    backgroundColor: '#eee',
    padding: 10,
    position: 'absolute',
    left: 0,
    right: 0,
    borderBottomWidth: StyleSheet.hairlineWidth,
    borderBottomColor: 'rgba(0,0,0,0.2)',
  },
  inputAccessoryButton: {
    paddingVertical: 5,
    paddingHorizontal: 10,
  },
  inputAccessoryButtonText: {
    color: 'rgba(0,0,0,0.7)',
    fontSize: 15,
  },
});
<commit_msg>QR button image from S3, fix button positioning gap.<commit_after>/**
 * Copyright 2015-present 650 Industries. All rights reserved.
 *
 * @providesModule ExponentApp
 * @flow
 */
'use strict';

import React, { PropTypes } from 'react';
import ReactNative, {
  BackAndroid,
  Dimensions,
  Image,
  Linking,
  NativeModules,
  ScrollView,
  StyleSheet,
  Keyboard,
  TouchableOpacity,
  Text,
  TextInput,
  View,
} from 'react-native';
import { connect } from 'react-redux';
import UrlHandler from 'react-native-url-handler';
import ResponsiveImage from '@exponent/react-native-responsive-image';
import TouchableNativeFeedbackSafe from '@exponent/react-native-touchable-native-feedback-safe';

import autobind from 'autobind-decorator';
import { bindActionCreators } from 'redux';

import BrowserActions from 'BrowserActions';
import ExColors from 'ExColors';
import ExLayout from 'ExLayout';
import ExUrls from 'ExUrls';
import ExperienceCollection from 'ExperienceCollection';
import FeaturedExperiences from 'FeaturedExperiences';
import NuxFirstExperienceOverlay from 'NuxFirstExperienceOverlay';

import { Components, Permissions } from 'exponent';

const {
  ExponentConstants,
  ExponentKernel,
} = NativeModules;

let deviceHeight = Dimensions.get('window').height;
let deviceMinusHeaderHeight = deviceHeight - ExLayout.headerHeight;
const AutoFillUrl = '';

class InputAccessoryButton extends React.Component {

  render() {
    return (
      <TouchableOpacity
        style={styles.inputAccessoryButton}
        onPress={this.props.onPress}>
        <Text style={styles.inputAccessoryButtonText}>
          {this.props.title}
        </Text>
      </TouchableOpacity>
    );
  }

}

class ExponentApp extends React.Component {
  static NUX_HAS_FINISHED_FIRST_RUN = 'nuxHasFinishedFirstRun';

  static propTypes = {
    exp: PropTypes.object,
  };

  static getDataProps(data) {
    return {
      history: data.browser.history,
    };
  }

  static getDispatchActions(dispatch) {
    return {
      browserActions: bindActionCreators(BrowserActions, dispatch),
    };
  }

  constructor(props, context) {
    super(props, context);

    BackAndroid.addEventListener('hardwareBackPress', this._closeCamera);

    FeaturedExperiences.setReferrer(this.props.exp.referrer);

    this.state = {
      keyboardHeight: 0,
      urlBarIsFocused: false,
      settingsUrl: '',
      urlSubmitButtonStyle: styles.urlBarSubmitButton,
      isShowingNux: false,
      nuxTooltipFrame: null,
      viewFinderActive: false,
      hasCameraPermission: null,
    };
  }

  async componentDidMount() {
    this._keyboardDidShowSubscription = Keyboard.addListener('keyboardDidShow', (e) => {
      this.setState({keyboardHeight: e.endCoordinates.height});
    });

    this._keyboardDidHideSubscription = Keyboard.addListener('keyboardDidHide', () => {
      this.setState({keyboardHeight: 0});
      TextInput.State.blurTextInput(TextInput.State.currentlyFocusedField());
    });

    let isFirstRunFinished = this.props.exp.nuxHasFinishedFirstRun;
    if (!isFirstRunFinished) {
      requestAnimationFrame(() => {
        this.setState({
          isShowingNux: true,
        }, () => {
          requestAnimationFrame(this._scrollPastUrlBar);
        });
      });
    }

    requestAnimationFrame(this._scrollPastUrlBar);
  }

  _scrollPastUrlBar = () => {
    this._scrollView.scrollTo({ y: ExLayout.navigationBarHeight + 4, x: 0, animated: false });
  }

  componentWillUnmount() {
    this._keyboardDidShowSubscription.remove();
    this._keyboardDidHideSubscription.remove();
  }

  render() {
    let camera;
    if (this.state.viewFinderActive) {
      camera = (
        <View style={StyleSheet.absoluteFill}>
          <Components.BarCodeScanner
            style={StyleSheet.absoluteFill}
            onBarCodeRead={this._handleBarCodeRead}
          />
          <TouchableOpacity
            onPress={this._closeCamera}
            style={{position: 'absolute', top: 85, left: 15}}>
            <Text style={{fontSize: 40, color: '#fff'}}>X</Text>
          </TouchableOpacity>
        </View>
      );
    }

    return (
      <View style={styles.container} ref={component => this._rootView = component}>
        {this._renderTitleBar()}
        <ScrollView
          style={styles.scrollView}
          contentContainerStyle={styles.scrollViewContentContainer}
          showsVerticalScrollIndicator={false}
          ref={view => { this._scrollView = view; }}
          keyboardShouldPersistTaps>
          {this._renderNuxSpacer()}
          {this._renderRecentSection()}
          {this._renderFeaturedSection()}
          {this._renderOverlay()}
          {this._renderURLBar()}
        </ScrollView>
        {this._renderNux()}
        {this._renderInputAccessory()}
        {camera}
      </View>
    );
  }

  _renderInputAccessory = () => {
    if (this.state.keyboardHeight === 0 || !this.state.urlBarIsFocused) {
      return;
    }

    return (
      <View style={[styles.inputAccessory, {bottom: this.state.keyboardHeight}]}>
        <InputAccessoryButton
          title="exp.host/@"
          onPress={this._addExpHostToTitle}
        />

        <InputAccessoryButton
          title=".exp.direct:80"
          onPress={this._addExpDirectToTitle}
        />
      </View>
    );
  }

  _addExpHostToTitle = () => {
    const text = 'exp.host/@';

    if (!this.state.settingsUrl) {
      this.setState({settingsUrl: text});
    } else {
      this.setState({settingsUrl: text + this.state.settingsUrl});
    }
  }

  _addExpDirectToTitle = () => {
    const text = '.exp.direct:80';
    if (!this.state.settingsUrl) {
      this.setState({settingsUrl: text});
    } else {
      this.setState({settingsUrl: this.state.settingsUrl + text});
    }
  }

  _renderOverlay() {
    if (this.state.urlBarIsFocused) {
      return (
        <View style={[StyleSheet.absoluteFill, {backgroundColor: 'rgba(0,0,0,0.5)'}]} />
      );
    }
  }

  _renderTitleBar() {
    return (
      <View style={styles.titleBar}>
        <ResponsiveImage
          sources={{
            2: { uri: 'https://s3.amazonaws.com/exp-us-standard/ios-home-header-logo@2x.png' },
            3: { uri: 'https://s3.amazonaws.com/exp-us-standard/ios-home-header-logo@3x.png' },
          }}
          style={styles.titleBarLogo}
        />
        <View>
          <ResponsiveImage
            sources={{
              2: { uri: 'https://s3.amazonaws.com/exp-us-standard/ios-home-header-wordmark@2x.png' },
              3: { uri: 'https://s3.amazonaws.com/exp-us-standard/ios-home-header-wordmark@3x.png' },
            }}
            style={styles.titleBarWordmark}
          />
        </View>
      </View>
    );
  }

  _handleFocus = () => {
    if (!this.state.settingsUrl) {
      this.setState({settingsUrl: AutoFillUrl, urlBarIsFocused: true});
      requestAnimationFrame(() => {
        this._scrollView.scrollTo({ y: 0, x: 0, animated: true });
      });
    } else {
      this.setState({urlBarIsFocused: true});
    }
  }

  _handleBlur = () => {
    if (this.state.settingsUrl === AutoFillUrl) {
      this.setState({settingsUrl: '', urlBarIsFocused: false});
    } else {
      this.setState({urlBarIsFocused: false});
    }
  }

  _renderURLBar() {
    let clearButton;
    let qrButton;
    let { urlBarIsFocused, settingsUrl } = this.state;

    if (urlBarIsFocused) {
      let isEmpty = settingsUrl === AutoFillUrl;
      clearButton = (
        <TouchableNativeFeedbackSafe
          testID="clear_button"
          hitSlop={{left: 10, top: 10, right: 10, bottom: 10}}
          style={[styles.urlBarClearButtonContainer, isEmpty ? {opacity: 0.5} : {}]}
          onPress={this._onClearUrlBar}>
          <Text style={styles.urlBarClearButtonText}>
            X
          </Text>
        </TouchableNativeFeedbackSafe>
      );
    } else {
      // TODO upload a proper image to s3
      qrButton = (
        <TouchableNativeFeedbackSafe
          onPress={this._onQrPress}>
          <Image
            style={{width: 60, height: 60}}
            source={{uri: 'https://s3.amazonaws.com/exp-us-standard/qr-code-exponent-is-pretty-cool.png'}}
          />
        </TouchableNativeFeedbackSafe>
      );
    }

    return (
      <View style={[styles.urlBarContainer, urlBarIsFocused ? {opacity: 1} : {opacity: 0.5}]}>
        {qrButton}
        <TextInput
          testID="url_bar"
          value={settingsUrl}
          onChangeText={text => this._updateUrlBarState(text)}
          onSubmitEditing={this._onSubmitUrlBar}
          keyboardType="url"
          autoCapitalize="none"
          autoCorrect={false}
          enablesReturnKeyAutomatically
          returnKeyType="go"
          onFocus={this._handleFocus}
          onBlur={this._handleBlur}
          placeholder={`Enter an experience URL`}
          placeholderTextColor="#888"
          underlineColorAndroid="transparent"
          style={styles.urlBarTextInput}
        />
        {clearButton}
      </View>
    );
  }

  _openCamera = () => {
    this.setState({viewFinderActive: true});
  }

  _closeCamera = () => {
    let alreadyOpen = this.state.viewFinderActive;
    this.setState({viewFinderActive: false});
    return alreadyOpen; // the event handler will still exit if the camera was not already open
  }

  _onQrPress = async () => {
    if (!this.state.hasCameraPermission) {
      const { status } = await Permissions.askAsync('camera');
      this.setState({hasCameraPermission: status === 'granted'});
    }

    // toggle the viewfinder so people can turn it off
    this._openCamera();
  }

  _handleBarCodeRead = ({ data: url }) => {
    this.setState({viewFinderActive: false});
    Linking.openURL(url);
  }

  _onClearUrlBar = () => {
    this.setState({settingsUrl: AutoFillUrl});
  }

  _renderNuxSpacer() {
    // if we're showing the nux, render some space
    // underneath the nux featured experiences row.
    if (!this.state.isShowingNux) {
      return;
    }
    return (
      <View ref={this._measureNux} style={styles.nuxSpacer} />
    );
  }

  _renderRecentSection() {
    if (this.props.history.size === 0) {
      return null;
    }

    let actionLabel = 'Clear';
    let experiences = (this.props.history) ? this.props.history.toArray() : [];

    return (
      <ExperienceCollection
        experiences={experiences}
        headingLabel="Recent"
        headingStyle={{marginTop: 0}}
        actionLabel={actionLabel}
        onPressItem={(experience) => this._onPressLink(experience.url)}
        onPressHeading={this._onPressRecentHeading}
      />
    );
  }

  _renderFeaturedSection() {
    return (
      <ExperienceCollection
        experiences={this._getFeaturedExperiences()}
        headingLabel="Featured"
        headingStyle={{marginBottom: 0}}
        onPressItem={(experience) => this._onPressLink(experience.url)}
      />
    );
  }

  _renderNux() {
    if (!this.state.isShowingNux) {
      return null;
    }
    return (
      <NuxFirstExperienceOverlay
        referrer={this.props.exp.referrer}
        firstExperienceFrame={this.state.nuxTooltipFrame}
        onPressExperience={this._onPressNuxIcon}
      />
    );
  }

  @autobind
  async _onPressNuxIcon(experience) {
    this.setState(() => ({
        isShowingNux: false,
    }));

    this._onPressLink(experience.url);
  }

  _measureNux = (view) => {
    // needed to correctly position the nux tooltip.
    if (view) {
      requestAnimationFrame(() => {
        view.measureLayout(
          ReactNative.findNodeHandle(this._rootView), (x, y, width, height) => {
            // account for initial scroll position with URL bar.
            y -= ExLayout.navigationBarHeight;
            this.setState((state) => ({
              nuxTooltipFrame: { x, y, width, height },
            }));
        });
      });
    }
  }

  _onPressLink = (url) => {
    Linking.openURL(url);
  }

  _onPressRecentHeading = () => {
    this.props.browserActions.clearHistoryAsync();
  }

  async _onPressCreateShortcut(url, manifest, bundleUrl) {
    await ExponentKernel.createShortcutAsync(url, manifest, bundleUrl);
  }

  _onSubmitUrlBar = () => {
    let { settingsUrl } = this.state;
    if (settingsUrl.toLowerCase() === 'dev menu') {
      ExponentKernel.addDevMenu();
    } else if (settingsUrl.length > 0) {
      let expUrl = ExUrls.normalizeUrl(settingsUrl);
      Linking.openURL(expUrl);
    }
  }

  _updateUrlBarState = (text) => {
    this.setState({
      settingsUrl: text,
    });
  }

  _getFeaturedExperiences() {
    return FeaturedExperiences.getFeatured();
  }
}

export default connect(
  data => ExponentApp.getDataProps(data),
  dispatch => ExponentApp.getDispatchActions(dispatch),
)(ExponentApp);

let styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  titleBar: {
    elevation: 3,
    paddingTop: ExponentConstants.statusBarHeight + 15,
    paddingBottom: 15,
    paddingLeft: 15,
    flexDirection: 'row',
    backgroundColor: ExColors.exponentBlue,
  },
  titleBarLogo: {
    width: 19.5,
    height: 17,
    marginTop: 4,
    marginRight: 12,
  },
  titleBarWordmark: {
    width: 92,
    height: 20,
    marginTop: 4,
  },
  scrollView: {
    flex: 1,
  },
  scrollViewContentContainer: {
    backgroundColor: '#fff',
    paddingTop: 60,
  },
  urlBarContainer: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    height: 60,
    elevation: 2,
    justifyContent: 'center',
    alignItems: 'center',
    flexDirection: 'row',
    backgroundColor: '#fff',
  },
  urlBarTextInput: {
    paddingLeft: 15,
    paddingRight: 55,
    fontSize: 16,
    flex: 1,
    height: 58,
    backgroundColor: '#fff',
  },
  urlBarClearButtonContainer: {
    position: 'absolute',
    right: 15,
    top: 20,
    height: 20,
    width: 20,
    backgroundColor: '#504747',
    borderRadius: 10,
    alignItems: 'center',
    justifyContent: 'center',
  },
  urlBarClearButtonText: {
    color: '#fff',
    fontSize: 12,
  },
  urlBarSubmitButton: {
    justifyContent: 'center',
    alignItems: 'center',
    marginLeft: 10,
    height: 40,
    flex: 1,
    overflow: 'hidden',
    borderRadius: 20,
    backgroundColor: '#bbbbbb',
  },
  urlBarSubmitButtonEnabled: {
    backgroundColor: ExColors.navy,
  },
  urlClearSubmitButtonText: {
    color: 'white',
  },
  nuxSpacer: {
    height: 140,
  },
  inputAccessory: {
    flexDirection: 'row',
    backgroundColor: '#eee',
    padding: 10,
    position: 'absolute',
    left: 0,
    right: 0,
    borderBottomWidth: StyleSheet.hairlineWidth,
    borderBottomColor: 'rgba(0,0,0,0.2)',
  },
  inputAccessoryButton: {
    paddingVertical: 5,
    paddingHorizontal: 10,
  },
  inputAccessoryButtonText: {
    color: 'rgba(0,0,0,0.7)',
    fontSize: 15,
  },
});
<|endoftext|>


<commit_before>/*
Language: LiveCode
Author: Ralf Bitter <rabit@revigniter.com>
Description: Language definition for LiveCode server accounting for revIgniter (a web application framework) characteristics.
Version: 1.0a
Date: 2013-06-03
Category: enterprise
*/

function(hljs) {
  var VARIABLE = {
    className: 'variable', begin: '\\b[gtps][A-Z]+[A-Za-z0-9_\\-]*\\b|\\$_[A-Z]+',
    relevance: 0
  };
  var COMMENT_MODES = [
    hljs.C_BLOCK_COMMENT_MODE,
    hljs.HASH_COMMENT_MODE,
    hljs.COMMENT('--', '$'),
    hljs.COMMENT('[^:]//', '$')
  ];
  var TITLE1 = hljs.inherit(hljs.TITLE_MODE, {
    variants: [
      {begin: '\\b_*rig[A-Z]+[A-Za-z0-9_\\-]*'},
      {begin: '\\b_[a-z0-9\\-]+'}
    ]
  });
  var TITLE2 = hljs.inherit(hljs.TITLE_MODE, {begin: '\\b([A-Za-z0-9_\\-]+)\\b'});
  return {
    case_insensitive: false,
    keywords: {
      keyword:
        '$_COOKIE $_FILES $_GET $_GET_BINARY $_GET_RAW $_POST $_POST_BINARY $_POST_RAW $_SESSION $_SERVER ' +
        'codepoint codepoints segment segments codeunit codeunits sentence sentences trueWord trueWords paragraph ' +
        'after byte bytes english the until http forever descending using line real8 with seventh ' +
        'for stdout finally element word words fourth before black ninth sixth characters chars stderr ' +
        'uInt1 uInt1s uInt2 uInt2s stdin string lines relative rel any fifth items from middle mid ' +
        'at else of catch then third it file milliseconds seconds second secs sec int1 int1s int4 ' +
        'int4s internet int2 int2s normal text item last long detailed effective uInt4 uInt4s repeat ' +
        'end repeat URL in try into switch to words https token binfile each tenth as ticks tick ' +
        'system real4 by dateItems without char character ascending eighth whole dateTime numeric short ' +
        'first ftp integer abbreviated abbr abbrev private case while if',
      constant:
        'SIX TEN FORMFEED NINE ZERO NONE SPACE FOUR FALSE COLON CRLF PI COMMA ENDOFFILE EOF EIGHT FIVE ' +
        'QUOTE EMPTY ONE TRUE RETURN CR LINEFEED RIGHT BACKSLASH NULL SEVEN TAB THREE TWO ' +
        'six ten formfeed nine zero none space four false colon crlf pi comma endoffile eof eight five ' +
        'quote empty one true return cr linefeed right backslash null seven tab three two ' +
        'RIVERSION RISTATE FILE_READ_MODE FILE_WRITE_MODE FILE_WRITE_MODE DIR_WRITE_MODE FILE_READ_UMASK ' +
        'FILE_WRITE_UMASK DIR_READ_UMASK DIR_WRITE_UMASK',
      operator:
        'div mod wrap and or bitAnd bitNot bitOr bitXor among not in a an within ' +
        'contains ends with begins the keys of keys',
      built_in:
        'put abs acos aliasReference annuity arrayDecode arrayEncode asin atan atan2 average avg avgDev base64Decode ' +
        'base64Encode baseConvert binaryDecode binaryEncode byteOffset byteToNum cachedURL cachedURLs charToNum ' +
        'cipherNames codepointOffset codepointProperty codepointToNum codeunitOffset commandNames compound compress ' +
        'constantNames cos date dateFormat decompress directories ' +
        'diskSpace DNSServers exp exp1 exp2 exp10 extents files flushEvents folders format functionNames geometricMean global ' +
        'globals hasMemory harmonicMean hostAddress hostAddressToName hostName hostNameToAddress isNumber ISOToMac itemOffset ' +
        'keys len length libURLErrorData libUrlFormData libURLftpCommand libURLLastHTTPHeaders libURLLastRHHeaders ' +
        'libUrlMultipartFormAddPart libUrlMultipartFormData libURLVersion lineOffset ln ln1 localNames log log2 log10 ' +
        'longFilePath lower macToISO matchChunk matchText matrixMultiply max md5Digest median merge millisec ' +
        'millisecs millisecond milliseconds min monthNames nativeCharToNum normalizeText num number numToByte numToChar ' +
        'numToCodepoint numToNativeChar offset open openfiles openProcesses openProcessIDs openSockets ' +
        'paragraphOffset paramCount param params peerAddress pendingMessages platform popStdDev populationStandardDeviation ' +
        'populationVariance popVariance processID random randomBytes replaceText result revCreateXMLTree revCreateXMLTreeFromFile ' +
        'revCurrentRecord revCurrentRecordIsFirst revCurrentRecordIsLast revDatabaseColumnCount revDatabaseColumnIsNull ' +
        'revDatabaseColumnLengths revDatabaseColumnNames revDatabaseColumnNamed revDatabaseColumnNumbered ' +
        'revDatabaseColumnTypes revDatabaseConnectResult revDatabaseCursors revDatabaseID revDatabaseTableNames ' +
        'revDatabaseType revDataFromQuery revdb_closeCursor revdb_columnbynumber revdb_columncount revdb_columnisnull ' +
        'revdb_columnlengths revdb_columnnames revdb_columntypes revdb_commit revdb_connect revdb_connections ' +
        'revdb_connectionerr revdb_currentrecord revdb_cursorconnection revdb_cursorerr revdb_cursors revdb_dbtype ' +
        'revdb_disconnect revdb_execute revdb_iseof revdb_isbof revdb_movefirst revdb_movelast revdb_movenext ' +
        'revdb_moveprev revdb_query revdb_querylist revdb_recordcount revdb_rollback revdb_tablenames ' +
        'revGetDatabaseDriverPath revNumberOfRecords revOpenDatabase revOpenDatabases revQueryDatabase ' +
        'revQueryDatabaseBlob revQueryResult revQueryIsAtStart revQueryIsAtEnd revUnixFromMacPath revXMLAttribute ' +
        'revXMLAttributes revXMLAttributeValues revXMLChildContents revXMLChildNames revXMLCreateTreeFromFileWithNamespaces ' +
        'revXMLCreateTreeWithNamespaces revXMLDataFromXPathQuery revXMLEvaluateXPath revXMLFirstChild revXMLMatchingNode ' +
        'revXMLNextSibling revXMLNodeContents revXMLNumberOfChildren revXMLParent revXMLPreviousSibling ' +
        'revXMLRootNode revXMLRPC_CreateRequest revXMLRPC_Documents revXMLRPC_Error ' +
        'revXMLRPC_GetHost revXMLRPC_GetMethod revXMLRPC_GetParam revXMLText revXMLRPC_Execute ' +
        'revXMLRPC_GetParamCount revXMLRPC_GetParamNode revXMLRPC_GetParamType revXMLRPC_GetPath revXMLRPC_GetPort ' +
        'revXMLRPC_GetProtocol revXMLRPC_GetRequest revXMLRPC_GetResponse revXMLRPC_GetSocket revXMLTree ' +
        'revXMLTrees revXMLValidateDTD revZipDescribeItem revZipEnumerateItems revZipOpenArchives round sampVariance ' +
        'sec secs seconds sentenceOffset sha1Digest shell shortFilePath sin specialFolderPath sqrt standardDeviation statRound ' +
        'stdDev sum sysError systemVersion tan tempName textDecode textEncode tick ticks time to tokenOffset toLower toUpper ' +
        'transpose truewordOffset trunc uniDecode uniEncode upper URLDecode URLEncode URLStatus uuid value variableNames ' +
        'variance version waitDepth weekdayNames wordOffset xsltApplyStylesheet xsltApplyStylesheetFromFile xsltLoadStylesheet ' +
        'xsltLoadStylesheetFromFile add breakpoint cancel clear local variable file word line folder directory URL close socket process ' +
        'combine constant convert create new alias folder directory decrypt delete variable word line folder ' +
        'directory URL dispatch divide do encrypt filter get include intersect kill libURLDownloadToFile ' +
        'libURLFollowHttpRedirects libURLftpUpload libURLftpUploadFile libURLresetAll libUrlSetAuthCallback ' +
        'libURLSetCustomHTTPHeaders libUrlSetExpect100 libURLSetFTPListCommand libURLSetFTPMode libURLSetFTPStopTime ' +
        'libURLSetStatusCallback load multiply socket prepare process post seek rel relative read from process rename ' +
        'replace require resetAll resolve revAddXMLNode revAppendXML revCloseCursor revCloseDatabase revCommitDatabase ' +
        'revCopyFile revCopyFolder revCopyXMLNode revDeleteFolder revDeleteXMLNode revDeleteAllXMLTrees ' +
        'revDeleteXMLTree revExecuteSQL revGoURL revInsertXMLNode revMoveFolder revMoveToFirstRecord revMoveToLastRecord ' +
        'revMoveToNextRecord revMoveToPreviousRecord revMoveToRecord revMoveXMLNode revPutIntoXMLNode revRollBackDatabase ' +
        'revSetDatabaseDriverPath revSetXMLAttribute revXMLRPC_AddParam revXMLRPC_DeleteAllDocuments revXMLAddDTD ' +
        'revXMLRPC_Free revXMLRPC_FreeAll revXMLRPC_DeleteDocument revXMLRPC_DeleteParam revXMLRPC_SetHost ' +
        'revXMLRPC_SetMethod revXMLRPC_SetPort revXMLRPC_SetProtocol revXMLRPC_SetSocket revZipAddItemWithData ' +
        'revZipAddItemWithFile revZipAddUncompressedItemWithData revZipAddUncompressedItemWithFile revZipCancel ' +
        'revZipCloseArchive revZipDeleteItem revZipExtractItemToFile revZipExtractItemToVariable revZipSetProgressCallback ' +
        'revZipRenameItem revZipReplaceItemWithData revZipReplaceItemWithFile revZipOpenArchive send set sort split start stop ' +
        'subtract union unload wait write'
    },
    contains: [
      VARIABLE,
      {
        className: 'keyword',
        begin: '\\bend\\sif\\b'
      },
      {
        className: 'function',
        beginKeywords: 'function', end: '$',
        contains: [
          VARIABLE,
          TITLE2,
          hljs.APOS_STRING_MODE,
          hljs.QUOTE_STRING_MODE,
          hljs.BINARY_NUMBER_MODE,
          hljs.C_NUMBER_MODE,
          TITLE1
        ]
      },
      {
        className: 'function',
        beginKeywords: 'end', end: '$',
        contains: [
          TITLE2,
          TITLE1
        ]
      },
      {
        className: 'command',
        beginKeywords: 'command on', end: '$',
        contains: [
          VARIABLE,
          TITLE2,
          hljs.APOS_STRING_MODE,
          hljs.QUOTE_STRING_MODE,
          hljs.BINARY_NUMBER_MODE,
          hljs.C_NUMBER_MODE,
          TITLE1
        ]
      },
      {
        className: 'command',
        beginKeywords: 'end', end: '$',
        contains: [
          TITLE2,
          TITLE1
        ]
      },
      {
        className: 'preprocessor',
        begin: '<\\?rev|<\\?lc|<\\?livecode',
        relevance: 10
      },
      {
        className: 'preprocessor',
        begin: '<\\?'
      },
      {
        className: 'preprocessor',
        begin: '\\?>'
      },
      hljs.APOS_STRING_MODE,
      hljs.QUOTE_STRING_MODE,
      hljs.BINARY_NUMBER_MODE,
      hljs.C_NUMBER_MODE,
      TITLE1
    ].concat(COMMENT_MODES),
    illegal: ';$|^\\[|^='
  };
}
<commit_msg>Remove command class for the keyword `end`<commit_after>/*
Language: LiveCode
Author: Ralf Bitter <rabit@revigniter.com>
Description: Language definition for LiveCode server accounting for revIgniter (a web application framework) characteristics.
Version: 1.0a
Date: 2013-06-03
Category: enterprise
*/

function(hljs) {
  var VARIABLE = {
    className: 'variable', begin: '\\b[gtps][A-Z]+[A-Za-z0-9_\\-]*\\b|\\$_[A-Z]+',
    relevance: 0
  };
  var COMMENT_MODES = [
    hljs.C_BLOCK_COMMENT_MODE,
    hljs.HASH_COMMENT_MODE,
    hljs.COMMENT('--', '$'),
    hljs.COMMENT('[^:]//', '$')
  ];
  var TITLE1 = hljs.inherit(hljs.TITLE_MODE, {
    variants: [
      {begin: '\\b_*rig[A-Z]+[A-Za-z0-9_\\-]*'},
      {begin: '\\b_[a-z0-9\\-]+'}
    ]
  });
  var TITLE2 = hljs.inherit(hljs.TITLE_MODE, {begin: '\\b([A-Za-z0-9_\\-]+)\\b'});
  return {
    case_insensitive: false,
    keywords: {
      keyword:
        '$_COOKIE $_FILES $_GET $_GET_BINARY $_GET_RAW $_POST $_POST_BINARY $_POST_RAW $_SESSION $_SERVER ' +
        'codepoint codepoints segment segments codeunit codeunits sentence sentences trueWord trueWords paragraph ' +
        'after byte bytes english the until http forever descending using line real8 with seventh ' +
        'for stdout finally element word words fourth before black ninth sixth characters chars stderr ' +
        'uInt1 uInt1s uInt2 uInt2s stdin string lines relative rel any fifth items from middle mid ' +
        'at else of catch then third it file milliseconds seconds second secs sec int1 int1s int4 ' +
        'int4s internet int2 int2s normal text item last long detailed effective uInt4 uInt4s repeat ' +
        'end repeat URL in try into switch to words https token binfile each tenth as ticks tick ' +
        'system real4 by dateItems without char character ascending eighth whole dateTime numeric short ' +
        'first ftp integer abbreviated abbr abbrev private case while if',
      constant:
        'SIX TEN FORMFEED NINE ZERO NONE SPACE FOUR FALSE COLON CRLF PI COMMA ENDOFFILE EOF EIGHT FIVE ' +
        'QUOTE EMPTY ONE TRUE RETURN CR LINEFEED RIGHT BACKSLASH NULL SEVEN TAB THREE TWO ' +
        'six ten formfeed nine zero none space four false colon crlf pi comma endoffile eof eight five ' +
        'quote empty one true return cr linefeed right backslash null seven tab three two ' +
        'RIVERSION RISTATE FILE_READ_MODE FILE_WRITE_MODE FILE_WRITE_MODE DIR_WRITE_MODE FILE_READ_UMASK ' +
        'FILE_WRITE_UMASK DIR_READ_UMASK DIR_WRITE_UMASK',
      operator:
        'div mod wrap and or bitAnd bitNot bitOr bitXor among not in a an within ' +
        'contains ends with begins the keys of keys',
      built_in:
        'put abs acos aliasReference annuity arrayDecode arrayEncode asin atan atan2 average avg avgDev base64Decode ' +
        'base64Encode baseConvert binaryDecode binaryEncode byteOffset byteToNum cachedURL cachedURLs charToNum ' +
        'cipherNames codepointOffset codepointProperty codepointToNum codeunitOffset commandNames compound compress ' +
        'constantNames cos date dateFormat decompress directories ' +
        'diskSpace DNSServers exp exp1 exp2 exp10 extents files flushEvents folders format functionNames geometricMean global ' +
        'globals hasMemory harmonicMean hostAddress hostAddressToName hostName hostNameToAddress isNumber ISOToMac itemOffset ' +
        'keys len length libURLErrorData libUrlFormData libURLftpCommand libURLLastHTTPHeaders libURLLastRHHeaders ' +
        'libUrlMultipartFormAddPart libUrlMultipartFormData libURLVersion lineOffset ln ln1 localNames log log2 log10 ' +
        'longFilePath lower macToISO matchChunk matchText matrixMultiply max md5Digest median merge millisec ' +
        'millisecs millisecond milliseconds min monthNames nativeCharToNum normalizeText num number numToByte numToChar ' +
        'numToCodepoint numToNativeChar offset open openfiles openProcesses openProcessIDs openSockets ' +
        'paragraphOffset paramCount param params peerAddress pendingMessages platform popStdDev populationStandardDeviation ' +
        'populationVariance popVariance processID random randomBytes replaceText result revCreateXMLTree revCreateXMLTreeFromFile ' +
        'revCurrentRecord revCurrentRecordIsFirst revCurrentRecordIsLast revDatabaseColumnCount revDatabaseColumnIsNull ' +
        'revDatabaseColumnLengths revDatabaseColumnNames revDatabaseColumnNamed revDatabaseColumnNumbered ' +
        'revDatabaseColumnTypes revDatabaseConnectResult revDatabaseCursors revDatabaseID revDatabaseTableNames ' +
        'revDatabaseType revDataFromQuery revdb_closeCursor revdb_columnbynumber revdb_columncount revdb_columnisnull ' +
        'revdb_columnlengths revdb_columnnames revdb_columntypes revdb_commit revdb_connect revdb_connections ' +
        'revdb_connectionerr revdb_currentrecord revdb_cursorconnection revdb_cursorerr revdb_cursors revdb_dbtype ' +
        'revdb_disconnect revdb_execute revdb_iseof revdb_isbof revdb_movefirst revdb_movelast revdb_movenext ' +
        'revdb_moveprev revdb_query revdb_querylist revdb_recordcount revdb_rollback revdb_tablenames ' +
        'revGetDatabaseDriverPath revNumberOfRecords revOpenDatabase revOpenDatabases revQueryDatabase ' +
        'revQueryDatabaseBlob revQueryResult revQueryIsAtStart revQueryIsAtEnd revUnixFromMacPath revXMLAttribute ' +
        'revXMLAttributes revXMLAttributeValues revXMLChildContents revXMLChildNames revXMLCreateTreeFromFileWithNamespaces ' +
        'revXMLCreateTreeWithNamespaces revXMLDataFromXPathQuery revXMLEvaluateXPath revXMLFirstChild revXMLMatchingNode ' +
        'revXMLNextSibling revXMLNodeContents revXMLNumberOfChildren revXMLParent revXMLPreviousSibling ' +
        'revXMLRootNode revXMLRPC_CreateRequest revXMLRPC_Documents revXMLRPC_Error ' +
        'revXMLRPC_GetHost revXMLRPC_GetMethod revXMLRPC_GetParam revXMLText revXMLRPC_Execute ' +
        'revXMLRPC_GetParamCount revXMLRPC_GetParamNode revXMLRPC_GetParamType revXMLRPC_GetPath revXMLRPC_GetPort ' +
        'revXMLRPC_GetProtocol revXMLRPC_GetRequest revXMLRPC_GetResponse revXMLRPC_GetSocket revXMLTree ' +
        'revXMLTrees revXMLValidateDTD revZipDescribeItem revZipEnumerateItems revZipOpenArchives round sampVariance ' +
        'sec secs seconds sentenceOffset sha1Digest shell shortFilePath sin specialFolderPath sqrt standardDeviation statRound ' +
        'stdDev sum sysError systemVersion tan tempName textDecode textEncode tick ticks time to tokenOffset toLower toUpper ' +
        'transpose truewordOffset trunc uniDecode uniEncode upper URLDecode URLEncode URLStatus uuid value variableNames ' +
        'variance version waitDepth weekdayNames wordOffset xsltApplyStylesheet xsltApplyStylesheetFromFile xsltLoadStylesheet ' +
        'xsltLoadStylesheetFromFile add breakpoint cancel clear local variable file word line folder directory URL close socket process ' +
        'combine constant convert create new alias folder directory decrypt delete variable word line folder ' +
        'directory URL dispatch divide do encrypt filter get include intersect kill libURLDownloadToFile ' +
        'libURLFollowHttpRedirects libURLftpUpload libURLftpUploadFile libURLresetAll libUrlSetAuthCallback ' +
        'libURLSetCustomHTTPHeaders libUrlSetExpect100 libURLSetFTPListCommand libURLSetFTPMode libURLSetFTPStopTime ' +
        'libURLSetStatusCallback load multiply socket prepare process post seek rel relative read from process rename ' +
        'replace require resetAll resolve revAddXMLNode revAppendXML revCloseCursor revCloseDatabase revCommitDatabase ' +
        'revCopyFile revCopyFolder revCopyXMLNode revDeleteFolder revDeleteXMLNode revDeleteAllXMLTrees ' +
        'revDeleteXMLTree revExecuteSQL revGoURL revInsertXMLNode revMoveFolder revMoveToFirstRecord revMoveToLastRecord ' +
        'revMoveToNextRecord revMoveToPreviousRecord revMoveToRecord revMoveXMLNode revPutIntoXMLNode revRollBackDatabase ' +
        'revSetDatabaseDriverPath revSetXMLAttribute revXMLRPC_AddParam revXMLRPC_DeleteAllDocuments revXMLAddDTD ' +
        'revXMLRPC_Free revXMLRPC_FreeAll revXMLRPC_DeleteDocument revXMLRPC_DeleteParam revXMLRPC_SetHost ' +
        'revXMLRPC_SetMethod revXMLRPC_SetPort revXMLRPC_SetProtocol revXMLRPC_SetSocket revZipAddItemWithData ' +
        'revZipAddItemWithFile revZipAddUncompressedItemWithData revZipAddUncompressedItemWithFile revZipCancel ' +
        'revZipCloseArchive revZipDeleteItem revZipExtractItemToFile revZipExtractItemToVariable revZipSetProgressCallback ' +
        'revZipRenameItem revZipReplaceItemWithData revZipReplaceItemWithFile revZipOpenArchive send set sort split start stop ' +
        'subtract union unload wait write'
    },
    contains: [
      VARIABLE,
      {
        className: 'keyword',
        begin: '\\bend\\sif\\b'
      },
      {
        className: 'function',
        beginKeywords: 'function', end: '$',
        contains: [
          VARIABLE,
          TITLE2,
          hljs.APOS_STRING_MODE,
          hljs.QUOTE_STRING_MODE,
          hljs.BINARY_NUMBER_MODE,
          hljs.C_NUMBER_MODE,
          TITLE1
        ]
      },
      {
        className: 'function',
        beginKeywords: 'end', end: '$',
        contains: [
          TITLE2,
          TITLE1
        ]
      },
      {
        className: 'command',
        beginKeywords: 'command on', end: '$',
        contains: [
          VARIABLE,
          TITLE2,
          hljs.APOS_STRING_MODE,
          hljs.QUOTE_STRING_MODE,
          hljs.BINARY_NUMBER_MODE,
          hljs.C_NUMBER_MODE,
          TITLE1
        ]
      },
      {
        className: 'preprocessor',
        begin: '<\\?rev|<\\?lc|<\\?livecode',
        relevance: 10
      },
      {
        className: 'preprocessor',
        begin: '<\\?'
      },
      {
        className: 'preprocessor',
        begin: '\\?>'
      },
      hljs.APOS_STRING_MODE,
      hljs.QUOTE_STRING_MODE,
      hljs.BINARY_NUMBER_MODE,
      hljs.C_NUMBER_MODE,
      TITLE1
    ].concat(COMMENT_MODES),
    illegal: ';$|^\\[|^='
  };
}
<|endoftext|>


<commit_before>/*-----------------------------------------------------------------------------
| Copyright (c) 2014-2015, S. Chris Colbert
|
| Distributed under the terms of the BSD 3-Clause License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
'use strict';

var del = require('del');
var concat = require('gulp-concat');
var gulp = require('gulp');
var header = require('gulp-header');
var nib = require('nib');
var rename = require('gulp-rename');
var stream = require('event-stream');
var stylus = require('gulp-stylus');
var typedoc = require('gulp-typedoc');
var typescript = require('gulp-typescript');
var uglify = require('gulp-uglify');

var buildTypings = [
  './typings/es6-promise/es6-promise.d.ts'
];

var examplesTypings = buildTypings.concat([
  './typings/codemirror/codemirror.d.ts'
]);

var testsTypings = buildTypings.concat([
  './typings/expect.js/expect.js.d.ts',
  './typings/mocha/mocha.d.ts'
]);

var tsSources = [
  'collections/algorithm',
  'collections/circularbuffer',
  'collections/common',
  'collections/queue',

  'utility/boxsizing',
  'utility/cursor',
  'utility/disposable',
  'utility/empty',
  'utility/hittest',
  'utility/pair',
  'utility/point',
  'utility/rect',
  'utility/size',
  'utility/viewport',

  'core/imessage',
  'core/imessagefilter',
  'core/imessagehandler',
  'core/message',
  'core/messageloop',
  'core/nodebase',
  'core/signaling',

  'di/token',
  'di/container',
  'di/icontainer',

  'virtualdom/factory',
  'virtualdom/basecomponent',
  'virtualdom/component',
  'virtualdom/dom',
  'virtualdom/elem',
  'virtualdom/icomponent',
  'virtualdom/renderer',

  'widgets/alignment',
  'widgets/childmessage',
  'widgets/direction',
  'widgets/dockmode',
  'widgets/ilayoutitem',
  'widgets/movemessage',
  'widgets/orientation',
  'widgets/resizemessage',
  'widgets/sizepolicy',
  'widgets/widgetflag',
  'widgets/layout',
  'widgets/layoutengine',
  'widgets/spaceritem',
  'widgets/widgetitem',
  'widgets/boxlayout',
  'widgets/splithandle',
  'widgets/splitlayout',
  'widgets/stackedlayout',
  'widgets/widget',
  'widgets/panel',
  'widgets/boxpanel',
  'widgets/splitpanel',
  'widgets/stackedpanel',
  'widgets/dockarea',
  'widgets/menuitem',
  'widgets/menu',
  'widgets/menubar',
  'widgets/renderwidget',
  'widgets/scrollbar',
  'widgets/tab',
  'widgets/tabbar',
  'widgets/tabpanel',

  'shell/iplugin',
  'shell/ipluginlist',
  'shell/ishellview',
  'shell/autohide',
  'shell/bootstrapper',
  'shell/menumanager',
  'shell/pluginlist',
  'shell/shellpanel',
  'shell/shellview'
].map(function(name) { return './src/' + name + '.ts'; });

var stylSources = './styl/index.styl';

gulp.task('clean', function(cb) {
  del(['./dist'], cb);
});

gulp.task('src', function() {
  var project = typescript.createProject({
    declarationFiles: true,
    noImplicitAny: true,
    target: 'ES5',
  });

  var src = gulp.src(buildTypings.concat(tsSources))
    .pipe(typescript(project));

  var dts = src.dts.pipe(concat('phosphor.d.ts'))
    .pipe(gulp.dest('./dist'));

  var js = src.pipe(concat('phosphor.js'))
    .pipe(header('"use strict";\n'))
    .pipe(gulp.dest('./dist'));

  return stream.merge(dts, js);
});

gulp.task('styl', function() {
  return gulp.src(stylSources)
    .pipe(stylus({ use: [nib()] }))
    .pipe(rename('phosphor.css'))
    .pipe(gulp.dest('./dist'));
});

gulp.task('build', ['src', 'styl']);

gulp.task('dist', ['build'], function() {
  return gulp.src('./dist/phosphor.js')
    .pipe(uglify())
    .pipe(rename('phosphor.min.js'))
    .pipe(gulp.dest('./dist'));
});

gulp.task('watch', function() {
  gulp.watch(tsSources, ['src']);
});

gulp.task('examples', function() {
  var project = typescript.createProject({
    declarationFiles: false,
    noImplicitAny: true,
    target: 'ES5',
  });

  var sources = examplesTypings.concat([
    'dist/phosphor.d.ts',
    'examples/**/*.ts'
  ]);

  var src = gulp.src(sources)
    .pipe(typescript(project))
    .pipe(rename(function (path) {
      path.dirname += '/build'; }))
    .pipe(header('"use strict";\n'))
    .pipe(gulp.dest('examples'));

  var css = gulp.src('examples/**/index.styl')
    .pipe(stylus({ use: [nib()] }))
    .pipe(rename(function (path) {
      path.dirname += '/build'; }))
    .pipe(gulp.dest('examples'));

  return stream.merge(src, css);
});

gulp.task('docs', function() {
  // FIXME order of concat is important here because typedoc link tag
  // resolving seems to have a bug in scope resolution order. As an
  // example using the [[find]] tag inside the `algorithm` module
  // will resolve to `CodeMirror#find` instead of the `find` function
  // defined in `algorithm`.
  return gulp.src(buildTypings.concat(tsSources))
    .pipe(typedoc({
      out: './build/docs',
      name: 'Phosphor',
      target: 'ES5',
      mode: 'file',
      includeDeclarations: true }));
});

gulp.task('tests', function() {
  var project = typescript.createProject({
    declarationFiles: false,
    noImplicitAny: true,
    target: 'ES5',
  });

  var sources = testsTypings.concat([
    'dist/phosphor.d.ts',
    'tests/**/*.ts'
  ]);

  return gulp.src(sources)
    .pipe(typescript(project))
    .pipe(concat('index.js'))
    .pipe(header('"use strict";\n'))
    .pipe(gulp.dest('tests/build'));
});

gulp.task('default', ['dist']);
<commit_msg>remove stale comment<commit_after>/*-----------------------------------------------------------------------------
| Copyright (c) 2014-2015, S. Chris Colbert
|
| Distributed under the terms of the BSD 3-Clause License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
'use strict';

var del = require('del');
var concat = require('gulp-concat');
var gulp = require('gulp');
var header = require('gulp-header');
var nib = require('nib');
var rename = require('gulp-rename');
var stream = require('event-stream');
var stylus = require('gulp-stylus');
var typedoc = require('gulp-typedoc');
var typescript = require('gulp-typescript');
var uglify = require('gulp-uglify');

var buildTypings = [
  './typings/es6-promise/es6-promise.d.ts'
];

var examplesTypings = buildTypings.concat([
  './typings/codemirror/codemirror.d.ts'
]);

var testsTypings = buildTypings.concat([
  './typings/expect.js/expect.js.d.ts',
  './typings/mocha/mocha.d.ts'
]);

var tsSources = [
  'collections/algorithm',
  'collections/circularbuffer',
  'collections/common',
  'collections/queue',

  'utility/boxsizing',
  'utility/cursor',
  'utility/disposable',
  'utility/empty',
  'utility/hittest',
  'utility/pair',
  'utility/point',
  'utility/rect',
  'utility/size',
  'utility/viewport',

  'core/imessage',
  'core/imessagefilter',
  'core/imessagehandler',
  'core/message',
  'core/messageloop',
  'core/nodebase',
  'core/signaling',

  'di/token',
  'di/container',
  'di/icontainer',

  'virtualdom/factory',
  'virtualdom/basecomponent',
  'virtualdom/component',
  'virtualdom/dom',
  'virtualdom/elem',
  'virtualdom/icomponent',
  'virtualdom/renderer',

  'widgets/alignment',
  'widgets/childmessage',
  'widgets/direction',
  'widgets/dockmode',
  'widgets/ilayoutitem',
  'widgets/movemessage',
  'widgets/orientation',
  'widgets/resizemessage',
  'widgets/sizepolicy',
  'widgets/widgetflag',
  'widgets/layout',
  'widgets/layoutengine',
  'widgets/spaceritem',
  'widgets/widgetitem',
  'widgets/boxlayout',
  'widgets/splithandle',
  'widgets/splitlayout',
  'widgets/stackedlayout',
  'widgets/widget',
  'widgets/panel',
  'widgets/boxpanel',
  'widgets/splitpanel',
  'widgets/stackedpanel',
  'widgets/dockarea',
  'widgets/menuitem',
  'widgets/menu',
  'widgets/menubar',
  'widgets/renderwidget',
  'widgets/scrollbar',
  'widgets/tab',
  'widgets/tabbar',
  'widgets/tabpanel',

  'shell/iplugin',
  'shell/ipluginlist',
  'shell/ishellview',
  'shell/autohide',
  'shell/bootstrapper',
  'shell/menumanager',
  'shell/pluginlist',
  'shell/shellpanel',
  'shell/shellview'
].map(function(name) { return './src/' + name + '.ts'; });

var stylSources = './styl/index.styl';

gulp.task('clean', function(cb) {
  del(['./dist'], cb);
});

gulp.task('src', function() {
  var project = typescript.createProject({
    declarationFiles: true,
    noImplicitAny: true,
    target: 'ES5',
  });

  var src = gulp.src(buildTypings.concat(tsSources))
    .pipe(typescript(project));

  var dts = src.dts.pipe(concat('phosphor.d.ts'))
    .pipe(gulp.dest('./dist'));

  var js = src.pipe(concat('phosphor.js'))
    .pipe(header('"use strict";\n'))
    .pipe(gulp.dest('./dist'));

  return stream.merge(dts, js);
});

gulp.task('styl', function() {
  return gulp.src(stylSources)
    .pipe(stylus({ use: [nib()] }))
    .pipe(rename('phosphor.css'))
    .pipe(gulp.dest('./dist'));
});

gulp.task('build', ['src', 'styl']);

gulp.task('dist', ['build'], function() {
  return gulp.src('./dist/phosphor.js')
    .pipe(uglify())
    .pipe(rename('phosphor.min.js'))
    .pipe(gulp.dest('./dist'));
});

gulp.task('watch', function() {
  gulp.watch(tsSources, ['src']);
});

gulp.task('examples', function() {
  var project = typescript.createProject({
    declarationFiles: false,
    noImplicitAny: true,
    target: 'ES5',
  });

  var sources = examplesTypings.concat([
    'dist/phosphor.d.ts',
    'examples/**/*.ts'
  ]);

  var src = gulp.src(sources)
    .pipe(typescript(project))
    .pipe(rename(function (path) {
      path.dirname += '/build'; }))
    .pipe(header('"use strict";\n'))
    .pipe(gulp.dest('examples'));

  var css = gulp.src('examples/**/index.styl')
    .pipe(stylus({ use: [nib()] }))
    .pipe(rename(function (path) {
      path.dirname += '/build'; }))
    .pipe(gulp.dest('examples'));

  return stream.merge(src, css);
});

gulp.task('docs', function() {
  return gulp.src(buildTypings.concat(tsSources))
    .pipe(typedoc({
      out: './build/docs',
      name: 'Phosphor',
      target: 'ES5',
      mode: 'file',
      includeDeclarations: true }));
});

gulp.task('tests', function() {
  var project = typescript.createProject({
    declarationFiles: false,
    noImplicitAny: true,
    target: 'ES5',
  });

  var sources = testsTypings.concat([
    'dist/phosphor.d.ts',
    'tests/**/*.ts'
  ]);

  return gulp.src(sources)
    .pipe(typescript(project))
    .pipe(concat('index.js'))
    .pipe(header('"use strict";\n'))
    .pipe(gulp.dest('tests/build'));
});

gulp.task('default', ['dist']);
<|endoftext|>


<commit_before>// @flow
import type { Modifier, ModifierArguments } from '../types';
import getNodeName from '../dom-utils/getNodeName';
import { isHTMLElement } from '../dom-utils/instanceOf';

// This modifier takes the styles prepared by the `computeStyles` modifier
// and applies them to the HTMLElements such as popper and arrow

function applyStyles({ state }: ModifierArguments<{||}>) {
  Object.keys(state.elements).forEach(name => {
    const style = state.styles[name] || {};

    const attributes = state.attributes[name] || {};
    const element = state.elements[name];

    // arrow is optional + virtual elements
    if (!isHTMLElement(element) || !getNodeName(element)) {
      return;
    }

    // Flow doesn't support to extend this property, but it's the most
    // effective way to apply styles to an HTMLElement
    // $FlowFixMe
    Object.assign(element.style, style);

    Object.entries(attributes).forEach(([name, value]: [string, any]) => {
      if (value === false) {
        element.removeAttribute(name);
      } else {
        element.setAttribute(name, value === true ? '' : value);
      }
    });
  });
}

function effect({ state }: ModifierArguments<{||}>) {
  const initialStyles = {
    position: 'absolute',
    left: '0',
    top: '0',
    margin: '0',
  };

  Object.assign(state.elements.popper.style, initialStyles);

  return () => {
    Object.keys(state.elements).forEach(name => {
      const element = state.elements[name];
      const styleProperties = Object.keys(
        state.styles.hasOwnProperty(name)
          ? { ...state.styles[name] }
          : initialStyles
      );
      const attributes = state.attributes[name] || {};

      // Set all values to an empty string to unset them
      const style = styleProperties.reduce(
        (style, property) => ({
          ...style,
          [String(property)]: '',
        }),
        {}
      );

      // arrow is optional + virtual elements
      if (!isHTMLElement(element) || !getNodeName(element)) {
        return;
      }

      // Flow doesn't support to extend this property, but it's the most
      // effective way to apply styles to an HTMLElement
      // $FlowFixMe
      Object.assign(element.style, style);

      Object.keys(attributes).forEach(attribute =>
        element.removeAttribute(attribute)
      );
    });
  };
}

export default ({
  name: 'applyStyles',
  enabled: true,
  phase: 'write',
  fn: applyStyles,
  effect,
  requires: ['computeStyles'],
}: Modifier<{||}>);
<commit_msg>fix: remove Object.entries<commit_after>// @flow
import type { Modifier, ModifierArguments } from '../types';
import getNodeName from '../dom-utils/getNodeName';
import { isHTMLElement } from '../dom-utils/instanceOf';

// This modifier takes the styles prepared by the `computeStyles` modifier
// and applies them to the HTMLElements such as popper and arrow

function applyStyles({ state }: ModifierArguments<{||}>) {
  Object.keys(state.elements).forEach(name => {
    const style = state.styles[name] || {};

    const attributes = state.attributes[name] || {};
    const element = state.elements[name];

    // arrow is optional + virtual elements
    if (!isHTMLElement(element) || !getNodeName(element)) {
      return;
    }

    // Flow doesn't support to extend this property, but it's the most
    // effective way to apply styles to an HTMLElement
    // $FlowFixMe
    Object.assign(element.style, style);

    Object.keys(attributes).forEach(name => {
      const value = attributes[name];
      if (value === false) {
        element.removeAttribute(name);
      } else {
        element.setAttribute(name, value === true ? '' : value);
      }
    });
  });
}

function effect({ state }: ModifierArguments<{||}>) {
  const initialStyles = {
    position: 'absolute',
    left: '0',
    top: '0',
    margin: '0',
  };

  Object.assign(state.elements.popper.style, initialStyles);

  return () => {
    Object.keys(state.elements).forEach(name => {
      const element = state.elements[name];
      const styleProperties = Object.keys(
        state.styles.hasOwnProperty(name)
          ? { ...state.styles[name] }
          : initialStyles
      );
      const attributes = state.attributes[name] || {};

      // Set all values to an empty string to unset them
      const style = styleProperties.reduce(
        (style, property) => ({
          ...style,
          [String(property)]: '',
        }),
        {}
      );

      // arrow is optional + virtual elements
      if (!isHTMLElement(element) || !getNodeName(element)) {
        return;
      }

      // Flow doesn't support to extend this property, but it's the most
      // effective way to apply styles to an HTMLElement
      // $FlowFixMe
      Object.assign(element.style, style);

      Object.keys(attributes).forEach(attribute =>
        element.removeAttribute(attribute)
      );
    });
  };
}

export default ({
  name: 'applyStyles',
  enabled: true,
  phase: 'write',
  fn: applyStyles,
  effect,
  requires: ['computeStyles'],
}: Modifier<{||}>);
<|endoftext|>


<commit_before>// used to hold the current SoundManager2 sound object
// this has been moved to ojplayer class
//var currentSound = null;

// update the seek bar for the clients at intervals
var okToUpdate = true;
// how often to update the client seek bar
var seekBarUpdateInterval = 2000;

var updateSeekBarDisplay = function(percentage) {
  // update the progress bar
  $('.progress-bar').width((percentage * 100) + '%');
}

// to be able to access the template from inside soundmanager callbacks
var hostplayerTemplateInstance;

// now we want all clients to get the same thing
var soundManagerOptions = {
  autoLoad: true,
  autoPlay: false,
  stream: true,
  //onload: function() {
    //var startingPosition;
    //Tracker.nonreactive(function() {
      //OJPlayer.loaded(true);
      //startingPosition = OJPlayer.getStartingPosition();
    //});
    //currentSound.setPosition(startingPosition);
    //currentSound.play();
    //currentSound.pause();
    //Session.set("loading", false);
  //},
  onplay: function() {
    console.log("play called");
    $(".playpause > i").removeClass("fa-play");
    $(".playpause > i").addClass("fa-pause");
    CurrentSong.update(hostplayerTemplateInstance.data._id, {
      $set: {paused: false}
    });
  },
  onpause: function() {
    console.log("pause called");
    $(".playpause > i").removeClass("fa-pause");
    $(".playpause > i").addClass("fa-play");
    CurrentSong.update(hostplayerTemplateInstance.data._id, {
      $set: {paused: true}
    });
  },
  onload: function() {
    console.log("on load");
  },
  whileplaying: function() {
    updateSeekBarDisplay(this.position / this.duration);
    // update the position
    //if (okToUpdate) {
      //// make sure we don't update too often
      //okToUpdate = false;
      //var current;
      //Tracker.nonreactive(function() {
        //current = CurrentSong.findOne();
      //});
      //CurrentSong.update(current._id, {
        //$set: {
          //position: this.position,
        //}
      //});
      //// set a timeout to be able to update again in a few seconds
      //Meteor.setTimeout(function() {
        //okToUpdate = true;
      //}, seekBarUpdateInterval);
    //}
  },
  onfinish: function() {
    // destroy the song and remove it from CurrentSong
    console.log("song finished playing");
    //console.log(hostplayerTemplateInstance);
    this.destruct();
    OJPlayer.nextSong(hostplayerTemplateInstance.data._id, hostplayerTemplateInstance.data.paused);
    updateSeekBarDisplay(0);
  }
}

Template.player.helpers({
  mainPlayer: function() {
    return Meteor.connection._lastSessionId === Settings.findOne().playerId;
  },
  playingSong: function() {
    return CurrentSong.findOne();
  },
});

Template.player.created = function() {
  console.log("player created");
  // if it's the first run of the player, start off paused
  var current = CurrentSong.findOne();
  if (current) {
    console.log("setting song to initially be paused");
    CurrentSong.update(current._id, {
      $set: {paused: true}
    });
  }
}

Template.hostPlayer.created = function() {
  console.log("hostplayer template created");
  hostplayerTemplateInstance = this;
  SC.whenStreamingReady(function() {
    console.log("streaming ready");
    // makes sure this autorun gets destroyed when the hostplayer template is
    hostplayerTemplateInstance.autorun(function() {
      // if there's already a sound, get rid of it first so two don't start
      // playing at the same time
      OJPlayer.currentSound && OJPlayer.currentSound.destruct();
      OJPlayer.currentSound = null;
      console.log("autorun");
      // this should set up a reactive variable
      var url = CurrentSong.findOne({}, {fields: {stream_url: 1}});
      //console.log(hostplayerTemplateInstance.data);
      //console.log(url);
      //console.log(soundManager.canPlayURL(url.stream_url));
      if (url) {
        SC.stream(
          url.stream_url, soundManagerOptions, function(sound) {
          console.log("streaming sound successful and created");
          OJPlayer.currentSound = sound;
          console.log("getting paused value");
          var paused = hostplayerTemplateInstance.data.paused;
          console.log(paused);
          if (!paused) {
            console.log("should be played");
            OJPlayer.currentSound.play();
          }
        });
      }
    });
  });
  // to handle playing/pausing using spacebar
  $(document).off('.hostplayer').on("keydown.hostplayer", function(event) {
    console.log("keydown event");
    if (event.which === 32 && document.activeElement.tagName !== "INPUT") {
      console.log("spacebar pressed");
      OJPlayer.currentSound.togglePause();
    }
  });
}

Template.hostPlayer.helpers({
  //loadStreaming: function() {
    //if (!this.loaded && Session.equals("loading", false)) {
      // don't want the song loading multiple times
      //Session.set("loading", true);
    //console.log("loadstreaming called");
      //SC.stream(
        //this.uri, soundManagerOptions, function(sound) {
        //currentSound = sound;
      //});
    //}
  //},
  //playingSong: function() {
    //return CurrentSong.findOne();
  //},
  playPauseIcon: function() {
    //if (this.paused) {
      //if (_.isObject(currentSound) &&
          //this.loaded &&
          //currentSound.paused === false) {
        //currentSound.pause();
      //}
      //return "play";
    //} else {
      //if (_.isObject(currentSound) &&
          //this.loaded &&
          //currentSound.paused === true) {
        //currentSound.resume();
      //}
      //return "pause";
    //}
    //return "play";
  },
  playerDisabled: function() {
    //return this.loaded ? "" : "disabled";
    return "";
  },
});

Template.clientPlayer.helpers({
  playingSong: function() {
    return CurrentSong.findOne();
  },
  playPauseIcon: function() {
    return this.paused ? "play" : "pause";
  },
  playerDisabled: function() {
    //return this.loaded ? "" : "disabled";
    return "";
  },
  updateSeekBar: function() {
    updateSeekBarDisplay(this.position / this.duration);
  }
});

Template.hostPlayer.events({
  // use togglepause on this one (soundmanager2 library)
  "click .playpause, touchstart .playpause" : function(event) {
    //event.preventDefault();
    //if (this.loaded === false) {
      //return;
    //}
    OJPlayer.currentSound.togglePause();
    //if ($(".playpause").has(".fa-play").length) {
      //CurrentSong.update(this._id, {
        //$set: {paused: false}
      //});
      //$(".fa-play").switchClass("fa-play", "fa-pause");
    //} else {
      //CurrentSong.update(this._id, {
        //$set: {paused: true}
      //});
      //$(".fa-pause").switchClass("fa-pause", "fa-play");
    //}
  },
  "touchend .playpause": function(event) {
    // click doubles as a touchend event, so prevent doubling up
    event.preventDefault();
  },
  "click .ff-next, touchstart .ff-next": function(event) {
    //event.preventDefault();
    //if (this.loaded === false) {
      //return;
    //}
    console.log("next clicked");
    var self = this;
    OJPlayer.currentSound.destruct();
    OJPlayer.nextSong(self._id, self.paused);
    updateSeekBarDisplay(0);
  },
  "touchend .ff-next": function(event) {
    event.preventDefault();
  },
  "click .ff-ten, touchstart .ff-ten": function(event) {
    //event.preventDefault();
    //if (this.loaded === false) {
      //return;
    //}
    // skip ahead 10 seconds
    OJPlayer.currentSound.setPosition(OJPlayer.currentSound.position + 10000);
  },
  "touchend .ff-ten": function(event) {
    event.preventDefault();
  },
});

Template.clientPlayer.events({
  "click .playpause, touchstart .playpause": function(event) {
    event.preventDefault();
    if (this.loaded === false) {
      return;
    }
    if ($(".playpause").has(".fa-play").length) {
      CurrentSong.update(this._id, {
        $set: {paused: false}
      });
      $(".fa-play").switchClass("fa-play", "fa-pause");
    } else {
      CurrentSong.update(this._id, {
        $set: {paused: true}
      });
      $(".fa-pause").switchClass("fa-pause", "fa-play");
    }
  },
});

<commit_msg>added in console log for error on streaming load. not sure what to do with error yet other than log it<commit_after>// used to hold the current SoundManager2 sound object
// this has been moved to ojplayer class
//var currentSound = null;

// update the seek bar for the clients at intervals
var okToUpdate = true;
// how often to update the client seek bar
var seekBarUpdateInterval = 2000;

var updateSeekBarDisplay = function(percentage) {
  // update the progress bar
  $('.progress-bar').width((percentage * 100) + '%');
}

// to be able to access the template from inside soundmanager callbacks
var hostplayerTemplateInstance;

// now we want all clients to get the same thing
var soundManagerOptions = {
  autoLoad: true,
  autoPlay: false,
  stream: true,
  //onload: function() {
    //var startingPosition;
    //Tracker.nonreactive(function() {
      //OJPlayer.loaded(true);
      //startingPosition = OJPlayer.getStartingPosition();
    //});
    //currentSound.setPosition(startingPosition);
    //currentSound.play();
    //currentSound.pause();
    //Session.set("loading", false);
  //},
  onplay: function() {
    console.log("play called");
    $(".playpause > i").removeClass("fa-play");
    $(".playpause > i").addClass("fa-pause");
    CurrentSong.update(hostplayerTemplateInstance.data._id, {
      $set: {paused: false}
    });
  },
  onpause: function() {
    console.log("pause called");
    $(".playpause > i").removeClass("fa-pause");
    $(".playpause > i").addClass("fa-play");
    CurrentSong.update(hostplayerTemplateInstance.data._id, {
      $set: {paused: true}
    });
  },
  onload: function() {
    //console.log("on load");
    if (this.readyState === 2) {
      console.log("error loading");
    }
  },
  whileplaying: function() {
    updateSeekBarDisplay(this.position / this.duration);
    // update the position
    //if (okToUpdate) {
      //// make sure we don't update too often
      //okToUpdate = false;
      //var current;
      //Tracker.nonreactive(function() {
        //current = CurrentSong.findOne();
      //});
      //CurrentSong.update(current._id, {
        //$set: {
          //position: this.position,
        //}
      //});
      //// set a timeout to be able to update again in a few seconds
      //Meteor.setTimeout(function() {
        //okToUpdate = true;
      //}, seekBarUpdateInterval);
    //}
  },
  onfinish: function() {
    // destroy the song and remove it from CurrentSong
    console.log("song finished playing");
    //console.log(hostplayerTemplateInstance);
    this.destruct();
    OJPlayer.nextSong(hostplayerTemplateInstance.data._id, hostplayerTemplateInstance.data.paused);
    updateSeekBarDisplay(0);
  }
}

Template.player.helpers({
  mainPlayer: function() {
    return Meteor.connection._lastSessionId === Settings.findOne().playerId;
  },
  playingSong: function() {
    return CurrentSong.findOne();
  },
});

Template.player.created = function() {
  console.log("player created");
  // if it's the first run of the player, start off paused
  var current = CurrentSong.findOne();
  if (current) {
    console.log("setting song to initially be paused");
    CurrentSong.update(current._id, {
      $set: {paused: true}
    });
  }
}

Template.hostPlayer.created = function() {
  console.log("hostplayer template created");
  hostplayerTemplateInstance = this;
  SC.whenStreamingReady(function() {
    console.log("streaming ready");
    // makes sure this autorun gets destroyed when the hostplayer template is
    hostplayerTemplateInstance.autorun(function() {
      // if there's already a sound, get rid of it first so two don't start
      // playing at the same time
      OJPlayer.currentSound && OJPlayer.currentSound.destruct();
      OJPlayer.currentSound = null;
      console.log("autorun");
      // this should set up a reactive variable
      var url = CurrentSong.findOne({}, {fields: {stream_url: 1}});
      //console.log(hostplayerTemplateInstance.data);
      //console.log(url);
      //console.log(soundManager.canPlayURL(url.stream_url));
      if (url) {
        SC.stream(
          url.stream_url, soundManagerOptions, function(sound) {
          console.log("streaming sound successful and created");
          OJPlayer.currentSound = sound;
          console.log("getting paused value");
          var paused = hostplayerTemplateInstance.data.paused;
          console.log(paused);
          if (!paused) {
            console.log("should be played");
            OJPlayer.currentSound.play();
          }
        });
      }
    });
  });
  // to handle playing/pausing using spacebar
  $(document).off('.hostplayer').on("keydown.hostplayer", function(event) {
    console.log("keydown event");
    if (event.which === 32 && document.activeElement.tagName !== "INPUT") {
      console.log("spacebar pressed");
      OJPlayer.currentSound.togglePause();
    }
  });
}

Template.hostPlayer.helpers({
  //loadStreaming: function() {
    //if (!this.loaded && Session.equals("loading", false)) {
      // don't want the song loading multiple times
      //Session.set("loading", true);
    //console.log("loadstreaming called");
      //SC.stream(
        //this.uri, soundManagerOptions, function(sound) {
        //currentSound = sound;
      //});
    //}
  //},
  //playingSong: function() {
    //return CurrentSong.findOne();
  //},
  playPauseIcon: function() {
    //if (this.paused) {
      //if (_.isObject(currentSound) &&
          //this.loaded &&
          //currentSound.paused === false) {
        //currentSound.pause();
      //}
      //return "play";
    //} else {
      //if (_.isObject(currentSound) &&
          //this.loaded &&
          //currentSound.paused === true) {
        //currentSound.resume();
      //}
      //return "pause";
    //}
    //return "play";
  },
  playerDisabled: function() {
    //return this.loaded ? "" : "disabled";
    return "";
  },
});

Template.clientPlayer.helpers({
  playingSong: function() {
    return CurrentSong.findOne();
  },
  playPauseIcon: function() {
    return this.paused ? "play" : "pause";
  },
  playerDisabled: function() {
    //return this.loaded ? "" : "disabled";
    return "";
  },
  updateSeekBar: function() {
    updateSeekBarDisplay(this.position / this.duration);
  }
});

Template.hostPlayer.events({
  // use togglepause on this one (soundmanager2 library)
  "click .playpause, touchstart .playpause" : function(event) {
    //event.preventDefault();
    //if (this.loaded === false) {
      //return;
    //}
    OJPlayer.currentSound.togglePause();
    //if ($(".playpause").has(".fa-play").length) {
      //CurrentSong.update(this._id, {
        //$set: {paused: false}
      //});
      //$(".fa-play").switchClass("fa-play", "fa-pause");
    //} else {
      //CurrentSong.update(this._id, {
        //$set: {paused: true}
      //});
      //$(".fa-pause").switchClass("fa-pause", "fa-play");
    //}
  },
  "touchend .playpause": function(event) {
    // click doubles as a touchend event, so prevent doubling up
    event.preventDefault();
  },
  "click .ff-next, touchstart .ff-next": function(event) {
    //event.preventDefault();
    //if (this.loaded === false) {
      //return;
    //}
    console.log("next clicked");
    var self = this;
    OJPlayer.currentSound.destruct();
    OJPlayer.nextSong(self._id, self.paused);
    updateSeekBarDisplay(0);
  },
  "touchend .ff-next": function(event) {
    event.preventDefault();
  },
  "click .ff-ten, touchstart .ff-ten": function(event) {
    //event.preventDefault();
    //if (this.loaded === false) {
      //return;
    //}
    // skip ahead 10 seconds
    OJPlayer.currentSound.setPosition(OJPlayer.currentSound.position + 10000);
  },
  "touchend .ff-ten": function(event) {
    event.preventDefault();
  },
});

Template.clientPlayer.events({
  "click .playpause, touchstart .playpause": function(event) {
    event.preventDefault();
    if (this.loaded === false) {
      return;
    }
    if ($(".playpause").has(".fa-play").length) {
      CurrentSong.update(this._id, {
        $set: {paused: false}
      });
      $(".fa-play").switchClass("fa-play", "fa-pause");
    } else {
      CurrentSong.update(this._id, {
        $set: {paused: true}
      });
      $(".fa-pause").switchClass("fa-pause", "fa-play");
    }
  },
});

<|endoftext|>