<commit_before>// Whether or not to show confetti is a kiosk parameter.
var g_confetti = false;
$(function() {
  KioskPoller.param_callback = function(parameters) {
    g_confetti = parameters.confetti;
  };

  // Upon displaying an Awards Presentation page, clear any current award.
  $.ajax('action.php',
         {type: 'POST',
          data: {action: 'award.present',
                 key: '',
                 reveal: '0'}
         });
});

var AwardPoller = {
  current_award_key: null,
  // revealed tells whether the previous poll result said to reveal the
  // recipient -- we trigger fade-in and video on the transition from
  // not-revealed to revealed.
  revealed: true,

  query_for_current_award: function() {
    $.ajax('action.php',
           {type: 'GET',
            data: {query: 'award.current'},
            success: function(data) {
              AwardPoller.queue_next_query();
              AwardPoller.process_current_award(data);
            },
            error: function() {
              AwardPoller.queue_next_query();
            }
           });
  },

  queue_next_query: function() {
    setTimeout(function() { AwardPoller.query_for_current_award(); }, 500 /* ms. */);
  },

  parse_award: function(data) {
    var award_xml = data.getElementsByTagName('award')[0];
    if (!award_xml) {
      return false;
    }
    return {key: award_xml.getAttribute('key'),
            reveal: award_xml.getAttribute('reveal') == 'true',
            awardname: award_xml.getAttribute('awardname'),
            carnumber: award_xml.getAttribute('carnumber'),
            carname: award_xml.getAttribute('carname'),
            firstname: award_xml.getAttribute('firstname'),
            lastname: award_xml.getAttribute('lastname'),
            subgroup: award_xml.getAttribute('subgroup'),
            headphoto: award_xml.getAttribute('headphoto'),
            carphoto: award_xml.getAttribute('carphoto')};
  },

  process_current_award: function(data) {
    var award = this.parse_award(data);
    if (!award) {
      $("#awardname").text("Award Presentation");
      $(".reveal").hide();
      return;
    }

    if (this.current_award_key != award.key) {
      $(".reveal").hide();

      $("#awardname").text(award.awardname);
      $("#carnumber").text(award.carnumber);
      $("#firstname").text(award.firstname);
      $("#lastname").text(award.lastname);
      if (award.carname && award.carname.length > 0) {
        $("#carname").text(award.carname);
        $("#carname").css('display', 'block');
      } else {
        $("#carname").css('display', 'none');
      }
      if (award.subgroup && award.subgroup.length > 0) {
        $("#subgroup").text(award.subgroup);
        $("#subgroup").css('display', 'block');
      } else {
        $("#subgroup").css('display', 'none');
      }
      // Need to account for the height of the award-racer text, even though
      // it's presently hidden.
      var previousCss  = $("#award-racer-text").attr("style");
      $("#award-racer-text")
        .css({
          position:   'absolute',
          visibility: 'hidden',
          display:    'block'
        });
      var textHeight = $("#award-racer-text").height();
      $("#award-racer-text").attr("style", previousCss ? previousCss : "");

      // TODO Literal 10 vaguely accounts for margins, but is basically just a guess.
      var maxPhotoHeight = $(window).height() - ($("#photos").offset().top + textHeight) - 10;

      $("#headphoto").empty();
      $("#headphoto").css('width', $(window).width() / 2 - 10);
      $("#headphoto").css('margin-left', 0);

      $("#carphoto").empty();
      $("#carphoto").css('width', $(window).width() / 2 - 10);
      $("#carphoto").css('margin-right', 0);

      if (award.headphoto && award.headphoto.length > 0) {
        $("#headphoto").append("");
        $("#headphoto img").css('max-height', maxPhotoHeight);
      } else {
        // If there's no head photo, center the car photo by adjusting its margin
        $("#carphoto").css('margin-right', $(window).width() / 4);
      }
      if (award.carphoto && award.carphoto.length > 0) {
        $("#carphoto").append("");
        $("#carphoto img").css('max-height', maxPhotoHeight);
      } else {
        // If there's no car photo, center the head photo
        $("#headphoto").css('margin-left', $(window).width() / 4);
      }
      // Changing awards: prime revealed=false so next reveal (or current one)
      // will trigger fade-in and video.
      this.revealed = false;
    }

    if (!award.reveal) {
      $(".reveal").hide();

      var video = $("video.confetti");
      video.hide();
      video.get(0).pause();
      video.get(0).currentTime = 0;
      video.show();
    } else if (!this.revealed) {
      $(".reveal").fadeIn(1000);
      if (g_confetti && this.current_award_key != null) {
        setTimeout(function() {
          $("video.confetti").get(0).play(); }, 500);
        // Confetti is a 10-second video, and <video> element says loop="true";
        // fade out after 20 seconds to play video twice.
        setTimeout(function() {
          $("video.confetti").fadeOut(
            500, function() {
              var video = $("video.confetti");
              video.get(0).pause();
              video.get(0).currentTime = 0;
              video.show();
            }); },
                   20500);
      }
    }
    this.revealed = award.reveal;
    this.current_award_key = award.key;
  }
}

$(function() { AwardPoller.queue_next_query(); });

<commit_msg>Fix initialization of unspecified confetti option, for award presentation page. Fixes #128.<commit_after>// Whether or not to show confetti is a kiosk parameter.
var g_confetti = false;
$(function() {
  KioskPoller.param_callback = function(parameters) {
    if (!parameters.hasOwnProperty('confetti')) {
      parameters.confetti = true;
    }
    g_confetti = parameters.confetti;
  };

  // Upon displaying an Awards Presentation page, clear any current award.
  $.ajax('action.php',
         {type: 'POST',
          data: {action: 'award.present',
                 key: '',
                 reveal: '0'}
         });
});

var AwardPoller = {
  current_award_key: null,
  // revealed tells whether the previous poll result said to reveal the
  // recipient -- we trigger fade-in and video on the transition from
  // not-revealed to revealed.
  revealed: true,

  query_for_current_award: function() {
    $.ajax('action.php',
           {type: 'GET',
            data: {query: 'award.current'},
            success: function(data) {
              AwardPoller.queue_next_query();
              AwardPoller.process_current_award(data);
            },
            error: function() {
              AwardPoller.queue_next_query();
            }
           });
  },

  queue_next_query: function() {
    setTimeout(function() { AwardPoller.query_for_current_award(); }, 500 /* ms. */);
  },

  parse_award: function(data) {
    var award_xml = data.getElementsByTagName('award')[0];
    if (!award_xml) {
      return false;
    }
    return {key: award_xml.getAttribute('key'),
            reveal: award_xml.getAttribute('reveal') == 'true',
            awardname: award_xml.getAttribute('awardname'),
            carnumber: award_xml.getAttribute('carnumber'),
            carname: award_xml.getAttribute('carname'),
            firstname: award_xml.getAttribute('firstname'),
            lastname: award_xml.getAttribute('lastname'),
            subgroup: award_xml.getAttribute('subgroup'),
            headphoto: award_xml.getAttribute('headphoto'),
            carphoto: award_xml.getAttribute('carphoto')};
  },

  process_current_award: function(data) {
    var award = this.parse_award(data);
    if (!award) {
      $("#awardname").text("Award Presentation");
      $(".reveal").hide();
      return;
    }

    if (this.current_award_key != award.key) {
      $(".reveal").hide();

      $("#awardname").text(award.awardname);
      $("#carnumber").text(award.carnumber);
      $("#firstname").text(award.firstname);
      $("#lastname").text(award.lastname);
      if (award.carname && award.carname.length > 0) {
        $("#carname").text(award.carname);
        $("#carname").css('display', 'block');
      } else {
        $("#carname").css('display', 'none');
      }
      if (award.subgroup && award.subgroup.length > 0) {
        $("#subgroup").text(award.subgroup);
        $("#subgroup").css('display', 'block');
      } else {
        $("#subgroup").css('display', 'none');
      }
      // Need to account for the height of the award-racer text, even though
      // it's presently hidden.
      var previousCss  = $("#award-racer-text").attr("style");
      $("#award-racer-text")
        .css({
          position:   'absolute',
          visibility: 'hidden',
          display:    'block'
        });
      var textHeight = $("#award-racer-text").height();
      $("#award-racer-text").attr("style", previousCss ? previousCss : "");

      // TODO Literal 10 vaguely accounts for margins, but is basically just a guess.
      var maxPhotoHeight = $(window).height() - ($("#photos").offset().top + textHeight) - 10;

      $("#headphoto").empty();
      $("#headphoto").css('width', $(window).width() / 2 - 10);
      $("#headphoto").css('margin-left', 0);

      $("#carphoto").empty();
      $("#carphoto").css('width', $(window).width() / 2 - 10);
      $("#carphoto").css('margin-right', 0);

      if (award.headphoto && award.headphoto.length > 0) {
        $("#headphoto").append("");
        $("#headphoto img").css('max-height', maxPhotoHeight);
      } else {
        // If there's no head photo, center the car photo by adjusting its margin
        $("#carphoto").css('margin-right', $(window).width() / 4);
      }
      if (award.carphoto && award.carphoto.length > 0) {
        $("#carphoto").append("");
        $("#carphoto img").css('max-height', maxPhotoHeight);
      } else {
        // If there's no car photo, center the head photo
        $("#headphoto").css('margin-left', $(window).width() / 4);
      }
      // Changing awards: prime revealed=false so next reveal (or current one)
      // will trigger fade-in and video.
      this.revealed = false;
    }

    if (!award.reveal) {
      $(".reveal").hide();

      var video = $("video.confetti");
      video.hide();
      video.get(0).pause();
      video.get(0).currentTime = 0;
      video.show();
    } else if (!this.revealed) {
      $(".reveal").fadeIn(1000);
      if (g_confetti && this.current_award_key != null) {
        setTimeout(function() {
          $("video.confetti").get(0).play(); }, 500);
        // Confetti is a 10-second video, and <video> element says loop="true";
        // fade out after 20 seconds to play video twice.
        setTimeout(function() {
          $("video.confetti").fadeOut(
            500, function() {
              var video = $("video.confetti");
              video.get(0).pause();
              video.get(0).currentTime = 0;
              video.show();
            }); },
                   20500);
      }
    }
    this.revealed = award.reveal;
    this.current_award_key = award.key;
  }
}

$(function() { AwardPoller.queue_next_query(); });

<|endoftext|>


<commit_before>import React from 'react'
import { Link } from 'react-router'
import { filter, find, isEmpty } from 'lodash'
const { array, bool, func, object } = React.PropTypes
import cx from 'classnames'
import { humanDate, present, sanitize, timeRange, timeRangeFull } from '../util/text'
import truncate from 'html-truncate'
import A from './A'
import Avatar from './Avatar'
import Comment from './Comment'
import CommentForm from './CommentForm'
import { connect } from 'react-redux'
import { fetchComments, createComment } from '../actions'

const spacer =   •

@connect((state, { post }) => ({comments: state.commentsByPost[post.id]}))
export default class Post extends React.Component {
  static propTypes = {
    post: object,
    onExpand: func,
    expanded: bool,
    commentsExpanded: bool,
    comments: array,
    dispatch: func,
    commentingDisabled: bool
  }

  constructor (props) {
    super(props)
    this.state = {commentsExpanded: props.commentsExpanded}
  }

  expand = () => {
    let {expanded, onExpand, post} = this.props
    if (!expanded) onExpand(post.id)
  }

  toggleComments = event => {
    event.stopPropagation()
    event.preventDefault()
    this.expand()

    let { post, comments } = this.props
    if (!comments) this.props.dispatch(fetchComments(post.id))
    this.setState({commentsExpanded: !this.state.commentsExpanded})
  }

  render () {
    let { post, comments, expanded, dispatch, commentingDisabled } = this.props
    if (!comments) comments = []
    let image = find(post.media, m => m.type === 'image')
    var style = image ? {backgroundImage: `url(${image.url})`} : {}
    var classes = cx('post', post.type, {expanded: expanded, image: !!image})
    var description

    var title = post.name
    var person = post.user
    if (post.type === 'welcome') {
      person = post.relatedUsers[0]
      title = `${person.name} joined ${post.communities[0].name}. Welcome them!`
    }

    const now = new Date()
    const createdAt = new Date(post.created_at)
    const updatedAt = new Date(post.updated_at)
    const shouldShowUpdatedAt = (now - updatedAt) < (now - createdAt) * 0.8

    if (expanded) {
      description = present(sanitize(this.props.post.description))
      var attachments = filter(post.media, m => m.type !== 'image')
    }

    let isEvent = post.type === 'event'
    if (isEvent) {
      let start = new Date(post.start_time)
      let end = post.end_time && new Date(post.end_time)
      var eventTime = timeRange(start, end)
      var eventTimeFull = timeRangeFull(start, end)
    }

    return

        <Avatar person={person}/>
        {person.name}

          <A to={`/p/${post.id}`}>{humanDate(createdAt)}
          {shouldShowUpdatedAt &&
            {spacer}updated {humanDate(updatedAt)}
          }
          {spacer}
          {post.votes} ♡
          {spacer}

            {post.numComments} comment{post.numComments === 1 ? '' : 's'}

          {post.public &&
            {spacer}Public
          }

{title}

      {isEvent &&

        {eventTime}

}

      {image &&

}

      {expanded &&

        {image && }

        {description &&
}

        {!isEmpty(attachments) &&

          {attachments.map((file, i) =>

              {truncate(file.name, 30)}
            )}

}

{post.type}

            {post.communities.map(c =>

              <Link to={`/c/${c.slug}`} key={c.id}>{c.name}</Link>

)}

        {this.state.commentsExpanded &&

          {comments.map(c => <Comment comment={c} key={c.id}/>)}
          {!commentingDisabled &&
            <CommentForm onCreate={text => dispatch(createComment(post.id, text))}/>}

}

}

  }
}
<commit_msg>a little refactoring<commit_after>import React from 'react'
import { Link } from 'react-router'
import { filter, find, isEmpty, pick } from 'lodash'
const { array, bool, func, object } = React.PropTypes
import cx from 'classnames'
import { humanDate, present, sanitize, timeRange, timeRangeFull } from '../util/text'
import truncate from 'html-truncate'
import A from './A'
import Avatar from './Avatar'
import Comment from './Comment'
import CommentForm from './CommentForm'
import { connect } from 'react-redux'
import { fetchComments, createComment } from '../actions'

const spacer =   •

@connect((state, { post }) => ({comments: state.commentsByPost[post.id]}))
export default class Post extends React.Component {
  static propTypes = {
    post: object,
    onExpand: func,
    expanded: bool,
    commentsExpanded: bool,
    comments: array,
    dispatch: func,
    commentingDisabled: bool
  }

  constructor (props) {
    super(props)
    this.state = {commentsExpanded: props.commentsExpanded}
  }

  expand = () => {
    let {expanded, onExpand, post} = this.props
    if (!expanded) onExpand(post.id)
  }

  toggleComments = event => {
    event.stopPropagation()
    event.preventDefault()
    this.expand()

    let { post, comments } = this.props
    if (!comments) this.props.dispatch(fetchComments(post.id))
    this.setState({commentsExpanded: !this.state.commentsExpanded})
  }

  render () {
    let { post, expanded } = this.props
    let image = find(post.media, m => m.type === 'image')
    var style = image ? {backgroundImage: `url(${image.url})`} : {}
    var classes = cx('post', post.type, {expanded: expanded, image: !!image})

    var title = post.name
    var person = post.user
    if (post.type === 'welcome') {
      person = post.relatedUsers[0]
      title = `${person.name} joined ${post.communities[0].name}. Welcome them!`
    }

    const now = new Date()
    const createdAt = new Date(post.created_at)
    const updatedAt = new Date(post.updated_at)
    const shouldShowUpdatedAt = (now - updatedAt) < (now - createdAt) * 0.8

    let isEvent = post.type === 'event'
    if (isEvent) {
      let start = new Date(post.start_time)
      let end = post.end_time && new Date(post.end_time)
      var eventTime = timeRange(start, end)
      var eventTimeFull = timeRangeFull(start, end)
    }

    return

        <Avatar person={person}/>
        {person.name}

          <A to={`/p/${post.id}`}>{humanDate(createdAt)}
          {shouldShowUpdatedAt &&
            {spacer}updated {humanDate(updatedAt)}
          }
          {spacer}
          {post.votes} ♡
          {spacer}

            {post.numComments} comment{post.numComments === 1 ? '' : 's'}

          {post.public &&
            {spacer}Public
          }

{title}

      {isEvent &&

        {eventTime}

}

      {image &&

}
      {expanded && <ExpandedPostDetails
        onCommentCreate={text => this.props.dispatch(createComment(post.id, text))}
        commentsExpanded={this.state.commentsExpanded}
        {...{post, image}}
        {...pick(this.props, 'comments', 'commentingDisabled')}/>}

  }
}

const ExpandedPostDetails = props => {
  let {
    post, image, comments, commentsExpanded,
    commentingDisabled, onCommentCreate
  } = props
  let description = present(sanitize(post.description))
  let attachments = filter(post.media, m => m.type !== 'image')
  if (!comments) comments = []

  return

    {image && }

    {description &&
}

    {!isEmpty(attachments) &&

      {attachments.map((file, i) =>

          {truncate(file.name, 30)}
        )}

}

{post.type}

        {post.communities.map(c =>

          <Link to={`/c/${c.slug}`} key={c.id}>{c.name}</Link>

)}

    {commentsExpanded &&

      {comments.map(c => <Comment comment={c} key={c.id}/>)}
      {!commentingDisabled && <CommentForm onCreate={onCommentCreate}/>}

}

}
<|endoftext|>


<commit_before>const {
  Repository
} = require('nodegit');

const logger = require('heimdalljs-logger');
const crypto = require('crypto');
const Change = require('./change');
const os = require('os');
const process = require('process');
const Error = require('@cardstack/data-source/error');
const Schema = require('@cardstack/server/schema');

module.exports = class Writer {
  constructor({ repo, idGenerator }) {
    this.repoPath = repo;
    this.repo = null;
    this.log = logger('writer');
    let hostname = os.hostname();
    this.myName = `PID${process.pid} on ${hostname}`;
    this.myEmail = `${os.userInfo().username}@${hostname}`;
    this.idGenerator = idGenerator;
  }

  async create(branch, user, type, document) {
    this._requireType(type, document);
    return this._withErrorHandling(document.id, type, async () => {
      while (true) {
        try {
          // 20 bytes is good enough for git, so it's good enough for
          // me. In practice we probably have a lower collision
          // probability too, because we're allowed to retry if we know
          // the id is already in use (so we can really only collide
          // with things that have not yet merged into our branch).
          let id;
          if (document.id == null) {
            id = this._generateId();
          } else {
            id = document.id;
          }
          let doc = await this._create(branch, user, document, id);
          return doc;
        } catch(err) {
          if (err instanceof Change.OverwriteRejected && document.id == null) {
            // ignore so our loop can retry
          } else {
            throw err;
          }
        }
      }
    });
  }

  async update(branch, user, type, id, document) {
    this._requireType(type, document);
    if (document.id == null) {
      throw new Error('missing required field "id"', {
        status: 400,
        source: { pointer: '/data/id' }
      });
    }
    if (String(document.id) !== id) {
      throw new Error('not allowed to change "id"', {
        status: 403,
        source: { pointer: '/data/id' }
      });
    }
    if (!document.meta || !document.meta.version) {
      throw new Error('missing required field "meta.version"', {
        status: 400,
        source: { pointer: '/data/meta/version' }
      });
    }
    await this._ensureRepo();

    return this._withErrorHandling(id, type, async () => {
      let change = await Change.create(this.repo, document.meta.version, branch);
      let file = await change.get(this._filenameFor(type, id), { allowUpdate: true });
      let finalDocument = patch(await file.getBuffer(), document);
      file.setContent(JSON.stringify(finalDocument));
      let commitId = await change.finalize(this._commitOptions('update', type, id, user));

      let responseDocument = {
        id,
        type,
        meta: {
          version: commitId
        }
      };
      if (finalDocument.attributes) {
        responseDocument.attributes = finalDocument.attributes;
      }
      if (finalDocument.relationships) {
        responseDocument.relationships = finalDocument.relationships;
      }
      return responseDocument;
    });
  }

  async delete(branch, user, version, type, id) {
    if (id == null) {
      throw new Error('id is required', {
        status: 400
      });
    }
    if (!version) {
      throw new Error('version is required', {
        status: 400,
        source: { pointer: '/data/meta/version' }
      });
    }
    await this._ensureRepo();
    return this._withErrorHandling(id, type, async () => {
      let change = await Change.create(this.repo, version, branch);
      let file = await change.get(this._filenameFor(type, id));
      file.delete();
      await change.finalize(this._commitOptions('delete', type, id, user));
    });
  }

  async _create(branch, user, document, id) {
    await this._ensureRepo();

    let gitDocument = {};
    if (document.attributes) {
      gitDocument.attributes = document.attributes;
    }
    if (document.relationships) {
      gitDocument.relationships = document.relationships;
    }

    let commitId = await Change.applyOperations(this.repo, null, branch, [
      {
        operation: 'create',
        filename: this._filenameFor(document.type, id),
        buffer: Buffer.from(JSON.stringify(gitDocument), 'utf8')
      }
    ], this._commitOptions('create', document.type, id, user));

    let responseDocument = {
      id,
      type: document.type,
      meta: {
        version: commitId
      }
    };
    if (gitDocument.attributes) {
      responseDocument.attributes = gitDocument.attributes;
    }
    if (gitDocument.relationships) {
      responseDocument.relationships = gitDocument.relationships;
    }

    return responseDocument;
  }

  async _withErrorHandling(id, type, fn) {
    try {
      return await fn();
    } catch (err) {
      if (/Unable to parse OID/.test(err.message) || /Object not found/.test(err.message)) {
        throw new Error(err.message, { status: 400, source: { pointer: '/data/meta/version' }});
      }
      if (err instanceof Change.GitConflict) {
        throw new Error("Merge conflict", { status: 409 });
      }
      if (err instanceof Change.OverwriteRejected) {
        throw new Error(`id ${id} is already in use`, { status: 409, source: { pointer: '/data/id'}});
      }
      if (err instanceof Change.NotFound) {
        throw new Error(`${type} with id ${id} does not exist`, {
          status: 404,
          source: { pointer: '/data/id' }
        });
      }
      throw err;
    }
  }

  _requireType(type, document) {
    if (document.type == null) {
      throw new Error('missing required field "type"', {
        status: 400,
        source: { pointer: '/data/type' }
      });
    }
    if (document.type !== type) {
      throw new Error(`the type "${document.type}" is not allowed here`, {
        status: 409,
        source: { pointer: '/data/type' }
      });
    }
  }

  _commitOptions(operation, type, id, user) {
    return {
      authorName: user.fullName,
      authorEmail: user.email,
      committerName: this.myName,
      committerEmail: this.myEmail,
      message: `${operation} ${type} ${id.slice(12)}`
    };
  }

  _filenameFor(type, id) {
    let category = Schema.ownTypes().includes(type) ? 'schema' : 'contents';
    return `${category}/${type}/${id}.json`;
  }

  async _ensureRepo() {
    if (!this.repo) {
      this.repo = await Repository.open(this.repoPath);
    }
  }

  _generateId() {
    if (this.idGenerator) {
      return this.idGenerator();
    } else {
      return crypto.randomBytes(20).toString('hex');
    }
  }

};

function patch(originalBuffer, newDocument) {
  let document = JSON.parse(originalBuffer);
  for (let section of ['attributes', 'relationships']) {
    if (newDocument[section]) {
      document[section] = Object.assign(
        {},
        document[section],
        newDocument[section]
      );
    }
  }
  return document;
}
<commit_msg>more refactor<commit_after>const {
  Repository
} = require('nodegit');

const logger = require('heimdalljs-logger');
const crypto = require('crypto');
const Change = require('./change');
const os = require('os');
const process = require('process');
const Error = require('@cardstack/data-source/error');
const Schema = require('@cardstack/server/schema');

module.exports = class Writer {
  constructor({ repo, idGenerator }) {
    this.repoPath = repo;
    this.repo = null;
    this.log = logger('writer');
    let hostname = os.hostname();
    this.myName = `PID${process.pid} on ${hostname}`;
    this.myEmail = `${os.userInfo().username}@${hostname}`;
    this.idGenerator = idGenerator;
  }

  async create(branch, user, type, document) {
    this._requireType(type, document);
    return this._withErrorHandling(document.id, type, async () => {
      while (true) {
        try {
          // 20 bytes is good enough for git, so it's good enough for
          // me. In practice we probably have a lower collision
          // probability too, because we're allowed to retry if we know
          // the id is already in use (so we can really only collide
          // with things that have not yet merged into our branch).
          let id;
          if (document.id == null) {
            id = this._generateId();
          } else {
            id = document.id;
          }
          let doc = await this._create(branch, user, document, id);
          return doc;
        } catch(err) {
          if (err instanceof Change.OverwriteRejected && document.id == null) {
            // ignore so our loop can retry
          } else {
            throw err;
          }
        }
      }
    });
  }

  async update(branch, user, type, id, document) {
    this._requireType(type, document);
    if (document.id == null) {
      throw new Error('missing required field "id"', {
        status: 400,
        source: { pointer: '/data/id' }
      });
    }
    if (String(document.id) !== id) {
      throw new Error('not allowed to change "id"', {
        status: 403,
        source: { pointer: '/data/id' }
      });
    }
    if (!document.meta || !document.meta.version) {
      throw new Error('missing required field "meta.version"', {
        status: 400,
        source: { pointer: '/data/meta/version' }
      });
    }
    await this._ensureRepo();

    return this._withErrorHandling(id, type, async () => {
      let change = await Change.create(this.repo, document.meta.version, branch);
      let file = await change.get(this._filenameFor(type, id), { allowUpdate: true });
      let finalDocument = patch(await file.getBuffer(), document);
      file.setContent(JSON.stringify(finalDocument));
      let commitId = await change.finalize(this._commitOptions('update', type, id, user));

      let responseDocument = {
        id,
        type,
        meta: {
          version: commitId
        }
      };
      if (finalDocument.attributes) {
        responseDocument.attributes = finalDocument.attributes;
      }
      if (finalDocument.relationships) {
        responseDocument.relationships = finalDocument.relationships;
      }
      return responseDocument;
    });
  }

  async delete(branch, user, version, type, id) {
    if (id == null) {
      throw new Error('id is required', {
        status: 400
      });
    }
    if (!version) {
      throw new Error('version is required', {
        status: 400,
        source: { pointer: '/data/meta/version' }
      });
    }
    await this._ensureRepo();
    return this._withErrorHandling(id, type, async () => {
      let change = await Change.create(this.repo, version, branch);
      let file = await change.get(this._filenameFor(type, id));
      file.delete();
      await change.finalize(this._commitOptions('delete', type, id, user));
    });
  }

  async _create(branch, user, document, id) {
    await this._ensureRepo();

    let gitDocument = {};
    if (document.attributes) {
      gitDocument.attributes = document.attributes;
    }
    if (document.relationships) {
      gitDocument.relationships = document.relationships;
    }

    let change = await Change.create(this.repo, null, branch);
    let file = await change.get(this._filenameFor(document.type, id), { allowCreate: true });
    file.setContent(JSON.stringify(gitDocument));
    let commitId = await change.finalize(this._commitOptions('create', document.type, id, user));

    let responseDocument = {
      id,
      type: document.type,
      meta: {
        version: commitId
      }
    };
    if (gitDocument.attributes) {
      responseDocument.attributes = gitDocument.attributes;
    }
    if (gitDocument.relationships) {
      responseDocument.relationships = gitDocument.relationships;
    }

    return responseDocument;
  }

  async _withErrorHandling(id, type, fn) {
    try {
      return await fn();
    } catch (err) {
      if (/Unable to parse OID/.test(err.message) || /Object not found/.test(err.message)) {
        throw new Error(err.message, { status: 400, source: { pointer: '/data/meta/version' }});
      }
      if (err instanceof Change.GitConflict) {
        throw new Error("Merge conflict", { status: 409 });
      }
      if (err instanceof Change.OverwriteRejected) {
        throw new Error(`id ${id} is already in use`, { status: 409, source: { pointer: '/data/id'}});
      }
      if (err instanceof Change.NotFound) {
        throw new Error(`${type} with id ${id} does not exist`, {
          status: 404,
          source: { pointer: '/data/id' }
        });
      }
      throw err;
    }
  }

  _requireType(type, document) {
    if (document.type == null) {
      throw new Error('missing required field "type"', {
        status: 400,
        source: { pointer: '/data/type' }
      });
    }
    if (document.type !== type) {
      throw new Error(`the type "${document.type}" is not allowed here`, {
        status: 409,
        source: { pointer: '/data/type' }
      });
    }
  }

  _commitOptions(operation, type, id, user) {
    return {
      authorName: user.fullName,
      authorEmail: user.email,
      committerName: this.myName,
      committerEmail: this.myEmail,
      message: `${operation} ${type} ${id.slice(12)}`
    };
  }

  _filenameFor(type, id) {
    let category = Schema.ownTypes().includes(type) ? 'schema' : 'contents';
    return `${category}/${type}/${id}.json`;
  }

  async _ensureRepo() {
    if (!this.repo) {
      this.repo = await Repository.open(this.repoPath);
    }
  }

  _generateId() {
    if (this.idGenerator) {
      return this.idGenerator();
    } else {
      return crypto.randomBytes(20).toString('hex');
    }
  }

};

function patch(originalBuffer, newDocument) {
  let document = JSON.parse(originalBuffer);
  for (let section of ['attributes', 'relationships']) {
    if (newDocument[section]) {
      document[section] = Object.assign(
        {},
        document[section],
        newDocument[section]
      );
    }
  }
  return document;
}
<|endoftext|>


<commit_before>import React                    from 'react';
import {RouteHandler, Link} from 'react-router';
import 'styles/bootstrap.css';
import 'styles/clean-blog.css';
import CleanBlogJS from 'lib/clean-blog';
import glucoseActions from '../actions/glucoseAction';
import glucoseStore from '../stores/glucoseStore';
import trend from './trend';
import _ from 'lodash';
import moment from 'moment-timezone';
import GlucoseChart from './glucoseChart';
import NextReading from './timer';

class Main extends React.Component {
    constructor(props) {
        super(props);
        this.onChange = this.onChange.bind(this);
        this.state = {};
    }

    componentDidMount() {
        glucoseStore.listen(this.onChange);
        var gState = glucoseStore.getState();
        var history = gState.glucose;
        if (history) {
            if (history.length > 0) {
                history = _.sortBy(history, 'time').reverse();
                gState.glucose = history[0].glucose;
                gState.history = history;
            }
        }

        this.setState(gState);
    }

    componentWillUnmount() {
        clearInterval(window.timer);
        glucoseStore.unlisten(this.onChange);
    }

    onChange(event) {
        var newState = glucoseStore.getState();
        this.setState(newState);
    }

    render() {
        var glucose = 'Not available';
        var sorted;
        if (this.state.glucose) {
            if (this.state.glucose == 401) {
                glucose = 'HIGH';
            } else {
                glucose = this.state.glucose;
            }
        }
        var currentTrend = 'Not available';
        if (this.state.trend) {
            currentTrend = trend.trendsToText(this.state.trend);
        }
        var lastReading = 'Not Available';
        if (this.state.lastEntry) {
            var last = new Date(this.state.lastEntry);
            var ampm = 'AM';
            if (last.getHours() == 0) {
                lastReading = '12:';
            } else if (last.getHours() == 12) {
                lastReading = '12:';
                ampm = 'PM'
            } else if (last.getHours() > 12) {
                lastReading = last.getHours() - 12 + ':';
                ampm = 'PM';
            } else {
                lastReading = last.getHours() + ':';
            }
            if (last.getMinutes() < 10) {
                lastReading += '0' + last.getMinutes();
            } else {
                lastReading += last.getMinutes();
            }
            if (last.getSeconds() < 10) {
                lastReading += ':0' + last.getSeconds();
            } else {
                lastReading += ':' + last.getSeconds();
            }
            lastReading += ' ' + ampm;
        }

        var noData =

;
        if (this.state.data != true) {
            noData =
Not getting any data from Dexcom
;
        }

        return (

                    {noData}

Current Reading: {glucose}
                                Direction: {currentTrend}

Last Reading: {lastReading}
                                <NextReading status={this.state.status} lastEntry={this.state.lastEntry}/>

                    <GlucoseChart history={this.state.history}/>

        );
    }
}

export default Main;
<commit_msg>working with fa<commit_after>import React                    from 'react';
import {RouteHandler, Link} from 'react-router';
import 'styles/bootstrap.css';
import 'styles/clean-blog.css';
import CleanBlogJS from 'lib/clean-blog';
import glucoseActions from '../actions/glucoseAction';
import glucoseStore from '../stores/glucoseStore';
import trend from './trend';
import _ from 'lodash';
import moment from 'moment-timezone';
import GlucoseChart from './glucoseChart';
import NextReading from './timer';

class Main extends React.Component {
    constructor(props) {
        super(props);
        this.onChange = this.onChange.bind(this);
        this.state = {};
    }

    componentDidMount() {
        glucoseStore.listen(this.onChange);
        var gState = glucoseStore.getState();
        var history = gState.glucose;
        if (history) {
            if (history.length > 0) {
                history = _.sortBy(history, 'time').reverse();
                gState.glucose = history[0].glucose;
                gState.history = history;
            }
        }

        this.setState(gState);
    }

    componentWillUnmount() {
        clearInterval(window.timer);
        glucoseStore.unlisten(this.onChange);
    }

    onChange(event) {
        var newState = glucoseStore.getState();
        this.setState(newState);
    }

    render() {
        var glucose = 'Not available';
        var sorted;
        if (this.state.glucose) {
            if (this.state.glucose == 401) {
                glucose = 'HIGH';
            } else {
                glucose = this.state.glucose;
            }
        }
        var currentTrend = 'Not available';
        if (this.state.trend) {
            currentTrend = trend.trendsToText(this.state.trend) + '';
        }
        var lastReading = 'Not Available';
        if (this.state.lastEntry) {
            var last = new Date(this.state.lastEntry);
            var ampm = 'AM';
            if (last.getHours() == 0) {
                lastReading = '12:';
            } else if (last.getHours() == 12) {
                lastReading = '12:';
                ampm = 'PM'
            } else if (last.getHours() > 12) {
                lastReading = last.getHours() - 12 + ':';
                ampm = 'PM';
            } else {
                lastReading = last.getHours() + ':';
            }
            if (last.getMinutes() < 10) {
                lastReading += '0' + last.getMinutes();
            } else {
                lastReading += last.getMinutes();
            }
            if (last.getSeconds() < 10) {
                lastReading += ':0' + last.getSeconds();
            } else {
                lastReading += ':' + last.getSeconds();
            }
            lastReading += ' ' + ampm;
        }

        var noData =

;
        if (this.state.data != true) {
            noData =
Not getting any data from Dexcom
;
        }

        return (

                    {noData}

Current Reading: {glucose}
                                Direction: <div dangerouslySetInnerHTML={{ __html: {currentTrend}}}

Last Reading: {lastReading}
                                <NextReading status={this.state.status} lastEntry={this.state.lastEntry}/>

                    <GlucoseChart history={this.state.history}/>

        );
    }
}

export default Main;
<|endoftext|>


<commit_before>import React from 'react';
import classNames from 'classnames';

export default class Page extends React.Component {

  render() {
    let {kataGroups} = this.props;
    const katasCount = kataGroups.reduce((old, {katas: {length}}) => old + length, 0);
    const {showWorkshopBanner} = this.props;
    return (

        <KataGroups groups={kataGroups}/>

        <WorkshopBanner showWorkshopBanner={showWorkshopBanner}/>

    );
  }
}

class Header extends React.Component {
  render() {
    return (

ES6 Katas

Learn ES6 by doing it, fix failing tests and keep the learnings.

    );
  }
}

class Footer extends React.Component {
  render() {
    const {katasCount} = this.props;
    return (

by

              Wolfram Kriesing

              Email me

              Book me for

              training, coaching, clean-coding

              imprint

              Making of a kata

            uses TDDbin

{katasCount} katas

              source of this site

              all katas

              ES6 Katas

              RSS

    );
  }
}

class KataGroups extends React.Component {
  render() {
    const {groups} = this.props;
    return (

        {groups.map((group) => <KataGroup group={group} isNewestKataCheck={groups.isNewestKata.bind(groups)}
                                          key={group.name}/>)}

    );
  }
}

class KataGroup extends React.Component {
  render() {
    const group = this.props.group;
    const {isNewestKataCheck} = this.props;
    return (

{group.name}

        {group.katas.map((kata) => <Kata kata={kata} isNewest={isNewestKataCheck(kata)} key={kata.id}/>)}

    );
  }
}

class Kata extends React.Component {
  render() {
    const {kata, isNewest} = this.props;
    const {url, name, level} = kata;
    const marker =
      isNewest ? new :
        (level === 'BEGINNER' ? easy : '');
    return

      {marker}<KataName name={name} />
      <KataDetails kata={kata} />

;
  }
}

class KataName extends React.Component {
  render() {
    const classNames = [];

    function renderWithName(name) {
      return
        {name}

    }

    const {name} = this.props;
    if (name.startsWith('`') && name.endsWith('`')) {
      classNames.push('code');
      const sourceCode = name.substr(1, name.length-2);
      return renderWithName(sourceCode);
    }
    return renderWithName(name);
  }
}

class KataDetails extends React.Component {
  render() {
    const {kata} = this.props;
    return (

{kata.name} (#{kata.id})

{kata.description}

        Difficulty: {kata.level.toLowerCase()}

        <KataLinks links={kata.links} />

    );
  }
}

class KataLinks extends React.Component {
  render() {
    const {links=[]} = this.props;
    if (links.length === 0) {
      return null;
    }
    return (

Links for futher reading

          {links.map(link =>

            {link.comment}

)}

    );
  }
}

class WorkshopBanner extends React.Component {
  constructor() {
    super();
    this.state = {bannerIsMinimized: false};
  }

  render() {
    const imageUrl = './workshopbanner.png';
    const workshopUrl = 'http://www.uxebu.com/all-workshops/es6-and-react-js/';
    var classes = classNames({
      'workshop-banner': true,
      'fade-in': this.props.showWorkshopBanner,
      'small': this.state.bannerIsMinimized
    });
    return

        <button className="close" onClick={this.minimizeBanner.bind(this)}>[x]</button>

  }

  minimizeBanner() {
    this.setState({bannerIsMinimized: true});
  }

  maximizeBanner() {
    this.setState({bannerIsMinimized: false});
  }
}
<commit_msg>Improve slogan.<commit_after>import React from 'react';
import classNames from 'classnames';

export default class Page extends React.Component {

  render() {
    let {kataGroups} = this.props;
    const katasCount = kataGroups.reduce((old, {katas: {length}}) => old + length, 0);
    const {showWorkshopBanner} = this.props;
    return (

        <KataGroups groups={kataGroups}/>

        <WorkshopBanner showWorkshopBanner={showWorkshopBanner}/>

    );
  }
}

class Header extends React.Component {
  render() {
    return (

ES6 Katas

Learn ES6 by doing it. Fix failing tests. Keep all learnings.

    );
  }
}

class Footer extends React.Component {
  render() {
    const {katasCount} = this.props;
    return (

by

              Wolfram Kriesing

              Email me

              Book me for

              training, coaching, clean-coding

              imprint

              Making of a kata

            uses TDDbin

{katasCount} katas

              source of this site

              all katas

              ES6 Katas

              RSS

    );
  }
}

class KataGroups extends React.Component {
  render() {
    const {groups} = this.props;
    return (

        {groups.map((group) => <KataGroup group={group} isNewestKataCheck={groups.isNewestKata.bind(groups)}
                                          key={group.name}/>)}

    );
  }
}

class KataGroup extends React.Component {
  render() {
    const group = this.props.group;
    const {isNewestKataCheck} = this.props;
    return (

{group.name}

        {group.katas.map((kata) => <Kata kata={kata} isNewest={isNewestKataCheck(kata)} key={kata.id}/>)}

    );
  }
}

class Kata extends React.Component {
  render() {
    const {kata, isNewest} = this.props;
    const {url, name, level} = kata;
    const marker =
      isNewest ? new :
        (level === 'BEGINNER' ? easy : '');
    return

      {marker}<KataName name={name} />
      <KataDetails kata={kata} />

;
  }
}

class KataName extends React.Component {
  render() {
    const classNames = [];

    function renderWithName(name) {
      return
        {name}

    }

    const {name} = this.props;
    if (name.startsWith('`') && name.endsWith('`')) {
      classNames.push('code');
      const sourceCode = name.substr(1, name.length-2);
      return renderWithName(sourceCode);
    }
    return renderWithName(name);
  }
}

class KataDetails extends React.Component {
  render() {
    const {kata} = this.props;
    return (

{kata.name} (#{kata.id})

{kata.description}

        Difficulty: {kata.level.toLowerCase()}

        <KataLinks links={kata.links} />

    );
  }
}

class KataLinks extends React.Component {
  render() {
    const {links=[]} = this.props;
    if (links.length === 0) {
      return null;
    }
    return (

Links for futher reading

          {links.map(link =>

            {link.comment}

)}

    );
  }
}

class WorkshopBanner extends React.Component {
  constructor() {
    super();
    this.state = {bannerIsMinimized: false};
  }

  render() {
    const imageUrl = './workshopbanner.png';
    const workshopUrl = 'http://www.uxebu.com/all-workshops/es6-and-react-js/';
    var classes = classNames({
      'workshop-banner': true,
      'fade-in': this.props.showWorkshopBanner,
      'small': this.state.bannerIsMinimized
    });
    return

        <button className="close" onClick={this.minimizeBanner.bind(this)}>[x]</button>

  }

  minimizeBanner() {
    this.setState({bannerIsMinimized: true});
  }

  maximizeBanner() {
    this.setState({bannerIsMinimized: false});
  }
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from "react";
import { getStyles } from "../utils/base";
import Radium from "radium";

@Radium
export default class Text extends Component {
  constructor() {
    super();
    this.resize = this.resize.bind(this);
    this.state = {
      scale: 1,
      height: 16
    };
  }
  componentDidMount() {
    this.resize();
    window.addEventListener("load", this.resize);
    window.addEventListener("resize", this.resize);
  }
  componentWillUnmount() {
    window.removeEventListener("load", this.resize);
    window.removeEventListener("resize", this.resize);
  }
  componentWillReceiveProps() {
    this.resize();
  }
  resize() {
    if (this.props.fit) {
      const text = this.refs.text;
      const container = this.refs.container;
      text.style.display = "inline-block";
      const scale = (container.offsetWidth / text.offsetWidth);
      const height = text.offsetHeight * scale;
      text.style.display = "block";
      this.setState({
        scale,
        height
      });
    }
  }
  render() {
    const { lineHeight, fit, style, children } = this.props;
    const styles = {
      container: {
        display: "block",
        width: "100%",
        height: this.state.height
      },
      text: {
        fontSize: 16,
        display: "block",
        margin: "0",
        padding: "0",
        lineHeight,
        transform: `scale(${this.state.scale})`,
        transformOrigin: "center top"
      },
      nonFit: {
        lineHeight
      }
    };
    return (
      fit ? (

            {children}

      ) : (

          {this.props.children}

      )
    );
  }
}

Text.defaultProps = {
  lineHeight: 1
};

Text.propTypes = {
  className: PropTypes.string,
  fit: PropTypes.bool,
  children: PropTypes.node,
  lineHeight: PropTypes.number,
  style: PropTypes.object
};

Text.contextTypes = {
  styles: PropTypes.object
};
<commit_msg>Removed unnecessary 'this.props' qualification for already destructured prop<commit_after>import React, { Component, PropTypes } from "react";
import { getStyles } from "../utils/base";
import Radium from "radium";

@Radium
export default class Text extends Component {
  constructor() {
    super();
    this.resize = this.resize.bind(this);
    this.state = {
      scale: 1,
      height: 16
    };
  }
  componentDidMount() {
    this.resize();
    window.addEventListener("load", this.resize);
    window.addEventListener("resize", this.resize);
  }
  componentWillUnmount() {
    window.removeEventListener("load", this.resize);
    window.removeEventListener("resize", this.resize);
  }
  componentWillReceiveProps() {
    this.resize();
  }
  resize() {
    if (this.props.fit) {
      const text = this.refs.text;
      const container = this.refs.container;
      text.style.display = "inline-block";
      const scale = (container.offsetWidth / text.offsetWidth);
      const height = text.offsetHeight * scale;
      text.style.display = "block";
      this.setState({
        scale,
        height
      });
    }
  }
  render() {
    const { lineHeight, fit, style, children } = this.props;
    const styles = {
      container: {
        display: "block",
        width: "100%",
        height: this.state.height
      },
      text: {
        fontSize: 16,
        display: "block",
        margin: "0",
        padding: "0",
        lineHeight,
        transform: `scale(${this.state.scale})`,
        transformOrigin: "center top"
      },
      nonFit: {
        lineHeight
      }
    };
    return (
      fit ? (

            {children}

      ) : (

          {children}

      )
    );
  }
}

Text.defaultProps = {
  lineHeight: 1
};

Text.propTypes = {
  className: PropTypes.string,
  fit: PropTypes.bool,
  children: PropTypes.node,
  lineHeight: PropTypes.number,
  style: PropTypes.object
};

Text.contextTypes = {
  styles: PropTypes.object
};
<|endoftext|>


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

    angular
        .module('app')
        .factory('AjaxHelper', AjaxHelper);

    // @ngInject
    function AjaxHelper() {

        var factory = {};

        factory.reset = function(vm) {
            vm.submitting = true;
            vm.success = null;
            vm.error = null;
            vm.errors = {};
        };

            factory.process = function(vm, data) {
            vm.submitting = false;
            if (data.success) {
                vm.success = data.success;
            }
            if (data.error) {
                vm.error = data.error;
            }
            if (data.errors) {
                vm.errors = data.errors;
            }
        };

        return factory;
    }

})();<commit_msg>fix tabbing (code style)<commit_after>(function () {
    'use strict';

    angular
        .module('app')
        .factory('AjaxHelper', AjaxHelper);

    // @ngInject
    function AjaxHelper() {

        var factory = {};

        factory.reset = function(vm) {
            vm.submitting = true;
            vm.success = null;
            vm.error = null;
            vm.errors = {};
        };

        factory.process = function(vm, data) {
            vm.submitting = false;
            if (data.success) {
                vm.success = data.success;
            }
            if (data.error) {
                vm.error = data.error;
            }
            if (data.errors) {
                vm.errors = data.errors;
            }
        };

        return factory;
    }

})();<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const os = require('os');
const chalk = require('chalk');
const shellQuote = require('shell-quote');

function isTerminalEditor(editor) {
  switch (editor) {
    case 'vim':
    case 'emacs':
    case 'nano':
      return true;
  }
  return false;
}

// Map from full process name to binary that starts the process
// We can't just re-use full process name, because it will spawn a new instance
// of the app every time
const COMMON_EDITORS_OSX = {
  '/Applications/Atom.app/Contents/MacOS/Atom': 'atom',
  '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta':
    '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta',
  '/Applications/Brackets.app/Contents/MacOS/Brackets': 'brackets',
  '/Applications/Sublime Text.app/Contents/MacOS/Sublime Text':
    '/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl',
  '/Applications/Sublime Text 2.app/Contents/MacOS/Sublime Text 2':
    '/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl',
  '/Applications/Visual Studio Code.app/Contents/MacOS/Electron': 'code',
};

const COMMON_EDITORS_WIN = [
  'Brackets.exe',
  'Code.exe',
  'atom.exe',
  'sublime_text.exe',
  'notepad++.exe',
];

function addWorkspaceToArgumentsIfExists(args, workspace) {
  if (workspace) {
    args.unshift(workspace);
  }
  return args;
}

function getArgumentsForLineNumber(editor, fileName, lineNumber, workspace) {
  const editorBasename = path.basename(editor).replace(/\.(exe|cmd|bat)$/i, '');
  switch (editorBasename) {
    case 'atom':
    case 'Atom':
    case 'Atom Beta':
    case 'subl':
    case 'sublime':
    case 'sublime_text':
    case 'wstorm':
    case 'appcode':
    case 'charm':
    case 'idea':
      return [fileName + ':' + lineNumber];
    case 'notepad++':
      return ['-n' + lineNumber, fileName];
    case 'vim':
    case 'mvim':
    case 'joe':
    case 'emacs':
    case 'emacsclient':
      return ['+' + lineNumber, fileName];
    case 'rmate':
    case 'mate':
    case 'mine':
      return ['--line', lineNumber, fileName];
    case 'code':
    case 'Code':
      return addWorkspaceToArgumentsIfExists(
        ['-g', fileName + ':' + lineNumber],
        workspace
      );
    case 'webstorm':
    case 'webstorm64':
    case 'phpstorm':
    case 'phpstorm64':
      return addWorkspaceToArgumentsIfExists(
        ['--line', lineNumber, fileName],
        workspace
      );
  }

  // For all others, drop the lineNumber until we have
  // a mapping above, since providing the lineNumber incorrectly
  // can result in errors or confusing behavior.
  return [fileName];
}

function guessEditor() {
  // Explicit config always wins
  if (process.env.REACT_EDITOR) {
    return shellQuote.parse(process.env.REACT_EDITOR);
  }

  // Using `ps x` on OSX or `Get-Process` on Windows we can find out which editor is currently running.
  // Potentially we could use similar technique for Linux
  try {
    if (process.platform === 'darwin') {
      const output = child_process.execSync('ps x').toString();
      const processNames = Object.keys(COMMON_EDITORS_OSX);
      for (let i = 0; i < processNames.length; i++) {
        const processName = processNames[i];
        if (output.indexOf(processName) !== -1) {
          return [COMMON_EDITORS_OSX[processName]];
        }
      }
    } else if (process.platform === 'win32') {
      const output = child_process
        .execSync('powershell -Command "Get-Process | Select-Object Path"', {
          stdio: ['pipe', 'pipe', 'ignore'],
        })
        .toString();
      const runningProcesses = output.split('\r\n');
      for (let i = 0; i < runningProcesses.length; i++) {
        // `Get-Process` sometimes returns empty lines
        if (!runningProcesses[i]) {
          continue;
        }

        const fullProcessPath = runningProcesses[i].trim();
        const shortProcessName = path.basename(fullProcessPath);

        if (COMMON_EDITORS_WIN.indexOf(shortProcessName) !== -1) {
          return [fullProcessPath];
        }
      }
    }
  } catch (error) {
    // Ignore...
  }

  // Last resort, use old skool env vars
  if (process.env.VISUAL) {
    return [process.env.VISUAL];
  } else if (process.env.EDITOR) {
    return [process.env.EDITOR];
  }

  return [null];
}

function printInstructions(fileName, errorMessage) {
  console.log();
  console.log(
    chalk.red('Could not open ' + path.basename(fileName) + ' in the editor.')
  );
  if (errorMessage) {
    if (errorMessage[errorMessage.length - 1] !== '.') {
      errorMessage += '.';
    }
    console.log(
      chalk.red('The editor process exited with an error: ' + errorMessage)
    );
  }
  console.log();
  console.log(
    'To set up the editor integration, add something like ' +
      chalk.cyan('REACT_EDITOR=atom') +
      ' to the ' +
      chalk.green('.env.local') +
      ' file in your project folder ' +
      'and restart the development server. Learn more: ' +
      chalk.green('https://goo.gl/MMTaZt')
  );
  console.log();
}

let _childProcess = null;
function launchEditor(fileName, lineNumber) {
  if (!fs.existsSync(fileName)) {
    return;
  }

  // Sanitize lineNumber to prevent malicious use on win32
  // via: https://github.com/nodejs/node/blob/c3bb4b1aa5e907d489619fb43d233c3336bfc03d/lib/child_process.js#L333
  if (lineNumber && isNaN(lineNumber)) {
    return;
  }

  let [editor, ...args] = guessEditor();
  if (!editor) {
    printInstructions(fileName, null);
    return;
  }

  if (
    process.platform === 'linux' &&
    fileName.startsWith('/mnt/') &&
    /Microsoft/i.test(os.release())
  ) {
    // Assume WSL / "Bash on Ubuntu on Windows" is being used, and
    // that the file exists on the Windows file system.
    // `os.release()` is "4.4.0-43-Microsoft" in the current release
    // build of WSL, see: https://github.com/Microsoft/BashOnWindows/issues/423#issuecomment-221627364
    // When a Windows editor is specified, interop functionality can
    // handle the path translation, but only if a relative path is used.
    fileName = path.relative('', fileName);
  }

  let workspace = null;
  if (lineNumber) {
    args = args.concat(
      getArgumentsForLineNumber(editor, fileName, lineNumber, workspace)
    );
  } else {
    args.push(fileName);
  }

  if (_childProcess && isTerminalEditor(editor)) {
    // There's an existing editor process already and it's attached
    // to the terminal, so go kill it. Otherwise two separate editor
    // instances attach to the stdin/stdout which gets confusing.
    _childProcess.kill('SIGKILL');
  }

  if (process.platform === 'win32') {
    // On Windows, launch the editor in a shell because spawn can only
    // launch .exe files.
    _childProcess = child_process.spawn(
      'cmd.exe',
      ['/C', editor].concat(args),
      { stdio: 'inherit' }
    );
  } else {
    _childProcess = child_process.spawn(editor, args, { stdio: 'inherit' });
  }
  _childProcess.on('exit', function(errorCode) {
    _childProcess = null;

    if (errorCode) {
      printInstructions(fileName, '(code ' + errorCode + ')');
    }
  });

  _childProcess.on('error', function(error) {
    printInstructions(fileName, error.message);
  });
}

module.exports = launchEditor;
<commit_msg>Support PyCharm in launchEditor (#2740)<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const os = require('os');
const chalk = require('chalk');
const shellQuote = require('shell-quote');

function isTerminalEditor(editor) {
  switch (editor) {
    case 'vim':
    case 'emacs':
    case 'nano':
      return true;
  }
  return false;
}

// Map from full process name to binary that starts the process
// We can't just re-use full process name, because it will spawn a new instance
// of the app every time
const COMMON_EDITORS_OSX = {
  '/Applications/Atom.app/Contents/MacOS/Atom': 'atom',
  '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta':
    '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta',
  '/Applications/Brackets.app/Contents/MacOS/Brackets': 'brackets',
  '/Applications/Sublime Text.app/Contents/MacOS/Sublime Text':
    '/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl',
  '/Applications/Sublime Text 2.app/Contents/MacOS/Sublime Text 2':
    '/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl',
  '/Applications/Visual Studio Code.app/Contents/MacOS/Electron': 'code',
};

const COMMON_EDITORS_WIN = [
  'Brackets.exe',
  'Code.exe',
  'atom.exe',
  'sublime_text.exe',
  'notepad++.exe',
];

function addWorkspaceToArgumentsIfExists(args, workspace) {
  if (workspace) {
    args.unshift(workspace);
  }
  return args;
}

function getArgumentsForLineNumber(editor, fileName, lineNumber, workspace) {
  const editorBasename = path.basename(editor).replace(/\.(exe|cmd|bat)$/i, '');
  switch (editorBasename) {
    case 'atom':
    case 'Atom':
    case 'Atom Beta':
    case 'subl':
    case 'sublime':
    case 'sublime_text':
    case 'wstorm':
    case 'appcode':
    case 'charm':
    case 'idea':
      return [fileName + ':' + lineNumber];
    case 'notepad++':
      return ['-n' + lineNumber, fileName];
    case 'vim':
    case 'mvim':
    case 'joe':
    case 'emacs':
    case 'emacsclient':
      return ['+' + lineNumber, fileName];
    case 'rmate':
    case 'mate':
    case 'mine':
      return ['--line', lineNumber, fileName];
    case 'code':
    case 'Code':
      return addWorkspaceToArgumentsIfExists(
        ['-g', fileName + ':' + lineNumber],
        workspace
      );
    case 'webstorm':
    case 'webstorm64':
    case 'phpstorm':
    case 'phpstorm64':
    case 'pycharm':
    case 'pycharm64':
      return addWorkspaceToArgumentsIfExists(
        ['--line', lineNumber, fileName],
        workspace
      );
  }

  // For all others, drop the lineNumber until we have
  // a mapping above, since providing the lineNumber incorrectly
  // can result in errors or confusing behavior.
  return [fileName];
}

function guessEditor() {
  // Explicit config always wins
  if (process.env.REACT_EDITOR) {
    return shellQuote.parse(process.env.REACT_EDITOR);
  }

  // Using `ps x` on OSX or `Get-Process` on Windows we can find out which editor is currently running.
  // Potentially we could use similar technique for Linux
  try {
    if (process.platform === 'darwin') {
      const output = child_process.execSync('ps x').toString();
      const processNames = Object.keys(COMMON_EDITORS_OSX);
      for (let i = 0; i < processNames.length; i++) {
        const processName = processNames[i];
        if (output.indexOf(processName) !== -1) {
          return [COMMON_EDITORS_OSX[processName]];
        }
      }
    } else if (process.platform === 'win32') {
      const output = child_process
        .execSync('powershell -Command "Get-Process | Select-Object Path"', {
          stdio: ['pipe', 'pipe', 'ignore'],
        })
        .toString();
      const runningProcesses = output.split('\r\n');
      for (let i = 0; i < runningProcesses.length; i++) {
        // `Get-Process` sometimes returns empty lines
        if (!runningProcesses[i]) {
          continue;
        }

        const fullProcessPath = runningProcesses[i].trim();
        const shortProcessName = path.basename(fullProcessPath);

        if (COMMON_EDITORS_WIN.indexOf(shortProcessName) !== -1) {
          return [fullProcessPath];
        }
      }
    }
  } catch (error) {
    // Ignore...
  }

  // Last resort, use old skool env vars
  if (process.env.VISUAL) {
    return [process.env.VISUAL];
  } else if (process.env.EDITOR) {
    return [process.env.EDITOR];
  }

  return [null];
}

function printInstructions(fileName, errorMessage) {
  console.log();
  console.log(
    chalk.red('Could not open ' + path.basename(fileName) + ' in the editor.')
  );
  if (errorMessage) {
    if (errorMessage[errorMessage.length - 1] !== '.') {
      errorMessage += '.';
    }
    console.log(
      chalk.red('The editor process exited with an error: ' + errorMessage)
    );
  }
  console.log();
  console.log(
    'To set up the editor integration, add something like ' +
      chalk.cyan('REACT_EDITOR=atom') +
      ' to the ' +
      chalk.green('.env.local') +
      ' file in your project folder ' +
      'and restart the development server. Learn more: ' +
      chalk.green('https://goo.gl/MMTaZt')
  );
  console.log();
}

let _childProcess = null;
function launchEditor(fileName, lineNumber) {
  if (!fs.existsSync(fileName)) {
    return;
  }

  // Sanitize lineNumber to prevent malicious use on win32
  // via: https://github.com/nodejs/node/blob/c3bb4b1aa5e907d489619fb43d233c3336bfc03d/lib/child_process.js#L333
  if (lineNumber && isNaN(lineNumber)) {
    return;
  }

  let [editor, ...args] = guessEditor();
  if (!editor) {
    printInstructions(fileName, null);
    return;
  }

  if (
    process.platform === 'linux' &&
    fileName.startsWith('/mnt/') &&
    /Microsoft/i.test(os.release())
  ) {
    // Assume WSL / "Bash on Ubuntu on Windows" is being used, and
    // that the file exists on the Windows file system.
    // `os.release()` is "4.4.0-43-Microsoft" in the current release
    // build of WSL, see: https://github.com/Microsoft/BashOnWindows/issues/423#issuecomment-221627364
    // When a Windows editor is specified, interop functionality can
    // handle the path translation, but only if a relative path is used.
    fileName = path.relative('', fileName);
  }

  let workspace = null;
  if (lineNumber) {
    args = args.concat(
      getArgumentsForLineNumber(editor, fileName, lineNumber, workspace)
    );
  } else {
    args.push(fileName);
  }

  if (_childProcess && isTerminalEditor(editor)) {
    // There's an existing editor process already and it's attached
    // to the terminal, so go kill it. Otherwise two separate editor
    // instances attach to the stdin/stdout which gets confusing.
    _childProcess.kill('SIGKILL');
  }

  if (process.platform === 'win32') {
    // On Windows, launch the editor in a shell because spawn can only
    // launch .exe files.
    _childProcess = child_process.spawn(
      'cmd.exe',
      ['/C', editor].concat(args),
      { stdio: 'inherit' }
    );
  } else {
    _childProcess = child_process.spawn(editor, args, { stdio: 'inherit' });
  }
  _childProcess.on('exit', function(errorCode) {
    _childProcess = null;

    if (errorCode) {
      printInstructions(fileName, '(code ' + errorCode + ')');
    }
  });

  _childProcess.on('error', function(error) {
    printInstructions(fileName, error.message);
  });
}

module.exports = launchEditor;
<|endoftext|>


<commit_before>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
"use strict";

// This widget is strongly coupled to the notebook because of the outputarea
// dependency.
var widgets = require("jupyter-js-widgets");
var _ = require("underscore");

var OutputModel = widgets.DOMWidgetModel.extend({
    defaults: _.extend({}, widgets.DOMWidgetModel.prototype.defaults, {
        _model_name: "OutputModel",
        _view_name: "OutputView",
        msg_id: "",
    }),

    initialize: function(attributes, options) {
        OutputModel.__super__.initialize.apply(this, arguments);
        this.kernel = this.comm.kernel;
        this.listenTo(this, 'change:msg_id', this.reset_msg_id);
        if (this.kernel) {
            this.kernel.set_callbacks_for_msg(this.id, this.callbacks(), false);
        }
        this._outputs = [];
    },

    // make callbacks
    callbacks: function() {
        return {
            iopub: {
                output: function(msg) {
                    this.trigger('new_message', msg);
                    this._outputs.push(msg);
                }.bind(this),
                clear_output: function(msg) {
                    this.trigger('clear_output', msg);
                    this._outputs = [];
                }.bind(this)
            }
        }
    },

    reset_msg_id: function() {
        var kernel = this.kernel;
        // Pop previous message id
        var prev_msg_id = this.previous('msg_id');
        if (prev_msg_id && kernel) {
            var previous_callback = kernel.output_callback_overrides_pop(prev_msg_id);
            if (previous_callback !== this.id) {
                console.error('Popped wrong message ('+previous_callback+' instead of '+this.id+') - likely the stack was not maintained in kernel.');
            }
        }
        var msg_id = this.get('msg_id');
        if (msg_id && kernel) {
            kernel.output_callback_overrides_push(msg_id, this.id);
        }
    },

});

var OutputView = widgets.DOMWidgetView.extend({

    initialize: function (parameters) {
        OutputView.__super__.initialize.apply(this, arguments);
    },

    render: function(){
        var that = this;
        var renderOutput = function(outputArea, events) {
            that.output_area = new outputArea.OutputArea({
                selector: that.el,
                // use default values for the output area config
                config: {OutputArea: {}},
                prompt_area: false,
                events: that.model.widget_manager.notebook.events,
                keyboard_manager: that.model.widget_manager.keyboard_manager });
            that.listenTo(that.model, 'new_message', function(msg) {
                that.output_area.handle_output(msg);
            }, that);
            that.listenTo(that.model, 'clear_output', function(msg) {
                that.output_area.handle_clear_output(msg);
                events.trigger('clear_output.OutputArea', {cell: {output_area: that.output_area}})
            });

            // Render initial contents from that.model._outputs
            that.model._outputs.forEach(function(msg) {
                that.output_area.handle_output(msg);
            }, that)
        }

        requirejs(["notebook/js/outputarea", "base/js/events"], renderOutput)
    },
});

module.exports = {
    OutputView: OutputView,
    OutputModel: OutputModel,
};
<commit_msg>Fix sync messages for the output widget.<commit_after>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
"use strict";

// This widget is strongly coupled to the notebook because of the outputarea
// dependency.
var widgets = require("jupyter-js-widgets");
var _ = require("underscore");

var OutputModel = widgets.DOMWidgetModel.extend({
    defaults: _.extend({}, widgets.DOMWidgetModel.prototype.defaults, {
        _model_name: "OutputModel",
        _view_name: "OutputView",
        msg_id: "",
    }),

    initialize: function(attributes, options) {
        OutputModel.__super__.initialize.apply(this, arguments);
        this.kernel = this.comm.kernel;
        this.listenTo(this, 'change:msg_id', this.reset_msg_id);
        if (this.kernel) {
            this.kernel.set_callbacks_for_msg(this.id, this.callbacks(), false);
        }
        this._outputs = [];
    },

    // make callbacks
    callbacks: function() {
        // Merge our callbacks with the base class callbacks.
        var cb = OutputModel.__super__.callbacks.apply(this, arguments);
        var iopub = cb.iopub || {};
        var iopubCallbacks = _.extend({}, iopub, {
            output: function(msg) {
                this.trigger('new_message', msg);
                this._outputs.push(msg);
                if (iopub.output) {
                    iopub.output.apply(this, arguments);
                }
            }.bind(this),
            clear_output: function(msg) {
                this.trigger('clear_output', msg);
                this._outputs = [];
                if (iopub.clear_output) {
                    iopub.clear_output.apply(this, arguments);
                }
            }.bind(this)
        });
        return _.extend({}, cb, {iopub: iopubCallbacks});
    },

    reset_msg_id: function() {
        var kernel = this.kernel;
        // Pop previous message id
        var prev_msg_id = this.previous('msg_id');
        if (prev_msg_id && kernel) {
            var previous_callback = kernel.output_callback_overrides_pop(prev_msg_id);
            if (previous_callback !== this.id) {
                console.error('Popped wrong message ('+previous_callback+' instead of '+this.id+') - likely the stack was not maintained in kernel.');
            }
        }
        var msg_id = this.get('msg_id');
        if (msg_id && kernel) {
            kernel.output_callback_overrides_push(msg_id, this.id);
        }
    },

});

var OutputView = widgets.DOMWidgetView.extend({
    render: function(){
        var that = this;
        var renderOutput = function(outputArea, events) {
            that.output_area = new outputArea.OutputArea({
                selector: that.el,
                // use default values for the output area config
                config: {OutputArea: {}},
                prompt_area: false,
                events: that.model.widget_manager.notebook.events,
                keyboard_manager: that.model.widget_manager.keyboard_manager });
            that.listenTo(that.model, 'new_message', function(msg) {
                that.output_area.handle_output(msg);
            }, that);
            that.listenTo(that.model, 'clear_output', function(msg) {
                that.output_area.handle_clear_output(msg);
                events.trigger('clear_output.OutputArea', {cell: {output_area: that.output_area}})
            });

            // Render initial contents from that.model._outputs
            that.model._outputs.forEach(function(msg) {
                that.output_area.handle_output(msg);
            }, that)
        }

        requirejs(["notebook/js/outputarea", "base/js/events"], renderOutput)
        OutputView.__super__.render.apply(this, arguments);
    },
});

module.exports = {
    OutputView: OutputView,
    OutputModel: OutputModel,
};
<|endoftext|>